v0.7.10 - Fixed api errors in accepting : in subscriptions

This commit is contained in:
Your Name
2025-10-12 10:31:03 -04:00
parent b27a56a296
commit 34bb1c34a2
27 changed files with 2293 additions and 403 deletions

View File

@@ -280,6 +280,52 @@ void send_notice_message(struct lws* wsi, const char* message) {
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Clean up stale SQLite WAL files that may cause lock issues after unclean shutdown
static void cleanup_stale_wal_files(const char* db_path) {
if (!db_path) return;
// Check if database file exists
if (access(db_path, F_OK) != 0) {
return; // Database doesn't exist yet, nothing to clean
}
// Build paths for WAL and SHM files
char wal_path[1024];
char shm_path[1024];
snprintf(wal_path, sizeof(wal_path), "%s-wal", db_path);
snprintf(shm_path, sizeof(shm_path), "%s-shm", db_path);
// Check if WAL or SHM files exist
int has_wal = (access(wal_path, F_OK) == 0);
int has_shm = (access(shm_path, F_OK) == 0);
if (has_wal || has_shm) {
log_warning("Detected stale SQLite WAL files from previous unclean shutdown");
// Try to remove WAL file
if (has_wal) {
if (unlink(wal_path) == 0) {
log_info("Removed stale WAL file");
} else {
char error_msg[256];
snprintf(error_msg, sizeof(error_msg), "Failed to remove WAL file: %s", strerror(errno));
log_warning(error_msg);
}
}
// Try to remove SHM file
if (has_shm) {
if (unlink(shm_path) == 0) {
log_info("Removed stale SHM file");
} else {
char error_msg[256];
snprintf(error_msg, sizeof(error_msg), "Failed to remove SHM file: %s", strerror(errno));
log_warning(error_msg);
}
}
}
}
// Initialize database connection and schema
int init_database(const char* database_path_override) {
// Priority 1: Command line database path override
@@ -295,6 +341,9 @@ int init_database(const char* database_path_override) {
db_path = DEFAULT_DATABASE_PATH;
}
// Clean up stale WAL files before opening database
cleanup_stale_wal_files(db_path);
int rc = sqlite3_open(db_path, &g_db);
if (rc != SQLITE_OK) {
log_error("Cannot open database");
@@ -438,14 +487,41 @@ int init_database(const char* database_path_override) {
return -1;
}
// Enable WAL mode for better concurrency and crash recovery
char* wal_error = NULL;
rc = sqlite3_exec(g_db, "PRAGMA journal_mode=WAL;", NULL, NULL, &wal_error);
if (rc != SQLITE_OK) {
char error_msg[256];
snprintf(error_msg, sizeof(error_msg), "Failed to enable WAL mode: %s",
wal_error ? wal_error : "unknown error");
log_warning(error_msg);
if (wal_error) sqlite3_free(wal_error);
// Continue anyway - WAL mode is optional
} else {
log_info("SQLite WAL mode enabled");
}
return 0;
}
// Close database connection
// Close database connection with proper WAL checkpoint
void close_database() {
if (g_db) {
// Perform WAL checkpoint to minimize stale files on next startup
log_info("Performing WAL checkpoint before database close");
char* checkpoint_error = NULL;
int rc = sqlite3_exec(g_db, "PRAGMA wal_checkpoint(TRUNCATE);", NULL, NULL, &checkpoint_error);
if (rc != SQLITE_OK) {
char error_msg[256];
snprintf(error_msg, sizeof(error_msg), "WAL checkpoint warning: %s",
checkpoint_error ? checkpoint_error : "unknown error");
log_warning(error_msg);
if (checkpoint_error) sqlite3_free(checkpoint_error);
}
sqlite3_close(g_db);
g_db = NULL;
log_info("Database connection closed");
}
}
@@ -1266,20 +1342,23 @@ int is_authorized_admin_event(cJSON* event, char* error_buffer, size_t error_buf
}
return -1;
}
// DEBUG: Log that we're checking admin authorization
log_info("DEBUG: Checking admin event authorization");
// Step 1: Verify event kind is admin type
cJSON *kind_json = cJSON_GetObjectItem(event, "kind");
if (!kind_json || !cJSON_IsNumber(kind_json)) {
snprintf(error_buffer, error_buffer_size, "Missing or invalid event kind");
return -1;
}
int event_kind = kind_json->valueint;
if (event_kind != 23456) {
snprintf(error_buffer, error_buffer_size, "Event kind %d is not an admin event type", event_kind);
return -1;
}
// Step 2: Check if event targets this relay (look for 'p' tag with our relay pubkey)
cJSON *tags = cJSON_GetObjectItem(event, "tags");
if (!tags || !cJSON_IsArray(tags)) {
@@ -1287,20 +1366,26 @@ int is_authorized_admin_event(cJSON* event, char* error_buffer, size_t error_buf
snprintf(error_buffer, error_buffer_size, "Admin event not targeting this relay (no tags)");
return -1;
}
int targets_this_relay = 0;
cJSON *tag;
cJSON_ArrayForEach(tag, tags) {
if (cJSON_IsArray(tag)) {
cJSON *tag_name = cJSON_GetArrayItem(tag, 0);
cJSON *tag_value = cJSON_GetArrayItem(tag, 1);
if (tag_name && cJSON_IsString(tag_name) &&
tag_value && cJSON_IsString(tag_value) &&
strcmp(tag_name->valuestring, "p") == 0) {
// Compare with our relay pubkey
const char* relay_pubkey = get_config_value("relay_pubkey");
char debug_msg1[256];
snprintf(debug_msg1, sizeof(debug_msg1), "DEBUG: Relay pubkey from config: %.64s", relay_pubkey ? relay_pubkey : "NULL");
log_info(debug_msg1);
char debug_msg2[256];
snprintf(debug_msg2, sizeof(debug_msg2), "DEBUG: Event p tag value: %.64s", tag_value->valuestring);
log_info(debug_msg2);
if (relay_pubkey && strcmp(tag_value->valuestring, relay_pubkey) == 0) {
targets_this_relay = 1;
break;
@@ -1308,13 +1393,14 @@ int is_authorized_admin_event(cJSON* event, char* error_buffer, size_t error_buf
}
}
}
if (!targets_this_relay) {
// Admin event for different relay - not an error, just not for us
log_info("DEBUG: Admin event not targeting this relay");
snprintf(error_buffer, error_buffer_size, "Admin event not targeting this relay");
return -1;
}
// Step 3: Verify admin signature authorization
cJSON *pubkey_json = cJSON_GetObjectItem(event, "pubkey");
if (!pubkey_json || !cJSON_IsString(pubkey_json)) {
@@ -1322,34 +1408,47 @@ int is_authorized_admin_event(cJSON* event, char* error_buffer, size_t error_buf
snprintf(error_buffer, error_buffer_size, "Unauthorized admin event attempt: missing pubkey");
return -1;
}
// Get admin pubkey from configuration
const char* admin_pubkey = get_config_value("admin_pubkey");
char debug_msg3[256];
snprintf(debug_msg3, sizeof(debug_msg3), "DEBUG: Admin pubkey from config: %.64s", admin_pubkey ? admin_pubkey : "NULL");
log_info(debug_msg3);
char debug_msg4[256];
snprintf(debug_msg4, sizeof(debug_msg4), "DEBUG: Event pubkey: %.64s", pubkey_json->valuestring);
log_info(debug_msg4);
if (!admin_pubkey || strlen(admin_pubkey) == 0) {
log_warning("Unauthorized admin event attempt: no admin pubkey configured");
snprintf(error_buffer, error_buffer_size, "Unauthorized admin event attempt: no admin configured");
return -1;
}
// Compare pubkeys
if (strcmp(pubkey_json->valuestring, admin_pubkey) != 0) {
log_warning("Unauthorized admin event attempt: pubkey mismatch");
char warning_msg[256];
snprintf(warning_msg, sizeof(warning_msg),
snprintf(warning_msg, sizeof(warning_msg),
"Unauthorized admin event attempt from pubkey: %.32s...", pubkey_json->valuestring);
log_warning(warning_msg);
log_info("DEBUG: Pubkey comparison failed - event pubkey != admin pubkey");
snprintf(error_buffer, error_buffer_size, "Unauthorized admin event attempt: invalid admin pubkey");
return -1;
}
log_info("DEBUG: Pubkey comparison passed");
// Step 4: Verify event signature
if (nostr_verify_event_signature(event) != 0) {
log_warning("Unauthorized admin event attempt: invalid signature");
log_info("DEBUG: Signature verification failed");
snprintf(error_buffer, error_buffer_size, "Unauthorized admin event attempt: signature verification failed");
return -1;
}
log_info("DEBUG: Signature verification passed");
// All checks passed - authorized admin event
log_info("DEBUG: Admin event authorization successful");
return 0;
}
@@ -1597,7 +1696,7 @@ int main(int argc, char* argv[]) {
printf(" Port: %d (overriding default)\n", cli_options.port_override);
}
// Add pubkeys to config table
// Add pubkeys to config table (single authoritative call)
if (add_pubkeys_to_config_table() != 0) {
log_error("Failed to add pubkeys to config table");
cleanup_configuration_system();
@@ -1605,17 +1704,6 @@ int main(int argc, char* argv[]) {
close_database();
return 1;
}
// Now store the pubkeys in config table since database is available
const char* admin_pubkey = get_admin_pubkey_cached();
const char* relay_pubkey_from_cache = get_relay_pubkey_cached();
if (admin_pubkey && strlen(admin_pubkey) == 64) {
set_config_value_in_table("admin_pubkey", admin_pubkey, "string", "Administrator public key", "authentication", 0);
}
if (relay_pubkey_from_cache && strlen(relay_pubkey_from_cache) == 64) {
set_config_value_in_table("relay_pubkey", relay_pubkey_from_cache, "string", "Relay public key", "relay", 0);
}
} else {
// Find existing database file
char** existing_files = find_existing_db_files();
@@ -1662,6 +1750,22 @@ int main(int argc, char* argv[]) {
nostr_cleanup();
return 1;
}
// DEBUG: Check config table row count before database initialization
{
sqlite3* temp_db = NULL;
if (sqlite3_open(g_database_path, &temp_db) == SQLITE_OK) {
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(temp_db, "SELECT COUNT(*) FROM config", -1, &stmt, NULL) == SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
int count = sqlite3_column_int(stmt, 0);
printf("[DEBUG] Config table row count before init_database(): %d\n", count);
}
sqlite3_finalize(stmt);
}
sqlite3_close(temp_db);
}
}
// Initialize database with existing database path
if (init_database(g_database_path) != 0) {
@@ -1675,32 +1779,93 @@ int main(int argc, char* argv[]) {
nostr_cleanup();
return 1;
}
// DEBUG: Check config table row count after database initialization
{
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(g_db, "SELECT COUNT(*) FROM config", -1, &stmt, NULL) == SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
int count = sqlite3_column_int(stmt, 0);
printf("[DEBUG] Config table row count after init_database(): %d\n", count);
}
sqlite3_finalize(stmt);
}
}
// Ensure default configuration values are populated (for any missing keys)
// This must be done AFTER database initialization
// COMMENTED OUT: Don't modify existing database config on restart
// if (populate_default_config_values() != 0) {
// log_warning("Failed to populate default config values for existing relay - continuing");
// }
// Load configuration from database
cJSON* config_event = load_config_event_from_database(relay_pubkey);
if (config_event) {
if (apply_configuration_from_event(config_event) != 0) {
log_warning("Failed to apply configuration from database");
} else {
// Extract admin pubkey from the config event and store in config table for unified cache access
cJSON* pubkey_obj = cJSON_GetObjectItem(config_event, "pubkey");
const char* admin_pubkey = pubkey_obj ? cJSON_GetStringValue(pubkey_obj) : NULL;
// Store both admin and relay pubkeys in config table for unified cache
if (admin_pubkey && strlen(admin_pubkey) == 64) {
set_config_value_in_table("admin_pubkey", admin_pubkey, "string", "Administrator public key", "authentication", 0);
}
if (relay_pubkey && strlen(relay_pubkey) == 64) {
set_config_value_in_table("relay_pubkey", relay_pubkey, "string", "Relay public key", "relay", 0);
}
}
cJSON_Delete(config_event);
} else {
log_warning("No configuration event found in existing database");
}
// Ensure pubkeys are in config table for existing relay
// This handles migration from old event-based config to table-based config
const char* admin_pubkey_from_table = get_config_value_from_table("admin_pubkey");
const char* relay_pubkey_from_table = get_config_value_from_table("relay_pubkey");
int need_to_add_pubkeys = 0;
// Check if admin_pubkey is missing or invalid
if (!admin_pubkey_from_table || strlen(admin_pubkey_from_table) != 64) {
log_warning("Admin pubkey missing or invalid in config table - will regenerate from cache");
need_to_add_pubkeys = 1;
}
if (admin_pubkey_from_table) free((char*)admin_pubkey_from_table);
// Check if relay_pubkey is missing or invalid
if (!relay_pubkey_from_table || strlen(relay_pubkey_from_table) != 64) {
log_warning("Relay pubkey missing or invalid in config table - will regenerate from cache");
need_to_add_pubkeys = 1;
}
if (relay_pubkey_from_table) free((char*)relay_pubkey_from_table);
// If either pubkey is missing, call add_pubkeys_to_config_table to populate both
if (need_to_add_pubkeys) {
// DEBUG: Check config table row count before add_pubkeys_to_config_table()
{
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(g_db, "SELECT COUNT(*) FROM config", -1, &stmt, NULL) == SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
int count = sqlite3_column_int(stmt, 0);
printf("[DEBUG] Config table row count before add_pubkeys_to_config_table(): %d\n", count);
}
sqlite3_finalize(stmt);
}
}
if (add_pubkeys_to_config_table() != 0) {
log_error("Failed to add pubkeys to config table for existing relay");
cleanup_configuration_system();
nostr_cleanup();
close_database();
return 1;
}
// DEBUG: Check config table row count after add_pubkeys_to_config_table()
{
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(g_db, "SELECT COUNT(*) FROM config", -1, &stmt, NULL) == SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
int count = sqlite3_column_int(stmt, 0);
printf("[DEBUG] Config table row count after add_pubkeys_to_config_table(): %d\n", count);
}
sqlite3_finalize(stmt);
}
}
}
// Apply CLI overrides for existing relay (port override should work even for existing relays)
if (cli_options.port_override > 0) {
char port_str[16];