2021-08-22 15:34:28 -04:00
# include "ssb.db.h"
2022-06-04 13:04:51 -04:00
# include "mem.h"
2021-08-22 15:34:28 -04:00
# include "ssb.h"
# include "trace.h"
2022-07-09 11:13:35 -04:00
# include "util.js.h"
2021-08-22 15:34:28 -04:00
# include <base64c.h>
# include <sodium/crypto_hash_sha256.h>
2022-08-14 22:23:45 -04:00
# include <sodium/crypto_scalarmult.h>
# include <sodium/crypto_scalarmult_curve25519.h>
# include <sodium/crypto_secretbox.h>
2022-07-13 21:01:14 -04:00
# include <sodium/crypto_sign.h>
2021-08-22 15:34:28 -04:00
# include <sqlite3.h>
# include <stdlib.h>
# include <string.h>
2022-02-11 20:44:11 -05:00
static void _tf_ssb_db_exec ( sqlite3 * db , const char * statement )
{
char * error = NULL ;
int result = sqlite3_exec ( db , statement , NULL , NULL , & error ) ;
if ( result ! = SQLITE_OK )
{
printf ( " Error running '%s': %s. \n " , statement , error ) ;
abort ( ) ;
}
}
2022-09-10 13:56:54 -04:00
static bool _tf_ssb_db_has_rows ( sqlite3 * db , const char * query )
{
bool found = false ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , query , - 1 , & statement , NULL ) = = SQLITE_OK )
{
int result = SQLITE_OK ;
while ( ( result = sqlite3_step ( statement ) ) = = SQLITE_ROW )
{
found = true ;
}
if ( result ! = SQLITE_DONE )
{
printf ( " %s \n " , sqlite3_errmsg ( db ) ) ;
abort ( ) ;
}
sqlite3_finalize ( statement ) ;
}
else
{
printf ( " %s \n " , sqlite3_errmsg ( db ) ) ;
abort ( ) ;
}
return found ;
}
2021-08-22 15:34:28 -04:00
void tf_ssb_db_init ( tf_ssb_t * ssb )
{
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
2022-02-11 20:44:11 -05:00
_tf_ssb_db_exec ( db , " PRAGMA journal_mode = WAL " ) ;
_tf_ssb_db_exec ( db , " PRAGMA synchronous = NORMAL " ) ;
_tf_ssb_db_exec ( db ,
2021-08-22 15:34:28 -04:00
" CREATE TABLE IF NOT EXISTS messages ( "
" author TEXT, "
" id TEXT PRIMARY KEY, "
" sequence INTEGER, "
2022-02-11 20:44:11 -05:00
" timestamp REAL, "
2021-08-22 15:34:28 -04:00
" previous TEXT, "
" hash TEXT, "
" content TEXT, "
" signature TEXT, "
2022-02-11 20:44:11 -05:00
" sequence_before_author INTEGER, "
2021-08-22 15:34:28 -04:00
" UNIQUE(author, sequence) "
2022-02-11 20:44:11 -05:00
" ) " ) ;
_tf_ssb_db_exec ( db , " CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id) " ) ;
_tf_ssb_db_exec ( db , " CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence) " ) ;
_tf_ssb_db_exec ( db , " CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp) " ) ;
_tf_ssb_db_exec ( db ,
2021-08-22 15:34:28 -04:00
" CREATE TABLE IF NOT EXISTS blobs ( "
" id TEXT PRIMARY KEY, "
" content BLOB, "
" created INTEGER "
2022-02-11 20:44:11 -05:00
" ) " ) ;
_tf_ssb_db_exec ( db ,
2021-09-06 13:50:38 -04:00
" CREATE TABLE IF NOT EXISTS blob_wants ( "
" id TEXT PRIMARY KEY "
2022-02-11 20:44:11 -05:00
" ) " ) ;
_tf_ssb_db_exec ( db ,
2021-08-22 15:34:28 -04:00
" CREATE TABLE IF NOT EXISTS properties ( "
" id TEXT, "
" key TEXT, "
" value TEXT, "
" UNIQUE(id, key) "
2022-02-11 20:44:11 -05:00
" ) " ) ;
_tf_ssb_db_exec ( db ,
2021-08-22 15:34:28 -04:00
" CREATE TABLE IF NOT EXISTS connections ( "
" host TEXT, "
" port INTEGER, "
" key TEXT, "
" last_attempt INTEGER, "
" last_success INTEGER, "
" UNIQUE(host, port, key) "
2022-02-11 20:44:11 -05:00
" ) " ) ;
2022-07-13 21:01:14 -04:00
_tf_ssb_db_exec ( db ,
" CREATE TABLE IF NOT EXISTS identities ( "
" user TEXT, "
" public_key TEXT UNIQUE, "
" private_key TEXT UNIQUE "
" ) " ) ;
_tf_ssb_db_exec ( db , " CREATE INDEX IF NOT EXISTS identities_user ON identities (user, public_key) " ) ;
2022-02-11 20:44:11 -05:00
2022-10-12 09:04:45 -04:00
bool populate_fts = false ;
if ( ! _tf_ssb_db_has_rows ( db , " PRAGMA table_list('messages_fts') " ) )
2022-09-10 13:56:54 -04:00
{
_tf_ssb_db_exec ( db , " CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(content, content=messages, content_rowid=rowid) " ) ;
2022-10-12 09:04:45 -04:00
populate_fts = true ;
}
2022-11-02 19:34:44 -04:00
if ( ! populate_fts & & /* HACK */ false )
2022-10-12 09:04:45 -04:00
{
printf ( " Checking FTS5 integrity... \n " ) ;
if ( sqlite3_exec ( db , " INSERT INTO messages_fts(messages_fts, rank) VALUES ('integrity-check', 0) " , NULL , NULL , NULL ) = = SQLITE_CORRUPT_VTAB )
{
populate_fts = true ;
}
printf ( " Done. \n " ) ;
}
if ( populate_fts )
{
2022-09-10 13:56:54 -04:00
printf ( " Populating full-text search... \n " ) ;
_tf_ssb_db_exec ( db , " INSERT INTO messages_fts (rowid, content) SELECT rowid, content FROM messages " ) ;
printf ( " Done. \n " ) ;
}
_tf_ssb_db_exec ( db , " CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN INSERT INTO messages_fts(rowid, content) VALUES (new.rowid, new.content); END " ) ;
_tf_ssb_db_exec ( db , " CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES ('delete', old.rowid, old.content); END " ) ;
2022-10-09 08:53:59 -04:00
if ( ! _tf_ssb_db_has_rows ( db , " PRAGMA table_list('messages_refs') " ) )
{
_tf_ssb_db_exec ( db ,
" CREATE TABLE IF NOT EXISTS messages_refs ( "
" message TEXT, "
" ref TEXT, "
" UNIQUE(message, ref) "
" ) " ) ;
printf ( " Populating messages_refs... \n " ) ;
_tf_ssb_db_exec ( db , " INSERT INTO messages_refs(message, ref) "
2022-12-07 18:24:31 -05:00
" SELECT messages.id, j.value FROM messages, json_tree(messages.content) as j WHERE "
2022-10-09 08:53:59 -04:00
" j.value LIKE '&%.sha256' OR "
" j.value LIKE '%%%.sha256' OR "
" j.value LIKE '@%.ed25519' "
" ON CONFLICT DO NOTHING " ) ;
printf ( " Done. \n " ) ;
}
2022-10-15 15:28:57 -04:00
_tf_ssb_db_exec ( db , " DROP TRIGGER IF EXISTS messages_ai_refs " ) ;
_tf_ssb_db_exec ( db , " CREATE TRIGGER IF NOT EXISTS messages_ai_refs AFTER INSERT ON messages BEGIN "
" INSERT INTO messages_refs(message, ref) "
2022-12-07 18:24:31 -05:00
" SELECT DISTINCT new.id, j.value FROM json_tree(new.content) as j WHERE "
2022-10-15 15:28:57 -04:00
" j.value LIKE '&%.sha256' OR "
" j.value LIKE '%%%.sha256' OR "
" j.value LIKE '@%.ed25519' "
" ON CONFLICT DO NOTHING; END " ) ;
2022-10-14 12:42:31 -04:00
_tf_ssb_db_exec ( db , " DROP TRIGGER IF EXISTS messages_ad_refs " ) ;
_tf_ssb_db_exec ( db , " CREATE TRIGGER IF NOT EXISTS messages_ad_refs AFTER DELETE ON messages BEGIN DELETE FROM messages_refs WHERE messages_refs.message = old.id; END " ) ;
2022-10-21 19:30:22 -04:00
_tf_ssb_db_exec ( db , " CREATE INDEX IF NOT EXISTS messages_refs_message_idx ON messages_refs (message) " ) ;
_tf_ssb_db_exec ( db , " CREATE INDEX IF NOT EXISTS messages_refs_ref_idx ON messages_refs (ref) " ) ;
2022-02-11 20:44:11 -05:00
bool need_add_sequence_before_author = true ;
bool need_convert_timestamp_to_real = false ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " PRAGMA table_info(messages) " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
2022-02-11 21:51:43 -05:00
int result = SQLITE_OK ;
while ( ( result = sqlite3_step ( statement ) ) = = SQLITE_ROW )
2022-02-11 20:44:11 -05:00
{
const char * name = ( const char * ) sqlite3_column_text ( statement , 1 ) ;
2022-02-11 21:51:43 -05:00
const char * type = ( const char * ) sqlite3_column_text ( statement , 2 ) ;
2022-02-11 20:44:11 -05:00
if ( name & & type & & strcmp ( name , " timestamp " ) = = 0 & & strcmp ( type , " INTEGER " ) = = 0 )
{
need_convert_timestamp_to_real = true ;
}
if ( name & & strcmp ( name , " sequence_before_author " ) = = 0 )
{
need_add_sequence_before_author = false ;
}
}
sqlite3_finalize ( statement ) ;
}
if ( need_convert_timestamp_to_real )
{
2022-02-11 21:51:43 -05:00
printf ( " Converting timestamp column from INTEGER to REAL. \n " ) ;
_tf_ssb_db_exec ( db , " BEGIN TRANSACTION " ) ;
_tf_ssb_db_exec ( db , " DROP INDEX IF EXISTS messages_author_timestamp_index " ) ;
2022-02-11 20:44:11 -05:00
_tf_ssb_db_exec ( db , " ALTER TABLE messages ADD COLUMN timestamp_real REAL " ) ;
_tf_ssb_db_exec ( db , " UPDATE messages SET timestamp_real = timestamp " ) ;
2022-02-11 21:51:43 -05:00
_tf_ssb_db_exec ( db , " ALTER TABLE messages DROP COLUMN timestamp " ) ;
2022-02-11 20:44:11 -05:00
_tf_ssb_db_exec ( db , " ALTER TABLE messages RENAME COLUMN timestamp_real TO timestamp " ) ;
2022-02-11 21:51:43 -05:00
_tf_ssb_db_exec ( db , " CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp) " ) ;
_tf_ssb_db_exec ( db , " COMMIT TRANSACTION " ) ;
2022-02-11 20:44:11 -05:00
}
if ( need_add_sequence_before_author )
{
2022-02-11 21:51:43 -05:00
printf ( " Adding sequence_before_author column. \n " ) ;
2022-02-11 20:44:11 -05:00
_tf_ssb_db_exec ( db , " ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER " ) ;
}
2021-08-22 15:34:28 -04:00
}
2022-02-05 22:51:25 -05:00
static bool _tf_ssb_db_previous_message_exists ( sqlite3 * db , const char * author , int64_t sequence , const char * previous )
{
bool exists = false ;
if ( sequence = = 1 )
{
exists = true ;
}
else
{
sqlite3_stmt * statement ;
if ( sqlite3_prepare ( db , " SELECT COUNT(*) FROM messages WHERE author = ?1 AND sequence = ?2 AND id = ?3 " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , author , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_int64 ( statement , 2 , sequence - 1 ) = = SQLITE_OK & &
sqlite3_bind_text ( statement , 3 , previous , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_step ( statement ) = = SQLITE_ROW )
{
exists = sqlite3_column_int ( statement , 0 ) ! = 0 ;
}
sqlite3_finalize ( statement ) ;
}
}
return exists ;
}
2022-02-02 21:00:05 -05:00
bool tf_ssb_db_store_message ( tf_ssb_t * ssb , JSContext * context , const char * id , JSValue val , const char * signature , bool sequence_before_author )
2021-08-22 15:34:28 -04:00
{
bool stored = false ;
JSValue previousval = JS_GetPropertyStr ( context , val , " previous " ) ;
const char * previous = JS_IsNull ( previousval ) ? NULL : JS_ToCString ( context , previousval ) ;
JSValue authorval = JS_GetPropertyStr ( context , val , " author " ) ;
const char * author = JS_ToCString ( context , authorval ) ;
int64_t sequence = - 1 ;
2022-06-26 14:25:31 -04:00
JSValue sequenceval = JS_GetPropertyStr ( context , val , " sequence " ) ;
JS_ToInt64 ( context , & sequence , sequenceval ) ;
JS_FreeValue ( context , sequenceval ) ;
2022-02-11 20:44:11 -05:00
double timestamp = - 1.0 ;
2022-06-26 14:25:31 -04:00
JSValue timestampval = JS_GetPropertyStr ( context , val , " timestamp " ) ;
JS_ToFloat64 ( context , & timestamp , timestampval ) ;
JS_FreeValue ( context , timestampval ) ;
2021-08-22 15:34:28 -04:00
JSValue contentval = JS_GetPropertyStr ( context , val , " content " ) ;
JSValue content = JS_JSONStringify ( context , contentval , JS_NULL , JS_NULL ) ;
size_t content_len ;
const char * contentstr = JS_ToCStringLen ( context , & content_len , content ) ;
JS_FreeValue ( context , contentval ) ;
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
sqlite3_stmt * statement ;
2021-09-06 13:50:38 -04:00
int64_t last_row_id = - 1 ;
2022-02-05 22:51:25 -05:00
if ( _tf_ssb_db_previous_message_exists ( db , author , sequence , previous ) )
2021-10-10 17:51:38 -04:00
{
2022-02-05 22:51:25 -05:00
const char * query = " INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature, sequence_before_author) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT DO NOTHING " ;
if ( sqlite3_prepare ( db , query , - 1 , & statement , NULL ) = = SQLITE_OK )
2021-10-10 17:51:38 -04:00
{
2022-02-05 22:51:25 -05:00
if ( sqlite3_bind_text ( statement , 1 , id , - 1 , NULL ) = = SQLITE_OK & &
( previous ? sqlite3_bind_text ( statement , 2 , previous , - 1 , NULL ) : sqlite3_bind_null ( statement , 2 ) ) = = SQLITE_OK & &
sqlite3_bind_text ( statement , 3 , author , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_int64 ( statement , 4 , sequence ) = = SQLITE_OK & &
2022-02-11 20:44:11 -05:00
sqlite3_bind_double ( statement , 5 , timestamp ) = = SQLITE_OK & &
2022-02-05 22:51:25 -05:00
sqlite3_bind_text ( statement , 6 , contentstr , content_len , NULL ) = = SQLITE_OK & &
sqlite3_bind_text ( statement , 7 , " sha256 " , 6 , NULL ) = = SQLITE_OK & &
sqlite3_bind_text ( statement , 8 , signature , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_int ( statement , 9 , sequence_before_author ) = = SQLITE_OK )
2021-09-06 13:50:38 -04:00
{
2022-02-05 22:51:25 -05:00
int r = sqlite3_step ( statement ) ;
if ( r ! = SQLITE_DONE )
{
printf ( " %s \n " , sqlite3_errmsg ( db ) ) ;
}
stored = r = = SQLITE_DONE & & sqlite3_changes ( db ) ! = 0 ;
if ( stored )
{
last_row_id = sqlite3_last_insert_rowid ( db ) ;
}
2021-09-06 13:50:38 -04:00
}
2022-05-20 21:38:13 -04:00
else
{
printf ( " bind failed \n " ) ;
}
2022-02-05 22:51:25 -05:00
sqlite3_finalize ( statement ) ;
}
else
{
printf ( " prepare failed: %s \n " , sqlite3_errmsg ( db ) ) ;
2021-08-22 15:34:28 -04:00
}
2021-10-10 17:51:38 -04:00
}
else
{
2022-02-05 22:51:25 -05:00
printf ( " Previous message doesn't exist. \n " ) ;
2021-08-22 15:34:28 -04:00
}
2021-09-06 13:50:38 -04:00
if ( last_row_id ! = - 1 )
{
2022-02-05 22:51:25 -05:00
const char * query = " INSERT INTO blob_wants (id) SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND json.value LIKE '&%%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL ON CONFLICT DO NOTHING RETURNING id " ;
2021-10-10 17:51:38 -04:00
if ( sqlite3_prepare ( db , query , - 1 , & statement , NULL ) = = SQLITE_OK )
{
2021-09-06 13:50:38 -04:00
if ( sqlite3_bind_int64 ( statement , 1 , last_row_id ) = = SQLITE_OK & &
2021-10-24 15:23:21 -04:00
sqlite3_bind_int ( statement , 2 , k_blob_id_len - 1 ) = = SQLITE_OK )
2021-10-10 17:51:38 -04:00
{
2021-09-06 13:50:38 -04:00
int r = SQLITE_OK ;
while ( ( r = sqlite3_step ( statement ) ) = = SQLITE_ROW )
{
tf_ssb_notify_blob_want_added ( ssb , ( const char * ) sqlite3_column_text ( statement , 0 ) ) ;
}
2021-10-10 17:51:38 -04:00
if ( r ! = SQLITE_DONE )
{
2021-09-06 13:50:38 -04:00
printf ( " %s \n " , sqlite3_errmsg ( db ) ) ;
}
}
sqlite3_finalize ( statement ) ;
2021-10-10 17:51:38 -04:00
}
else
{
2021-09-06 13:50:38 -04:00
printf ( " prepare failed: %s \n " , sqlite3_errmsg ( db ) ) ;
}
}
2021-08-22 15:34:28 -04:00
JS_FreeValue ( context , previousval ) ;
JS_FreeCString ( context , author ) ;
JS_FreeValue ( context , authorval ) ;
JS_FreeCString ( context , previous ) ;
JS_FreeCString ( context , contentstr ) ;
JS_FreeValue ( context , content ) ;
return stored ;
}
2021-08-22 15:41:27 -04:00
bool tf_ssb_db_message_content_get ( tf_ssb_t * ssb , const char * id , uint8_t * * out_blob , size_t * out_size )
2021-08-22 15:34:28 -04:00
{
bool result = false ;
sqlite3_stmt * statement ;
const char * query = " SELECT content FROM messages WHERE id = ? " ;
2021-10-10 17:51:38 -04:00
if ( sqlite3_prepare ( tf_ssb_get_db ( ssb ) , query , - 1 , & statement , NULL ) = = SQLITE_OK )
{
2021-08-22 15:34:28 -04:00
if ( sqlite3_bind_text ( statement , 1 , id , - 1 , NULL ) = = SQLITE_OK & &
2021-10-10 17:51:38 -04:00
sqlite3_step ( statement ) = = SQLITE_ROW )
{
2021-08-22 15:34:28 -04:00
const uint8_t * blob = sqlite3_column_blob ( statement , 0 ) ;
int size = sqlite3_column_bytes ( statement , 0 ) ;
2021-10-10 17:51:38 -04:00
if ( out_blob )
{
2022-06-04 13:04:51 -04:00
* out_blob = tf_malloc ( size + 1 ) ;
2021-08-22 15:34:28 -04:00
memcpy ( * out_blob , blob , size ) ;
( * out_blob ) [ size ] = ' \0 ' ;
}
2021-10-10 17:51:38 -04:00
if ( out_size )
{
2021-08-22 15:34:28 -04:00
* out_size = size ;
}
result = true ;
}
sqlite3_finalize ( statement ) ;
}
return result ;
}
2022-11-16 20:49:34 -05:00
bool tf_ssb_db_blob_has ( tf_ssb_t * ssb , const char * id )
{
bool result = false ;
sqlite3_stmt * statement ;
const char * query = " SELECT COUNT(*) FROM blobs WHERE id = $1 " ;
if ( sqlite3_prepare ( tf_ssb_get_db ( ssb ) , query , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , id , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_step ( statement ) = = SQLITE_ROW )
{
result = sqlite3_column_int64 ( statement , 0 ) ! = 0 ;
}
sqlite3_finalize ( statement ) ;
}
return result ;
}
2021-08-22 15:41:27 -04:00
bool tf_ssb_db_blob_get ( tf_ssb_t * ssb , const char * id , uint8_t * * out_blob , size_t * out_size )
2021-08-22 15:34:28 -04:00
{
bool result = false ;
sqlite3_stmt * statement ;
const char * query = " SELECT content FROM blobs WHERE id = $1 " ;
2021-10-10 17:51:38 -04:00
if ( sqlite3_prepare ( tf_ssb_get_db ( ssb ) , query , - 1 , & statement , NULL ) = = SQLITE_OK )
{
2021-08-22 15:34:28 -04:00
if ( sqlite3_bind_text ( statement , 1 , id , - 1 , NULL ) = = SQLITE_OK & &
2021-10-10 17:51:38 -04:00
sqlite3_step ( statement ) = = SQLITE_ROW )
{
2021-08-22 15:34:28 -04:00
const uint8_t * blob = sqlite3_column_blob ( statement , 0 ) ;
int size = sqlite3_column_bytes ( statement , 0 ) ;
2021-10-10 17:51:38 -04:00
if ( out_blob )
{
2022-06-04 13:04:51 -04:00
* out_blob = tf_malloc ( size + 1 ) ;
2022-01-17 16:46:32 -05:00
if ( size )
{
memcpy ( * out_blob , blob , size ) ;
}
2021-08-22 15:34:28 -04:00
( * out_blob ) [ size ] = ' \0 ' ;
}
2021-10-10 17:51:38 -04:00
if ( out_size )
{
2021-08-22 15:34:28 -04:00
* out_size = size ;
}
result = true ;
}
sqlite3_finalize ( statement ) ;
}
return result ;
}
2022-10-12 08:27:32 -04:00
bool tf_ssb_db_blob_store ( tf_ssb_t * ssb , const uint8_t * blob , size_t size , char * out_id , size_t out_id_size , bool * out_new )
2021-08-22 15:34:28 -04:00
{
bool result = false ;
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
sqlite3_stmt * statement ;
uint8_t hash [ crypto_hash_sha256_BYTES ] ;
crypto_hash_sha256 ( hash , blob , size ) ;
char hash64 [ 256 ] ;
base64c_encode ( hash , sizeof ( hash ) , ( uint8_t * ) hash64 , sizeof ( hash64 ) ) ;
char id [ 512 ] ;
snprintf ( id , sizeof ( id ) , " &%s.sha256 " , hash64 ) ;
2022-01-08 20:32:33 -05:00
int rows = 0 ;
2021-10-31 17:15:18 -04:00
if ( sqlite3_prepare ( db , " INSERT INTO blobs (id, content, created) VALUES ($1, $2, CAST(strftime('%s') AS INTEGER)) ON CONFLICT DO NOTHING " , - 1 , & statement , NULL ) = = SQLITE_OK )
2021-10-10 17:51:38 -04:00
{
2021-08-22 15:34:28 -04:00
if ( sqlite3_bind_text ( statement , 1 , id , - 1 , NULL ) = = SQLITE_OK & &
2021-10-10 17:51:38 -04:00
sqlite3_bind_blob ( statement , 2 , blob , size , NULL ) = = SQLITE_OK )
{
2021-08-22 15:34:28 -04:00
result = sqlite3_step ( statement ) = = SQLITE_DONE ;
2022-01-08 20:32:33 -05:00
rows = sqlite3_changes ( db ) ;
2021-10-10 17:51:38 -04:00
}
else
{
2021-08-22 15:34:28 -04:00
printf ( " bind failed: %s \n " , sqlite3_errmsg ( db ) ) ;
}
sqlite3_finalize ( statement ) ;
2021-10-10 17:51:38 -04:00
}
else
{
2021-08-22 15:34:28 -04:00
printf ( " prepare failed: %s \n " , sqlite3_errmsg ( db ) ) ;
}
2022-10-12 08:27:32 -04:00
if ( rows & & ! out_new )
2022-01-08 20:32:33 -05:00
{
printf ( " blob stored %s %zd => %d \n " , id , size , result ) ;
}
2021-10-31 17:15:18 -04:00
if ( result )
{
if ( sqlite3_prepare ( db , " DELETE FROM blob_wants WHERE id = ?1 " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , id , - 1 , NULL ) = = SQLITE_OK )
{
sqlite3_step ( statement ) ;
}
sqlite3_finalize ( statement ) ;
}
}
2021-10-10 17:51:38 -04:00
if ( result & & out_id )
{
2021-08-22 15:34:28 -04:00
snprintf ( out_id , out_id_size , " %s " , id ) ;
}
2022-10-12 08:27:32 -04:00
if ( out_new )
{
* out_new = rows ! = 0 ;
}
2021-08-22 15:34:28 -04:00
return result ;
}
2022-02-11 20:44:11 -05:00
bool tf_ssb_db_get_message_by_author_and_sequence ( tf_ssb_t * ssb , const char * author , int64_t sequence , char * out_message_id , size_t out_message_id_size , double * out_timestamp , char * * out_content )
2021-08-22 15:34:28 -04:00
{
bool found = false ;
sqlite3_stmt * statement ;
const char * query = " SELECT id, timestamp, content FROM messages WHERE author = $1 AND sequence = $2 " ;
2021-10-10 17:51:38 -04:00
if ( sqlite3_prepare ( tf_ssb_get_db ( ssb ) , query , - 1 , & statement , NULL ) = = SQLITE_OK )
{
2021-08-22 15:34:28 -04:00
if ( sqlite3_bind_text ( statement , 1 , author , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_int64 ( statement , 2 , sequence ) = = SQLITE_OK & &
2021-10-10 17:51:38 -04:00
sqlite3_step ( statement ) = = SQLITE_ROW )
{
if ( out_message_id )
{
2021-08-22 15:34:28 -04:00
strncpy ( out_message_id , ( const char * ) sqlite3_column_text ( statement , 0 ) , out_message_id_size - 1 ) ;
}
2021-10-10 17:51:38 -04:00
if ( out_timestamp )
{
2022-02-11 20:44:11 -05:00
* out_timestamp = sqlite3_column_double ( statement , 1 ) ;
2021-08-22 15:34:28 -04:00
}
2021-10-10 17:51:38 -04:00
if ( out_content )
{
2022-06-04 13:04:51 -04:00
* out_content = tf_strdup ( ( const char * ) sqlite3_column_text ( statement , 2 ) ) ;
2021-08-22 15:34:28 -04:00
}
found = true ;
}
sqlite3_finalize ( statement ) ;
2021-10-10 17:51:38 -04:00
}
else
{
2021-08-22 15:34:28 -04:00
printf ( " prepare failed: %s \n " , sqlite3_errmsg ( tf_ssb_get_db ( ssb ) ) ) ;
}
return found ;
}
2021-08-22 15:41:27 -04:00
bool tf_ssb_db_get_latest_message_by_author ( tf_ssb_t * ssb , const char * author , int64_t * out_sequence , char * out_message_id , size_t out_message_id_size )
2021-08-22 15:34:28 -04:00
{
bool found = false ;
sqlite3_stmt * statement ;
const char * query = " SELECT id, sequence FROM messages WHERE author = $1 AND sequence = (SELECT MAX(sequence) FROM messages WHERE author = $1) " ;
2021-10-10 17:51:38 -04:00
if ( sqlite3_prepare ( tf_ssb_get_db ( ssb ) , query , - 1 , & statement , NULL ) = = SQLITE_OK )
{
2021-08-22 15:34:28 -04:00
if ( sqlite3_bind_text ( statement , 1 , author , - 1 , NULL ) = = SQLITE_OK & &
2021-10-10 17:51:38 -04:00
sqlite3_step ( statement ) = = SQLITE_ROW )
{
if ( out_sequence )
{
2021-08-22 15:34:28 -04:00
* out_sequence = sqlite3_column_int64 ( statement , 1 ) ;
}
2021-10-10 17:51:38 -04:00
if ( out_message_id )
{
2021-08-22 15:34:28 -04:00
strncpy ( out_message_id , ( const char * ) sqlite3_column_text ( statement , 0 ) , out_message_id_size - 1 ) ;
}
found = true ;
}
sqlite3_finalize ( statement ) ;
2021-10-10 17:51:38 -04:00
}
else
{
2021-08-22 15:34:28 -04:00
printf ( " prepare failed: %s \n " , sqlite3_errmsg ( tf_ssb_get_db ( ssb ) ) ) ;
}
return found ;
}
2022-09-03 21:58:11 -04:00
static JSValue _tf_ssb_sqlite_bind_json ( JSContext * context , sqlite3 * db , sqlite3_stmt * statement , JSValue binds )
2021-10-10 17:51:38 -04:00
{
if ( JS_IsUndefined ( binds ) )
{
2022-09-03 21:58:11 -04:00
return JS_UNDEFINED ;
2021-08-22 15:34:28 -04:00
}
2022-07-09 11:13:35 -04:00
if ( ! JS_IsArray ( context , binds ) )
{
2022-09-03 21:58:11 -04:00
return JS_ThrowTypeError ( context , " Expected bind parameters to be an array. " ) ;
2022-07-09 11:13:35 -04:00
}
2022-09-03 21:58:11 -04:00
JSValue result = JS_UNDEFINED ;
2022-07-09 11:13:35 -04:00
int32_t length = tf_util_get_length ( context , binds ) ;
2022-09-03 21:58:11 -04:00
for ( int i = 0 ; i < length & & JS_IsUndefined ( result ) ; i + + )
2021-10-10 17:51:38 -04:00
{
2022-07-09 11:13:35 -04:00
JSValue value = JS_GetPropertyUint32 ( context , binds , i ) ;
2022-09-03 21:58:11 -04:00
if ( JS_IsNumber ( value ) )
2021-10-10 17:51:38 -04:00
{
2022-09-03 21:58:11 -04:00
int64_t number = 0 ;
JS_ToInt64 ( context , & number , value ) ;
if ( sqlite3_bind_int64 ( statement , i + 1 , number ) ! = SQLITE_OK )
2021-10-10 17:51:38 -04:00
{
2022-09-03 21:58:11 -04:00
result = JS_ThrowInternalError ( context , " Failed to bind: %s. " , sqlite3_errmsg ( db ) ) ;
2021-10-10 17:51:38 -04:00
}
2022-07-09 11:13:35 -04:00
}
2022-09-03 21:58:11 -04:00
else if ( JS_IsBool ( value ) )
2022-07-09 11:13:35 -04:00
{
2022-09-03 21:58:11 -04:00
if ( sqlite3_bind_int ( statement , i + 1 , JS_ToBool ( context , value ) ? 1 : 0 ) ! = SQLITE_OK )
2021-10-10 17:51:38 -04:00
{
2022-09-03 21:58:11 -04:00
result = JS_ThrowInternalError ( context , " Failed to bind: %s. " , sqlite3_errmsg ( db ) ) ;
2021-08-22 15:34:28 -04:00
}
}
2022-07-09 11:13:35 -04:00
else if ( JS_IsNull ( value ) )
{
if ( sqlite3_bind_null ( statement , i + 1 ) ! = SQLITE_OK )
{
2022-09-03 21:58:11 -04:00
result = JS_ThrowInternalError ( context , " Failed to bind: %s. " , sqlite3_errmsg ( db ) ) ;
2022-07-09 11:13:35 -04:00
}
}
else
{
2022-09-03 21:58:11 -04:00
size_t str_len = 0 ;
const char * str = JS_ToCStringLen ( context , & str_len , value ) ;
if ( str )
{
if ( sqlite3_bind_text ( statement , i + 1 , str , str_len , SQLITE_TRANSIENT ) ! = SQLITE_OK )
{
result = JS_ThrowInternalError ( context , " Failed to bind: %s. " , sqlite3_errmsg ( db ) ) ;
}
JS_FreeCString ( context , str ) ;
}
else
{
result = JS_ThrowInternalError ( context , " Could not convert bind argument %d to string. " , i ) ;
}
2022-07-09 11:13:35 -04:00
}
JS_FreeValue ( context , value ) ;
2021-10-10 17:51:38 -04:00
}
2022-09-03 21:58:11 -04:00
return result ;
2021-08-22 15:34:28 -04:00
}
2021-10-10 17:51:38 -04:00
static JSValue _tf_ssb_sqlite_row_to_json ( JSContext * context , sqlite3_stmt * row )
{
2021-08-22 15:34:28 -04:00
JSValue result = JS_NewObject ( context ) ;
2021-10-10 17:51:38 -04:00
for ( int i = 0 ; i < sqlite3_column_count ( row ) ; i + + )
{
2021-08-22 15:34:28 -04:00
const char * name = sqlite3_column_name ( row , i ) ;
2021-10-10 17:51:38 -04:00
switch ( sqlite3_column_type ( row , i ) )
{
2021-08-22 15:34:28 -04:00
case SQLITE_INTEGER :
JS_SetPropertyStr ( context , result , name , JS_NewInt64 ( context , sqlite3_column_int64 ( row , i ) ) ) ;
break ;
case SQLITE_FLOAT :
JS_SetPropertyStr ( context , result , name , JS_NewFloat64 ( context , sqlite3_column_double ( row , i ) ) ) ;
break ;
case SQLITE_TEXT :
JS_SetPropertyStr ( context , result , name , JS_NewStringLen ( context , ( const char * ) sqlite3_column_text ( row , i ) , sqlite3_column_bytes ( row , i ) ) ) ;
break ;
case SQLITE_BLOB :
JS_SetPropertyStr ( context , result , name , JS_NewArrayBufferCopy ( context , sqlite3_column_blob ( row , i ) , sqlite3_column_bytes ( row , i ) ) ) ;
break ;
case SQLITE_NULL :
JS_SetPropertyStr ( context , result , name , JS_NULL ) ;
break ;
}
}
return result ;
}
static int _tf_ssb_sqlite_authorizer ( void * user_data , int action_code , const char * arg0 , const char * arg1 , const char * arg2 , const char * arg3 )
{
2022-10-21 19:30:22 -04:00
int result = SQLITE_DENY ;
2021-10-10 17:51:38 -04:00
switch ( action_code )
{
2021-08-22 15:34:28 -04:00
case SQLITE_SELECT :
case SQLITE_FUNCTION :
2022-10-21 19:30:22 -04:00
result = SQLITE_OK ;
break ;
2021-08-22 15:34:28 -04:00
case SQLITE_READ :
2022-10-21 19:30:22 -04:00
result = (
2022-09-03 21:36:55 -04:00
strcmp ( arg0 , " blob_wants " ) = = 0 | |
strcmp ( arg0 , " json_each " ) = = 0 | |
2022-12-07 18:24:31 -05:00
strcmp ( arg0 , " json_tree " ) = = 0 | |
2022-09-03 21:36:55 -04:00
strcmp ( arg0 , " messages " ) = = 0 | |
2022-09-10 13:56:54 -04:00
strcmp ( arg0 , " messages_fts " ) = = 0 | |
strcmp ( arg0 , " messages_fts_idx " ) = = 0 | |
2022-12-28 12:27:31 -05:00
strcmp ( arg0 , " messages_fts_config " ) = = 0 | |
2022-10-09 08:53:59 -04:00
strcmp ( arg0 , " messages_refs " ) = = 0 | |
2022-10-21 19:30:22 -04:00
strcmp ( arg0 , " messages_refs_message_idx " ) = = 0 | |
strcmp ( arg0 , " messages_refs_ref_idx " ) = = 0 | |
2022-09-03 21:36:55 -04:00
strcmp ( arg0 , " sqlite_master " ) = = 0 | |
false )
2021-09-06 13:50:38 -04:00
? SQLITE_OK : SQLITE_DENY ;
2021-08-22 15:34:28 -04:00
break ;
2022-09-10 13:56:54 -04:00
case SQLITE_PRAGMA :
2022-10-21 19:30:22 -04:00
result = strcmp ( arg0 , " data_version " ) = = 0 ? SQLITE_OK : SQLITE_DENY ;
break ;
2022-09-10 14:09:10 -04:00
case SQLITE_UPDATE :
2022-10-21 19:30:22 -04:00
result = strcmp ( arg0 , " sqlite_master " ) = = 0 ? SQLITE_OK : SQLITE_DENY ;
break ;
2021-08-22 15:34:28 -04:00
}
2022-10-21 19:30:22 -04:00
if ( result ! = SQLITE_OK )
{
printf ( " Denying sqlite access to %d %s %s %s %s \n " , action_code , arg0 , arg1 , arg2 , arg3 ) ;
fflush ( stdout ) ;
}
return result ;
2021-08-22 15:34:28 -04:00
}
2022-04-17 20:24:00 -04:00
JSValue tf_ssb_db_visit_query ( tf_ssb_t * ssb , const char * query , const JSValue binds , void ( * callback ) ( JSValue row , void * user_data ) , void * user_data )
2021-08-22 15:34:28 -04:00
{
2022-04-17 20:24:00 -04:00
JSValue result = JS_UNDEFINED ;
2021-08-22 15:34:28 -04:00
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
2022-04-17 20:24:00 -04:00
JSContext * context = tf_ssb_get_context ( ssb ) ;
2021-08-22 15:34:28 -04:00
sqlite3_stmt * statement ;
sqlite3_set_authorizer ( db , _tf_ssb_sqlite_authorizer , ssb ) ;
2021-10-10 17:51:38 -04:00
if ( sqlite3_prepare ( db , query , - 1 , & statement , NULL ) = = SQLITE_OK )
{
2022-09-03 21:58:11 -04:00
JSValue bind_result = _tf_ssb_sqlite_bind_json ( context , db , statement , binds ) ;
if ( JS_IsUndefined ( bind_result ) )
2021-10-10 17:51:38 -04:00
{
2022-04-17 20:24:00 -04:00
int r = SQLITE_OK ;
while ( ( r = sqlite3_step ( statement ) ) = = SQLITE_ROW )
2021-10-10 17:51:38 -04:00
{
2021-08-22 15:34:28 -04:00
JSValue row = _tf_ssb_sqlite_row_to_json ( context , statement ) ;
tf_trace_t * trace = tf_ssb_get_trace ( ssb ) ;
tf_trace_begin ( trace , " callback " ) ;
callback ( row , user_data ) ;
tf_trace_end ( trace ) ;
JS_FreeValue ( context , row ) ;
}
2022-04-17 20:24:00 -04:00
if ( r ! = SQLITE_DONE )
{
result = JS_ThrowInternalError ( context , " SQL Error %s: running \" %s \" . " , sqlite3_errmsg ( db ) , query ) ;
}
2021-08-22 15:34:28 -04:00
}
2022-09-03 21:58:11 -04:00
else
{
result = bind_result ;
}
2021-08-22 15:34:28 -04:00
sqlite3_finalize ( statement ) ;
2021-10-10 17:51:38 -04:00
}
else
{
2022-04-17 20:24:00 -04:00
result = JS_ThrowInternalError ( context , " SQL Error %s: preparing \" %s \" . " , sqlite3_errmsg ( db ) , query ) ;
2021-08-22 15:34:28 -04:00
}
sqlite3_set_authorizer ( db , NULL , NULL ) ;
2022-04-17 20:24:00 -04:00
return result ;
2021-08-22 15:34:28 -04:00
}
2022-02-09 22:58:33 -05:00
2022-02-11 20:44:11 -05:00
static JSValue _tf_ssb_format_message ( JSContext * context , const char * previous , const char * author , int64_t sequence , double timestamp , const char * hash , const char * content , const char * signature , bool sequence_before_author )
2022-02-09 22:58:33 -05:00
{
JSValue value = JS_NewObject ( context ) ;
JS_SetPropertyStr ( context , value , " previous " , previous ? JS_NewString ( context , previous ) : JS_NULL ) ;
if ( sequence_before_author )
{
JS_SetPropertyStr ( context , value , " sequence " , JS_NewInt64 ( context , sequence ) ) ;
JS_SetPropertyStr ( context , value , " author " , JS_NewString ( context , author ) ) ;
}
else
{
JS_SetPropertyStr ( context , value , " author " , JS_NewString ( context , author ) ) ;
JS_SetPropertyStr ( context , value , " sequence " , JS_NewInt64 ( context , sequence ) ) ;
}
2022-02-11 20:44:11 -05:00
JS_SetPropertyStr ( context , value , " timestamp " , JS_NewFloat64 ( context , timestamp ) ) ;
2022-02-09 22:58:33 -05:00
JS_SetPropertyStr ( context , value , " hash " , JS_NewString ( context , hash ) ) ;
JS_SetPropertyStr ( context , value , " content " , JS_ParseJSON ( context , content , strlen ( content ) , NULL ) ) ;
JS_SetPropertyStr ( context , value , " signature " , JS_NewString ( context , signature ) ) ;
return value ;
}
bool _tf_ssb_update_message_id ( sqlite3 * db , const char * old_id , const char * new_id )
{
bool success = false ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " UPDATE messages SET id = ? WHERE id = ? " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , new_id , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_text ( statement , 2 , old_id , - 1 , NULL ) = = SQLITE_OK )
{
success = sqlite3_step ( statement ) = = SQLITE_DONE ;
}
sqlite3_finalize ( statement ) ;
}
return success ;
}
2022-02-10 21:44:27 -05:00
bool tf_ssb_db_check ( sqlite3 * db , const char * check_author )
2022-02-09 22:58:33 -05:00
{
2022-06-20 13:57:07 -04:00
JSMallocFunctions funcs = { 0 } ;
tf_get_js_malloc_functions ( & funcs ) ;
JSRuntime * runtime = JS_NewRuntime2 ( & funcs , NULL ) ;
2022-02-09 22:58:33 -05:00
JSContext * context = JS_NewContext ( runtime ) ;
sqlite3_stmt * statement = NULL ;
2022-02-10 21:44:27 -05:00
int result = check_author ?
sqlite3_prepare ( db , " SELECT id, previous, author, sequence, timestamp, hash, content, signature, sequence_before_author FROM messages WHERE author = ? ORDER BY author, sequence " , - 1 , & statement , NULL ) :
sqlite3_prepare ( db , " SELECT id, previous, author, sequence, timestamp, hash, content, signature, sequence_before_author FROM messages ORDER BY author, sequence " , - 1 , & statement , NULL ) ;
if ( result = = SQLITE_OK )
2022-02-09 22:58:33 -05:00
{
2022-02-10 21:44:27 -05:00
if ( check_author )
{
sqlite3_bind_text ( statement , 1 , check_author , - 1 , NULL ) ;
}
2022-02-09 22:58:33 -05:00
char previous_id [ k_id_base64_len ] ;
2022-10-14 13:39:08 -04:00
int64_t previous_sequence = - 1 ;
char previous_author [ k_id_base64_len ] = { 0 } ;
2022-02-09 22:58:33 -05:00
while ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
const char * id = ( const char * ) sqlite3_column_text ( statement , 0 ) ;
const char * previous = ( const char * ) sqlite3_column_text ( statement , 1 ) ;
const char * author = ( const char * ) sqlite3_column_text ( statement , 2 ) ;
int64_t sequence = sqlite3_column_int64 ( statement , 3 ) ;
2022-02-11 20:44:11 -05:00
double timestamp = sqlite3_column_double ( statement , 4 ) ;
2022-02-09 22:58:33 -05:00
const char * hash = ( const char * ) sqlite3_column_text ( statement , 5 ) ;
const char * content = ( const char * ) sqlite3_column_text ( statement , 6 ) ;
const char * signature = ( const char * ) sqlite3_column_text ( statement , 7 ) ;
bool sequence_before_author = sqlite3_column_int ( statement , 8 ) ;
JSValue message = _tf_ssb_format_message ( context , previous , author , sequence , timestamp , hash , content , signature , sequence_before_author ) ;
char out_signature [ 512 ] ;
char actual_id [ k_id_base64_len ] ;
bool actual_sequence_before_author = false ;
JSValue j = JS_JSONStringify ( context , message , JS_NULL , JS_NewInt32 ( context , 2 ) ) ;
const char * jv = JS_ToCString ( context , j ) ;
2022-10-14 13:39:08 -04:00
bool delete_following = false ;
if ( strcmp ( author , previous_author ) )
2022-02-09 22:58:33 -05:00
{
2022-10-14 13:39:08 -04:00
printf ( " %s \n " , author ) ;
}
if ( strcmp ( author , previous_author ) = = 0 & & sequence ! = previous_sequence + 1 )
{
printf ( " Detected gap in messages for %s at sequence = % " PRId64 " => % " PRId64 " . \n " , author , previous_sequence , sequence ) ;
delete_following = true ;
}
else
{
if ( tf_ssb_verify_and_strip_signature ( context , message , actual_id , sizeof ( actual_id ) , out_signature , sizeof ( out_signature ) , & actual_sequence_before_author ) )
2022-02-09 22:58:33 -05:00
{
2022-10-14 13:39:08 -04:00
if ( previous & & strcmp ( previous , previous_id ) )
2022-02-09 22:58:33 -05:00
{
2022-10-14 13:39:08 -04:00
printf ( " %s:%d previous was %s should be %s \n " , id , ( int ) sequence , previous_id , previous ) ;
2022-02-09 22:58:33 -05:00
}
2022-10-14 13:39:08 -04:00
if ( strcmp ( id , actual_id ) )
2022-02-09 22:58:33 -05:00
{
2022-10-14 13:39:08 -04:00
if ( _tf_ssb_update_message_id ( db , id , actual_id ) )
{
printf ( " updated %s to %s \n " , id , actual_id ) ;
}
else
{
printf ( " failed to update %s to %s \n " , id , actual_id ) ;
}
2022-02-09 22:58:33 -05:00
}
}
2022-10-14 13:39:08 -04:00
else
{
printf ( " %s sequence=% " PRId64 " unable to verify signature for %s sequence_before_author=%d message=[%.*s] \n " , author , sequence , id , sequence_before_author , ( int ) strlen ( jv ) , jv ) ;
delete_following = true ;
}
2022-02-09 22:58:33 -05:00
}
2022-02-11 21:51:43 -05:00
2022-10-14 13:39:08 -04:00
if ( delete_following )
{
2022-02-11 21:51:43 -05:00
printf ( " Deleting author = %s sequence >= % " PRId64 " . \n " , author , sequence ) ;
sqlite3_stmt * delete_statement = NULL ;
if ( sqlite3_prepare ( db , " DELETE FROM messages WHERE author = ? AND sequence >= ? " , - 1 , & delete_statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( delete_statement , 1 , author , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_int64 ( delete_statement , 2 , sequence ) = = SQLITE_OK )
{
if ( sqlite3_step ( delete_statement ) ! = SQLITE_DONE )
{
printf ( " Error deleting author = %s sequence >= % " PRId64 " . \n " , author , sequence ) ;
}
}
sqlite3_finalize ( delete_statement ) ;
}
2022-02-09 22:58:33 -05:00
}
2022-10-14 13:39:08 -04:00
snprintf ( previous_author , sizeof ( previous_author ) , " %s " , author ) ;
previous_sequence = sequence ;
2022-02-09 22:58:33 -05:00
JS_FreeCString ( context , jv ) ;
JS_FreeValue ( context , j ) ;
snprintf ( previous_id , sizeof ( previous_id ) , " %s " , id ) ;
JS_FreeValue ( context , message ) ;
}
sqlite3_finalize ( statement ) ;
}
JS_FreeContext ( context ) ;
JS_FreeRuntime ( runtime ) ;
return false ;
}
2022-07-13 21:01:14 -04:00
int tf_ssb_db_identity_get_count_for_user ( tf_ssb_t * ssb , const char * user )
{
int count = 0 ;
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " SELECT COUNT(*) FROM identities WHERE user = ? " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , user , - 1 , NULL ) = = SQLITE_OK )
{
if ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
count = sqlite3_column_int ( statement , 0 ) ;
}
}
sqlite3_finalize ( statement ) ;
}
return count ;
}
2022-10-14 08:27:34 -04:00
bool tf_ssb_db_identity_create ( tf_ssb_t * ssb , const char * user , uint8_t * out_public_key , uint8_t * out_private_key )
{
int count = tf_ssb_db_identity_get_count_for_user ( ssb , user ) ;
if ( count < 16 )
{
char public [ 512 ] ;
char private [ 512 ] ;
tf_ssb_generate_keys_buffer ( public , sizeof ( public ) , private , sizeof ( private ) ) ;
if ( tf_ssb_db_identity_add ( ssb , user , public , private ) )
{
tf_ssb_id_str_to_bin ( out_public_key , public ) ;
tf_ssb_id_str_to_bin ( out_private_key , private ) ;
return true ;
}
}
return false ;
}
2022-07-13 21:01:14 -04:00
bool tf_ssb_db_identity_add ( tf_ssb_t * ssb , const char * user , const char * public_key , const char * private_key )
{
bool added = false ;
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " INSERT INTO identities (user, public_key, private_key) VALUES (?, ?, ?) ON CONFLICT DO NOTHING " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , user , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_text ( statement , 2 , public_key , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_text ( statement , 3 , private_key , - 1 , NULL ) = = SQLITE_OK )
{
added =
sqlite3_step ( statement ) = = SQLITE_DONE & &
sqlite3_changes ( db ) ! = 0 ;
if ( ! added )
{
printf ( " Unable to add identity: %s. \n " , sqlite3_errmsg ( db ) ) ;
}
}
sqlite3_finalize ( statement ) ;
}
return added ;
}
void tf_ssb_db_identity_visit ( tf_ssb_t * ssb , const char * user , void ( * callback ) ( const char * identity , void * user_data ) , void * user_data )
{
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " SELECT public_key FROM identities WHERE user = ? ORDER BY public_key " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , user , - 1 , NULL ) = = SQLITE_OK )
{
while ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
callback ( ( const char * ) sqlite3_column_text ( statement , 0 ) , user_data ) ;
}
}
sqlite3_finalize ( statement ) ;
}
}
2022-07-31 15:01:08 -04:00
void tf_ssb_db_identity_visit_all ( tf_ssb_t * ssb , void ( * callback ) ( const char * identity , void * user_data ) , void * user_data )
{
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " SELECT public_key FROM identities ORDER BY public_key " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
while ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
callback ( ( const char * ) sqlite3_column_text ( statement , 0 ) , user_data ) ;
}
sqlite3_finalize ( statement ) ;
}
}
2022-07-13 21:01:14 -04:00
bool tf_ssb_db_identity_get_private_key ( tf_ssb_t * ssb , const char * user , const char * public_key , uint8_t * out_private_key , size_t private_key_size )
{
bool success = false ;
memset ( out_private_key , 0 , crypto_sign_SECRETKEYBYTES ) ;
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " SELECT private_key FROM identities WHERE user = ? AND public_key = ? " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , user , - 1 , NULL ) = = SQLITE_OK & &
sqlite3_bind_text ( statement , 2 , ( public_key & & * public_key = = ' @ ' ) ? public_key + 1 : public_key , - 1 , NULL ) = = SQLITE_OK )
{
if ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
const uint8_t * key = sqlite3_column_text ( statement , 0 ) ;
int r = base64c_decode ( key , sqlite3_column_bytes ( statement , 0 ) - strlen ( " .ed25519 " ) , out_private_key , private_key_size ) ;
success = r > 0 ;
}
}
sqlite3_finalize ( statement ) ;
}
return success ;
}
2022-08-14 22:23:45 -04:00
2022-12-31 16:44:48 -05:00
typedef struct _following_t following_t ;
typedef struct _following_t
{
char id [ k_id_base64_len ] ;
following_t * * following ;
following_t * * blocking ;
int following_count ;
int blocking_count ;
int depth ;
} following_t ;
static int _following_compare ( const void * a , const void * b )
{
const char * ida = a ;
const following_t * const * followingb = b ;
return strcmp ( ida , ( * followingb ) - > id ) ;
}
static void _add_following_entry ( following_t * * * list , int * count , following_t * add )
{
int index = tf_util_insert_index ( add - > id , * list , * count , sizeof ( following_t * ) , _following_compare ) ;
if ( index > = * count | | strcmp ( add - > id , ( * list ) [ index ] - > id ) = = 0 )
{
* list = tf_resize_vec ( * list , sizeof ( * * list ) * ( * count + 1 ) ) ;
if ( * count - index )
{
memmove ( * list + index + 1 , * list + index , sizeof ( following_t * ) * ( * count - index ) ) ;
}
( * list ) [ index ] = add ;
}
}
static following_t * _get_following ( tf_ssb_t * ssb , const char * id , following_t * * * following , int * following_count , int depth , int max_depth )
{
int index = tf_util_insert_index ( id , * following , * following_count , sizeof ( following_t * ) , _following_compare ) ;
following_t * entry = NULL ;
if ( index < * following_count & & strcmp ( id , ( * following ) [ index ] - > id ) = = 0 )
{
entry = ( * following ) [ index ] ;
}
else
{
* following = tf_resize_vec ( * following , sizeof ( * following ) * ( * following_count + 1 ) ) ;
if ( * following_count - index )
{
memmove ( * following + index + 1 , * following + index , sizeof ( following_t * ) * ( * following_count - index ) ) ;
}
entry = tf_malloc ( sizeof ( following_t ) ) ;
( * following ) [ index ] = entry ;
( * following_count ) + + ;
* entry = ( following_t ) { 0 } ;
snprintf ( entry - > id , sizeof ( entry - > id ) , " %s " , id ) ;
entry - > depth = depth ;
if ( depth < max_depth )
{
sqlite3 * db = tf_ssb_get_db ( ssb ) ;
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " SELECT json_extract(content, '$.contact'), json_extract(content, '$.following'), json_extract(content, '$.blocking') FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'contact' ORDER BY sequence " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
if ( sqlite3_bind_text ( statement , 1 , id , - 1 , NULL ) = = SQLITE_OK )
{
while ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
const char * contact = ( const char * ) sqlite3_column_text ( statement , 0 ) ;
if ( sqlite3_column_type ( statement , 1 ) ! = SQLITE_NULL )
{
bool is_following = sqlite3_column_int ( statement , 1 ) ;
following_t * next = _get_following ( ssb , contact , following , following_count , depth + 1 , max_depth ) ;
if ( is_following )
{
_add_following_entry ( & entry - > following , & entry - > following_count , next ) ;
}
}
if ( sqlite3_column_type ( statement , 2 ) ! = SQLITE_NULL )
{
bool is_blocking = sqlite3_column_int ( statement , 2 ) ;
following_t * next = _get_following ( ssb , contact , following , following_count , depth + 1 , 0 /* don't dig deeper into blocked users */ ) ;
if ( is_blocking )
{
_add_following_entry ( & entry - > blocking , & entry - > blocking_count , next ) ;
}
}
}
}
sqlite3_finalize ( statement ) ;
}
}
}
return entry ;
}
const char * * tf_ssb_db_following_deep ( tf_ssb_t * ssb , const char * * ids , int count , int depth )
{
following_t * * following = NULL ;
int following_count = 0 ;
for ( int i = 0 ; i < count ; i + + )
{
_get_following ( ssb , ids [ i ] , & following , & following_count , 0 , depth ) ;
}
char * * result = tf_malloc ( sizeof ( char * ) * ( following_count + 1 ) + k_id_base64_len * following_count ) ;
char * result_ids = ( char * ) result + sizeof ( char * ) * ( following_count + 1 ) ;
for ( int i = 0 ; i < following_count ; i + + )
{
result [ i ] = result_ids + k_id_base64_len * i ;
snprintf ( result [ i ] , k_id_base64_len , " %s " , following [ i ] - > id ) ;
}
result [ following_count ] = NULL ;
for ( int i = 0 ; i < following_count ; i + + )
{
tf_free ( following [ i ] - > following ) ;
tf_free ( following [ i ] - > blocking ) ;
tf_free ( following [ i ] ) ;
}
tf_free ( following ) ;
return ( const char * * ) result ;
}
2022-08-14 22:23:45 -04:00
static void _test_private ( sqlite3 * db , const uint8_t * private_key )
{
sqlite3_stmt * statement = NULL ;
2022-08-15 22:38:25 -04:00
if ( sqlite3_prepare ( db , " SELECT content FROM messages WHERE content LIKE ' \" %%.box \" ' " , - 1 , & statement , NULL ) = = SQLITE_OK )
2022-08-14 22:23:45 -04:00
{
while ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
uint8_t buffer [ 8192 ] ;
int r = base64c_decode ( sqlite3_column_text ( statement , 0 ) + 1 , sqlite3_column_bytes ( statement , 0 ) - strlen ( " \" .box \" " ) , buffer , sizeof ( buffer ) ) ;
2022-08-15 22:38:25 -04:00
2022-08-14 22:23:45 -04:00
if ( r > 1 )
{
uint8_t * nonce = buffer ;
uint8_t * public_key = buffer + 24 ;
if ( public_key + 32 < buffer + r )
{
uint8_t shared_secret [ crypto_scalarmult_curve25519_SCALARBYTES ] ;
2022-08-15 22:38:25 -04:00
if ( crypto_scalarmult ( shared_secret , private_key , public_key ) = = 0 )
2022-08-14 22:23:45 -04:00
{
2022-08-15 22:38:25 -04:00
for ( uint8_t * p = buffer + 24 + 32 ; p < = buffer + r - 49 ; p + = 49 )
2022-08-14 22:23:45 -04:00
{
uint8_t out [ 49 ] ;
2022-08-15 22:38:25 -04:00
int o = crypto_secretbox_open_easy ( out , p , 49 , nonce , shared_secret ) ;
if ( o ! = - 1 )
2022-08-14 22:23:45 -04:00
{
2022-08-15 22:38:25 -04:00
int recipients = ( int ) out [ 0 ] ;
uint8_t * body = buffer + 24 + 32 + 49 * recipients ;
size_t body_size = buffer + r - body ;
uint8_t result [ 8192 ] ;
uint8_t * key = out + 1 ;
if ( crypto_secretbox_open_easy ( result , body , body_size , nonce , key ) ! = - 1 )
{
printf ( " %.*s \n " , ( int ) body_size , result ) ;
}
2022-08-14 22:23:45 -04:00
}
}
}
2022-08-15 22:38:25 -04:00
else
{
printf ( " scalarmult failed \n " ) ;
}
2022-08-14 22:23:45 -04:00
}
}
2022-08-15 22:38:25 -04:00
else
{
printf ( " base64 failed \n " ) ;
}
2022-08-14 22:23:45 -04:00
}
sqlite3_finalize ( statement ) ;
}
}
void tf_ssb_db_private ( sqlite3 * db )
{
sqlite3_stmt * statement = NULL ;
if ( sqlite3_prepare ( db , " SELECT public_key, private_key FROM identities " , - 1 , & statement , NULL ) = = SQLITE_OK )
{
while ( sqlite3_step ( statement ) = = SQLITE_ROW )
{
2022-08-15 22:38:25 -04:00
uint8_t private_key [ crypto_sign_SECRETKEYBYTES ] = { 0 } ;
2022-08-14 22:23:45 -04:00
printf ( " -> %s \n " , sqlite3_column_text ( statement , 0 ) ) ;
int r = base64c_decode ( sqlite3_column_text ( statement , 1 ) , sqlite3_column_bytes ( statement , 1 ) - strlen ( " .ed25519 " ) , private_key , sizeof ( private_key ) ) ;
2022-08-15 22:38:25 -04:00
if ( r = = sizeof ( private_key ) )
2022-08-14 22:23:45 -04:00
{
2022-08-15 22:38:25 -04:00
uint8_t key [ crypto_sign_SECRETKEYBYTES ] = { 0 } ;
if ( crypto_sign_ed25519_sk_to_curve25519 ( key , private_key ) ! = 0 )
{
printf ( " key convert failed \n " ) ;
}
else
{
_test_private ( db , key ) ;
}
2022-08-14 22:23:45 -04:00
}
}
sqlite3_finalize ( statement ) ;
}
}