diff --git a/src/main.c b/src/main.c index 5d92f738..4e1e3039 100644 --- a/src/main.c +++ b/src/main.c @@ -149,6 +149,7 @@ 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_create_invite(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[]); @@ -171,6 +172,7 @@ const command_t k_commands[] = { { "export", _tf_command_export, "Export apps from SSB." }, { "publish", _tf_command_publish, "Append a message to a feed." }, { "private", _tf_command_private, "Append a private post message to a feed." }, + { "create_invite", _tf_command_create_invite, "Create an invite." }, { "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." }, @@ -801,6 +803,94 @@ static int _tf_command_has_blob(const char* file, int argc, char* argv[]) return has ? EXIT_SUCCESS : EXIT_FAILURE; } +static int _tf_command_create_invite(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; + int use_count = 0; + int expires = 0; + bool show_usage = false; + const char* host = NULL; + int port = 0; + + while (!show_usage) + { + static const struct option k_options[] = { + { "db-path", required_argument, NULL, 'd' }, + { "identity", required_argument, NULL, 'i' }, + { "address", required_argument, NULL, 'a' }, + { "port", required_argument, NULL, 'p' }, + { "use_count", required_argument, NULL, 'u' }, + { "expires", required_argument, NULL, 'e' }, + { "help", no_argument, NULL, 'h' }, + { 0 }, + }; + int c = getopt_long(argc, argv, "d:i:a:p:u:e: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; + case 'a': + host = optarg; + break; + case 'p': + port = atoi(optarg); + break; + case 'u': + use_count = atoi(optarg); + break; + case 'e': + expires = atoi(optarg); + break; + } + } + + if (show_usage || !identity || !use_count || !expires || !host || !port) + { + 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(" -a, --address address Address to which the recipient will connect.\n"); + tf_printf(" -p, --port port Port to which the recipient will connect.\n"); + tf_printf(" -u, --use_count count Number of times this invite may be used.\n"); + tf_printf(" -e, --expires seconds How long this invite is valid in seconds (-1 for indefinitely).\n"); + tf_printf(" -h, --help Show this usage information.\n"); + tf_free((void*)default_db_path); + return EXIT_FAILURE; + } + + int result = EXIT_FAILURE; + tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); + tf_ssb_set_quiet(ssb, true); + char invite[1024] = ""; + sqlite3* db = tf_ssb_acquire_db_writer(ssb); + if (tf_ssb_db_generate_invite(db, identity, host, port, use_count, expires, invite, sizeof(invite))) + { + tf_printf("%s\n", invite); + result = EXIT_SUCCESS; + } + tf_ssb_release_db_writer(ssb, db); + tf_ssb_destroy(ssb); + tf_free((void*)default_db_path); + return result; +} + 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.c b/src/ssb.c index 00639341..cd3328ef 100644 --- a/src/ssb.c +++ b/src/ssb.c @@ -2612,18 +2612,13 @@ void tf_ssb_destroy(tf_ssb_t* ssb) while (ssb->broadcast_listener.data || ssb->broadcast_sender.data || ssb->broadcast_timer.data || ssb->broadcast_cleanup_timer.data || ssb->trace_timer.data || ssb->server.data || ssb->ref_count || ssb->request_activity_timer.data || ssb->timers_count) { - tf_printf("bl=%p bs=%p bt=%p bc=%p tt=%p s=%p rc=%d rat=%p tc=%d\n", - ssb->broadcast_listener.data, - ssb->broadcast_sender.data, - ssb->broadcast_timer.data, - ssb->broadcast_cleanup_timer.data, - ssb->trace_timer.data, - ssb->server.data, - ssb->ref_count, - ssb->request_activity_timer.data, - ssb->timers_count); - tf_printf("--\n"); - uv_print_all_handles(ssb->loop, stdout); + if (!ssb->quiet) + { + tf_printf("bl=%p bs=%p bt=%p bc=%p tt=%p s=%p rc=%d rat=%p tc=%d\n", ssb->broadcast_listener.data, ssb->broadcast_sender.data, ssb->broadcast_timer.data, + ssb->broadcast_cleanup_timer.data, ssb->trace_timer.data, ssb->server.data, ssb->ref_count, ssb->request_activity_timer.data, ssb->timers_count); + tf_printf("--\n"); + uv_print_all_handles(ssb->loop, stdout); + } uv_run(ssb->loop, UV_RUN_ONCE); } @@ -3080,7 +3075,8 @@ void tf_ssb_connect(tf_ssb_t* ssb, const char* host, int port, const uint8_t* ke } } -static void _tf_ssb_connect_with_invite(tf_ssb_t* ssb, const char* host, int port, const uint8_t* key, const uint8_t* invite, int connect_flags, tf_ssb_connect_callback_t* callback, void* user_data) +static void _tf_ssb_connect_with_invite( + tf_ssb_t* ssb, const char* host, int port, const uint8_t* key, const uint8_t* invite, int connect_flags, tf_ssb_connect_callback_t* callback, void* user_data) { if (ssb->shutting_down) { @@ -3388,8 +3384,7 @@ static bool _tf_ssb_parse_connect_string(const char* in_broadcast, tf_ssb_broadc { out_broadcast->addr.sin_family = AF_INET; out_broadcast->addr.sin_port = htons((uint16_t)port); - return - tf_ssb_id_str_to_bin(out_broadcast->pub, public_key_str) && + return tf_ssb_id_str_to_bin(out_broadcast->pub, public_key_str) && tf_base64_decode(secret_key_str, strlen(secret_key_str), out_broadcast->invite, sizeof(out_broadcast->invite)); } else if (strncmp(in_broadcast, "ws:", 3) == 0) diff --git a/src/ssb.db.c b/src/ssb.db.c index 3efc6ad8..19d2bc8a 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -2170,14 +2170,10 @@ bool tf_ssb_db_generate_invite(sqlite3* db, const char* id, const char* host, in bool inserted = false; sqlite3_stmt* statement; - if (sqlite3_prepare(db, - "INSERT INTO invites (invite_public_key, account, use_count, expires) VALUES (?, ?, ?, ?)", - -1, &statement, NULL) == SQLITE_OK) + if (sqlite3_prepare(db, "INSERT INTO invites (invite_public_key, account, use_count, expires) VALUES (?, ?, ?, ?)", -1, &statement, NULL) == SQLITE_OK) { - if (sqlite3_bind_text(statement, 1, public, -1, NULL) == SQLITE_OK && - sqlite3_bind_text(statement, 2, id, -1, NULL) == SQLITE_OK && - sqlite3_bind_int(statement, 3, use_count) == SQLITE_OK && - sqlite3_bind_int64(statement, 4, (int64_t)time(NULL) + expires_seconds) == SQLITE_OK) + if (sqlite3_bind_text(statement, 1, public, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, id, -1, NULL) == SQLITE_OK && + sqlite3_bind_int(statement, 3, use_count) == SQLITE_OK && sqlite3_bind_int64(statement, 4, (int64_t)time(NULL) + expires_seconds) == SQLITE_OK) { inserted = sqlite3_step(statement) == SQLITE_DONE; } @@ -2192,15 +2188,12 @@ bool tf_ssb_db_use_invite(sqlite3* db, const char* id) { bool used = false; sqlite3_stmt* statement; - if (sqlite3_prepare(db, - "UPDATE invites SET use_count = use_count - 1 WHERE invite_public_key = ? AND expires > ? AND (use_count > 0 OR use_count = -1)", - -1, &statement, NULL) == SQLITE_OK) + if (sqlite3_prepare(db, "UPDATE invites SET use_count = use_count - 1 WHERE invite_public_key = ? AND expires > ? AND (use_count > 0 OR use_count = -1)", -1, &statement, + NULL) == SQLITE_OK) { - if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK && - sqlite3_bind_int64(statement, 2, (int64_t)time(NULL)) == SQLITE_OK) + if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, (int64_t)time(NULL)) == SQLITE_OK) { - used = sqlite3_step(statement) == SQLITE_DONE && - sqlite3_changes(db) > 0; + used = sqlite3_step(statement) == SQLITE_DONE && sqlite3_changes(db) > 0; } sqlite3_finalize(statement); } diff --git a/src/ssb.rpc.c b/src/ssb.rpc.c index c2331039..fb656aff 100644 --- a/src/ssb.rpc.c +++ b/src/ssb.rpc.c @@ -1202,6 +1202,18 @@ static void _tf_ssb_rpc_ebt_replicate_server( tf_ssb_connection_add_request(connection, -request_number, "ebt.replicate", _tf_ssb_rpc_ebt_replicate, NULL, NULL, NULL); } +static void _tf_ssb_rpc_invite_use_callback( + tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) +{ + /* + ** Follow the pub back: + ** { "type": "contact", "contact": "@VJM7w1W19ZsKmG2KnfaoKIM66BRoreEkzaVm/J//wl8=.ed25519", "following": true } + ** + ** Post a pub message: + ** { "type": "pub", "address": { "host": "one.butt.nz", "port": 8008, "key": "@VJM7w1W19ZsKmG2KnfaoKIM66BRoreEkzaVm/J//wl8=.ed25519" } } + */ +} + static void _tf_ssb_rpc_send_invite_use(tf_ssb_connection_t* connection) { tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection); @@ -1219,8 +1231,8 @@ static void _tf_ssb_rpc_send_invite_use(tf_ssb_connection_t* connection) JS_SetPropertyStr(context, object, "feed", JS_NewString(context, id)); JS_SetPropertyUint32(context, args, 0, object); JS_SetPropertyStr(context, message, "args", args); - tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_new_request, tf_ssb_connection_next_request_number(connection), "invite.use", message, - _tf_ssb_rpc_connection_tunnel_isRoom_callback, NULL, NULL); + tf_ssb_connection_rpc_send_json( + connection, k_ssb_rpc_flag_new_request, tf_ssb_connection_next_request_number(connection), "invite.use", message, _tf_ssb_rpc_invite_use_callback, NULL, NULL); JS_FreeValue(context, message); } @@ -1679,13 +1691,13 @@ static void _tf_ssb_invite_use_message_store_callback(const char* id, bool verif tf_ssb_connection_t* connection = work->connection; if (verified && is_new) { - tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error, -work->request_number, NULL, - (const uint8_t*)work->message, strlen(work->message), NULL, NULL, NULL); + tf_ssb_connection_rpc_send( + connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error, -work->request_number, NULL, (const uint8_t*)work->message, strlen(work->message), NULL, NULL, NULL); } else { - tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error, -work->request_number, NULL, - (const uint8_t*)"false", strlen("false"), NULL, NULL, NULL); + tf_ssb_connection_rpc_send( + connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error, -work->request_number, NULL, (const uint8_t*)"false", strlen("false"), NULL, NULL, NULL); } if (work->message) { @@ -1722,16 +1734,15 @@ static void _tf_ssb_rpc_invite_use_after_work(tf_ssb_connection_t* connection, i } else { - tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error, -work->request_number, NULL, - (const uint8_t*)"false", strlen("false"), NULL, NULL, NULL); + tf_ssb_connection_rpc_send( + connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error, -work->request_number, NULL, (const uint8_t*)"false", strlen("false"), NULL, NULL, NULL); } } static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) { invite_t* work = tf_malloc(sizeof(invite_t)); - *work = (invite_t) - { + *work = (invite_t) { .connection = connection, .request_number = request_number, };