diff --git a/FIX_ME.md b/FIX_ME.md deleted file mode 100644 index e5b72eb..0000000 --- a/FIX_ME.md +++ /dev/null @@ -1,94 +0,0 @@ -Inconsistency audit with exact fixes (treating README.md as authoritative) - -Backend auth_rules schema mismatch -Evidence: -Migration (creates mismatched columns): -See "CREATE TABLE IF NOT EXISTS auth_rules ... UNIQUE(rule_type, operation, rule_target)". -Active code uses rule_type, pattern_type, pattern_value, action: -Insert: "INSERT INTO auth_rules (rule_type, pattern_type, pattern_value, action)" -Delete: "DELETE FROM auth_rules WHERE rule_type = ? AND pattern_type = ? AND pattern_value = ?" -Query mapping: map_auth_query_type_to_response() -Queries: "... WHERE rule_type LIKE '%blacklist%'" -Validator checks: -"... WHERE rule_type = 'blacklist' AND pattern_type = 'pubkey' AND pattern_value = ?" -"... WHERE rule_type = 'blacklist' AND pattern_type = 'hash' AND pattern_value = ?" -"... WHERE rule_type = 'whitelist' AND pattern_type = 'pubkey' AND pattern_value = ?" -Embedded schema expects pattern columns and active/indexes: -"CREATE TABLE auth_rules ( ... )" -"CREATE INDEX idx_auth_rules_pattern ON auth_rules(pattern_type, pattern_value)" -"CREATE INDEX idx_auth_rules_active ON auth_rules(active)" -Fix (update migration to align with sql_schema.h/config.c): -Replace the DDL at "create_auth_rules_sql" with: CREATE TABLE IF NOT EXISTS auth_rules ( id INTEGER PRIMARY KEY AUTOINCREMENT, rule_type TEXT NOT NULL, -- 'whitelist' | 'blacklist' pattern_type TEXT NOT NULL, -- 'pubkey' | 'hash' | future pattern_value TEXT NOT NULL, -- hex pubkey/hash action TEXT NOT NULL, -- 'allow' | 'deny' active INTEGER DEFAULT 1, created_at INTEGER DEFAULT (strftime('%s','now')), UNIQUE(rule_type, pattern_type, pattern_value) ); -After creation, also create indexes as in "sql_schema.h": -CREATE INDEX idx_auth_rules_pattern ON auth_rules(pattern_type, pattern_value); -CREATE INDEX idx_auth_rules_type ON auth_rules(rule_type); -CREATE INDEX idx_auth_rules_active ON auth_rules(active); -Duplicate UI function + stale DOM id usage -Evidence: -Duplicate definition of disconnectFromRelay() and disconnectFromRelay(); the second overwrites the first and uses legacy element access paths. -Stale variable: "const relayUrl = document.getElementById('relay-url');" — no element with id="relay-url" exists; the real input is "relay-connection-url" and is referenced as "relayConnectionUrl". -Calls using relayUrl.value.trim() (must use relayConnectionUrl): -"sendConfigUpdateCommand() publish URL" -"loadAuthRules() publish URL" -"deleteAuthRule() publish URL" -Tests: -"testGetAuthRules()" -"testClearAuthRules()" -"testAddBlacklist()" -"testAddWhitelist()" -"testConfigQuery()" -"testPostEvent()" -Fix: -Remove the duplicate legacy function entirely: delete the second disconnectFromRelay(). -Remove stale variable: delete "const relayUrl = document.getElementById('relay-url');". -Replace every relayUrl.value.trim() occurrence with relayConnectionUrl.value.trim() at the lines listed above. -Supported NIPs inconsistency (README vs UI fallback) -Evidence: -README implemented NIPs checklist (authoritative): "NIPs list" shows: 1, 9, 11, 13, 15, 20, 33, 40, 42 implemented. -UI fallback for manual relay info includes unsupported/undocumented NIPs and misses implemented ones: -"supported_nips: [1, 2, 4, 9, 11, 12, 15, 16, 20, 22]" -"supported_nips: [1, 2, 4, 9, 11, 12, 15, 16, 20, 22]" -Fix: -Replace both arrays with: [1, 9, 11, 13, 15, 20, 33, 40, 42] -Config key mismatches (README vs UI edit form) -Evidence: -README keys (authoritative): "Available Configuration Keys"–(README.md:110) -relay_description, relay_contact, max_connections, max_subscriptions_per_client, max_event_tags, max_content_length, auth_enabled, nip42_auth_required, nip42_auth_required_kinds, nip42_challenge_timeout, pow_min_difficulty, nip40_expiration_enabled -UI currently declares/uses many non-README keys: -Field types: "fieldTypes" include nip42_auth_required_events, nip42_auth_required_subscriptions, relay_port, pow_mode, nip40_expiration_strict, nip40_expiration_filter, nip40_expiration_grace_period, max_total_subscriptions, max_filters_per_subscription, max_message_length, default_limit, max_limit. -Descriptions: "descriptions" reflect the same non-README keys. -Fix: -Restrict UI form generation to README keys and rename mismatches: -Combine nip42_auth_required_events/subscriptions into README’s "nip42_auth_required" (boolean). -Rename nip42_challenge_expiration to "nip42_challenge_timeout". -Remove or hide (advanced section) non-README keys: relay_port, pow_mode, nip40_expiration_strict, nip40_expiration_filter, nip40_expiration_grace_period, max_total_subscriptions, max_filters_per_subscription, max_message_length, default_limit, max_limit. -Update both "fieldTypes" and "descriptions" to reflect only README keys (data types and labels consistent). -First-time startup port override (-p) ignored when -a and -r are also provided -Observation: -You confirmed: first run with -p 7777 works, but with -p plus -a and -r the override isn’t honored. -Likely cause: -The code path that handles admin/relay key overrides on first-time setup bypasses persisting the CLI port override to config/unified cache before server start, so "start_websocket_relay(-1, ...)" falls back to default. -Fix: -Ensure first_time_startup_sequence applies cli_options.port_override to persistent config and cache BEFORE default config insertion and before starting the server. Specifically: -In the first-time path (main): -After "first_time_startup_sequence(&cli_options)" and before creating defaults on the -a/-r path at "populate_default_config_values()", write the port override: -set_config_value_in_table("relay_port", "", "integer", "WebSocket port", "relay", 0); -and update unified cache if required by the port resolution code. -Verify the code path where -a/-r trigger direct table population also applies/overwrites the port with the CLI-provided value. -Add a regression test to assert that -p is honored with and without -a/-r on first run. -Minor consistency recommendations -UI NIP-11 fallback version string: -Consider aligning with backend version source (e.g., src/version.h). The UI currently hardcodes "1.0.0" at "version: '1.0.0'". -UI hardcoded relay pubkey fallback: -"getRelayPubkey()" returns a constant when not connected. Safe for dev, but should not leak into production paths. -Added TODO items (as requested) - -The following todos were added/organized: - Remove duplicate disconnectFromRelay() and standardize to relay-connection-url - Replace all relayUrl.value references with relayConnectionUrl.value in api/index.html - Align Supported NIPs fallback arrays in api/index.html with README (1,9,11,13,15,20,33,40,42) - Update config form keys/descriptions in api/index.html to match README keys - Fix backend auth_rules migration in src/main.c to match src/sql_schema.h/src/config.c - Investigate and fix first-time startup port override ignored when -a and -r are provided - Add tests for port override and auth_rules flows - Rebuild via ./make_and_restart_relay.sh and validate against README diff --git a/Makefile b/Makefile index d407639..ad29d47 100644 --- a/Makefile +++ b/Makefile @@ -36,10 +36,10 @@ $(NOSTR_CORE_LIB): @echo "Building nostr_core_lib..." cd nostr_core_lib && ./build.sh -# Generate version.h from git tags -src/version.h: +# Generate main.h from git tags +src/main.h: @if [ -d .git ]; then \ - echo "Generating version.h from git tags..."; \ + echo "Generating main.h from git tags..."; \ RAW_VERSION=$$(git describe --tags --always 2>/dev/null || echo "unknown"); \ if echo "$$RAW_VERSION" | grep -q "^v[0-9]"; then \ CLEAN_VERSION=$$(echo "$$RAW_VERSION" | sed 's/^v//' | cut -d- -f1); \ @@ -51,54 +51,98 @@ src/version.h: VERSION="v0.0.0"; \ MAJOR=0; MINOR=0; PATCH=0; \ fi; \ - echo "/* Auto-generated version information */" > src/version.h; \ - echo "#ifndef VERSION_H" >> src/version.h; \ - echo "#define VERSION_H" >> src/version.h; \ - echo "" >> src/version.h; \ - echo "#define VERSION \"$$VERSION\"" >> src/version.h; \ - echo "#define VERSION_MAJOR $$MAJOR" >> src/version.h; \ - echo "#define VERSION_MINOR $$MINOR" >> src/version.h; \ - echo "#define VERSION_PATCH $$PATCH" >> src/version.h; \ - echo "" >> src/version.h; \ - echo "#endif /* VERSION_H */" >> src/version.h; \ - echo "Generated version.h with clean version: $$VERSION"; \ - elif [ ! -f src/version.h ]; then \ - echo "Git not available and version.h missing, creating fallback version.h..."; \ + echo "/*" > src/main.h; \ + echo " * C-Relay Main Header - Version and Metadata Information" >> src/main.h; \ + echo " *" >> src/main.h; \ + echo " * This header contains version information and relay metadata that is" >> src/main.h; \ + echo " * automatically updated by the build system (build_and_push.sh)." >> src/main.h; \ + echo " *" >> src/main.h; \ + echo " * The build_and_push.sh script updates VERSION and related macros when" >> src/main.h; \ + echo " * creating new releases." >> src/main.h; \ + echo " */" >> src/main.h; \ + echo "" >> src/main.h; \ + echo "#ifndef MAIN_H" >> src/main.h; \ + echo "#define MAIN_H" >> src/main.h; \ + echo "" >> src/main.h; \ + echo "// Version information (auto-updated by build_and_push.sh)" >> src/main.h; \ + echo "#define VERSION \"$$VERSION\"" >> src/main.h; \ + echo "#define VERSION_MAJOR $$MAJOR" >> src/main.h; \ + echo "#define VERSION_MINOR $$MINOR" >> src/main.h; \ + echo "#define VERSION_PATCH $$PATCH" >> src/main.h; \ + echo "" >> src/main.h; \ + echo "// Relay metadata (authoritative source for NIP-11 information)" >> src/main.h; \ + echo "#define RELAY_NAME \"C-Relay\"" >> src/main.h; \ + echo "#define RELAY_DESCRIPTION \"High-performance C Nostr relay with SQLite storage\"" >> src/main.h; \ + echo "#define RELAY_CONTACT \"\"" >> src/main.h; \ + echo "#define RELAY_SOFTWARE \"https://git.laantungir.net/laantungir/c-relay.git\"" >> src/main.h; \ + echo "#define RELAY_VERSION VERSION // Use the same version as the build" >> src/main.h; \ + echo "#define SUPPORTED_NIPS \"1,2,4,9,11,12,13,15,16,20,22,33,40,42\"" >> src/main.h; \ + echo "#define LANGUAGE_TAGS \"\"" >> src/main.h; \ + echo "#define RELAY_COUNTRIES \"\"" >> src/main.h; \ + echo "#define POSTING_POLICY \"\"" >> src/main.h; \ + echo "#define PAYMENTS_URL \"\"" >> src/main.h; \ + echo "" >> src/main.h; \ + echo "#endif /* MAIN_H */" >> src/main.h; \ + echo "Generated main.h with clean version: $$VERSION"; \ + elif [ ! -f src/main.h ]; then \ + echo "Git not available and main.h missing, creating fallback main.h..."; \ VERSION="v0.0.0"; \ - echo "/* Auto-generated version information */" > src/version.h; \ - echo "#ifndef VERSION_H" >> src/version.h; \ - echo "#define VERSION_H" >> src/version.h; \ - echo "" >> src/version.h; \ - echo "#define VERSION \"$$VERSION\"" >> src/version.h; \ - echo "#define VERSION_MAJOR 0" >> src/version.h; \ - echo "#define VERSION_MINOR 0" >> src/version.h; \ - echo "#define VERSION_PATCH 0" >> src/version.h; \ - echo "" >> src/version.h; \ - echo "#endif /* VERSION_H */" >> src/version.h; \ - echo "Created fallback version.h with version: $$VERSION"; \ + echo "/*" > src/main.h; \ + echo " * C-Relay Main Header - Version and Metadata Information" >> src/main.h; \ + echo " *" >> src/main.h; \ + echo " * This header contains version information and relay metadata that is" >> src/main.h; \ + echo " * automatically updated by the build system (build_and_push.sh)." >> src/main.h; \ + echo " *" >> src/main.h; \ + echo " * The build_and_push.sh script updates VERSION and related macros when" >> src/main.h; \ + echo " * creating new releases." >> src/main.h; \ + echo " */" >> src/main.h; \ + echo "" >> src/main.h; \ + echo "#ifndef MAIN_H" >> src/main.h; \ + echo "#define MAIN_H" >> src/main.h; \ + echo "" >> src/main.h; \ + echo "// Version information (auto-updated by build_and_push.sh)" >> src/main.h; \ + echo "#define VERSION \"$$VERSION\"" >> src/main.h; \ + echo "#define VERSION_MAJOR 0" >> src/main.h; \ + echo "#define VERSION_MINOR 0" >> src/main.h; \ + echo "#define VERSION_PATCH 0" >> src/main.h; \ + echo "" >> src/main.h; \ + echo "// Relay metadata (authoritative source for NIP-11 information)" >> src/main.h; \ + echo "#define RELAY_NAME \"C-Relay\"" >> src/main.h; \ + echo "#define RELAY_DESCRIPTION \"High-performance C Nostr relay with SQLite storage\"" >> src/main.h; \ + echo "#define RELAY_CONTACT \"\"" >> src/main.h; \ + echo "#define RELAY_SOFTWARE \"https://git.laantungir.net/laantungir/c-relay.git\"" >> src/main.h; \ + echo "#define RELAY_VERSION VERSION // Use the same version as the build" >> src/main.h; \ + echo "#define SUPPORTED_NIPS \"1,2,4,9,11,12,13,15,16,20,22,33,40,42\"" >> src/main.h; \ + echo "#define LANGUAGE_TAGS \"\"" >> src/main.h; \ + echo "#define RELAY_COUNTRIES \"\"" >> src/main.h; \ + echo "#define POSTING_POLICY \"\"" >> src/main.h; \ + echo "#define PAYMENTS_URL \"\"" >> src/main.h; \ + echo "" >> src/main.h; \ + echo "#endif /* MAIN_H */" >> src/main.h; \ + echo "Created fallback main.h with version: $$VERSION"; \ else \ - echo "Git not available, preserving existing version.h"; \ + echo "Git not available, preserving existing main.h"; \ fi -# Force version.h regeneration (useful for development) +# Force main.h regeneration (useful for development) force-version: - @echo "Force regenerating version.h..." - @rm -f src/version.h - @$(MAKE) src/version.h + @echo "Force regenerating main.h..." + @rm -f src/main.h + @$(MAKE) src/main.h # Build the relay -$(TARGET): $(BUILD_DIR) src/version.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) +$(TARGET): $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) @echo "Compiling C-Relay for architecture: $(ARCH)" $(CC) $(CFLAGS) $(INCLUDES) $(MAIN_SRC) -o $(TARGET) $(NOSTR_CORE_LIB) $(LIBS) @echo "Build complete: $(TARGET)" # Build for specific architectures -x86: $(BUILD_DIR) src/version.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) +x86: $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) @echo "Building C-Relay for x86_64..." $(CC) $(CFLAGS) $(INCLUDES) $(MAIN_SRC) -o $(BUILD_DIR)/c_relay_x86 $(NOSTR_CORE_LIB) $(LIBS) @echo "Build complete: $(BUILD_DIR)/c_relay_x86" -arm64: $(BUILD_DIR) src/version.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) +arm64: $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) @echo "Cross-compiling C-Relay for ARM64..." @if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then \ echo "ERROR: ARM64 cross-compiler not found."; \ @@ -171,7 +215,7 @@ init-db: # Clean build artifacts clean: rm -rf $(BUILD_DIR) - rm -f src/version.h + rm -f src/main.h @echo "Clean complete" # Clean everything including nostr_core_lib @@ -210,6 +254,6 @@ help: @echo " make check-toolchain # Check what compilers are available" @echo " make test # Run tests" @echo " make init-db # Set up database" - @echo " make force-version # Force regenerate version.h from git" + @echo " make force-version # Force regenerate main.h from git" .PHONY: all x86 arm64 test init-db clean clean-all install-deps install-cross-tools install-arm64-deps check-toolchain help force-version \ No newline at end of file diff --git a/README.md b/README.md index 077a08c..f2b04ab 100644 --- a/README.md +++ b/README.md @@ -91,8 +91,16 @@ All commands are sent as NIP-44 encrypted JSON arrays in the event content. The ### Available Configuration Keys **Basic Relay Settings:** +- `relay_name`: Relay name (displayed in NIP-11) - `relay_description`: Relay description text - `relay_contact`: Contact information +- `relay_software`: Software URL +- `relay_version`: Software version +- `supported_nips`: Comma-separated list of supported NIP numbers (e.g., "1,2,4,9,11,12,13,15,16,20,22,33,40,42") +- `language_tags`: Comma-separated list of supported language tags (e.g., "en,es,fr" or "*" for all) +- `relay_countries`: Comma-separated list of supported country codes (e.g., "US,CA,MX" or "*" for all) +- `posting_policy`: Posting policy URL or text +- `payments_url`: Payment URL for premium features - `max_connections`: Maximum concurrent connections - `max_subscriptions_per_client`: Max subscriptions per client - `max_event_tags`: Maximum tags per event diff --git a/build_and_push.sh b/build_and_push.sh index 2c28bd4..afe8f01 100755 --- a/build_and_push.sh +++ b/build_and_push.sh @@ -139,11 +139,11 @@ compile_project() { print_warning "Clean failed or no Makefile found" fi - # Force regenerate version.h to pick up new tags + # Force regenerate main.h to pick up new tags if make force-version > /dev/null 2>&1; then - print_success "Regenerated version.h" + print_success "Regenerated main.h" else - print_warning "Failed to regenerate version.h" + print_warning "Failed to regenerate main.h" fi # Compile the project diff --git a/e1d3c52a4a330293e4de1327403a9d11057e9afabf61a89cd5a31375c96eacde.db b/e1d3c52a4a330293e4de1327403a9d11057e9afabf61a89cd5a31375c96eacde.db deleted file mode 100644 index d1c5ad4..0000000 Binary files a/e1d3c52a4a330293e4de1327403a9d11057e9afabf61a89cd5a31375c96eacde.db and /dev/null differ diff --git a/relay.pid b/relay.pid index 4a842f6..599b812 100644 --- a/relay.pid +++ b/relay.pid @@ -1 +1 @@ -1878384 +2263673 diff --git a/src/config.c b/src/config.c index 1706337..55a09c9 100644 --- a/src/config.c +++ b/src/config.c @@ -99,19 +99,25 @@ static char g_temp_relay_privkey[65] = {0}; static int get_cache_timeout(void) { char *no_cache = getenv("GINX_NO_CACHE"); char *cache_timeout = getenv("GINX_CACHE_TIMEOUT"); - + if (no_cache && strcmp(no_cache, "1") == 0) { return 0; // No caching } - + if (cache_timeout) { int timeout = atoi(cache_timeout); return (timeout >= 0) ? timeout : 300; // Use provided value or default } - + return 300; // Default 5 minutes } +// Helper function to safely return dynamically allocated string from static buffer +static char* safe_strdup_from_static(const char* static_str) { + if (!static_str) return NULL; + return strdup(static_str); +} + // Force cache refresh - invalidates current cache void force_config_cache_refresh(void) { pthread_mutex_lock(&g_unified_cache.cache_lock); @@ -396,58 +402,61 @@ const char* get_config_value(const char* key) { if (!key) { return NULL; } - + // Special fast path for frequently accessed keys via unified cache if (strcmp(key, "admin_pubkey") == 0) { - return get_admin_pubkey_cached(); + const char* cached_value = get_admin_pubkey_cached(); + return safe_strdup_from_static(cached_value); } if (strcmp(key, "relay_pubkey") == 0) { - return get_relay_pubkey_cached(); + const char* cached_value = get_relay_pubkey_cached(); + return safe_strdup_from_static(cached_value); } - + // For other keys, try config table first const char* table_value = get_config_value_from_table(key); if (table_value) { return table_value; } - + // Fallback to legacy event-based config for backward compatibility // Use unified cache buffer instead of static buffer - + if (!g_current_config) { return NULL; } - + // Look for key in current configuration tags cJSON* tags = cJSON_GetObjectItem(g_current_config, "tags"); if (!tags || !cJSON_IsArray(tags)) { return NULL; } - + pthread_mutex_lock(&g_unified_cache.cache_lock); - + cJSON* tag = NULL; cJSON_ArrayForEach(tag, tags) { if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) { cJSON* tag_key = cJSON_GetArrayItem(tag, 0); cJSON* tag_value = cJSON_GetArrayItem(tag, 1); - + if (tag_key && tag_value && cJSON_IsString(tag_key) && cJSON_IsString(tag_value)) { - + if (strcmp(cJSON_GetStringValue(tag_key), key) == 0) { strncpy(g_unified_cache.temp_buffer, cJSON_GetStringValue(tag_value), sizeof(g_unified_cache.temp_buffer) - 1); g_unified_cache.temp_buffer[sizeof(g_unified_cache.temp_buffer) - 1] = '\0'; + const char* result = safe_strdup_from_static(g_unified_cache.temp_buffer); pthread_mutex_unlock(&g_unified_cache.cache_lock); - return g_unified_cache.temp_buffer; + return result; } } } } - + pthread_mutex_unlock(&g_unified_cache.cache_lock); - + return NULL; } @@ -456,14 +465,18 @@ int get_config_int(const char* key, int default_value) { if (!str_value) { return default_value; } - + char* endptr; long val = strtol(str_value, &endptr, 10); - + if (endptr == str_value || *endptr != '\0') { + // Free the dynamically allocated string + free((char*)str_value); return default_value; } - + + // Free the dynamically allocated string + free((char*)str_value); return (int)val; } @@ -472,18 +485,23 @@ int get_config_bool(const char* key, int default_value) { if (!str_value) { return default_value; } - - if (strcasecmp(str_value, "true") == 0 || - strcasecmp(str_value, "yes") == 0 || + + int result; + if (strcasecmp(str_value, "true") == 0 || + strcasecmp(str_value, "yes") == 0 || strcasecmp(str_value, "1") == 0) { - return 1; - } else if (strcasecmp(str_value, "false") == 0 || - strcasecmp(str_value, "no") == 0 || + result = 1; + } else if (strcasecmp(str_value, "false") == 0 || + strcasecmp(str_value, "no") == 0 || strcasecmp(str_value, "0") == 0) { - return 0; + result = 0; + } else { + result = default_value; } - - return default_value; + + // Free the dynamically allocated string + free((char*)str_value); + return result; } // ================================ @@ -1063,33 +1081,38 @@ int startup_existing_relay(const char* relay_pubkey) { log_error("Invalid relay pubkey for existing relay startup"); return -1; } - + log_info("Starting existing relay..."); printf(" Relay pubkey: %s\n", relay_pubkey); - + // Store relay pubkey in unified cache pthread_mutex_lock(&g_unified_cache.cache_lock); strncpy(g_unified_cache.relay_pubkey, relay_pubkey, sizeof(g_unified_cache.relay_pubkey) - 1); g_unified_cache.relay_pubkey[sizeof(g_unified_cache.relay_pubkey) - 1] = '\0'; pthread_mutex_unlock(&g_unified_cache.cache_lock); - + // Set database path char* db_name = get_database_name_from_relay_pubkey(relay_pubkey); if (!db_name) { log_error("Failed to generate database name"); return -1; } - + strncpy(g_database_path, db_name, sizeof(g_database_path) - 1); g_database_path[sizeof(g_database_path) - 1] = '\0'; free(db_name); - + + // Ensure default configuration values are populated (for any missing keys) + if (populate_default_config_values() != 0) { + log_warning("Failed to populate default config values for existing relay - continuing"); + } + // Configuration will be migrated from events to table after database initialization log_info("Configuration migration will be performed after database is available"); - + // Load configuration event from database (after database is initialized) // This will be done in apply_configuration_from_database() - + log_success("Existing relay startup prepared"); return 0; } @@ -1814,31 +1837,90 @@ int handle_configuration_event(cJSON* event, char* error_message, size_t error_s // Get value from config table const char* get_config_value_from_table(const char* key) { if (!g_db || !key) return NULL; - + const char* sql = "SELECT value FROM config WHERE key = ?"; - + sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL); if (rc != SQLITE_OK) { return NULL; } - + sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC); - + const char* result = NULL; - + if (sqlite3_step(stmt) == SQLITE_ROW) { const char* value = (char*)sqlite3_column_text(stmt, 0); if (value) { - // Use unified cache buffer with thread safety - pthread_mutex_lock(&g_unified_cache.cache_lock); - strncpy(g_unified_cache.temp_buffer, value, sizeof(g_unified_cache.temp_buffer) - 1); - g_unified_cache.temp_buffer[sizeof(g_unified_cache.temp_buffer) - 1] = '\0'; - result = g_unified_cache.temp_buffer; - pthread_mutex_unlock(&g_unified_cache.cache_lock); + // For NIP-11 fields, store in cache buffers but return dynamically allocated strings for consistency + if (strcmp(key, "relay_name") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.name, value, sizeof(g_unified_cache.relay_info.name) - 1); + g_unified_cache.relay_info.name[sizeof(g_unified_cache.relay_info.name) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "relay_description") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.description, value, sizeof(g_unified_cache.relay_info.description) - 1); + g_unified_cache.relay_info.description[sizeof(g_unified_cache.relay_info.description) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "relay_contact") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.contact, value, sizeof(g_unified_cache.relay_info.contact) - 1); + g_unified_cache.relay_info.contact[sizeof(g_unified_cache.relay_info.contact) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "relay_software") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.software, value, sizeof(g_unified_cache.relay_info.software) - 1); + g_unified_cache.relay_info.software[sizeof(g_unified_cache.relay_info.software) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "relay_version") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.version, value, sizeof(g_unified_cache.relay_info.version) - 1); + g_unified_cache.relay_info.version[sizeof(g_unified_cache.relay_info.version) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "supported_nips") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.supported_nips_str, value, sizeof(g_unified_cache.relay_info.supported_nips_str) - 1); + g_unified_cache.relay_info.supported_nips_str[sizeof(g_unified_cache.relay_info.supported_nips_str) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "language_tags") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.language_tags_str, value, sizeof(g_unified_cache.relay_info.language_tags_str) - 1); + g_unified_cache.relay_info.language_tags_str[sizeof(g_unified_cache.relay_info.language_tags_str) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "relay_countries") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.relay_countries_str, value, sizeof(g_unified_cache.relay_info.relay_countries_str) - 1); + g_unified_cache.relay_info.relay_countries_str[sizeof(g_unified_cache.relay_info.relay_countries_str) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "posting_policy") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.posting_policy, value, sizeof(g_unified_cache.relay_info.posting_policy) - 1); + g_unified_cache.relay_info.posting_policy[sizeof(g_unified_cache.relay_info.posting_policy) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else if (strcmp(key, "payments_url") == 0) { + pthread_mutex_lock(&g_unified_cache.cache_lock); + strncpy(g_unified_cache.relay_info.payments_url, value, sizeof(g_unified_cache.relay_info.payments_url) - 1); + g_unified_cache.relay_info.payments_url[sizeof(g_unified_cache.relay_info.payments_url) - 1] = '\0'; + result = strdup(value); // Return dynamically allocated copy + pthread_mutex_unlock(&g_unified_cache.cache_lock); + } else { + // For other keys, return a dynamically allocated string to prevent buffer reuse + result = strdup(value); + } } } - + sqlite3_finalize(stmt); return result; } @@ -3906,12 +3988,17 @@ const char* get_config_value_hybrid(const char* key) { if (is_config_table_ready()) { const char* table_value = get_config_value_from_table(key); if (table_value) { - return table_value; + return table_value; // Already dynamically allocated } } - - // Fall back to event-based config - return get_config_value(key); + + // Fall back to event-based config, but ensure it's dynamically allocated + const char* fallback_value = get_config_value(key); + if (fallback_value) { + return strdup(fallback_value); // Make a copy since fallback might be static + } + + return NULL; } // Check if config table is ready diff --git a/src/config.h b/src/config.h index bc7504b..e300f2e 100644 --- a/src/config.h +++ b/src/config.h @@ -56,6 +56,11 @@ typedef struct { char version[64]; char privacy_policy[RELAY_URL_MAX_LENGTH]; char terms_of_service[RELAY_URL_MAX_LENGTH]; + // Raw string values for parsing into cJSON arrays + char supported_nips_str[CONFIG_VALUE_MAX_LENGTH]; + char language_tags_str[CONFIG_VALUE_MAX_LENGTH]; + char relay_countries_str[CONFIG_VALUE_MAX_LENGTH]; + // Parsed cJSON arrays cJSON* supported_nips; cJSON* limitation; cJSON* retention; diff --git a/src/default_config_event.h b/src/default_config_event.h index f857548..6687773 100644 --- a/src/default_config_event.h +++ b/src/default_config_event.h @@ -3,6 +3,7 @@ #include #include "config.h" // For cli_options_t definition +#include "main.h" // For relay metadata constants /* * Default Configuration Event Template @@ -33,10 +34,16 @@ static const struct { {"max_connections", "100"}, // NIP-11 Relay Information (relay keys will be populated at runtime) - {"relay_description", "High-performance C Nostr relay with SQLite storage"}, - {"relay_contact", ""}, - {"relay_software", "https://git.laantungir.net/laantungir/c-relay.git"}, - {"relay_version", "v1.0.0"}, + {"relay_name", RELAY_NAME}, + {"relay_description", RELAY_DESCRIPTION}, + {"relay_contact", RELAY_CONTACT}, + {"relay_software", RELAY_SOFTWARE}, + {"relay_version", RELAY_VERSION}, + {"supported_nips", SUPPORTED_NIPS}, + {"language_tags", LANGUAGE_TAGS}, + {"relay_countries", RELAY_COUNTRIES}, + {"posting_policy", POSTING_POLICY}, + {"payments_url", PAYMENTS_URL}, // NIP-13 Proof of Work (pow_min_difficulty = 0 means PoW disabled) {"pow_min_difficulty", "0"}, diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..5020369 --- /dev/null +++ b/src/main.h @@ -0,0 +1,32 @@ +/* + * C-Relay Main Header - Version and Metadata Information + * + * This header contains version information and relay metadata that is + * automatically updated by the build system (build_and_push.sh). + * + * The build_and_push.sh script updates VERSION and related macros when + * creating new releases. + */ + +#ifndef MAIN_H +#define MAIN_H + +// Version information (auto-updated by build_and_push.sh) +#define VERSION "v0.4.2" +#define VERSION_MAJOR 0 +#define VERSION_MINOR 4 +#define VERSION_PATCH 2 + +// Relay metadata (authoritative source for NIP-11 information) +#define RELAY_NAME "C-Relay" +#define RELAY_DESCRIPTION "High-performance C Nostr relay with SQLite storage" +#define RELAY_CONTACT "" +#define RELAY_SOFTWARE "https://git.laantungir.net/laantungir/c-relay.git" +#define RELAY_VERSION VERSION // Use the same version as the build +#define SUPPORTED_NIPS "1,2,4,9,11,12,13,15,16,20,22,33,40,42" +#define LANGUAGE_TAGS "" +#define RELAY_COUNTRIES "" +#define POSTING_POLICY "" +#define PAYMENTS_URL "" + +#endif /* MAIN_H */ diff --git a/src/nip011.c b/src/nip011.c index 9fb6035..d47919a 100644 --- a/src/nip011.c +++ b/src/nip011.c @@ -34,76 +34,213 @@ extern unified_config_cache_t g_unified_cache; ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// +// Helper function to parse comma-separated string into cJSON array +cJSON* parse_comma_separated_array(const char* csv_string) { + log_info("parse_comma_separated_array called"); + if (!csv_string || strlen(csv_string) == 0) { + log_info("Empty or null csv_string, returning empty array"); + return cJSON_CreateArray(); + } + + log_info("Creating cJSON array"); + cJSON* array = cJSON_CreateArray(); + if (!array) { + log_info("Failed to create cJSON array"); + return NULL; + } + + log_info("Duplicating csv_string"); + char* csv_copy = strdup(csv_string); + if (!csv_copy) { + log_info("Failed to duplicate csv_string"); + cJSON_Delete(array); + return NULL; + } + + log_info("Starting token parsing"); + char* token = strtok(csv_copy, ","); + while (token) { + log_info("Processing token"); + // Trim whitespace + while (*token == ' ') token++; + char* end = token + strlen(token) - 1; + while (end > token && *end == ' ') *end-- = '\0'; + + if (strlen(token) > 0) { + log_info("Token has content, parsing"); + // Try to parse as number first (for supported_nips) + char* endptr; + long num = strtol(token, &endptr, 10); + if (*endptr == '\0') { + log_info("Token is number, adding to array"); + // It's a number + cJSON_AddItemToArray(array, cJSON_CreateNumber(num)); + } else { + log_info("Token is string, adding to array"); + // It's a string + cJSON_AddItemToArray(array, cJSON_CreateString(token)); + } + } else { + log_info("Token is empty, skipping"); + } + token = strtok(NULL, ","); + } + + log_info("Freeing csv_copy"); + free(csv_copy); + log_info("Returning parsed array"); + return array; +} + // Initialize relay information using configuration system void init_relay_info() { + log_info("Initializing relay information from configuration..."); + // Get all config values first (without holding mutex to avoid deadlock) + // Note: These may be dynamically allocated strings that need to be freed + log_info("Fetching relay configuration values..."); const char* relay_name = get_config_value("relay_name"); + log_info("relay_name fetched"); const char* relay_description = get_config_value("relay_description"); + log_info("relay_description fetched"); const char* relay_software = get_config_value("relay_software"); + log_info("relay_software fetched"); const char* relay_version = get_config_value("relay_version"); + log_info("relay_version fetched"); const char* relay_contact = get_config_value("relay_contact"); + log_info("relay_contact fetched"); const char* relay_pubkey = get_config_value("relay_pubkey"); - + log_info("relay_pubkey fetched"); + const char* supported_nips_csv = get_config_value("supported_nips"); + log_info("supported_nips fetched"); + const char* language_tags_csv = get_config_value("language_tags"); + log_info("language_tags fetched"); + const char* relay_countries_csv = get_config_value("relay_countries"); + log_info("relay_countries fetched"); + const char* posting_policy = get_config_value("posting_policy"); + log_info("posting_policy fetched"); + const char* payments_url = get_config_value("payments_url"); + log_info("payments_url fetched"); + // Get config values for limitations + log_info("Fetching limitation configuration values..."); int max_message_length = get_config_int("max_message_length", 16384); + log_info("max_message_length fetched"); int max_subscriptions_per_client = get_config_int("max_subscriptions_per_client", 20); + log_info("max_subscriptions_per_client fetched"); int max_limit = get_config_int("max_limit", 5000); + log_info("max_limit fetched"); int max_event_tags = get_config_int("max_event_tags", 100); + log_info("max_event_tags fetched"); int max_content_length = get_config_int("max_content_length", 8196); + log_info("max_content_length fetched"); int default_limit = get_config_int("default_limit", 500); + log_info("default_limit fetched"); int admin_enabled = get_config_bool("admin_enabled", 0); - + log_info("admin_enabled fetched"); + pthread_mutex_lock(&g_unified_cache.cache_lock); - + // Update relay information fields + log_info("Storing string values in cache..."); if (relay_name) { + log_info("Storing relay_name"); strncpy(g_unified_cache.relay_info.name, relay_name, sizeof(g_unified_cache.relay_info.name) - 1); + free((char*)relay_name); // Free dynamically allocated string + log_info("relay_name stored and freed"); } else { + log_info("Using default relay_name"); strncpy(g_unified_cache.relay_info.name, "C Nostr Relay", sizeof(g_unified_cache.relay_info.name) - 1); } - + if (relay_description) { + log_info("Storing relay_description"); strncpy(g_unified_cache.relay_info.description, relay_description, sizeof(g_unified_cache.relay_info.description) - 1); + free((char*)relay_description); // Free dynamically allocated string + log_info("relay_description stored and freed"); } else { + log_info("Using default relay_description"); strncpy(g_unified_cache.relay_info.description, "A high-performance Nostr relay implemented in C with SQLite storage", sizeof(g_unified_cache.relay_info.description) - 1); } - + if (relay_software) { + log_info("Storing relay_software"); strncpy(g_unified_cache.relay_info.software, relay_software, sizeof(g_unified_cache.relay_info.software) - 1); + free((char*)relay_software); // Free dynamically allocated string + log_info("relay_software stored and freed"); } else { + log_info("Using default relay_software"); strncpy(g_unified_cache.relay_info.software, "https://git.laantungir.net/laantungir/c-relay.git", sizeof(g_unified_cache.relay_info.software) - 1); } - + if (relay_version) { + log_info("Storing relay_version"); strncpy(g_unified_cache.relay_info.version, relay_version, sizeof(g_unified_cache.relay_info.version) - 1); + free((char*)relay_version); // Free dynamically allocated string + log_info("relay_version stored and freed"); } else { + log_info("Using default relay_version"); strncpy(g_unified_cache.relay_info.version, "0.2.0", sizeof(g_unified_cache.relay_info.version) - 1); } - + if (relay_contact) { + log_info("Storing relay_contact"); strncpy(g_unified_cache.relay_info.contact, relay_contact, sizeof(g_unified_cache.relay_info.contact) - 1); + free((char*)relay_contact); // Free dynamically allocated string + log_info("relay_contact stored and freed"); } - + if (relay_pubkey) { + log_info("Storing relay_pubkey"); strncpy(g_unified_cache.relay_info.pubkey, relay_pubkey, sizeof(g_unified_cache.relay_info.pubkey) - 1); + free((char*)relay_pubkey); // Free dynamically allocated string + log_info("relay_pubkey stored and freed"); + } + + if (posting_policy) { + log_info("Storing posting_policy"); + strncpy(g_unified_cache.relay_info.posting_policy, posting_policy, sizeof(g_unified_cache.relay_info.posting_policy) - 1); + free((char*)posting_policy); // Free dynamically allocated string + log_info("posting_policy stored and freed"); + } + + if (payments_url) { + log_info("Storing payments_url"); + strncpy(g_unified_cache.relay_info.payments_url, payments_url, sizeof(g_unified_cache.relay_info.payments_url) - 1); + free((char*)payments_url); // Free dynamically allocated string + log_info("payments_url stored and freed"); } - // Initialize supported NIPs array - g_unified_cache.relay_info.supported_nips = cJSON_CreateArray(); - if (g_unified_cache.relay_info.supported_nips) { - cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(1)); // NIP-01: Basic protocol - cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(9)); // NIP-09: Event deletion - cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(11)); // NIP-11: Relay information - cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(13)); // NIP-13: Proof of Work - cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(15)); // NIP-15: EOSE - cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(20)); // NIP-20: Command results - cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(40)); // NIP-40: Expiration Timestamp - cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(42)); // NIP-42: Authentication + // Initialize supported NIPs array from config + log_info("Initializing supported_nips array"); + if (supported_nips_csv) { + log_info("Parsing supported_nips from config"); + g_unified_cache.relay_info.supported_nips = parse_comma_separated_array(supported_nips_csv); + log_info("supported_nips parsed successfully"); + free((char*)supported_nips_csv); // Free dynamically allocated string + log_info("supported_nips_csv freed"); + } else { + log_info("Using default supported_nips"); + // Fallback to default supported NIPs + g_unified_cache.relay_info.supported_nips = cJSON_CreateArray(); + if (g_unified_cache.relay_info.supported_nips) { + cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(1)); // NIP-01: Basic protocol + cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(9)); // NIP-09: Event deletion + cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(11)); // NIP-11: Relay information + cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(13)); // NIP-13: Proof of Work + cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(15)); // NIP-15: EOSE + cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(20)); // NIP-20: Command results + cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(40)); // NIP-40: Expiration Timestamp + cJSON_AddItemToArray(g_unified_cache.relay_info.supported_nips, cJSON_CreateNumber(42)); // NIP-42: Authentication + } + log_info("Default supported_nips created"); } // Initialize server limitations using configuration + log_info("Initializing server limitations"); g_unified_cache.relay_info.limitation = cJSON_CreateObject(); if (g_unified_cache.relay_info.limitation) { + log_info("Adding limitation fields"); cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_message_length", max_message_length); cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_subscriptions", max_subscriptions_per_client); cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "max_limit", max_limit); @@ -117,31 +254,60 @@ void init_relay_info() { cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_lower_limit", 0); cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "created_at_upper_limit", 2147483647); cJSON_AddNumberToObject(g_unified_cache.relay_info.limitation, "default_limit", default_limit); + log_info("Limitation fields added"); + } else { + log_info("Failed to create limitation object"); } // Initialize empty retention policies (can be configured later) + log_info("Initializing retention policies"); g_unified_cache.relay_info.retention = cJSON_CreateArray(); - - // Initialize language tags - set to global for now - g_unified_cache.relay_info.language_tags = cJSON_CreateArray(); - if (g_unified_cache.relay_info.language_tags) { - cJSON_AddItemToArray(g_unified_cache.relay_info.language_tags, cJSON_CreateString("*")); + + // Initialize language tags from config + log_info("Initializing language_tags"); + if (language_tags_csv) { + log_info("Parsing language_tags from config"); + g_unified_cache.relay_info.language_tags = parse_comma_separated_array(language_tags_csv); + log_info("language_tags parsed successfully"); + free((char*)language_tags_csv); // Free dynamically allocated string + log_info("language_tags_csv freed"); + } else { + log_info("Using default language_tags"); + // Fallback to global + g_unified_cache.relay_info.language_tags = cJSON_CreateArray(); + if (g_unified_cache.relay_info.language_tags) { + cJSON_AddItemToArray(g_unified_cache.relay_info.language_tags, cJSON_CreateString("*")); + } } - - // Initialize relay countries - set to global for now - g_unified_cache.relay_info.relay_countries = cJSON_CreateArray(); - if (g_unified_cache.relay_info.relay_countries) { - cJSON_AddItemToArray(g_unified_cache.relay_info.relay_countries, cJSON_CreateString("*")); + + // Initialize relay countries from config + log_info("Initializing relay_countries"); + if (relay_countries_csv) { + log_info("Parsing relay_countries from config"); + g_unified_cache.relay_info.relay_countries = parse_comma_separated_array(relay_countries_csv); + log_info("relay_countries parsed successfully"); + free((char*)relay_countries_csv); // Free dynamically allocated string + log_info("relay_countries_csv freed"); + } else { + log_info("Using default relay_countries"); + // Fallback to global + g_unified_cache.relay_info.relay_countries = cJSON_CreateArray(); + if (g_unified_cache.relay_info.relay_countries) { + cJSON_AddItemToArray(g_unified_cache.relay_info.relay_countries, cJSON_CreateString("*")); + } } - + // Initialize content tags as empty array + log_info("Initializing tags"); g_unified_cache.relay_info.tags = cJSON_CreateArray(); - + // Initialize fees as empty object (no payment required by default) + log_info("Initializing fees"); g_unified_cache.relay_info.fees = cJSON_CreateObject(); - + + log_info("Unlocking cache mutex"); pthread_mutex_unlock(&g_unified_cache.cache_lock); - + log_success("Relay information initialized with default values"); } diff --git a/src/nip013.c b/src/nip013.c index a8b91e0..832df38 100644 --- a/src/nip013.c +++ b/src/nip013.c @@ -60,6 +60,7 @@ void init_pow_config() { g_unified_cache.pow_config.enabled = 0; log_info("PoW validation disabled via configuration"); } + free((char*)pow_mode); // Free dynamically allocated string } else { // Default to basic mode g_unified_cache.pow_config.validation_flags = NOSTR_POW_VALIDATE_BASIC; diff --git a/src/request_validator.c b/src/request_validator.c index 68803ab..21177d6 100644 --- a/src/request_validator.c +++ b/src/request_validator.c @@ -211,13 +211,15 @@ int ginxsom_request_validator_init(const char *db_path, const char *app_name) { // Initialize NIP-42 challenge manager using unified config memset(&g_challenge_manager, 0, sizeof(g_challenge_manager)); - + const char* nip42_timeout = get_config_value("nip42_challenge_timeout"); g_challenge_manager.timeout_seconds = nip42_timeout ? atoi(nip42_timeout) : 600; - + if (nip42_timeout) free((char*)nip42_timeout); + const char* nip42_tolerance = get_config_value("nip42_time_tolerance"); g_challenge_manager.time_tolerance_seconds = nip42_tolerance ? atoi(nip42_tolerance) : 300; - + if (nip42_tolerance) free((char*)nip42_tolerance); + g_challenge_manager.last_cleanup = time(NULL); g_validator_initialized = 1; @@ -232,13 +234,20 @@ int ginxsom_request_validator_init(const char *db_path, const char *app_name) { int nostr_auth_rules_enabled(void) { // Use unified cache from config.c const char* auth_enabled = get_config_value("auth_enabled"); + int result = 0; if (auth_enabled && strcmp(auth_enabled, "true") == 0) { - return 1; + result = 1; } - + if (auth_enabled) free((char*)auth_enabled); + // Also check legacy key const char* auth_rules_enabled = get_config_value("auth_rules_enabled"); - return (auth_rules_enabled && strcmp(auth_rules_enabled, "true") == 0) ? 1 : 0; + if (auth_rules_enabled && strcmp(auth_rules_enabled, "true") == 0) { + result = 1; + } + if (auth_rules_enabled) free((char*)auth_rules_enabled); + + return result; } /////////////////////////////////////////////////////////////////////////////////////// @@ -344,9 +353,11 @@ int nostr_validate_unified_request(const char* json_string, size_t json_length) const char* nip42_enabled = get_config_value("nip42_auth_enabled"); if (nip42_enabled && strcmp(nip42_enabled, "false") == 0) { validator_debug_log("VALIDATOR_DEBUG: STEP 8 FAILED - NIP-42 is disabled\n"); + free((char*)nip42_enabled); cJSON_Delete(event); return NOSTR_ERROR_NIP42_DISABLED; } + if (nip42_enabled) free((char*)nip42_enabled); // TODO: Implement full NIP-42 challenge validation // For now, accept all valid NIP-42 events