From d0bbd7f24fc90880d52942a7abcacccc2ac353cf Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Mon, 6 Jan 2025 20:46:16 -0500 Subject: [PATCH] ssb: Add a has_blob command. #89 --- src/main.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ssb.db.c | 4 +--- src/ssb.db.h | 4 ++-- src/ssb.rpc.c | 4 +++- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index 9792e33c..c6c89a0a 100644 --- a/src/main.c +++ b/src/main.c @@ -146,6 +146,7 @@ static int _tf_command_import(const char* file, int argc, char* argv[]); static int _tf_command_publish(const char* file, int argc, char* argv[]); static int _tf_command_run(const char* file, int argc, char* argv[]); static int _tf_command_sandbox(const char* file, int argc, char* argv[]); +static int _tf_command_has_blob(const char* file, int argc, char* argv[]); static int _tf_command_store_blob(const char* file, int argc, char* argv[]); static int _tf_command_get_sequence(const char* file, int argc, char* argv[]); static int _tf_command_get_identity(const char* file, int argc, char* argv[]); @@ -170,6 +171,7 @@ const command_t k_commands[] = { { "get_sequence", _tf_command_get_sequence, "Get the last sequence number for a feed." }, { "get_identity", _tf_command_get_identity, "Get the server account identity." }, { "get_profile", _tf_command_get_profile, "Get profile information for the given identity." }, + { "has_blob", _tf_command_has_blob, "Check whether a blob is in the blob store." }, { "store_blob", _tf_command_store_blob, "Write a file to the blob store." }, { "verify", _tf_command_verify, "Verify a feed." }, { "test", _tf_command_test, "Test SSB." }, @@ -599,6 +601,64 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[]) return EXIT_SUCCESS; } +static int _tf_command_has_blob(const char* file, int argc, char* argv[]) +{ + const char* default_db_path = _get_db_path(); + const char* db_path = default_db_path; + const char* blob_id = NULL; + bool show_usage = false; + + while (!show_usage) + { + static const struct option k_options[] = { + { "db-path", required_argument, NULL, 'd' }, + { "blob_id", required_argument, NULL, 'b' }, + { "help", no_argument, NULL, 'h' }, + { 0 }, + }; + int c = getopt_long(argc, argv, "d:b:h", k_options, NULL); + if (c == -1) + { + break; + } + + switch (c) + { + case '?': + case 'h': + default: + show_usage = true; + break; + case 'd': + db_path = optarg; + break; + case 'b': + blob_id = optarg; + break; + } + } + + if (show_usage || !blob_id) + { + tf_printf("\n%s has_blob [options]\n\n", file); + tf_printf("options:\n"); + tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path); + tf_printf(" -b, --blob_id blob_id ID of blob to query.\n"); + tf_printf(" -h, --help Show this usage information.\n"); + tf_free((void*)default_db_path); + return EXIT_FAILURE; + } + + sqlite3* db = NULL; + sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, NULL); + tf_ssb_db_init_reader(db); + bool has = tf_ssb_db_blob_has(db, blob_id); + sqlite3_close(db); + tf_free((void*)default_db_path); + tf_printf("%s\n", has ? "true" : "false"); + return has ? EXIT_SUCCESS : EXIT_FAILURE; +} + static int _tf_command_get_sequence(const char* file, int argc, char* argv[]) { const char* default_db_path = _get_db_path(); diff --git a/src/ssb.db.c b/src/ssb.db.c index 1ef89d78..63d4dde3 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -603,11 +603,10 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_ return result; } -bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id) +bool tf_ssb_db_blob_has(sqlite3* db, const char* id) { bool result = false; sqlite3_stmt* statement; - sqlite3* db = tf_ssb_acquire_db_reader(ssb); const char* query = "SELECT COUNT(*) FROM blobs WHERE id = ?1"; if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK) { @@ -617,7 +616,6 @@ bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id) } sqlite3_finalize(statement); } - tf_ssb_release_db_reader(ssb, db); return result; } diff --git a/src/ssb.db.h b/src/ssb.db.h index 60b4fda4..6315ec56 100644 --- a/src/ssb.db.h +++ b/src/ssb.db.h @@ -39,11 +39,11 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_ /** ** Determine whether a blob is in the database by ID. -** @param ssb The SSB instasnce. +** @param db The SQLite database instance to use. ** @param id The blob identifier. ** @return true If the blob is in the database. */ -bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id); +bool tf_ssb_db_blob_has(sqlite3* db, const char* id); /** ** Retrieve a blob from the database. diff --git a/src/ssb.rpc.c b/src/ssb.rpc.c index e30879d1..6719f10b 100644 --- a/src/ssb.rpc.c +++ b/src/ssb.rpc.c @@ -139,7 +139,9 @@ static void _tf_ssb_rpc_blobs_has_work(tf_ssb_connection_t* connection, void* us { blobs_has_work_t* work = user_data; tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection); - work->found = tf_ssb_db_blob_has(ssb, work->id); + sqlite3* db = tf_ssb_acquire_db_reader(ssb); + work->found = tf_ssb_db_blob_has(db, work->id); + tf_ssb_release_db_reader(ssb, db); } static void _tf_ssb_rpc_blobs_has_after_work(tf_ssb_connection_t* connection, int status, void* user_data)