2021-10-24 11:46:30 -04:00
# include "ssb.js.h"
2021-01-02 13:10:00 -05:00
2023-03-07 12:50:17 -05:00
# include "log.h"
2022-06-04 13:04:51 -04:00
# include "mem.h"
2021-08-22 15:41:27 -04:00
# include "ssb.db.h"
2021-01-02 13:10:00 -05:00
# include "ssb.h"
2021-11-03 18:15:46 -04:00
# include "util.js.h"
2021-01-02 13:10:00 -05:00
2023-05-21 17:36:51 -04:00
# include "sodium/crypto_box.h"
# include "sodium/crypto_scalarmult.h"
# include "sodium/crypto_scalarmult_curve25519.h"
2024-01-03 20:17:30 -05:00
# include "sodium/crypto_scalarmult_ed25519.h"
2023-05-21 17:36:51 -04:00
# include "sodium/crypto_secretbox.h"
# include "sodium/crypto_sign.h"
# include "sodium/randombytes.h"
# include "string.h"
# include "sqlite3.h"
# include "uv.h"
2021-01-02 13:10:00 -05:00
2023-02-26 14:51:54 -05:00
# include <assert.h>
2023-02-07 20:29:44 -05:00
# include <inttypes.h>
2023-08-25 14:22:09 -04:00
# if !defined(_countof)
# define _countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
# endif
2023-08-25 16:23:40 -04:00
static const int k_sql_async_timeout_ms = 60 * 1000 ;
2021-01-02 13:10:00 -05:00
static JSClassID _tf_ssb_classId ;
2023-10-20 10:37:24 -04:00
static JSValue _tf_ssb_appendMessageWithIdentity ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv ) ;
2021-12-22 14:57:34 -05:00
2024-06-12 20:47:48 -04:00
typedef struct _create_identity_t
2022-07-13 21:01:14 -04:00
{
2024-06-12 20:47:48 -04:00
char id [ k_id_base64_len ] ;
bool error_add ;
bool error_too_many ;
JSValue promise [ 2 ] ;
char user [ ] ;
} create_identity_t ;
static void _tf_ssb_create_identity_work ( tf_ssb_t * ssb , void * user_data )
{
create_identity_t * work = user_data ;
int count = tf_ssb_db_identity_get_count_for_user ( ssb , work - > user ) ;
if ( count < 16 )
2022-07-13 21:01:14 -04:00
{
2024-06-12 20:47:48 -04:00
char public [ k_id_base64_len - 1 ] ;
char private [ 512 ] ;
tf_ssb_generate_keys_buffer ( public , sizeof ( public ) , private , sizeof ( private ) ) ;
if ( tf_ssb_db_identity_add ( ssb , work - > user , public , private ) )
2022-07-13 21:01:14 -04:00
{
2024-06-12 20:47:48 -04:00
snprintf ( work - > id , sizeof ( work - > id ) , " @%s " , public ) ;
2022-07-13 21:01:14 -04:00
}
else
{
2024-06-12 20:47:48 -04:00
work - > error_add = true ;
2022-07-13 21:01:14 -04:00
}
2024-06-12 20:47:48 -04:00
}
else
{
work - > error_too_many = true ;
}
}
static void _tf_ssb_create_identity_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue result = JS_UNDEFINED ;
create_identity_t * work = user_data ;
if ( work - > error_too_many )
{
result = JS_ThrowInternalError ( context , " Too many identities for user. " ) ;
}
else if ( work - > error_add )
{
result = JS_ThrowInternalError ( context , " Unable to add identity. " ) ;
}
else
{
result = JS_NewString ( context , work - > id ) ;
}
JSValue error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
tf_free ( work ) ;
}
static JSValue _tf_ssb_createIdentity ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
JSValue result = JS_UNDEFINED ;
if ( ssb )
{
size_t length = 0 ;
const char * user = JS_ToCStringLen ( context , & length , argv [ 0 ] ) ;
create_identity_t * work = tf_malloc ( sizeof ( create_identity_t ) + length + 1 ) ;
* work = ( create_identity_t ) { 0 } ;
memcpy ( work - > user , user , length + 1 ) ;
2022-07-13 21:01:14 -04:00
JS_FreeCString ( context , user ) ;
2024-06-12 20:47:48 -04:00
result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_create_identity_work , _tf_ssb_create_identity_after_work , work ) ;
2022-07-13 21:01:14 -04:00
}
return result ;
}
2024-06-12 21:06:30 -04:00
typedef struct _add_identity_t
{
uint8_t key [ crypto_sign_SECRETKEYBYTES / 2 ] ;
bool added ;
JSValue promise [ 2 ] ;
char user [ ] ;
} add_identity_t ;
static void _tf_ssb_add_identity_work ( tf_ssb_t * ssb , void * user_data )
{
add_identity_t * work = user_data ;
uint8_t public_key [ crypto_sign_PUBLICKEYBYTES ] ;
unsigned char seed [ crypto_sign_SEEDBYTES ] ;
uint8_t secret_key [ crypto_sign_SECRETKEYBYTES ] = { 0 } ;
memcpy ( secret_key , work - > key , sizeof ( secret_key ) / 2 ) ;
if ( crypto_sign_ed25519_sk_to_seed ( seed , secret_key ) = = 0 & & crypto_sign_seed_keypair ( public_key , secret_key , seed ) = = 0 )
{
char public_key_b64 [ 512 ] ;
tf_base64_encode ( public_key , sizeof ( public_key ) , public_key_b64 , sizeof ( public_key_b64 ) ) ;
snprintf ( public_key_b64 + strlen ( public_key_b64 ) , sizeof ( public_key_b64 ) - strlen ( public_key_b64 ) , " .ed25519 " ) ;
uint8_t combined [ crypto_sign_SECRETKEYBYTES ] ;
memcpy ( combined , work - > key , sizeof ( work - > key ) ) ;
memcpy ( combined + sizeof ( work - > key ) , public_key , sizeof ( public_key ) ) ;
char combined_b64 [ 512 ] ;
tf_base64_encode ( combined , sizeof ( combined ) , combined_b64 , sizeof ( combined_b64 ) ) ;
snprintf ( combined_b64 + strlen ( combined_b64 ) , sizeof ( combined_b64 ) - strlen ( combined_b64 ) , " .ed25519 " ) ;
work - > added = tf_ssb_db_identity_add ( ssb , work - > user , public_key_b64 , combined_b64 ) ;
}
}
static void _tf_ssb_add_identity_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
add_identity_t * work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue result = work - > added ? JS_TRUE : JS_UNDEFINED ;
JSValue error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
tf_free ( work ) ;
}
2024-01-03 20:17:30 -05:00
static JSValue _tf_ssb_addIdentity ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
JSValue result = JS_UNDEFINED ;
if ( ssb )
{
2024-06-12 21:06:30 -04:00
size_t user_length = 0 ;
const char * user = JS_ToCStringLen ( context , & user_length , argv [ 0 ] ) ;
2024-01-03 20:17:30 -05:00
JSValue buffer = JS_UNDEFINED ;
size_t length = 0 ;
uint8_t * array = tf_util_try_get_array_buffer ( context , & length , argv [ 1 ] ) ;
if ( ! array )
{
size_t offset ;
size_t element_size ;
buffer = tf_util_try_get_typed_array_buffer ( context , argv [ 1 ] , & offset , & length , & element_size ) ;
if ( ! JS_IsException ( buffer ) )
{
array = tf_util_try_get_array_buffer ( context , & length , buffer ) ;
}
}
if ( array )
{
if ( length = = crypto_sign_SECRETKEYBYTES / 2 )
{
2024-06-12 21:06:30 -04:00
add_identity_t * work = tf_malloc ( sizeof ( add_identity_t ) + user_length + 1 ) ;
* work = ( add_identity_t ) { 0 } ;
memcpy ( work - > key , array , sizeof ( work - > key ) ) ;
memcpy ( work - > user , user , user_length + 1 ) ;
result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_add_identity_work , _tf_ssb_add_identity_after_work , work ) ;
2024-01-03 20:17:30 -05:00
}
else
{
2024-06-12 21:08:41 -04:00
result = JS_ThrowInternalError ( context , " Unexpected private key size: %d vs. %d \n " , ( int ) length , crypto_sign_SECRETKEYBYTES ) ;
2024-01-03 20:17:30 -05:00
}
}
else
{
2024-06-12 21:06:30 -04:00
result = JS_ThrowInternalError ( context , " Expected array argument. " ) ;
2024-01-03 20:17:30 -05:00
}
JS_FreeValue ( context , buffer ) ;
JS_FreeCString ( context , user ) ;
}
return result ;
}
2024-06-14 17:39:24 -04:00
typedef struct _delete_identity_t
{
char id [ k_id_base64_len ] ;
bool deleted ;
JSValue promise [ 2 ] ;
char user [ ] ;
} delete_identity_t ;
static void _tf_ssb_delete_identity_work ( tf_ssb_t * ssb , void * user_data )
{
delete_identity_t * work = user_data ;
work - > deleted = tf_ssb_db_identity_delete ( ssb , work - > user , work - > id ) ;
}
static void _tf_ssb_delete_identity_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
delete_identity_t * work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue result = work - > deleted ? JS_TRUE : JS_FALSE ;
JSValue error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
tf_free ( work ) ;
}
2024-01-06 14:22:49 -05:00
static JSValue _tf_ssb_deleteIdentity ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
JSValue result = JS_UNDEFINED ;
if ( ssb )
{
2024-06-14 17:39:24 -04:00
size_t user_length = 0 ;
const char * user = JS_ToCStringLen ( context , & user_length , argv [ 0 ] ) ;
2024-01-06 14:22:49 -05:00
const char * id = JS_ToCString ( context , argv [ 1 ] ) ;
if ( id & & user )
{
2024-06-14 17:39:24 -04:00
delete_identity_t * work = tf_malloc ( sizeof ( delete_identity_t ) + user_length + 1 ) ;
* work = ( delete_identity_t ) { 0 } ;
snprintf ( work - > id , sizeof ( work - > id ) , " %s " , * id = = ' @ ' ? id + 1 : id ) ;
memcpy ( work - > user , user , user_length + 1 ) ;
result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_delete_identity_work , _tf_ssb_delete_identity_after_work , work ) ;
2024-01-06 14:22:49 -05:00
}
JS_FreeCString ( context , id ) ;
JS_FreeCString ( context , user ) ;
}
return result ;
}
2024-06-16 16:22:59 -04:00
static JSValue _set_server_following_internal ( tf_ssb_t * ssb , JSValueConst this_val , const char * id , bool follow )
2023-10-20 10:37:24 -04:00
{
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue message = JS_NewObject ( context ) ;
JSValue server_user = JS_NewString ( context , " :admin " ) ;
char server_id_buffer [ k_id_base64_len ] = { 0 } ;
tf_ssb_whoami ( ssb , server_id_buffer , sizeof ( server_id_buffer ) ) ;
JSValue server_id = JS_NewString ( context , server_id_buffer ) ;
JS_SetPropertyStr ( context , message , " type " , JS_NewString ( context , " contact " ) ) ;
2024-06-16 16:22:59 -04:00
JS_SetPropertyStr ( context , message , " contact " , JS_NewString ( context , id ) ) ;
JS_SetPropertyStr ( context , message , " following " , JS_NewBool ( context , follow ) ) ;
2024-02-15 18:35:01 -05:00
JSValue args [ ] = {
2023-10-20 10:37:24 -04:00
server_user ,
server_id ,
message ,
} ;
JSValue result = _tf_ssb_appendMessageWithIdentity ( context , this_val , _countof ( args ) , args ) ;
JS_FreeValue ( context , server_id ) ;
JS_FreeValue ( context , server_user ) ;
JS_FreeValue ( context , message ) ;
return result ;
}
2024-06-16 16:22:59 -04:00
typedef struct _set_server_following_me_t
2023-10-20 10:37:24 -04:00
{
2024-06-16 16:22:59 -04:00
const char * user ;
const char * key ;
bool follow ;
JSValue this_val ;
JSValue promise [ 2 ] ;
bool error_does_not_own_key ;
bool append_message ;
} set_server_following_me_t ;
static void _tf_ssb_set_server_following_me_work ( tf_ssb_t * ssb , void * user_data )
{
set_server_following_me_t * work = user_data ;
if ( ! tf_ssb_db_identity_get_private_key ( ssb , work - > user , work - > key , NULL , 0 ) )
2023-10-20 10:37:24 -04:00
{
2024-06-16 16:22:59 -04:00
work - > error_does_not_own_key = true ;
}
else
{
char server_id [ k_id_base64_len ] = { 0 } ;
2023-10-20 10:37:24 -04:00
tf_ssb_whoami ( ssb , server_id , sizeof ( server_id ) ) ;
2024-06-16 16:22:59 -04:00
const char * server_id_ptr = server_id ;
const char * * current_following = tf_ssb_db_following_deep_ids ( ssb , & server_id_ptr , 1 , 1 ) ;
bool is_following = false ;
for ( const char * * it = current_following ; * it ; it + + )
2023-10-20 10:37:24 -04:00
{
2024-06-16 16:22:59 -04:00
if ( strcmp ( work - > key , * it ) = = 0 )
2023-10-20 10:37:24 -04:00
{
2024-06-16 16:22:59 -04:00
is_following = true ;
break ;
2023-10-20 10:37:24 -04:00
}
}
2024-06-16 16:22:59 -04:00
tf_free ( current_following ) ;
work - > append_message = ( work - > follow & & ! is_following ) | | ( ! work - > follow & & is_following ) ;
}
}
static void _tf_ssb_set_server_following_me_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
set_server_following_me_t * work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue result = JS_UNDEFINED ;
if ( work - > error_does_not_own_key )
{
result = JS_ThrowInternalError ( context , " User %s does not own key %s. " , work - > user , work - > key ) ;
}
else if ( work - > append_message )
{
result = _set_server_following_internal ( ssb , work - > this_val , work - > key , work - > follow ) ;
}
JS_FreeCString ( context , work - > key ) ;
JS_FreeCString ( context , work - > user ) ;
JSValue error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
JS_FreeValue ( context , work - > this_val ) ;
tf_free ( work ) ;
}
static JSValue _tf_ssb_set_server_following_me ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
JSValue result = JS_UNDEFINED ;
if ( ssb )
{
set_server_following_me_t * work = tf_malloc ( sizeof ( set_server_following_me_t ) ) ;
2024-06-16 17:08:10 -04:00
* work = ( set_server_following_me_t ) {
2024-06-16 16:22:59 -04:00
. user = JS_ToCString ( context , argv [ 0 ] ) ,
. key = JS_ToCString ( context , argv [ 1 ] ) ,
. follow = JS_ToBool ( context , argv [ 2 ] ) ,
. this_val = JS_DupValue ( context , this_val ) ,
} ;
result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_set_server_following_me_work , _tf_ssb_set_server_following_me_after_work , work ) ;
2023-10-20 10:37:24 -04:00
}
return result ;
}
2022-07-13 21:01:14 -04:00
typedef struct _identities_visit_t
{
JSContext * context ;
2024-06-10 17:18:29 -04:00
JSValue promise [ 2 ] ;
const char * * identities ;
2022-07-13 21:01:14 -04:00
int count ;
2024-06-10 17:18:29 -04:00
char user [ ] ;
2022-07-13 21:01:14 -04:00
} identities_visit_t ;
2024-06-10 17:18:29 -04:00
static void _tf_ssb_getIdentities_visit ( const char * identity , void * user_data )
2022-07-13 21:01:14 -04:00
{
2024-06-10 17:18:29 -04:00
identities_visit_t * work = user_data ;
work - > identities = tf_resize_vec ( work - > identities , ( work - > count + 1 ) * sizeof ( const char * ) ) ;
2022-07-31 15:01:08 -04:00
char id [ k_id_base64_len ] ;
snprintf ( id , sizeof ( id ) , " @%s " , identity ) ;
2024-06-10 17:18:29 -04:00
work - > identities [ work - > count + + ] = tf_strdup ( id ) ;
2022-07-13 21:01:14 -04:00
}
2024-06-10 17:18:29 -04:00
static void _tf_ssb_get_identities_work ( tf_ssb_t * ssb , void * user_data )
{
identities_visit_t * work = user_data ;
2024-09-17 12:47:28 -04:00
if ( tf_ssb_db_user_has_permission ( ssb , work - > user , " administration " ) )
{
char id [ k_id_base64_len ] = " " ;
if ( tf_ssb_whoami ( ssb , id , sizeof ( id ) ) )
{
_tf_ssb_getIdentities_visit ( * id = = ' @ ' ? id + 1 : id , work ) ;
}
}
2024-06-10 17:18:29 -04:00
tf_ssb_db_identity_visit ( ssb , work - > user , _tf_ssb_getIdentities_visit , user_data ) ;
}
static void _tf_ssb_get_all_identities_work ( tf_ssb_t * ssb , void * user_data )
{
tf_ssb_db_identity_visit_all ( ssb , _tf_ssb_getIdentities_visit , user_data ) ;
}
static void _tf_ssb_get_identities_after_work ( tf_ssb_t * ssb , int status , void * user_data )
2022-07-13 21:01:14 -04:00
{
2024-06-10 17:18:29 -04:00
identities_visit_t * work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
2022-07-13 21:01:14 -04:00
JSValue result = JS_NewArray ( context ) ;
2024-06-10 17:18:29 -04:00
for ( int i = 0 ; i < work - > count ; i + + )
{
JS_SetPropertyUint32 ( context , result , i , JS_NewString ( context , work - > identities [ i ] ) ) ;
tf_free ( ( void * ) work - > identities [ i ] ) ;
}
tf_free ( work - > identities ) ;
JSValue error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
tf_free ( work ) ;
}
static JSValue _tf_ssb_getIdentities ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_UNDEFINED ;
2022-07-13 21:01:14 -04:00
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
if ( ssb )
{
2024-06-10 17:18:29 -04:00
size_t user_length = 0 ;
const char * user = JS_ToCStringLen ( context , & user_length , argv [ 0 ] ) ;
identities_visit_t * work = tf_malloc ( sizeof ( identities_visit_t ) + user_length + 1 ) ;
2024-06-10 20:22:28 -04:00
* work = ( identities_visit_t ) {
2022-07-13 21:01:14 -04:00
. context = context ,
} ;
2024-06-10 17:18:29 -04:00
memcpy ( work - > user , user , user_length + 1 ) ;
2022-07-13 21:01:14 -04:00
JS_FreeCString ( context , user ) ;
2024-06-10 17:18:29 -04:00
result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_get_identities_work , _tf_ssb_get_identities_after_work , work ) ;
2022-07-13 21:01:14 -04:00
}
return result ;
}
2024-06-16 08:07:02 -04:00
typedef struct _get_private_key_t
2024-01-03 19:21:15 -05:00
{
2024-06-16 08:07:02 -04:00
JSContext * context ;
JSValue promise [ 2 ] ;
char id [ k_id_base64_len ] ;
2024-01-03 19:21:15 -05:00
uint8_t private_key [ crypto_sign_SECRETKEYBYTES ] ;
2024-06-16 08:07:02 -04:00
bool got_private_key ;
char user [ ] ;
} get_private_key_t ;
static void _tf_ssb_get_private_key_work ( tf_ssb_t * ssb , void * user_data )
{
get_private_key_t * work = user_data ;
work - > got_private_key = tf_ssb_db_identity_get_private_key ( ssb , work - > user , work - > id , work - > private_key , sizeof ( work - > private_key ) ) ;
2024-09-17 12:47:28 -04:00
if ( ! work - > got_private_key & & tf_ssb_db_user_has_permission ( ssb , work - > user , " administration " ) )
{
work - > got_private_key = tf_ssb_db_identity_get_private_key ( ssb , " :admin " , work - > id , work - > private_key , sizeof ( work - > private_key ) ) ;
}
2024-06-16 08:07:02 -04:00
}
static void _tf_ssb_get_private_key_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
get_private_key_t * work = user_data ;
JSValue result = JS_UNDEFINED ;
JSContext * context = work - > context ;
if ( work - > got_private_key )
2024-01-03 19:21:15 -05:00
{
2024-06-16 08:07:02 -04:00
result = tf_util_new_uint8_array ( context , work - > private_key , sizeof ( work - > private_key ) / 2 ) ;
2024-01-03 19:21:15 -05:00
}
2024-06-16 08:07:02 -04:00
JSValue error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
tf_free ( work ) ;
}
static JSValue _tf_ssb_getPrivateKey ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
size_t user_length = 0 ;
const char * user = JS_ToCStringLen ( context , & user_length , argv [ 0 ] ) ;
const char * id = JS_ToCString ( context , argv [ 1 ] ) ;
get_private_key_t * work = tf_malloc ( sizeof ( get_private_key_t ) + user_length + 1 ) ;
* work = ( get_private_key_t ) { . context = context } ;
memcpy ( work - > user , user , user_length + 1 ) ;
snprintf ( work - > id , sizeof ( work - > id ) , " %s " , id ) ;
JSValue result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_get_private_key_work , _tf_ssb_get_private_key_after_work , work ) ;
2024-01-03 19:21:15 -05:00
JS_FreeCString ( context , user ) ;
JS_FreeCString ( context , id ) ;
return result ;
}
2023-10-20 08:55:05 -04:00
static JSValue _tf_ssb_getServerIdentity ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
2024-03-03 12:20:03 -05:00
JSValue result = JS_UNDEFINED ;
2023-10-20 08:55:05 -04:00
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
if ( ssb )
{
char id [ k_id_base64_len ] = { 0 } ;
if ( tf_ssb_whoami ( ssb , id , sizeof ( id ) ) )
{
result = JS_NewString ( context , id ) ;
}
}
return result ;
}
2022-07-31 15:01:08 -04:00
static JSValue _tf_ssb_getAllIdentities ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
2024-06-10 17:18:29 -04:00
JSValue result = JS_UNDEFINED ;
2022-07-31 15:01:08 -04:00
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
if ( ssb )
{
2024-06-10 17:18:29 -04:00
identities_visit_t * work = tf_malloc ( sizeof ( identities_visit_t ) ) ;
2024-06-10 20:22:28 -04:00
* work = ( identities_visit_t ) {
2022-07-31 15:01:08 -04:00
. context = context ,
} ;
2024-06-10 17:18:29 -04:00
result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_get_all_identities_work , _tf_ssb_get_identities_after_work , work ) ;
2022-07-31 15:01:08 -04:00
}
return result ;
}
2024-05-05 12:55:32 -04:00
typedef struct _active_identity_work_t
{
JSContext * context ;
const char * name ;
const char * package_owner ;
const char * package_name ;
char identity [ k_id_base64_len ] ;
int result ;
JSValue promise [ 2 ] ;
} active_identity_work_t ;
static void _tf_ssb_getActiveIdentity_visit ( const char * identity , void * user_data )
{
active_identity_work_t * request = user_data ;
if ( ! * request - > identity )
{
snprintf ( request - > identity , sizeof ( request - > identity ) , " %s " , identity ) ;
}
}
2024-05-08 21:00:37 -04:00
static void _tf_ssb_getActiveIdentity_work ( tf_ssb_t * ssb , void * user_data )
2024-05-05 12:55:32 -04:00
{
2024-05-08 21:00:37 -04:00
active_identity_work_t * request = user_data ;
sqlite3 * db = tf_ssb_acquire_db_reader ( ssb ) ;
2024-05-05 13:48:22 -04:00
tf_ssb_db_identity_get_active ( db , request - > name , request - > package_owner , request - > package_name , request - > identity , sizeof ( request - > identity ) ) ;
2024-05-08 21:00:37 -04:00
tf_ssb_release_db_reader ( ssb , db ) ;
2024-05-05 12:55:32 -04:00
if ( ! * request - > identity )
{
2024-05-08 21:00:37 -04:00
tf_ssb_db_identity_visit ( ssb , request - > name , _tf_ssb_getActiveIdentity_visit , request ) ;
2024-05-05 12:55:32 -04:00
}
}
2024-05-08 21:00:37 -04:00
static void _tf_ssb_getActiveIdentity_after_work ( tf_ssb_t * ssb , int status , void * user_data )
2024-05-05 12:55:32 -04:00
{
2024-05-08 21:00:37 -04:00
active_identity_work_t * request = user_data ;
2024-05-05 12:55:32 -04:00
JSContext * context = request - > context ;
if ( request - > result = = 0 )
{
JSValue identity = JS_NewString ( context , request - > identity ) ;
JSValue error = JS_Call ( context , request - > promise [ 0 ] , JS_UNDEFINED , 1 , & identity ) ;
JS_FreeValue ( context , identity ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
}
else
{
JSValue error = JS_Call ( context , request - > promise [ 1 ] , JS_UNDEFINED , 0 , NULL ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
}
JS_FreeValue ( context , request - > promise [ 0 ] ) ;
JS_FreeValue ( context , request - > promise [ 1 ] ) ;
tf_free ( ( void * ) request - > name ) ;
tf_free ( ( void * ) request - > package_owner ) ;
tf_free ( ( void * ) request - > package_name ) ;
tf_free ( request ) ;
}
static JSValue _tf_ssb_getActiveIdentity ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
2024-05-08 21:00:37 -04:00
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
2024-05-05 12:55:32 -04:00
const char * name = JS_ToCString ( context , argv [ 0 ] ) ;
const char * package_owner = JS_ToCString ( context , argv [ 1 ] ) ;
const char * package_name = JS_ToCString ( context , argv [ 2 ] ) ;
active_identity_work_t * work = tf_malloc ( sizeof ( active_identity_work_t ) ) ;
2024-05-08 12:20:57 -04:00
* work = ( active_identity_work_t ) {
2024-05-05 12:55:32 -04:00
. context = context ,
. name = tf_strdup ( name ) ,
. package_owner = tf_strdup ( package_owner ) ,
. package_name = tf_strdup ( package_name ) ,
} ;
JSValue result = JS_NewPromiseCapability ( context , work - > promise ) ;
JS_FreeCString ( context , name ) ;
JS_FreeCString ( context , package_owner ) ;
JS_FreeCString ( context , package_name ) ;
2024-05-08 21:00:37 -04:00
tf_ssb_run_work ( ssb , _tf_ssb_getActiveIdentity_work , _tf_ssb_getActiveIdentity_after_work , work ) ;
2024-05-05 12:55:32 -04:00
return result ;
}
2024-05-05 13:48:22 -04:00
typedef struct _identity_info_work_t
{
JSContext * context ;
const char * name ;
const char * package_owner ;
const char * package_name ;
int count ;
char * * identities ;
char * * names ;
int result ;
char active_identity [ k_id_base64_len ] ;
JSValue promise [ 2 ] ;
} identity_info_work_t ;
static void _tf_ssb_getIdentityInfo_visit ( const char * identity , void * data )
{
identity_info_work_t * request = data ;
request - > identities = tf_resize_vec ( request - > identities , ( request - > count + 1 ) * sizeof ( char * ) ) ;
request - > names = tf_resize_vec ( request - > names , ( request - > count + 1 ) * sizeof ( char * ) ) ;
2024-05-11 10:23:07 -04:00
char buffer [ k_id_base64_len ] ;
snprintf ( buffer , sizeof ( buffer ) , " @%s " , identity ) ;
request - > identities [ request - > count ] = tf_strdup ( buffer ) ;
2024-05-05 13:48:22 -04:00
request - > names [ request - > count ] = NULL ;
2024-05-08 12:20:57 -04:00
request - > count + + ;
2024-05-05 13:48:22 -04:00
}
2024-05-08 21:00:37 -04:00
static void _tf_ssb_getIdentityInfo_work ( tf_ssb_t * ssb , void * user_data )
2024-05-05 13:48:22 -04:00
{
2024-05-08 21:00:37 -04:00
identity_info_work_t * request = user_data ;
2024-09-17 12:47:28 -04:00
if ( tf_ssb_db_user_has_permission ( ssb , request - > name , " administration " ) )
{
char id [ k_id_base64_len ] = " " ;
if ( tf_ssb_whoami ( ssb , id , sizeof ( id ) ) )
{
_tf_ssb_getIdentityInfo_visit ( * id = = ' @ ' ? id + 1 : id , request ) ;
}
}
2024-05-08 21:00:37 -04:00
tf_ssb_db_identity_visit ( ssb , request - > name , _tf_ssb_getIdentityInfo_visit , request ) ;
2024-05-05 13:48:22 -04:00
2024-05-08 21:00:37 -04:00
sqlite3 * db = tf_ssb_acquire_db_reader ( ssb ) ;
2024-05-05 13:48:22 -04:00
sqlite3_stmt * statement = NULL ;
request - > result = sqlite3_prepare ( db ,
" SELECT author, name FROM ( "
" SELECT "
" messages.author, "
" RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, "
" messages.content ->> 'name' AS name "
" FROM messages "
2024-05-11 10:23:07 -04:00
" JOIN identities ON messages.author = ('@' || identities.public_key) "
" WHERE identities.user = ? AND json_extract(messages.content, '$.type') = 'about' AND content ->> 'about' = messages.author AND name IS NOT NULL) "
2024-05-08 12:20:57 -04:00
" WHERE author_rank = 1 " ,
- 1 , & statement , NULL ) ;
2024-05-05 13:48:22 -04:00
if ( request - > result = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , request - > name , - 1 , NULL ) = = SQLITE_OK )
{
int r = SQLITE_OK ;
2024-05-11 10:23:07 -04:00
while ( ( r = sqlite3_step ( statement ) ) = = SQLITE_ROW )
2024-05-05 13:48:22 -04:00
{
2024-05-11 10:53:21 -04:00
const char * identity = ( const char * ) sqlite3_column_text ( statement , 0 ) ;
const char * name = ( const char * ) sqlite3_column_text ( statement , 1 ) ;
2024-05-05 13:48:22 -04:00
for ( int i = 0 ; i < request - > count ; i + + )
{
2024-05-11 10:53:21 -04:00
if ( ! request - > names [ i ] & & strcmp ( request - > identities [ i ] , identity ) = = 0 )
2024-05-05 13:48:22 -04:00
{
request - > names [ i ] = tf_strdup ( name ) ;
2024-05-11 10:53:21 -04:00
break ;
2024-05-05 13:48:22 -04:00
}
}
}
}
sqlite3_finalize ( statement ) ;
}
2024-05-11 10:23:07 -04:00
else
{
tf_printf ( " prepare failed: %s. \n " , sqlite3_errmsg ( db ) ) ;
}
2024-05-05 13:48:22 -04:00
tf_ssb_db_identity_get_active ( db , request - > name , request - > package_owner , request - > package_name , request - > active_identity , sizeof ( request - > active_identity ) ) ;
if ( ! * request - > active_identity & & request - > count )
{
snprintf ( request - > active_identity , sizeof ( request - > active_identity ) , " %s " , request - > identities [ 0 ] ) ;
}
2024-05-08 21:00:37 -04:00
tf_ssb_release_db_reader ( ssb , db ) ;
2024-05-05 13:48:22 -04:00
}
2024-05-08 21:00:37 -04:00
static void _tf_ssb_getIdentityInfo_after_work ( tf_ssb_t * ssb , int status , void * user_data )
2024-05-05 13:48:22 -04:00
{
2024-05-08 21:00:37 -04:00
identity_info_work_t * request = user_data ;
2024-05-05 13:48:22 -04:00
JSContext * context = request - > context ;
JSValue result = JS_NewObject ( context ) ;
JSValue identities = JS_NewArray ( context ) ;
for ( int i = 0 ; i < request - > count ; i + + )
{
JS_SetPropertyUint32 ( context , identities , i , JS_NewString ( context , request - > identities [ i ] ) ) ;
}
JS_SetPropertyStr ( context , result , " identities " , identities ) ;
JSValue names = JS_NewObject ( context ) ;
for ( int i = 0 ; i < request - > count ; i + + )
{
JS_SetPropertyStr ( context , names , request - > identities [ i ] , JS_NewString ( context , request - > names [ i ] ? request - > names [ i ] : request - > identities [ i ] ) ) ;
}
JS_SetPropertyStr ( context , result , " names " , names ) ;
JS_SetPropertyStr ( context , result , " identity " , JS_NewString ( context , request - > active_identity ) ) ;
JSValue error = JS_Call ( context , request - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , result ) ;
JS_FreeValue ( context , request - > promise [ 0 ] ) ;
JS_FreeValue ( context , request - > promise [ 1 ] ) ;
for ( int i = 0 ; i < request - > count ; i + + )
{
tf_free ( request - > identities [ i ] ) ;
tf_free ( request - > names [ i ] ) ;
}
tf_free ( request - > identities ) ;
tf_free ( request - > names ) ;
tf_free ( ( void * ) request - > name ) ;
tf_free ( ( void * ) request - > package_owner ) ;
tf_free ( ( void * ) request - > package_name ) ;
tf_free ( request ) ;
}
static JSValue _tf_ssb_getIdentityInfo ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
2024-05-08 21:00:37 -04:00
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
2024-05-05 13:48:22 -04:00
const char * name = JS_ToCString ( context , argv [ 0 ] ) ;
const char * package_owner = JS_ToCString ( context , argv [ 1 ] ) ;
const char * package_name = JS_ToCString ( context , argv [ 2 ] ) ;
identity_info_work_t * work = tf_malloc ( sizeof ( identity_info_work_t ) ) ;
2024-05-08 12:20:57 -04:00
* work = ( identity_info_work_t ) {
2024-05-05 13:48:22 -04:00
. context = context ,
. name = tf_strdup ( name ) ,
. package_owner = tf_strdup ( package_owner ) ,
. package_name = tf_strdup ( package_name ) ,
} ;
JSValue result = JS_NewPromiseCapability ( context , work - > promise ) ;
JS_FreeCString ( context , name ) ;
JS_FreeCString ( context , package_owner ) ;
JS_FreeCString ( context , package_name ) ;
2024-05-08 21:00:37 -04:00
tf_ssb_run_work ( ssb , _tf_ssb_getIdentityInfo_work , _tf_ssb_getIdentityInfo_after_work , work ) ;
2024-05-05 13:48:22 -04:00
return result ;
}
2023-07-19 21:02:50 -04:00
typedef struct _append_message_t
{
2024-06-16 07:51:06 -04:00
char id [ k_id_base64_len ] ;
uint8_t private_key [ crypto_sign_SECRETKEYBYTES ] ;
bool got_private_key ;
char previous_id [ 512 ] ;
int64_t previous_sequence ;
2023-07-19 21:02:50 -04:00
JSContext * context ;
JSValue promise [ 2 ] ;
2024-06-16 07:51:06 -04:00
JSValue message ;
char user [ ] ;
2023-07-19 21:02:50 -04:00
} append_message_t ;
static void _tf_ssb_appendMessage_finish ( append_message_t * async , bool success , JSValue result )
2022-07-13 21:01:14 -04:00
{
2023-07-19 21:02:50 -04:00
JSValue error = JS_Call ( async - > context , success ? async - > promise [ 0 ] : async - > promise [ 1 ] , JS_UNDEFINED , 1 , & result ) ;
tf_util_report_error ( async - > context , error ) ;
JS_FreeValue ( async - > context , error ) ;
2024-06-16 07:51:06 -04:00
JS_FreeValue ( async - > context , async - > message ) ;
2023-07-19 21:02:50 -04:00
JS_FreeValue ( async - > context , async - > promise [ 0 ] ) ;
JS_FreeValue ( async - > context , async - > promise [ 1 ] ) ;
tf_free ( async ) ;
}
static void _tf_ssb_appendMessageWithIdentity_callback ( const char * id , bool verified , bool is_new , void * user_data )
{
append_message_t * async = user_data ;
2022-07-13 21:01:14 -04:00
JSValue result = JS_UNDEFINED ;
2023-07-19 21:02:50 -04:00
if ( verified )
{
result = is_new ? JS_TRUE : JS_FALSE ;
}
_tf_ssb_appendMessage_finish ( async , verified , result ) ;
}
2024-06-16 07:51:06 -04:00
static void _tf_ssb_append_message_with_identity_get_key_work ( tf_ssb_t * ssb , void * user_data )
2023-07-19 21:02:50 -04:00
{
2024-06-16 07:51:06 -04:00
append_message_t * work = user_data ;
work - > got_private_key = tf_ssb_db_identity_get_private_key ( ssb , work - > user , work - > id , work - > private_key , sizeof ( work - > private_key ) ) ;
2024-09-17 12:47:28 -04:00
if ( ! work - > got_private_key & & tf_ssb_db_user_has_permission ( ssb , work - > user , " administration " ) )
{
work - > got_private_key = tf_ssb_db_identity_get_private_key ( ssb , " :admin " , work - > id , work - > private_key , sizeof ( work - > private_key ) ) ;
}
2024-06-16 07:51:06 -04:00
tf_ssb_db_get_latest_message_by_author ( ssb , work - > id , & work - > previous_sequence , work - > previous_id , sizeof ( work - > previous_id ) ) ;
}
2023-07-19 21:02:50 -04:00
2024-06-16 07:51:06 -04:00
static void _tf_ssb_append_message_with_identity_get_key_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
append_message_t * work = user_data ;
if ( work - > got_private_key )
2022-07-13 21:01:14 -04:00
{
2024-06-16 07:51:06 -04:00
JSValue signed_message = tf_ssb_sign_message ( ssb , work - > id , work - > private_key , work - > message , work - > previous_id , work - > previous_sequence ) ;
tf_ssb_verify_strip_and_store_message ( ssb , signed_message , _tf_ssb_appendMessageWithIdentity_callback , work ) ;
JS_FreeValue ( work - > context , signed_message ) ;
2022-07-13 21:01:14 -04:00
}
2023-07-19 21:02:50 -04:00
else
{
2024-06-16 07:51:06 -04:00
_tf_ssb_appendMessage_finish ( work , false , JS_ThrowInternalError ( work - > context , " Unable to get private key for user %s with identity %s. " , work - > user , work - > id ) ) ;
}
}
static JSValue _tf_ssb_appendMessageWithIdentity ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
if ( ! ssb )
{
return JS_ThrowInternalError ( context , " No SSB instance. " ) ;
2023-07-19 21:02:50 -04:00
}
2024-06-16 07:51:06 -04:00
size_t user_length = 0 ;
const char * user = JS_ToCStringLen ( context , & user_length , argv [ 0 ] ) ;
const char * id = JS_ToCString ( context , argv [ 1 ] ) ;
append_message_t * work = tf_malloc ( sizeof ( append_message_t ) + user_length + 1 ) ;
* work = ( append_message_t ) { . context = context , . message = JS_DupValue ( context , argv [ 2 ] ) } ;
memcpy ( work - > user , user , user_length + 1 ) ;
snprintf ( work - > id , sizeof ( work - > id ) , " %s " , id ) ;
JS_FreeCString ( context , id ) ;
JS_FreeCString ( context , user ) ;
JSValue result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_append_message_with_identity_get_key_work , _tf_ssb_append_message_with_identity_get_key_after_work , work ) ;
2022-07-13 21:01:14 -04:00
return result ;
}
2024-06-10 11:45:20 -04:00
typedef struct _blob_get_t
{
JSContext * context ;
JSValue promise [ 2 ] ;
} blob_get_t ;
static void _tf_ssb_blobGet_callback ( bool found , const uint8_t * data , size_t size , void * user_data )
{
blob_get_t * get = user_data ;
JSValue result = JS_UNDEFINED ;
if ( found )
{
result = JS_NewArrayBufferCopy ( get - > context , data , size ) ;
}
JSValue error = JS_Call ( get - > context , get - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( get - > context , result ) ;
JS_FreeValue ( get - > context , get - > promise [ 0 ] ) ;
JS_FreeValue ( get - > context , get - > promise [ 1 ] ) ;
tf_util_report_error ( get - > context , error ) ;
JS_FreeValue ( get - > context , error ) ;
tf_free ( get ) ;
}
2021-01-02 13:10:00 -05:00
static JSValue _tf_ssb_blobGet ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_NULL ;
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
2021-10-10 17:51:38 -04:00
if ( ssb )
{
2021-01-02 13:10:00 -05:00
const char * id = JS_ToCString ( context , argv [ 0 ] ) ;
2024-06-10 11:45:20 -04:00
blob_get_t * get = tf_malloc ( sizeof ( blob_get_t ) ) ;
* get = ( blob_get_t ) { . context = context } ;
result = JS_NewPromiseCapability ( context , get - > promise ) ;
tf_ssb_db_blob_get_async ( ssb , id , _tf_ssb_blobGet_callback , get ) ;
2021-09-06 16:54:44 -04:00
JS_FreeCString ( context , id ) ;
2021-01-02 13:10:00 -05:00
}
return result ;
}
2023-07-18 19:46:15 -04:00
typedef struct _blob_store_t
{
JSContext * context ;
JSValue promise [ 2 ] ;
uint8_t * buffer ;
} blob_store_t ;
2024-02-10 11:50:00 -05:00
static void _tf_ssb_blob_store_complete ( blob_store_t * store , const char * id )
2023-07-18 19:46:15 -04:00
{
2023-07-21 21:33:28 -04:00
JSValue result = JS_UNDEFINED ;
if ( id )
{
JSValue id_value = JS_NewString ( store - > context , id ) ;
2024-01-07 21:30:08 -05:00
JSValue result = JS_Call ( store - > context , store - > promise [ 0 ] , JS_UNDEFINED , 1 , & id_value ) ;
2024-03-18 12:46:12 -04:00
JS_FreeValue ( store - > context , id_value ) ;
2024-01-07 21:30:08 -05:00
tf_util_report_error ( store - > context , result ) ;
JS_FreeValue ( store - > context , result ) ;
2023-07-21 21:33:28 -04:00
}
else
{
2024-01-07 21:30:08 -05:00
JSValue result = JS_Call ( store - > context , store - > promise [ 1 ] , JS_UNDEFINED , 0 , NULL ) ;
tf_util_report_error ( store - > context , result ) ;
JS_FreeValue ( store - > context , result ) ;
2023-07-21 21:33:28 -04:00
}
2023-07-18 19:46:15 -04:00
tf_util_report_error ( store - > context , result ) ;
JS_FreeValue ( store - > context , result ) ;
JS_FreeValue ( store - > context , store - > promise [ 0 ] ) ;
JS_FreeValue ( store - > context , store - > promise [ 1 ] ) ;
tf_free ( store - > buffer ) ;
tf_free ( store ) ;
}
2024-02-10 11:50:00 -05:00
static void _tf_ssb_blob_store_callback ( const char * id , bool is_new , void * user_data )
2023-07-18 19:46:15 -04:00
{
blob_store_t * store = user_data ;
_tf_ssb_blob_store_complete ( store , id ) ;
}
2021-01-02 13:10:00 -05:00
static JSValue _tf_ssb_blobStore ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
2023-07-18 19:46:15 -04:00
blob_store_t * store = tf_malloc ( sizeof ( blob_store_t ) ) ;
2023-07-21 21:24:58 -04:00
* store = ( blob_store_t ) { . context = context } ;
2023-07-18 19:46:15 -04:00
JSValue result = JS_NewPromiseCapability ( context , store - > promise ) ;
2021-01-02 13:10:00 -05:00
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
2021-10-10 17:51:38 -04:00
if ( ssb )
{
2021-01-02 13:10:00 -05:00
uint8_t * blob = NULL ;
size_t size = 0 ;
2021-10-10 17:51:38 -04:00
if ( JS_IsString ( argv [ 0 ] ) )
{
2021-01-02 13:10:00 -05:00
const char * text = JS_ToCStringLen ( context , & size , argv [ 0 ] ) ;
2023-07-18 19:46:15 -04:00
store - > buffer = tf_malloc ( size ) ;
memcpy ( store - > buffer , text , size ) ;
tf_ssb_db_blob_store_async ( ssb , store - > buffer , size , _tf_ssb_blob_store_callback , store ) ;
2021-01-02 13:10:00 -05:00
JS_FreeCString ( context , text ) ;
2021-10-10 17:51:38 -04:00
}
2021-11-03 18:15:46 -04:00
else if ( ( blob = tf_util_try_get_array_buffer ( context , & size , argv [ 0 ] ) ) ! = 0 )
2021-10-10 17:51:38 -04:00
{
2023-07-18 19:46:15 -04:00
store - > buffer = tf_malloc ( size ) ;
memcpy ( store - > buffer , blob , size ) ;
tf_ssb_db_blob_store_async ( ssb , store - > buffer , size , _tf_ssb_blob_store_callback , store ) ;
2021-01-02 13:10:00 -05:00
}
2021-10-31 17:15:18 -04:00
else
{
size_t offset ;
size_t element_size ;
2021-11-03 18:15:46 -04:00
JSValue buffer = tf_util_try_get_typed_array_buffer ( context , argv [ 0 ] , & offset , & size , & element_size ) ;
2021-10-31 17:15:18 -04:00
if ( ! JS_IsException ( buffer ) )
{
2021-11-03 18:15:46 -04:00
blob = tf_util_try_get_array_buffer ( context , & size , buffer ) ;
2021-10-31 17:15:18 -04:00
if ( blob )
{
2023-07-18 19:46:15 -04:00
store - > buffer = tf_malloc ( size ) ;
memcpy ( store - > buffer , blob , size ) ;
tf_ssb_db_blob_store_async ( ssb , store - > buffer , size , _tf_ssb_blob_store_callback , store ) ;
2021-10-31 17:15:18 -04:00
}
2023-07-18 19:46:15 -04:00
else
{
_tf_ssb_blob_store_complete ( store , NULL ) ;
}
}
else
{
_tf_ssb_blob_store_complete ( store , NULL ) ;
2021-10-31 17:15:18 -04:00
}
JS_FreeValue ( context , buffer ) ;
}
2021-01-02 13:10:00 -05:00
}
return result ;
}
static JSValue _tf_ssb_connections ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_NULL ;
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
2021-10-10 17:51:38 -04:00
if ( ssb )
{
2023-08-25 14:22:09 -04:00
tf_ssb_connection_t * connections [ 32 ] ;
int count = tf_ssb_get_connections ( ssb , connections , _countof ( connections ) ) ;
result = JS_NewArray ( context ) ;
for ( int i = 0 ; i < count ; i + + )
2021-10-10 17:51:38 -04:00
{
2023-08-25 14:22:09 -04:00
char id [ k_id_base64_len ] = { 0 } ;
tf_ssb_connection_t * connection = connections [ i ] ;
JSValue object = JS_NewObject ( context ) ;
tf_ssb_connection_get_id ( connection , id , sizeof ( id ) ) ;
JS_SetPropertyStr ( context , object , " id " , JS_NewString ( context , id ) ) ;
JS_SetPropertyStr ( context , object , " host " , JS_NewString ( context , tf_ssb_connection_get_host ( connection ) ) ) ;
JS_SetPropertyStr ( context , object , " port " , JS_NewInt32 ( context , tf_ssb_connection_get_port ( connection ) ) ) ;
tf_ssb_connection_t * tunnel = tf_ssb_connection_get_tunnel ( connection ) ;
if ( tunnel )
2021-10-10 17:51:38 -04:00
{
2023-08-25 14:22:09 -04:00
int tunnel_index = - 1 ;
for ( int j = 0 ; j < count ; j + + )
{
if ( connections [ j ] = = tunnel )
{
tunnel_index = j ;
break ;
}
}
JS_SetPropertyStr ( context , object , " tunnel " , JS_NewInt32 ( context , tunnel_index ) ) ;
2021-01-02 13:10:00 -05:00
}
2024-05-02 19:02:23 -04:00
JS_SetPropertyStr ( context , object , " requests " , tf_ssb_connection_requests_to_object ( connection ) ) ;
2023-08-25 14:22:09 -04:00
JS_SetPropertyUint32 ( context , result , i , object ) ;
2021-01-02 13:10:00 -05:00
}
}
return result ;
}
2024-06-16 15:29:59 -04:00
typedef struct _stored_connections_t
{
int count ;
tf_ssb_db_stored_connection_t * connections ;
JSValue promise [ 2 ] ;
} stored_connections_t ;
static void _tf_ssb_stored_connections_work ( tf_ssb_t * ssb , void * user_data )
{
stored_connections_t * work = user_data ;
work - > connections = tf_ssb_db_get_stored_connections ( ssb , & work - > count ) ;
}
static void _tf_ssb_stored_connections_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
stored_connections_t * work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue result = JS_NewArray ( context ) ;
for ( int i = 0 ; i < work - > count ; i + + )
{
JSValue connection = JS_NewObject ( context ) ;
JS_SetPropertyStr ( context , connection , " address " , JS_NewString ( context , work - > connections [ i ] . address ) ) ;
JS_SetPropertyStr ( context , connection , " port " , JS_NewInt32 ( context , work - > connections [ i ] . port ) ) ;
JS_SetPropertyStr ( context , connection , " pubkey " , JS_NewString ( context , work - > connections [ i ] . pubkey ) ) ;
JS_SetPropertyUint32 ( context , result , i , connection ) ;
}
tf_free ( work - > connections ) ;
JSValue error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
tf_free ( work ) ;
}
2023-01-17 19:37:45 -05:00
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 )
{
2024-06-16 15:29:59 -04:00
stored_connections_t * work = tf_malloc ( sizeof ( stored_connections_t ) ) ;
* work = ( stored_connections_t ) { 0 } ;
result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_stored_connections_work , _tf_ssb_stored_connections_after_work , work ) ;
2023-01-17 19:37:45 -05:00
}
return result ;
}
2022-11-02 19:34:44 -04:00
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 ) ;
const char * id = JS_ToCString ( context , argv [ 0 ] ) ;
tf_ssb_connection_t * connection = tf_ssb_connection_get ( ssb , id ) ;
JS_FreeCString ( context , id ) ;
return JS_DupValue ( context , tf_ssb_connection_get_object ( connection ) ) ;
}
2022-11-08 20:31:18 -05:00
static JSValue _tf_ssb_closeConnection ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
const char * id = JS_ToCString ( context , argv [ 0 ] ) ;
tf_ssb_connection_t * connection = tf_ssb_connection_get ( ssb , id ) ;
if ( connection )
{
tf_ssb_connection_close ( connection ) ;
}
JS_FreeCString ( context , id ) ;
return connection ? JS_TRUE : JS_FALSE ;
}
2023-02-07 20:29:44 -05:00
typedef struct _sql_work_t
{
tf_ssb_t * ssb ;
2023-08-25 16:23:40 -04:00
sqlite3 * db ;
2024-01-10 20:38:30 -05:00
char * error ;
2023-02-07 20:29:44 -05:00
const char * query ;
uint8_t * binds ;
uint8_t * rows ;
2024-01-10 20:38:30 -05:00
size_t binds_count ;
2023-02-07 20:29:44 -05:00
size_t rows_count ;
2024-01-10 20:38:30 -05:00
uv_async_t async ;
uv_timer_t timeout ;
uv_mutex_t lock ;
2023-02-07 20:29:44 -05:00
JSValue callback ;
JSValue promise [ 2 ] ;
int result ;
} sql_work_t ;
static void _tf_ssb_sql_append ( uint8_t * * rows , size_t * rows_count , const void * data , size_t size )
{
* rows = tf_resize_vec ( * rows , * rows_count + size ) ;
memcpy ( * rows + * rows_count , data , size ) ;
* rows_count + = size ;
}
2024-05-08 21:00:37 -04:00
static void _tf_ssb_sqlAsync_work ( tf_ssb_t * ssb , void * user_data )
2023-02-07 20:29:44 -05:00
{
2024-05-08 21:00:37 -04:00
sql_work_t * sql_work = user_data ;
sqlite3 * db = tf_ssb_acquire_db_reader_restricted ( ssb ) ;
2023-08-25 16:23:40 -04:00
uv_mutex_lock ( & sql_work - > lock ) ;
sql_work - > db = db ;
uv_mutex_unlock ( & sql_work - > lock ) ;
uv_async_send ( & sql_work - > async ) ;
2023-02-07 20:29:44 -05:00
sqlite3_stmt * statement = NULL ;
2023-02-15 19:06:45 -05:00
sql_work - > result = sqlite3_prepare ( db , sql_work - > query , - 1 , & statement , NULL ) ;
if ( sql_work - > result = = SQLITE_OK )
2023-02-07 20:29:44 -05:00
{
const uint8_t * p = sql_work - > binds ;
int column = 0 ;
while ( p < sql_work - > binds + sql_work - > binds_count )
{
switch ( * p + + )
{
case SQLITE_INTEGER :
{
int64_t value = 0 ;
memcpy ( & value , p , sizeof ( value ) ) ;
sqlite3_bind_int64 ( statement , column + 1 , value ) ;
p + = sizeof ( value ) ;
}
break ;
case SQLITE_TEXT :
{
size_t length = 0 ;
memcpy ( & length , p , sizeof ( length ) ) ;
p + = sizeof ( length ) ;
sqlite3_bind_text ( statement , column + 1 , ( const char * ) p , length , NULL ) ;
2023-02-13 21:13:08 -05:00
p + = length ;
2023-02-07 20:29:44 -05:00
}
break ;
case SQLITE_NULL :
sqlite3_bind_null ( statement , column + 1 ) ;
break ;
default :
abort ( ) ;
}
column + + ;
}
int r = SQLITE_OK ;
while ( ( r = sqlite3_step ( statement ) ) = = SQLITE_ROW )
{
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , & ( uint8_t [ ] ) { ' r ' } , 1 ) ;
for ( int i = 0 ; i < sqlite3_column_count ( statement ) ; i + + )
{
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , & ( uint8_t [ ] ) { ' c ' } , 1 ) ;
const char * name = sqlite3_column_name ( statement , i ) ;
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , name , strlen ( name ) + 1 ) ;
uint8_t type = sqlite3_column_type ( statement , i ) ;
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , & type , sizeof ( type ) ) ;
switch ( type )
{
case SQLITE_INTEGER :
{
int64_t value = sqlite3_column_int64 ( statement , i ) ;
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , & value , sizeof ( value ) ) ;
}
break ;
case SQLITE_FLOAT :
{
double value = sqlite3_column_double ( statement , i ) ;
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , & value , sizeof ( value ) ) ;
}
break ;
case SQLITE_TEXT :
{
size_t bytes = sqlite3_column_bytes ( statement , i ) ;
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , & bytes , sizeof ( bytes ) ) ;
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , sqlite3_column_text ( statement , i ) , bytes ) ;
}
break ;
case SQLITE_BLOB :
{
size_t bytes = sqlite3_column_bytes ( statement , i ) ;
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , & bytes , sizeof ( bytes ) ) ;
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , sqlite3_column_blob ( statement , i ) , bytes ) ;
}
break ;
case SQLITE_NULL :
break ;
default :
abort ( ) ;
}
}
}
2023-02-16 21:04:48 -05:00
sql_work - > result = r ;
if ( r ! = SQLITE_OK & & r ! = SQLITE_DONE )
{
2023-08-25 16:23:40 -04:00
if ( sqlite3_is_interrupted ( db ) )
{
sql_work - > error = tf_strdup ( " Timed out " ) ;
}
else
{
sql_work - > error = tf_strdup ( sqlite3_errmsg ( db ) ) ;
}
2023-02-16 21:04:48 -05:00
}
2023-02-07 20:29:44 -05:00
_tf_ssb_sql_append ( & sql_work - > rows , & sql_work - > rows_count , & ( uint8_t [ ] ) { 0 } , 1 ) ;
sqlite3_finalize ( statement ) ;
}
else
{
2023-02-15 19:06:45 -05:00
sql_work - > error = tf_strdup ( sqlite3_errmsg ( db ) ) ;
2023-02-07 20:29:44 -05:00
}
2023-08-25 16:23:40 -04:00
uv_mutex_lock ( & sql_work - > lock ) ;
sql_work - > db = NULL ;
uv_mutex_unlock ( & sql_work - > lock ) ;
2024-05-08 21:00:37 -04:00
tf_ssb_release_db_reader ( ssb , db ) ;
2023-02-07 20:29:44 -05:00
}
2023-08-25 16:23:40 -04:00
static void _tf_ssb_sqlAsync_handle_close ( uv_handle_t * handle )
{
sql_work_t * work = handle - > data ;
handle - > data = NULL ;
2024-02-15 18:35:01 -05:00
if ( ! work - > async . data & & ! work - > timeout . data )
2023-08-25 16:23:40 -04:00
{
tf_free ( work ) ;
}
}
static void _tf_ssb_sqlAsync_destroy ( sql_work_t * work )
{
tf_free ( work - > binds ) ;
tf_free ( work - > error ) ;
if ( work - > rows )
{
tf_free ( work - > rows ) ;
}
uv_mutex_destroy ( & work - > lock ) ;
uv_close ( ( uv_handle_t * ) & work - > timeout , _tf_ssb_sqlAsync_handle_close ) ;
uv_close ( ( uv_handle_t * ) & work - > async , _tf_ssb_sqlAsync_handle_close ) ;
}
2024-05-08 21:00:37 -04:00
static void _tf_ssb_sqlAsync_after_work ( tf_ssb_t * ssb , int status , void * user_data )
2023-02-07 20:29:44 -05:00
{
2024-05-08 21:00:37 -04:00
sql_work_t * sql_work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
2023-02-07 20:29:44 -05:00
uint8_t * p = sql_work - > rows ;
while ( p < sql_work - > rows + sql_work - > rows_count )
{
if ( * p + + = = ' r ' )
{
JSValue row = JS_NewObject ( context ) ;
while ( * p = = ' c ' )
{
p + + ;
const char * column_name = ( const char * ) p ;
size_t length = strlen ( ( char * ) p ) ;
p + = length + 1 ;
switch ( * p + + )
{
case SQLITE_INTEGER :
{
int64_t value = 0 ;
memcpy ( & value , p , sizeof ( value ) ) ;
JS_SetPropertyStr ( context , row , column_name , JS_NewInt64 ( context , value ) ) ;
p + = sizeof ( value ) ;
}
break ;
case SQLITE_FLOAT :
{
double value = 0.0 ;
memcpy ( & value , p , sizeof ( value ) ) ;
JS_SetPropertyStr ( context , row , column_name , JS_NewFloat64 ( context , value ) ) ;
p + = sizeof ( value ) ;
}
break ;
case SQLITE_TEXT :
case SQLITE_BLOB :
{
size_t length = 0 ;
memcpy ( & length , p , sizeof ( length ) ) ;
p + = sizeof ( length ) ;
JS_SetPropertyStr ( context , row , column_name , JS_NewStringLen ( context , ( const char * ) p , length ) ) ;
p + = length ;
}
break ;
case SQLITE_NULL :
JS_SetPropertyStr ( context , row , column_name , JS_NULL ) ;
break ;
}
}
JSValue response = JS_Call ( context , sql_work - > callback , JS_UNDEFINED , 1 , & row ) ;
2023-08-25 16:23:40 -04:00
bool is_error = tf_util_report_error ( context , response ) ;
2023-02-14 21:56:01 -05:00
JS_FreeValue ( context , response ) ;
2023-02-07 20:29:44 -05:00
JS_FreeValue ( context , row ) ;
2023-08-25 16:23:40 -04:00
if ( is_error )
{
break ;
}
2023-02-07 20:29:44 -05:00
}
else
{
break ;
}
}
2023-02-15 19:06:45 -05:00
JSValue result = JS_UNDEFINED ;
2023-02-16 21:04:48 -05:00
if ( sql_work - > result = = SQLITE_OK | | sql_work - > result = = SQLITE_DONE )
2023-02-15 19:06:45 -05:00
{
result = JS_Call ( context , sql_work - > promise [ 0 ] , JS_UNDEFINED , 0 , NULL ) ;
tf_util_report_error ( context , result ) ;
}
else
{
JSValue error = JS_ThrowInternalError ( context , " SQL Error %s: %s " , sql_work - > error , sql_work - > query ) ;
JSValue exception = JS_GetException ( context ) ;
result = JS_Call ( context , sql_work - > promise [ 1 ] , JS_UNDEFINED , 1 , & exception ) ;
tf_util_report_error ( context , result ) ;
JS_FreeValue ( context , exception ) ;
JS_FreeValue ( context , error ) ;
}
2023-02-14 21:59:46 -05:00
JS_FreeValue ( context , result ) ;
2023-02-07 20:29:44 -05:00
JS_FreeValue ( context , sql_work - > promise [ 0 ] ) ;
JS_FreeValue ( context , sql_work - > promise [ 1 ] ) ;
2023-02-07 20:50:47 -05:00
JS_FreeValue ( context , sql_work - > callback ) ;
2024-01-27 13:26:01 -05:00
JS_FreeCString ( context , sql_work - > query ) ;
2023-08-25 16:23:40 -04:00
_tf_ssb_sqlAsync_destroy ( sql_work ) ;
2023-02-07 20:29:44 -05:00
}
2023-08-25 16:23:40 -04:00
static void _tf_ssb_sqlAsync_timeout ( uv_timer_t * timer )
{
sql_work_t * work = timer - > data ;
uv_mutex_lock ( & work - > lock ) ;
if ( work - > db )
{
sqlite3_interrupt ( work - > db ) ;
}
uv_mutex_unlock ( & work - > lock ) ;
}
static void _tf_ssb_sqlAsync_start_timer ( uv_async_t * async )
{
sql_work_t * work = async - > data ;
uv_timer_start ( & work - > timeout , _tf_ssb_sqlAsync_timeout , k_sql_async_timeout_ms , 0 ) ;
}
2023-02-07 20:29:44 -05:00
static JSValue _tf_ssb_sqlAsync ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
2023-02-13 21:13:08 -05:00
const char * query = JS_ToCString ( context , argv [ 0 ] ) ;
sql_work_t * work = tf_malloc ( sizeof ( sql_work_t ) ) ;
* work = ( sql_work_t )
2023-02-07 20:29:44 -05:00
{
2023-08-25 16:23:40 -04:00
. async =
{
. data = work ,
} ,
. timeout =
{
. data = work ,
} ,
2023-02-13 21:13:08 -05:00
. ssb = ssb ,
. callback = JS_DupValue ( context , argv [ 2 ] ) ,
. query = query ,
} ;
2023-08-25 16:23:40 -04:00
uv_mutex_init ( & work - > lock ) ;
uv_async_init ( tf_ssb_get_loop ( ssb ) , & work - > async , _tf_ssb_sqlAsync_start_timer ) ;
uv_timer_init ( tf_ssb_get_loop ( ssb ) , & work - > timeout ) ;
2023-02-13 21:13:08 -05:00
JSValue result = JS_NewPromiseCapability ( context , work - > promise ) ;
JSValue error_value = JS_UNDEFINED ;
if ( ssb )
{
2023-02-07 20:29:44 -05:00
int32_t length = tf_util_get_length ( context , argv [ 1 ] ) ;
for ( int i = 0 ; i < length ; i + + )
{
JSValue value = JS_GetPropertyUint32 ( context , argv [ 1 ] , i ) ;
if ( JS_IsNumber ( value ) )
{
uint8_t type = SQLITE_INTEGER ;
int64_t number = 0 ;
JS_ToInt64 ( context , & number , value ) ;
_tf_ssb_sql_append ( & work - > binds , & work - > binds_count , & type , sizeof ( type ) ) ;
_tf_ssb_sql_append ( & work - > binds , & work - > binds_count , & number , sizeof ( number ) ) ;
}
else if ( JS_IsBool ( value ) )
{
uint8_t type = SQLITE_INTEGER ;
int64_t number = JS_ToBool ( context , value ) ? 1 : 0 ;
_tf_ssb_sql_append ( & work - > binds , & work - > binds_count , & type , sizeof ( type ) ) ;
_tf_ssb_sql_append ( & work - > binds , & work - > binds_count , & number , sizeof ( number ) ) ;
}
2023-02-13 21:13:08 -05:00
else if ( JS_IsNull ( value ) )
2023-02-07 20:29:44 -05:00
{
uint8_t type = SQLITE_NULL ;
_tf_ssb_sql_append ( & work - > binds , & work - > binds_count , & type , sizeof ( type ) ) ;
}
else
{
uint8_t type = SQLITE_TEXT ;
size_t length = 0 ;
const char * string = JS_ToCStringLen ( context , & length , value ) ;
if ( ! string )
{
string = " " ;
}
_tf_ssb_sql_append ( & work - > binds , & work - > binds_count , & type , sizeof ( type ) ) ;
2023-02-13 21:13:08 -05:00
_tf_ssb_sql_append ( & work - > binds , & work - > binds_count , & length , sizeof ( length ) ) ;
2023-02-07 20:29:44 -05:00
_tf_ssb_sql_append ( & work - > binds , & work - > binds_count , string , length ) ;
JS_FreeCString ( context , string ) ;
}
2023-02-19 08:51:06 -05:00
JS_FreeValue ( context , value ) ;
2023-02-07 20:29:44 -05:00
}
2024-05-08 21:00:37 -04:00
tf_ssb_run_work ( ssb , _tf_ssb_sqlAsync_work , _tf_ssb_sqlAsync_after_work , work ) ;
2023-02-07 20:29:44 -05:00
}
2023-02-13 21:13:08 -05:00
if ( ! JS_IsUndefined ( error_value ) )
{
JSValue call_result = JS_Call ( context , work - > promise [ 1 ] , JS_UNDEFINED , 1 , & error_value ) ;
tf_util_report_error ( context , call_result ) ;
2023-02-14 21:59:46 -05:00
JS_FreeValue ( context , call_result ) ;
2023-08-25 16:23:40 -04:00
JS_FreeValue ( context , error_value ) ;
JS_FreeCString ( context , query ) ;
2023-02-13 21:13:08 -05:00
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
JS_FreeValue ( context , work - > callback ) ;
2023-08-25 16:23:40 -04:00
_tf_ssb_sqlAsync_destroy ( work ) ;
2023-02-13 21:13:08 -05:00
}
2023-02-07 20:29:44 -05:00
return result ;
}
2023-07-19 21:02:50 -04:00
typedef struct _message_store_t
{
JSContext * context ;
JSValue promise [ 2 ] ;
} message_store_t ;
2024-02-10 11:50:00 -05:00
static void _tf_ssb_message_store_callback ( const char * id , bool verified , bool is_new , void * user_data )
2023-07-19 21:02:50 -04:00
{
message_store_t * store = user_data ;
JSValue result = JS_Call ( store - > context , id ? store - > promise [ 0 ] : store - > promise [ 1 ] , JS_UNDEFINED , 0 , NULL ) ;
tf_util_report_error ( store - > context , result ) ;
JS_FreeValue ( store - > context , result ) ;
JS_FreeValue ( store - > context , store - > promise [ 0 ] ) ;
JS_FreeValue ( store - > context , store - > promise [ 1 ] ) ;
tf_free ( store ) ;
}
2021-09-06 13:50:38 -04:00
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 ) ;
2023-07-19 21:02:50 -04:00
message_store_t * store = tf_malloc ( sizeof ( message_store_t ) ) ;
* store = ( message_store_t ) { . context = context } ;
JSValue result = JS_NewPromiseCapability ( context , store - > promise ) ;
tf_ssb_verify_strip_and_store_message ( ssb , argv [ 0 ] , _tf_ssb_message_store_callback , store ) ;
return result ;
2021-09-06 13:50:38 -04:00
}
2021-01-02 13:10:00 -05:00
typedef struct _broadcasts_t
{
JSContext * context ;
JSValue array ;
int length ;
} broadcasts_t ;
2024-08-14 19:40:20 -04:00
static void _tf_ssb_broadcasts_visit (
const char * host , const struct sockaddr_in * addr , tf_ssb_broadcast_origin_t origin , tf_ssb_connection_t * tunnel , const uint8_t * pub , void * user_data )
2021-01-02 13:10:00 -05:00
{
broadcasts_t * broadcasts = user_data ;
JSValue entry = JS_NewObject ( broadcasts - > context ) ;
char pubkey [ k_id_base64_len ] ;
tf_ssb_id_bin_to_str ( pubkey , sizeof ( pubkey ) , pub ) ;
2024-08-14 19:23:01 -04:00
switch ( origin )
{
case k_tf_ssb_broadcast_origin_discovery :
JS_SetPropertyStr ( broadcasts - > context , entry , " origin " , JS_NewString ( broadcasts - > context , " discovery " ) ) ;
break ;
case k_tf_ssb_broadcast_origin_room :
JS_SetPropertyStr ( broadcasts - > context , entry , " origin " , JS_NewString ( broadcasts - > context , " room " ) ) ;
break ;
case k_tf_ssb_broadcast_origin_peer_exchange :
JS_SetPropertyStr ( broadcasts - > context , entry , " origin " , JS_NewString ( broadcasts - > context , " peer_exchange " ) ) ;
break ;
}
2022-11-02 19:34:44 -04:00
if ( tunnel )
{
JS_SetPropertyStr ( broadcasts - > context , entry , " tunnel " , JS_DupValue ( broadcasts - > context , tf_ssb_connection_get_object ( tunnel ) ) ) ;
}
else
{
2022-11-08 22:51:31 -05:00
JS_SetPropertyStr ( broadcasts - > context , entry , " address " , JS_NewString ( broadcasts - > context , host ) ) ;
2022-11-02 19:34:44 -04:00
JS_SetPropertyStr ( broadcasts - > context , entry , " port " , JS_NewInt32 ( broadcasts - > context , ntohs ( addr - > sin_port ) ) ) ;
}
2021-01-02 13:10:00 -05:00
JS_SetPropertyStr ( broadcasts - > context , entry , " pubkey " , JS_NewString ( broadcasts - > context , pubkey ) ) ;
JS_SetPropertyUint32 ( broadcasts - > context , broadcasts - > array , broadcasts - > length + + , entry ) ;
}
static JSValue _tf_ssb_getBroadcasts ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_UNDEFINED ;
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
2021-10-10 17:51:38 -04:00
if ( ssb )
{
2021-01-02 13:10:00 -05:00
result = JS_NewArray ( context ) ;
broadcasts_t broadcasts = {
. context = context ,
. array = result ,
. length = 0 ,
} ;
tf_ssb_visit_broadcasts ( ssb , _tf_ssb_broadcasts_visit , & broadcasts ) ;
}
return result ;
}
static JSValue _tf_ssb_connect ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue args = argv [ 0 ] ;
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
2021-10-10 17:51:38 -04:00
if ( ssb )
{
if ( JS_IsString ( args ) )
{
2021-01-02 13:10:00 -05:00
const char * address_str = JS_ToCString ( context , args ) ;
2023-03-07 12:50:17 -05:00
tf_printf ( " Connecting to %s \n " , address_str ) ;
2021-01-02 13:10:00 -05:00
tf_ssb_connect_str ( ssb , address_str ) ;
JS_FreeCString ( context , address_str ) ;
2021-10-10 17:51:38 -04:00
}
else
{
2021-01-02 13:10:00 -05:00
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 ) ;
2022-11-08 20:31:18 -05:00
if ( pubkey_str )
{
2023-03-07 12:50:17 -05:00
tf_printf ( " Connecting to %s:%d \n " , address_str , port_int ) ;
2022-11-08 20:31:18 -05:00
uint8_t pubkey_bin [ k_id_bin_len ] ;
tf_ssb_id_str_to_bin ( pubkey_bin , pubkey_str ) ;
tf_ssb_connect ( ssb , address_str , port_int , pubkey_bin ) ;
}
else
{
2023-03-07 12:50:17 -05:00
tf_printf ( " Not connecting to null. \n " ) ;
2022-11-08 20:31:18 -05:00
}
2021-01-02 13:10:00 -05:00
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 ;
}
2024-06-16 15:57:19 -04:00
typedef struct _forget_stored_connection_t
{
const char * address ;
int32_t port ;
const char * pubkey ;
JSValue promise [ 2 ] ;
} forget_stored_connection_t ;
static void _tf_ssb_forget_stored_connection_work ( tf_ssb_t * ssb , void * user_data )
{
forget_stored_connection_t * work = user_data ;
if ( work - > pubkey )
{
tf_ssb_db_forget_stored_connection ( ssb , work - > address , work - > port , work - > pubkey ) ;
}
}
static void _tf_ssb_forget_stored_connection_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
forget_stored_connection_t * work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
JS_FreeCString ( context , work - > pubkey ) ;
JS_FreeCString ( context , work - > address ) ;
JSValue result = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 0 , NULL ) ;
tf_util_report_error ( context , result ) ;
JS_FreeValue ( context , result ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
tf_free ( work ) ;
}
2023-01-17 19:37:45 -05:00
static JSValue _tf_ssb_forgetStoredConnection ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
2024-06-16 15:57:19 -04:00
JSValue result = JS_UNDEFINED ;
2023-01-17 19:37:45 -05:00
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 ) ;
2024-06-16 15:57:19 -04:00
forget_stored_connection_t * work = tf_malloc ( sizeof ( forget_stored_connection_t ) ) ;
* work = ( forget_stored_connection_t ) {
. address = address_str ,
. port = port_int ,
. pubkey = pubkey_str ,
} ;
result = JS_NewPromiseCapability ( context , work - > promise ) ;
2023-01-17 19:37:45 -05:00
JS_FreeValue ( context , address ) ;
JS_FreeValue ( context , port ) ;
JS_FreeValue ( context , pubkey ) ;
2024-06-16 15:57:19 -04:00
tf_ssb_run_work ( ssb , _tf_ssb_forget_stored_connection_work , _tf_ssb_forget_stored_connection_after_work , work ) ;
2023-01-17 19:37:45 -05:00
}
2024-06-16 15:57:19 -04:00
return result ;
2023-01-17 19:37:45 -05:00
}
2022-11-02 19:34:44 -04:00
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 ) ;
}
2021-11-10 19:05:07 -05:00
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 ) ;
JSValue callback = JS_MKPTR ( JS_TAG_OBJECT , user_data ) ;
JSValue string = JS_NewString ( context , id ) ;
JSValue response = JS_Call ( context , callback , JS_UNDEFINED , 1 , & string ) ;
2021-12-27 16:48:16 -05:00
if ( tf_util_report_error ( context , response ) )
{
tf_ssb_remove_message_added_callback ( ssb , _tf_ssb_on_message_added_callback , user_data ) ;
}
2021-11-10 19:05:07 -05:00
JS_FreeValue ( context , response ) ;
JS_FreeValue ( context , string ) ;
}
2021-11-07 17:28:58 -05:00
static void _tf_ssb_on_blob_want_added_callback ( tf_ssb_t * ssb , const char * id , void * user_data )
2021-09-06 13:50:38 -04:00
{
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue callback = JS_MKPTR ( JS_TAG_OBJECT , user_data ) ;
JSValue string = JS_NewString ( context , id ) ;
JSValue response = JS_Call ( context , callback , JS_UNDEFINED , 1 , & string ) ;
2021-12-27 16:48:16 -05:00
if ( tf_util_report_error ( context , response ) )
{
tf_ssb_remove_blob_want_added_callback ( ssb , _tf_ssb_on_blob_want_added_callback , user_data ) ;
}
2021-09-06 13:50:38 -04:00
JS_FreeValue ( context , response ) ;
JS_FreeValue ( context , string ) ;
}
2021-11-07 17:28:58 -05:00
static void _tf_ssb_on_connections_changed_callback ( tf_ssb_t * ssb , tf_ssb_change_t change , tf_ssb_connection_t * connection , void * user_data )
2021-09-06 13:50:38 -04:00
{
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue callback = JS_MKPTR ( JS_TAG_OBJECT , user_data ) ;
JSValue response = JS_UNDEFINED ;
switch ( change )
{
case k_tf_ssb_change_create :
break ;
2024-05-02 19:02:23 -04:00
case k_tf_ssb_change_update :
{
JSValue object = JS_DupValue ( context , tf_ssb_connection_get_object ( connection ) ) ;
JSValue args [ ] = {
JS_NewString ( context , " update " ) ,
object ,
} ;
response = JS_Call ( context , callback , JS_UNDEFINED , 2 , args ) ;
if ( tf_util_report_error ( context , response ) )
{
tf_ssb_remove_connections_changed_callback ( ssb , _tf_ssb_on_connections_changed_callback , user_data ) ;
}
JS_FreeValue ( context , args [ 0 ] ) ;
JS_FreeValue ( context , object ) ;
}
break ;
2021-09-06 13:50:38 -04:00
case k_tf_ssb_change_connect :
{
2021-09-06 14:23:22 -04:00
JSValue object = JS_DupValue ( context , tf_ssb_connection_get_object ( connection ) ) ;
2024-02-15 18:35:01 -05:00
JSValue args [ ] = {
2021-09-06 13:50:38 -04:00
JS_NewString ( context , " add " ) ,
object ,
} ;
response = JS_Call ( context , callback , JS_UNDEFINED , 2 , args ) ;
2021-12-27 16:48:16 -05:00
if ( tf_util_report_error ( context , response ) )
{
tf_ssb_remove_connections_changed_callback ( ssb , _tf_ssb_on_connections_changed_callback , user_data ) ;
}
2021-09-06 14:23:22 -04:00
JS_FreeValue ( context , args [ 0 ] ) ;
JS_FreeValue ( context , object ) ;
2021-09-06 13:50:38 -04:00
}
break ;
case k_tf_ssb_change_remove :
{
2021-09-06 14:23:22 -04:00
JSValue object = JS_DupValue ( context , tf_ssb_connection_get_object ( connection ) ) ;
2024-02-15 18:35:01 -05:00
JSValue args [ ] = {
2021-09-06 13:50:38 -04:00
JS_NewString ( context , " remove " ) ,
object ,
} ;
response = JS_Call ( context , callback , JS_UNDEFINED , 2 , args ) ;
2021-12-27 16:48:16 -05:00
if ( tf_util_report_error ( context , response ) )
{
tf_ssb_remove_connections_changed_callback ( ssb , _tf_ssb_on_connections_changed_callback , user_data ) ;
}
2021-09-06 14:23:22 -04:00
JS_FreeValue ( context , args [ 0 ] ) ;
JS_FreeValue ( context , object ) ;
2021-09-06 13:50:38 -04:00
}
break ;
}
JS_FreeValue ( context , response ) ;
}
2021-11-07 17:28:58 -05:00
static void _tf_ssb_on_broadcasts_changed_callback ( tf_ssb_t * ssb , void * user_data )
2021-09-06 13:50:38 -04:00
{
2021-11-07 17:28:58 -05:00
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue callback = JS_MKPTR ( JS_TAG_OBJECT , user_data ) ;
2021-12-17 18:58:59 -05:00
JSValue argv = JS_UNDEFINED ;
JSValue response = JS_Call ( context , callback , JS_UNDEFINED , 1 , & argv ) ;
2021-12-27 16:48:16 -05:00
if ( tf_util_report_error ( context , response ) )
{
2021-12-27 17:00:37 -05:00
tf_ssb_remove_broadcasts_changed_callback ( ssb , _tf_ssb_on_broadcasts_changed_callback , user_data ) ;
2021-12-27 16:48:16 -05:00
}
2021-11-07 17:28:58 -05:00
JS_FreeValue ( context , response ) ;
2021-09-06 13:50:38 -04:00
}
2021-11-07 17:28:58 -05:00
static JSValue _tf_ssb_add_event_listener ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
const char * event_name = JS_ToCString ( context , argv [ 0 ] ) ;
JSValue callback = argv [ 1 ] ;
JSValue result = JS_UNDEFINED ;
if ( ! event_name )
{
result = JS_ThrowTypeError ( context , " Expected argument 1 to be a string event name. " ) ;
}
else if ( ! JS_IsFunction ( context , callback ) )
{
result = JS_ThrowTypeError ( context , " Expected argument 2 to be a function. " ) ;
}
else
{
if ( strcmp ( event_name , " connections " ) = = 0 )
{
void * ptr = JS_VALUE_GET_PTR ( JS_DupValue ( context , callback ) ) ;
tf_ssb_add_connections_changed_callback ( ssb , _tf_ssb_on_connections_changed_callback , _tf_ssb_cleanup_value , ptr ) ;
}
else if ( strcmp ( event_name , " broadcasts " ) = = 0 )
{
void * ptr = JS_VALUE_GET_PTR ( JS_DupValue ( context , callback ) ) ;
tf_ssb_add_broadcasts_changed_callback ( ssb , _tf_ssb_on_broadcasts_changed_callback , _tf_ssb_cleanup_value , ptr ) ;
}
2021-11-10 19:05:07 -05:00
else if ( strcmp ( event_name , " message " ) = = 0 )
{
void * ptr = JS_VALUE_GET_PTR ( JS_DupValue ( context , callback ) ) ;
tf_ssb_add_message_added_callback ( ssb , _tf_ssb_on_message_added_callback , _tf_ssb_cleanup_value , ptr ) ;
}
2021-11-07 17:28:58 -05:00
else if ( strcmp ( event_name , " blob_want_added " ) = = 0 )
{
void * ptr = JS_VALUE_GET_PTR ( JS_DupValue ( context , callback ) ) ;
tf_ssb_add_blob_want_added_callback ( ssb , _tf_ssb_on_blob_want_added_callback , _tf_ssb_cleanup_value , ptr ) ;
}
}
JS_FreeCString ( context , event_name ) ;
return result ;
}
static JSValue _tf_ssb_remove_event_listener ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
const char * event_name = JS_ToCString ( context , argv [ 0 ] ) ;
JSValue callback = argv [ 1 ] ;
JSValue result = JS_UNDEFINED ;
if ( ! event_name )
{
result = JS_ThrowTypeError ( context , " Expected argument 1 to be a string event name. " ) ;
}
else if ( ! JS_IsFunction ( context , callback ) )
{
result = JS_ThrowTypeError ( context , " Expected argument 2 to be a function. " ) ;
}
else
{
if ( strcmp ( event_name , " connections " ) = = 0 )
{
void * ptr = JS_VALUE_GET_PTR ( JS_DupValue ( context , callback ) ) ;
tf_ssb_remove_connections_changed_callback ( ssb , _tf_ssb_on_connections_changed_callback , ptr ) ;
}
else if ( strcmp ( event_name , " broadcasts " ) = = 0 )
{
void * ptr = JS_VALUE_GET_PTR ( JS_DupValue ( context , callback ) ) ;
tf_ssb_remove_broadcasts_changed_callback ( ssb , _tf_ssb_on_broadcasts_changed_callback , ptr ) ;
}
2021-11-17 18:47:55 -05:00
else if ( strcmp ( event_name , " message " ) = = 0 )
2021-11-10 19:05:07 -05:00
{
void * ptr = JS_VALUE_GET_PTR ( JS_DupValue ( context , callback ) ) ;
tf_ssb_remove_message_added_callback ( ssb , _tf_ssb_on_message_added_callback , ptr ) ;
}
2021-11-07 17:28:58 -05:00
else if ( strcmp ( event_name , " blob_want_added " ) = = 0 )
{
void * ptr = JS_VALUE_GET_PTR ( JS_DupValue ( context , callback ) ) ;
tf_ssb_remove_blob_want_added_callback ( ssb , _tf_ssb_on_blob_want_added_callback , ptr ) ;
}
}
JS_FreeCString ( context , event_name ) ;
return result ;
}
2022-11-30 20:19:35 -05:00
static JSValue _tf_ssb_createTunnel ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
2023-01-17 18:10:17 -05:00
JSValue result = JS_UNDEFINED ;
2022-11-30 20:19:35 -05:00
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
const char * portal_id = JS_ToCString ( context , argv [ 0 ] ) ;
2023-01-17 18:10:17 -05:00
const char * target_id = JS_ToCString ( context , argv [ 1 ] ) ;
tf_ssb_connection_t * connection = tf_ssb_connection_get ( ssb , portal_id ) ;
if ( connection )
{
int32_t request_number = tf_ssb_connection_next_request_number ( connection ) ;
JSValue message = JS_NewObject ( context ) ;
JSValue name = JS_NewArray ( context ) ;
JS_SetPropertyUint32 ( context , name , 0 , JS_NewString ( context , " tunnel " ) ) ;
JS_SetPropertyUint32 ( context , name , 1 , JS_NewString ( context , " connect " ) ) ;
JS_SetPropertyStr ( context , message , " name " , name ) ;
JSValue arg = JS_NewObject ( context ) ;
JS_SetPropertyStr ( context , arg , " portal " , JS_NewString ( context , portal_id ) ) ;
JS_SetPropertyStr ( context , arg , " target " , JS_NewString ( context , target_id ) ) ;
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 " ) ) ;
2024-05-02 19:02:23 -04:00
tf_ssb_connection_rpc_send_json ( connection , k_ssb_rpc_flag_stream | k_ssb_rpc_flag_new_request , request_number , " tunnel.connect " , message , NULL , NULL , NULL ) ;
2023-01-17 18:10:17 -05:00
JS_FreeValue ( context , message ) ;
tf_ssb_connection_tunnel_create ( ssb , portal_id , request_number , target_id ) ;
result = JS_TRUE ;
}
2022-11-30 20:19:35 -05:00
JS_FreeCString ( context , target_id ) ;
JS_FreeCString ( context , portal_id ) ;
2023-01-17 18:10:17 -05:00
return result ;
2022-11-30 20:19:35 -05:00
}
2024-02-15 18:35:01 -05:00
enum
{
k_max_private_message_recipients = 8
} ;
2023-02-26 14:51:54 -05:00
static bool _tf_ssb_get_private_key_curve25519 ( sqlite3 * db , const char * user , const char * identity , uint8_t out_private_key [ static crypto_sign_SECRETKEYBYTES ] )
{
if ( ! user | | ! identity | | ! out_private_key )
{
2023-07-20 01:06:15 -04:00
tf_printf ( " user=%p identity=%p out_private_key=%p \n " , user , identity , out_private_key ) ;
2023-02-26 14:51:54 -05:00
return false ;
}
bool success = false ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " SELECT private_key FROM identities WHERE user = ? AND public_key = ? " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
2024-02-17 14:22:02 -05:00
if ( sqlite3_bind_text ( statement , 1 , user , - 1 , NULL ) = = SQLITE_OK & & sqlite3_bind_text ( statement , 2 , * identity = = ' @ ' ? identity + 1 : identity , - 1 , NULL ) = = SQLITE_OK )
2023-02-26 14:51:54 -05:00
{
while ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
uint8_t key [ crypto_sign_SECRETKEYBYTES ] = { 0 } ;
2024-02-17 14:22:02 -05:00
int length = tf_base64_decode ( ( const char * ) sqlite3_column_text ( statement , 0 ) , sqlite3_column_bytes ( statement , 0 ) - strlen ( " .ed25519 " ) , key , sizeof ( key ) ) ;
2023-02-26 14:51:54 -05:00
if ( length = = crypto_sign_SECRETKEYBYTES )
{
success = crypto_sign_ed25519_sk_to_curve25519 ( out_private_key , key ) = = 0 ;
}
}
}
sqlite3_finalize ( statement ) ;
}
return success ;
}
2024-06-16 17:07:12 -04:00
typedef struct _private_message_encrypt_t
{
const char * signer_user ;
const char * signer_identity ;
uint8_t recipients [ k_max_private_message_recipients ] [ crypto_scalarmult_curve25519_SCALARBYTES ] ;
int recipient_count ;
const char * message ;
size_t message_size ;
JSValue promise [ 2 ] ;
bool error_id_not_found ;
bool error_secretbox_failed ;
bool error_scalarmult_failed ;
char * encrypted ;
size_t encrypted_length ;
} private_message_encrypt_t ;
static void _tf_ssb_private_message_encrypt_work ( tf_ssb_t * ssb , void * user_data )
{
private_message_encrypt_t * work = user_data ;
uint8_t private_key [ crypto_sign_SECRETKEYBYTES ] = { 0 } ;
sqlite3 * db = tf_ssb_acquire_db_reader ( ssb ) ;
bool found = _tf_ssb_get_private_key_curve25519 ( db , work - > signer_user , work - > signer_identity , private_key ) ;
tf_ssb_release_db_reader ( ssb , db ) ;
if ( found )
{
uint8_t public_key [ crypto_box_PUBLICKEYBYTES ] = { 0 } ;
uint8_t secret_key [ crypto_box_SECRETKEYBYTES ] = { 0 } ;
uint8_t nonce [ crypto_box_NONCEBYTES ] = { 0 } ;
uint8_t body_key [ crypto_box_SECRETKEYBYTES ] = { 0 } ;
crypto_box_keypair ( public_key , secret_key ) ;
randombytes_buf ( nonce , sizeof ( nonce ) ) ;
randombytes_buf ( body_key , sizeof ( body_key ) ) ;
uint8_t length_and_key [ 1 + sizeof ( body_key ) ] ;
length_and_key [ 0 ] = ( uint8_t ) work - > recipient_count ;
memcpy ( length_and_key + 1 , body_key , sizeof ( body_key ) ) ;
size_t payload_size =
sizeof ( nonce ) + sizeof ( public_key ) + ( crypto_secretbox_MACBYTES + sizeof ( length_and_key ) ) * work - > recipient_count + crypto_secretbox_MACBYTES + work - > message_size ;
uint8_t * payload = tf_malloc ( payload_size ) ;
uint8_t * p = payload ;
memcpy ( p , nonce , sizeof ( nonce ) ) ;
p + = sizeof ( nonce ) ;
memcpy ( p , public_key , sizeof ( public_key ) ) ;
p + = sizeof ( public_key ) ;
for ( int i = 0 ; i < work - > recipient_count ; i + + )
{
uint8_t shared_secret [ crypto_secretbox_KEYBYTES ] = { 0 } ;
if ( crypto_scalarmult ( shared_secret , secret_key , work - > recipients [ i ] ) = = 0 )
{
if ( crypto_secretbox_easy ( p , length_and_key , sizeof ( length_and_key ) , nonce , shared_secret ) ! = 0 )
{
work - > error_secretbox_failed = true ;
break ;
}
else
{
p + = crypto_secretbox_MACBYTES + sizeof ( length_and_key ) ;
}
}
else
{
work - > error_scalarmult_failed = true ;
break ;
}
}
if ( ! work - > error_secretbox_failed & & ! work - > error_scalarmult_failed )
{
if ( crypto_secretbox_easy ( p , ( const uint8_t * ) work - > message , work - > message_size , nonce , body_key ) ! = 0 )
{
work - > error_scalarmult_failed = true ;
}
else
{
p + = crypto_secretbox_MACBYTES + work - > message_size ;
assert ( ( size_t ) ( p - payload ) = = payload_size ) ;
char * encoded = tf_malloc ( payload_size * 2 + 5 ) ;
size_t encoded_length = tf_base64_encode ( payload , payload_size , encoded , payload_size * 2 + 5 ) ;
memcpy ( encoded + encoded_length , " .box " , 5 ) ;
encoded_length + = 4 ;
work - > encrypted = encoded ;
work - > encrypted_length = encoded_length ;
}
}
tf_free ( payload ) ;
}
else
{
work - > error_id_not_found = true ;
}
}
static void _tf_ssb_private_message_encrypt_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
private_message_encrypt_t * work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue result = JS_UNDEFINED ;
if ( work - > error_secretbox_failed )
{
result = JS_ThrowInternalError ( context , " crypto_secretbox_easy failed " ) ;
}
else if ( work - > error_scalarmult_failed )
{
result = JS_ThrowInternalError ( context , " crypto_scalarmult failed " ) ;
}
else if ( work - > error_id_not_found )
{
result = JS_ThrowInternalError ( context , " Unable to get key for ID %s of user %s. " , work - > signer_identity , work - > signer_user ) ;
}
else
{
result = JS_NewStringLen ( context , work - > encrypted , work - > encrypted_length ) ;
tf_free ( ( void * ) work - > encrypted ) ;
}
JSValue error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
JS_FreeCString ( context , work - > signer_user ) ;
JS_FreeCString ( context , work - > signer_identity ) ;
JS_FreeCString ( context , work - > message ) ;
tf_free ( work ) ;
}
2023-02-26 14:51:54 -05:00
static JSValue _tf_ssb_private_message_encrypt ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
JSValue result = JS_UNDEFINED ;
int recipient_count = tf_util_get_length ( context , argv [ 2 ] ) ;
if ( recipient_count < 1 | | recipient_count > k_max_private_message_recipients )
{
return JS_ThrowRangeError ( context , " Number of recipients must be between 1 and %d. " , k_max_private_message_recipients ) ;
}
2024-06-16 17:07:12 -04:00
uint8_t recipients [ k_max_private_message_recipients ] [ crypto_scalarmult_curve25519_SCALARBYTES ] = { 0 } ;
2023-02-26 14:51:54 -05:00
for ( int i = 0 ; i < recipient_count & & JS_IsUndefined ( result ) ; i + + )
{
JSValue recipient = JS_GetPropertyUint32 ( context , argv [ 2 ] , i ) ;
const char * id = JS_ToCString ( context , recipient ) ;
if ( id )
{
const char * type = strstr ( id , " .ed25519 " ) ;
const char * id_start = * id = = ' @ ' ? id + 1 : id ;
uint8_t key [ crypto_box_PUBLICKEYBYTES ] = { 0 } ;
if ( tf_base64_decode ( id_start , type ? ( size_t ) ( type - id_start ) : strlen ( id_start ) , key , sizeof ( key ) ) ! = sizeof ( key ) )
{
result = JS_ThrowInternalError ( context , " Invalid recipient: %s. \n " , id ) ;
}
else if ( crypto_sign_ed25519_pk_to_curve25519 ( recipients [ i ] , key ) ! = 0 )
{
result = JS_ThrowInternalError ( context , " Failed to convert recipient ID. \n " ) ;
}
JS_FreeCString ( context , id ) ;
}
JS_FreeValue ( context , recipient ) ;
}
if ( JS_IsUndefined ( result ) )
{
2024-06-16 17:07:12 -04:00
const char * signer_user = JS_ToCString ( context , argv [ 0 ] ) ;
const char * signer_identity = JS_ToCString ( context , argv [ 1 ] ) ;
size_t message_size = 0 ;
const char * message = JS_ToCStringLen ( context , & message_size , argv [ 3 ] ) ;
2023-02-26 14:51:54 -05:00
2024-06-16 17:07:12 -04:00
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
private_message_encrypt_t * work = tf_malloc ( sizeof ( private_message_encrypt_t ) ) ;
* work = ( private_message_encrypt_t ) {
. signer_user = signer_user ,
. signer_identity = signer_identity ,
. recipient_count = recipient_count ,
. message = message ,
. message_size = message_size ,
} ;
static_assert ( sizeof ( work - > recipients ) = = sizeof ( recipients ) , " size mismatch " ) ;
memcpy ( work - > recipients , recipients , sizeof ( recipients ) ) ;
result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_private_message_encrypt_work , _tf_ssb_private_message_encrypt_after_work , work ) ;
2023-02-26 14:51:54 -05:00
}
return result ;
}
2024-06-16 17:22:26 -04:00
typedef struct _private_message_decrypt_t
2023-02-26 14:51:54 -05:00
{
2024-06-16 17:22:26 -04:00
const char * user ;
const char * identity ;
size_t message_size ;
const char * message ;
const char * decrypted ;
size_t decrypted_size ;
const char * error ;
JSValue promise [ 2 ] ;
} private_message_decrypt_t ;
2023-02-26 14:51:54 -05:00
2024-06-16 17:22:26 -04:00
static void _tf_ssb_private_message_decrypt_work ( tf_ssb_t * ssb , void * user_data )
{
private_message_decrypt_t * work = user_data ;
2023-02-26 14:51:54 -05:00
2024-06-16 17:22:26 -04:00
uint8_t private_key [ crypto_sign_SECRETKEYBYTES ] = { 0 } ;
2023-06-14 20:27:49 -04:00
sqlite3 * db = tf_ssb_acquire_db_reader ( ssb ) ;
2024-06-16 17:22:26 -04:00
bool found = _tf_ssb_get_private_key_curve25519 ( db , work - > user , work - > identity , private_key ) ;
2023-07-20 01:06:15 -04:00
tf_ssb_release_db_reader ( ssb , db ) ;
if ( found )
2023-02-26 14:51:54 -05:00
{
2024-06-16 17:22:26 -04:00
uint8_t * decoded = tf_malloc ( work - > message_size ) ;
int decoded_length = tf_base64_decode ( work - > message , work - > message_size - strlen ( " .box " ) , decoded , work - > message_size ) ;
2023-02-26 14:51:54 -05:00
uint8_t * nonce = decoded ;
uint8_t * public_key = decoded + crypto_box_NONCEBYTES ;
if ( public_key + crypto_secretbox_KEYBYTES < decoded + decoded_length )
{
uint8_t shared_secret [ crypto_secretbox_KEYBYTES ] = { 0 } ;
if ( crypto_scalarmult ( shared_secret , private_key , public_key ) = = 0 )
{
2024-02-15 18:35:01 -05:00
enum
{
k_recipient_header_bytes = crypto_secretbox_MACBYTES + sizeof ( uint8_t ) + crypto_secretbox_KEYBYTES
} ;
for ( uint8_t * p = decoded + crypto_box_NONCEBYTES + crypto_secretbox_KEYBYTES ; p < = decoded + decoded_length - k_recipient_header_bytes ;
2024-02-17 14:22:02 -05:00
p + = k_recipient_header_bytes )
2023-02-26 14:51:54 -05:00
{
uint8_t out [ k_recipient_header_bytes ] = { 0 } ;
int opened = crypto_secretbox_open_easy ( out , p , k_recipient_header_bytes , nonce , shared_secret ) ;
if ( opened ! = - 1 )
{
int recipients = ( int ) out [ 0 ] ;
uint8_t * body = decoded + crypto_box_NONCEBYTES + crypto_secretbox_KEYBYTES + k_recipient_header_bytes * recipients ;
size_t body_size = decoded + decoded_length - body ;
uint8_t * decrypted = tf_malloc ( body_size ) ;
uint8_t * key = out + 1 ;
if ( crypto_secretbox_open_easy ( decrypted , body , body_size , nonce , key ) ! = - 1 )
{
2024-06-16 17:22:26 -04:00
work - > decrypted = ( const char * ) decrypted ;
work - > decrypted_size = body_size - crypto_secretbox_MACBYTES ;
2023-02-26 14:51:54 -05:00
}
else
{
2024-06-16 17:22:26 -04:00
work - > error = " Received key to open secret box containing message body, but it did not work. " ;
2023-02-26 14:51:54 -05:00
}
}
}
}
else
{
2024-06-16 17:22:26 -04:00
work - > error = " crypto_scalarmult failed. " ;
2023-02-26 14:51:54 -05:00
}
}
else
{
2024-06-16 17:22:26 -04:00
work - > error = " Encrypted message was not long enough to contains its one-time public key. " ;
2023-02-26 14:51:54 -05:00
}
tf_free ( decoded ) ;
}
else
{
2024-06-16 17:22:26 -04:00
work - > error = " Private key not found for user. " ;
2023-02-26 14:51:54 -05:00
}
2024-06-16 17:22:26 -04:00
}
2023-02-26 14:51:54 -05:00
2024-06-16 17:22:26 -04:00
static void _tf_ssb_private_message_decrypt_after_work ( tf_ssb_t * ssb , int status , void * user_data )
{
private_message_decrypt_t * work = user_data ;
JSContext * context = tf_ssb_get_context ( ssb ) ;
JSValue error = JS_UNDEFINED ;
if ( work - > error )
{
JSValue result = JS_ThrowInternalError ( context , " %s " , work - > error ) ;
error = JS_Call ( context , work - > promise [ 1 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
}
else if ( work - > decrypted )
{
JSValue result = JS_NewStringLen ( context , work - > decrypted , work - > decrypted_size ) ;
error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
JS_FreeValue ( context , result ) ;
}
else
{
JSValue result = JS_UNDEFINED ;
error = JS_Call ( context , work - > promise [ 0 ] , JS_UNDEFINED , 1 , & result ) ;
}
tf_util_report_error ( context , error ) ;
JS_FreeValue ( context , error ) ;
JS_FreeValue ( context , work - > promise [ 0 ] ) ;
JS_FreeValue ( context , work - > promise [ 1 ] ) ;
JS_FreeCString ( context , work - > user ) ;
JS_FreeCString ( context , work - > identity ) ;
JS_FreeCString ( context , work - > message ) ;
tf_free ( ( void * ) work - > decrypted ) ;
tf_free ( work ) ;
}
2023-02-26 14:51:54 -05:00
2024-06-16 17:22:26 -04:00
static JSValue _tf_ssb_private_message_decrypt ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
const char * user = JS_ToCString ( context , argv [ 0 ] ) ;
const char * identity = JS_ToCString ( context , argv [ 1 ] ) ;
size_t message_size = 0 ;
const char * message = JS_ToCStringLen ( context , & message_size , argv [ 2 ] ) ;
private_message_decrypt_t * work = tf_malloc ( sizeof ( private_message_decrypt_t ) ) ;
* work = ( private_message_decrypt_t ) {
. user = user ,
. identity = identity ,
. message_size = message_size ,
. message = message ,
} ;
JSValue result = JS_NewPromiseCapability ( context , work - > promise ) ;
tf_ssb_run_work ( ssb , _tf_ssb_private_message_decrypt_work , _tf_ssb_private_message_decrypt_after_work , work ) ;
2023-02-26 14:51:54 -05:00
return result ;
}
2023-10-29 15:05:32 -04:00
typedef struct _following_t
{
JSContext * context ;
JSValue promise [ 2 ] ;
2023-11-02 20:45:30 -04:00
tf_ssb_following_t * out_following ;
2023-10-29 15:05:32 -04:00
int depth ;
int ids_count ;
const char * ids [ ] ;
} following_t ;
2024-05-08 21:00:37 -04:00
static void _tf_ssb_following_work ( tf_ssb_t * ssb , void * user_data )
2023-10-29 15:05:32 -04:00
{
2024-05-08 21:00:37 -04:00
following_t * following = user_data ;
following - > out_following = tf_ssb_db_following_deep ( ssb , following - > ids , following - > ids_count , following - > depth ) ;
2023-10-29 15:05:32 -04:00
}
2024-05-08 21:00:37 -04:00
static void _tf_ssb_following_after_work ( tf_ssb_t * ssb , int status , void * user_data )
2023-10-29 15:05:32 -04:00
{
2024-05-08 21:00:37 -04:00
following_t * following = user_data ;
2023-10-29 15:05:32 -04:00
JSContext * context = following - > context ;
if ( status = = 0 )
{
2023-11-02 20:45:30 -04:00
JSValue object = JS_NewObject ( context ) ;
for ( int i = 0 ; * following - > out_following [ i ] . id ; i + + )
2023-10-29 15:05:32 -04:00
{
2023-11-02 20:45:30 -04:00
JSValue entry = JS_NewObject ( context ) ;
JS_SetPropertyStr ( context , entry , " of " , JS_NewInt32 ( context , following - > out_following [ i ] . following_count ) ) ;
JS_SetPropertyStr ( context , entry , " ob " , JS_NewInt32 ( context , following - > out_following [ i ] . blocking_count ) ) ;
JS_SetPropertyStr ( context , entry , " if " , JS_NewInt32 ( context , following - > out_following [ i ] . followed_by_count ) ) ;
JS_SetPropertyStr ( context , entry , " ib " , JS_NewInt32 ( context , following - > out_following [ i ] . blocked_by_count ) ) ;
JS_SetPropertyStr ( context , object , following - > out_following [ i ] . id , entry ) ;
2023-10-29 15:05:32 -04:00
}
2024-01-07 21:30:08 -05:00
JSValue result = JS_Call ( context , following - > promise [ 0 ] , JS_UNDEFINED , 1 , & object ) ;
tf_util_report_error ( context , result ) ;
JS_FreeValue ( context , result ) ;
2023-11-02 20:45:30 -04:00
JS_FreeValue ( context , object ) ;
2023-10-29 15:05:32 -04:00
}
else
{
char buffer [ 256 ] ;
uv_strerror_r ( status , buffer , sizeof ( buffer ) ) ;
JSValue message = JS_NewString ( context , buffer ) ;
2024-01-07 21:30:08 -05:00
JSValue result = JS_Call ( context , following - > promise [ 1 ] , JS_UNDEFINED , 1 , & message ) ;
tf_util_report_error ( context , result ) ;
JS_FreeValue ( context , result ) ;
2023-10-29 15:05:32 -04:00
JS_FreeValue ( context , message ) ;
}
2024-01-09 21:49:44 -05:00
JS_FreeValue ( context , following - > promise [ 0 ] ) ;
JS_FreeValue ( context , following - > promise [ 1 ] ) ;
2023-10-29 15:05:32 -04:00
for ( int i = 0 ; i < following - > ids_count ; i + + )
{
tf_free ( ( void * ) following - > ids [ i ] ) ;
}
2023-11-02 20:45:30 -04:00
tf_free ( following - > out_following ) ;
2023-10-29 15:05:32 -04:00
tf_free ( following ) ;
}
static JSValue _tf_ssb_following ( JSContext * context , JSValueConst this_val , int argc , JSValueConst * argv )
{
tf_ssb_t * ssb = JS_GetOpaque ( this_val , _tf_ssb_classId ) ;
int ids_count = tf_util_get_length ( context , argv [ 0 ] ) ;
following_t * following = tf_malloc ( sizeof ( following_t ) + sizeof ( char * ) * ids_count ) ;
2024-05-08 21:00:37 -04:00
* following = ( following_t ) {
2023-10-29 15:05:32 -04:00
. context = context ,
} ;
JS_ToInt32 ( context , & following - > depth , argv [ 1 ] ) ;
JSValue result = JS_NewPromiseCapability ( context , following - > promise ) ;
for ( int i = 0 ; i < ids_count ; i + + )
{
JSValue id_value = JS_GetPropertyUint32 ( context , argv [ 0 ] , i ) ;
if ( ! JS_IsUndefined ( id_value ) )
{
const char * id_string = JS_ToCString ( context , id_value ) ;
following - > ids [ following - > ids_count + + ] = tf_strdup ( id_string ) ;
JS_FreeCString ( context , id_string ) ;
JS_FreeValue ( context , id_value ) ;
}
}
2024-05-08 21:00:37 -04:00
tf_ssb_run_work ( ssb , _tf_ssb_following_work , _tf_ssb_following_after_work , following ) ;
2023-10-29 15:05:32 -04:00
return result ;
}
2021-10-24 11:46:30 -04:00
void tf_ssb_register ( JSContext * context , tf_ssb_t * ssb )
2021-01-02 13:10:00 -05:00
{
JS_NewClassID ( & _tf_ssb_classId ) ;
2024-02-15 18:35:01 -05:00
JSClassDef def = {
2021-01-02 13:10:00 -05:00
. class_name = " ssb " ,
} ;
2021-10-10 17:51:38 -04:00
if ( JS_NewClass ( JS_GetRuntime ( context ) , _tf_ssb_classId , & def ) ! = 0 )
{
2021-01-02 13:10:00 -05:00
fprintf ( stderr , " Failed to register ssb. \n " ) ;
}
JSValue global = JS_GetGlobalObject ( context ) ;
JSValue object = JS_NewObjectClass ( context , _tf_ssb_classId ) ;
JS_SetPropertyStr ( context , global , " ssb " , object ) ;
JS_SetOpaque ( object , ssb ) ;
2022-07-31 15:01:08 -04:00
/* Requires an identity. */
2022-07-13 21:01:14 -04:00
JS_SetPropertyStr ( context , object , " createIdentity " , JS_NewCFunction ( context , _tf_ssb_createIdentity , " createIdentity " , 1 ) ) ;
2024-01-03 20:17:30 -05:00
JS_SetPropertyStr ( context , object , " addIdentity " , JS_NewCFunction ( context , _tf_ssb_addIdentity , " addIdentity " , 2 ) ) ;
2024-01-06 14:22:49 -05:00
JS_SetPropertyStr ( context , object , " deleteIdentity " , JS_NewCFunction ( context , _tf_ssb_deleteIdentity , " deleteIdentity " , 2 ) ) ;
2023-10-20 10:37:24 -04:00
JS_SetPropertyStr ( context , object , " setServerFollowingMe " , JS_NewCFunction ( context , _tf_ssb_set_server_following_me , " setServerFollowingMe " , 3 ) ) ;
2022-07-13 21:01:14 -04:00
JS_SetPropertyStr ( context , object , " getIdentities " , JS_NewCFunction ( context , _tf_ssb_getIdentities , " getIdentities " , 1 ) ) ;
2024-01-03 19:21:15 -05:00
JS_SetPropertyStr ( context , object , " getPrivateKey " , JS_NewCFunction ( context , _tf_ssb_getPrivateKey , " getPrivateKey " , 2 ) ) ;
2023-02-26 14:51:54 -05:00
JS_SetPropertyStr ( context , object , " privateMessageEncrypt " , JS_NewCFunction ( context , _tf_ssb_private_message_encrypt , " privateMessageEncrypt " , 4 ) ) ;
JS_SetPropertyStr ( context , object , " privateMessageDecrypt " , JS_NewCFunction ( context , _tf_ssb_private_message_decrypt , " privateMessageDecrypt " , 3 ) ) ;
2023-07-18 19:46:15 -04:00
/* Write. */
JS_SetPropertyStr ( context , object , " appendMessageWithIdentity " , JS_NewCFunction ( context , _tf_ssb_appendMessageWithIdentity , " appendMessageWithIdentity " , 3 ) ) ;
2022-07-13 21:01:14 -04:00
2022-07-31 15:01:08 -04:00
/* Does not require an identity. */
2023-10-20 08:55:05 -04:00
JS_SetPropertyStr ( context , object , " getServerIdentity " , JS_NewCFunction ( context , _tf_ssb_getServerIdentity , " getServerIdentity " , 0 ) ) ;
2022-07-31 15:01:08 -04:00
JS_SetPropertyStr ( context , object , " getAllIdentities " , JS_NewCFunction ( context , _tf_ssb_getAllIdentities , " getAllIdentities " , 0 ) ) ;
2024-05-05 12:55:32 -04:00
JS_SetPropertyStr ( context , object , " getActiveIdentity " , JS_NewCFunction ( context , _tf_ssb_getActiveIdentity , " getActiveIdentity " , 3 ) ) ;
2024-05-05 13:48:22 -04:00
JS_SetPropertyStr ( context , object , " getIdentityInfo " , JS_NewCFunction ( context , _tf_ssb_getIdentityInfo , " getIdentityInfo " , 3 ) ) ;
2021-01-02 13:10:00 -05:00
JS_SetPropertyStr ( context , object , " blobGet " , JS_NewCFunction ( context , _tf_ssb_blobGet , " blobGet " , 1 ) ) ;
JS_SetPropertyStr ( context , object , " connections " , JS_NewCFunction ( context , _tf_ssb_connections , " connections " , 0 ) ) ;
2023-01-17 19:37:45 -05:00
JS_SetPropertyStr ( context , object , " storedConnections " , JS_NewCFunction ( context , _tf_ssb_storedConnections , " storedConnections " , 0 ) ) ;
2022-11-02 19:34:44 -04:00
JS_SetPropertyStr ( context , object , " getConnection " , JS_NewCFunction ( context , _tf_ssb_getConnection , " getConnection " , 1 ) ) ;
2022-11-08 20:31:18 -05:00
JS_SetPropertyStr ( context , object , " closeConnection " , JS_NewCFunction ( context , _tf_ssb_closeConnection , " closeConnection " , 1 ) ) ;
2023-01-17 19:37:45 -05:00
JS_SetPropertyStr ( context , object , " forgetStoredConnection " , JS_NewCFunction ( context , _tf_ssb_forgetStoredConnection , " forgetStoredConnection " , 1 ) ) ;
2023-02-07 20:29:44 -05:00
JS_SetPropertyStr ( context , object , " sqlAsync " , JS_NewCFunction ( context , _tf_ssb_sqlAsync , " sqlAsync " , 3 ) ) ;
2021-01-02 13:10:00 -05:00
JS_SetPropertyStr ( context , object , " getBroadcasts " , JS_NewCFunction ( context , _tf_ssb_getBroadcasts , " getBroadcasts " , 0 ) ) ;
JS_SetPropertyStr ( context , object , " connect " , JS_NewCFunction ( context , _tf_ssb_connect , " connect " , 1 ) ) ;
2022-11-30 20:19:35 -05:00
JS_SetPropertyStr ( context , object , " createTunnel " , JS_NewCFunction ( context , _tf_ssb_createTunnel , " createTunnel " , 3 ) ) ;
2023-10-29 15:05:32 -04:00
JS_SetPropertyStr ( context , object , " following " , JS_NewCFunction ( context , _tf_ssb_following , " following " , 2 ) ) ;
2023-07-18 19:46:15 -04:00
/* Write. */
JS_SetPropertyStr ( context , object , " storeMessage " , JS_NewCFunction ( context , _tf_ssb_storeMessage , " storeMessage " , 1 ) ) ;
2023-09-07 18:44:18 -04:00
JS_SetPropertyStr ( context , object , " blobStore " , JS_NewCFunction ( context , _tf_ssb_blobStore , " blobStore " , 1 ) ) ;
2021-11-07 17:28:58 -05:00
2024-05-22 18:51:21 -04:00
/* Trusted only. */
2021-11-07 17:28:58 -05:00
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 ) ) ;
2021-09-06 13:50:38 -04:00
2021-01-02 13:10:00 -05:00
JS_FreeValue ( context , global ) ;
}