ssb: Add get_identity, get_sequence, and get_profile commands. #89
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Build Tilde Friends / Build-All (push) Successful in 25m41s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Build Tilde Friends / Build-All (push) Successful in 25m41s
				
			This commit is contained in:
		
							
								
								
									
										174
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								src/main.c
									
									
									
									
									
								
							@@ -15,6 +15,7 @@
 | 
				
			|||||||
#include "unzip.h"
 | 
					#include "unzip.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <getopt.h>
 | 
					#include <getopt.h>
 | 
				
			||||||
 | 
					#include <inttypes.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -146,6 +147,9 @@ 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_run(const char* file, int argc, char* argv[]);
 | 
				
			||||||
static int _tf_command_sandbox(const char* file, int argc, char* argv[]);
 | 
					static int _tf_command_sandbox(const char* file, int argc, char* argv[]);
 | 
				
			||||||
static int _tf_command_store_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[]);
 | 
				
			||||||
 | 
					static int _tf_command_get_profile(const char* file, int argc, char* argv[]);
 | 
				
			||||||
static int _tf_command_test(const char* file, int argc, char* argv[]);
 | 
					static int _tf_command_test(const char* file, int argc, char* argv[]);
 | 
				
			||||||
static int _tf_command_verify(const char* file, int argc, char* argv[]);
 | 
					static int _tf_command_verify(const char* file, int argc, char* argv[]);
 | 
				
			||||||
static int _tf_command_usage(const char* file);
 | 
					static int _tf_command_usage(const char* file);
 | 
				
			||||||
@@ -163,6 +167,9 @@ const command_t k_commands[] = {
 | 
				
			|||||||
	{ "import", _tf_command_import, "Import apps to SSB." },
 | 
						{ "import", _tf_command_import, "Import apps to SSB." },
 | 
				
			||||||
	{ "export", _tf_command_export, "Export apps from SSB." },
 | 
						{ "export", _tf_command_export, "Export apps from SSB." },
 | 
				
			||||||
	{ "publish", _tf_command_publish, "Append a message to a feed." },
 | 
						{ "publish", _tf_command_publish, "Append a message to a feed." },
 | 
				
			||||||
 | 
						{ "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." },
 | 
				
			||||||
	{ "store_blob", _tf_command_store_blob, "Write a file to the blob store." },
 | 
						{ "store_blob", _tf_command_store_blob, "Write a file to the blob store." },
 | 
				
			||||||
	{ "verify", _tf_command_verify, "Verify a feed." },
 | 
						{ "verify", _tf_command_verify, "Verify a feed." },
 | 
				
			||||||
	{ "test", _tf_command_test, "Test SSB." },
 | 
						{ "test", _tf_command_test, "Test SSB." },
 | 
				
			||||||
@@ -592,6 +599,173 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
 | 
				
			|||||||
	return EXIT_SUCCESS;
 | 
						return EXIT_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int _tf_command_get_sequence(const char* file, int argc, char* argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char* default_db_path = _get_db_path();
 | 
				
			||||||
 | 
						const char* db_path = default_db_path;
 | 
				
			||||||
 | 
						const char* identity = NULL;
 | 
				
			||||||
 | 
						bool show_usage = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (!show_usage)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							static const struct option k_options[] = {
 | 
				
			||||||
 | 
								{ "db-path", required_argument, NULL, 'd' },
 | 
				
			||||||
 | 
								{ "id", required_argument, NULL, 'i' },
 | 
				
			||||||
 | 
								{ "help", no_argument, NULL, 'h' },
 | 
				
			||||||
 | 
								{ 0 },
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							int c = getopt_long(argc, argv, "d:i: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 'i':
 | 
				
			||||||
 | 
								identity = optarg;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (show_usage || !identity)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							tf_printf("\n%s get_sequence [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("  -i, --identity identity  Account from which to get latest sequence number.\n");
 | 
				
			||||||
 | 
							tf_printf("  -h, --help               Show this usage information.\n");
 | 
				
			||||||
 | 
							tf_free((void*)default_db_path);
 | 
				
			||||||
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
 | 
				
			||||||
 | 
						int64_t sequence = -1;
 | 
				
			||||||
 | 
						int result = tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, NULL, 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 | 
				
			||||||
 | 
						tf_printf("%" PRId64 "\n", sequence);
 | 
				
			||||||
 | 
						tf_ssb_destroy(ssb);
 | 
				
			||||||
 | 
						tf_free((void*)default_db_path);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int _tf_command_get_identity(const char* file, int argc, char* argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char* default_db_path = _get_db_path();
 | 
				
			||||||
 | 
						const char* db_path = default_db_path;
 | 
				
			||||||
 | 
						bool show_usage = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (!show_usage)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							static const struct option k_options[] = {
 | 
				
			||||||
 | 
								{ "db-path", required_argument, NULL, 'd' },
 | 
				
			||||||
 | 
								{ "help", no_argument, NULL, 'h' },
 | 
				
			||||||
 | 
								{ 0 },
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							int c = getopt_long(argc, argv, "d:i:h", k_options, NULL);
 | 
				
			||||||
 | 
							if (c == -1)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (c)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
							case '?':
 | 
				
			||||||
 | 
							case 'h':
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								show_usage = true;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case 'd':
 | 
				
			||||||
 | 
								db_path = optarg;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (show_usage)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							tf_printf("\n%s get_identity [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("  -h, --help               Show this usage information.\n");
 | 
				
			||||||
 | 
							tf_free((void*)default_db_path);
 | 
				
			||||||
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char id[k_id_base64_len] = { 0 };
 | 
				
			||||||
 | 
						tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
 | 
				
			||||||
 | 
						int result = tf_ssb_whoami(ssb, id, sizeof(id)) ? EXIT_SUCCESS : EXIT_FAILURE;
 | 
				
			||||||
 | 
						tf_printf("%s\n", id);
 | 
				
			||||||
 | 
						tf_ssb_destroy(ssb);
 | 
				
			||||||
 | 
						tf_free((void*)default_db_path);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int _tf_command_get_profile(const char* file, int argc, char* argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char* default_db_path = _get_db_path();
 | 
				
			||||||
 | 
						const char* db_path = default_db_path;
 | 
				
			||||||
 | 
						const char* identity = NULL;
 | 
				
			||||||
 | 
						bool show_usage = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (!show_usage)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							static const struct option k_options[] = {
 | 
				
			||||||
 | 
								{ "db-path", required_argument, NULL, 'd' },
 | 
				
			||||||
 | 
								{ "id", required_argument, NULL, 'i' },
 | 
				
			||||||
 | 
								{ "help", no_argument, NULL, 'h' },
 | 
				
			||||||
 | 
								{ 0 },
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							int c = getopt_long(argc, argv, "d:i: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 'i':
 | 
				
			||||||
 | 
								identity = optarg;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (show_usage || !identity)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							tf_printf("\n%s get_profile [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("  -i, --identity identity  Account from which to get latest sequence number.\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);
 | 
				
			||||||
 | 
						const char* profile = tf_ssb_db_get_profile(db, identity);
 | 
				
			||||||
 | 
						tf_printf("%s\n", profile);
 | 
				
			||||||
 | 
						sqlite3_close(db);
 | 
				
			||||||
 | 
						tf_free((void*)profile);
 | 
				
			||||||
 | 
						tf_free((void*)default_db_path);
 | 
				
			||||||
 | 
						return profile != NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int _tf_command_verify(const char* file, int argc, char* argv[])
 | 
					static int _tf_command_verify(const char* file, int argc, char* argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const char* identity = NULL;
 | 
						const char* identity = NULL;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										26
									
								
								src/ssb.db.c
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								src/ssb.db.c
									
									
									
									
									
								
							@@ -2084,3 +2084,29 @@ bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* ou
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* tf_ssb_db_get_profile(sqlite3* db, const char* id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char* result = NULL;
 | 
				
			||||||
 | 
						sqlite3_stmt* statement;
 | 
				
			||||||
 | 
						if (sqlite3_prepare(db,
 | 
				
			||||||
 | 
								"SELECT json(json_group_object(key, value)) FROM (SELECT fields.key, RANK() OVER (PARTITION BY fields.key ORDER BY messages.sequence DESC) AS rank, fields.value FROM "
 | 
				
			||||||
 | 
								"messages, json_each(messages.content) AS fields WHERE messages.author = ? AND messages.content ->> '$.type' = 'about' AND messages.content ->> '$.about' = "
 | 
				
			||||||
 | 
								"messages.author AND NOT fields.key IN ('about', 'type')) WHERE rank = 1",
 | 
				
			||||||
 | 
								-1, &statement, NULL) == SQLITE_OK)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (sqlite3_step(statement) == SQLITE_ROW)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									result = tf_strdup((const char*)sqlite3_column_text(statement, 0));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sqlite3_finalize(statement);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -483,6 +483,14 @@ bool tf_ssb_db_get_global_setting_int64(sqlite3* db, const char* name, int64_t*
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* out_value, size_t size);
 | 
					bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* out_value, size_t size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					** Get the latest profile information for the given identity.
 | 
				
			||||||
 | 
					** @param db The database.
 | 
				
			||||||
 | 
					** @param id The identity.
 | 
				
			||||||
 | 
					** @return A JSON representation of the latest profile information set for the account.  Free with tf_free().
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					const char* tf_ssb_db_get_profile(sqlite3* db, const char* id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
** An SQLite authorizer callback.  See https://www.sqlite.org/c3ref/set_authorizer.html for use.
 | 
					** An SQLite authorizer callback.  See https://www.sqlite.org/c3ref/set_authorizer.html for use.
 | 
				
			||||||
** @param user_data User data registered with the authorizer.
 | 
					** @param user_data User data registered with the authorizer.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user