v0.7.10 - Fixed api errors in accepting : in subscriptions
This commit is contained in:
245
src/main.c
245
src/main.c
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user