Start upload functionality

This commit is contained in:
Your Name
2025-08-19 08:00:40 -04:00
parent e8bac95aca
commit d8803b1ad0
16 changed files with 2386 additions and 42 deletions

84
src/ginxsom.h Normal file
View File

@@ -0,0 +1,84 @@
/*
* Ginxsom Blossom Server Header
*
* This header contains all function declarations and type definitions
* organized by BUD (Blossom Unified Draft) sections.
*/
#ifndef GINXSOM_H
#define GINXSOM_H
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include <fcgi_stdio.h>
#include <sqlite3.h>
#include "../nostr_core_lib/cjson/cJSON.h"
#include "../nostr_core_lib/nostr_core/nostr_core.h"
#ifdef __cplusplus
extern "C" {
#endif
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// BUD 01 - Basic Protocol Flow
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Database connection management
extern sqlite3* db;
int init_database(void);
void close_database(void);
// SHA-256 extraction and validation
const char* extract_sha256_from_uri(const char* uri);
// HEAD request handling
void handle_head_request(const char* uri);
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// BUD 02 - Upload & Authentication
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Authorization header parsing
int parse_authorization_header(const char* auth_header, char* event_json, size_t json_size);
// Blossom event validation (specific to kind 24242)
int validate_blossom_event(cJSON* event, const char* expected_hash, const char* method);
// Main authentication orchestrator
int authenticate_request(const char* auth_header, const char* method, const char* file_hash);
// Upload handling
void handle_upload_request(void);
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// BUD 06 - Upload Requirements
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Upload policy management (for future implementation)
void handle_upload_requirements_request(void);
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// UTILITY FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// HTTP response helpers
void send_error_response(int status_code, const char* message);
void send_json_response(int status_code, const char* json_content);
// Logging utilities
void log_request(const char* method, const char* uri, int status_code);
#ifdef __cplusplus
}
#endif
#endif // GINXSOM_H

View File

@@ -3,14 +3,18 @@
* Handles HEAD requests and other dynamic operations
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <fcgi_stdio.h>
#include <sqlite3.h>
#include <sys/stat.h>
#include <time.h>
#include <stdint.h>
#include "ginxsom.h"
#define MAX_SHA256_LEN 65
#define MAX_PATH_LEN 512
@@ -218,6 +222,266 @@ const char* extract_sha256_from_uri(const char* uri) {
return sha256_buffer;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// BUD 02 - Upload & Authentication
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// Parse Authorization header and extract JSON event
int parse_authorization_header(const char* auth_header, char* event_json, size_t json_size) {
if (!auth_header || !event_json) {
return NOSTR_ERROR_INVALID_INPUT;
}
// Check for "Nostr " prefix (case-insensitive)
const char* prefix = "nostr ";
size_t prefix_len = strlen(prefix);
if (strncasecmp(auth_header, prefix, prefix_len) != 0) {
printf("DEBUG: Authorization header missing 'Nostr ' prefix\r\n");
return NOSTR_ERROR_INVALID_INPUT;
}
// Extract base64 encoded event after "Nostr "
const char* base64_event = auth_header + prefix_len;
// Decode base64 to JSON
// For now, we'll assume the event is already JSON (not base64 encoded)
// This is a simplified implementation - in production you'd need proper base64 decoding
size_t event_len = strlen(base64_event);
if (event_len >= json_size) {
printf("DEBUG: Event JSON too large for buffer\r\n");
return NOSTR_ERROR_INVALID_INPUT;
}
strncpy(event_json, base64_event, json_size - 1);
event_json[json_size - 1] = '\0';
printf("DEBUG: Parsed authorization header, extracted JSON: %.100s...\r\n", event_json);
return NOSTR_SUCCESS;
}
// Validate Blossom-specific event requirements (kind 24242)
int validate_blossom_event(cJSON* event, const char* expected_hash, const char* method) {
if (!event) {
return NOSTR_ERROR_INVALID_INPUT;
}
printf("DEBUG: Validating Blossom event\r\n");
// Check event kind (must be 24242 for Blossom uploads)
cJSON* kind_json = cJSON_GetObjectItem(event, "kind");
if (!kind_json || !cJSON_IsNumber(kind_json)) {
printf("DEBUG: Event missing or invalid 'kind' field\r\n");
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
int kind = cJSON_GetNumberValue(kind_json);
if (kind != 24242) {
printf("DEBUG: Event kind %d is not 24242 (Blossom upload)\r\n", kind);
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
// Check that created_at exists (basic validation)
cJSON* created_at_json = cJSON_GetObjectItem(event, "created_at");
if (!created_at_json || !cJSON_IsNumber(created_at_json)) {
printf("DEBUG: Event missing or invalid 'created_at' field\r\n");
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
// Look for expiration in tags
cJSON* tags = cJSON_GetObjectItem(event, "tags");
if (!tags || !cJSON_IsArray(tags)) {
printf("DEBUG: Event missing or invalid 'tags' field\r\n");
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
time_t expiration = 0;
int found_method = 0;
int found_hash = 0;
// Parse tags for 't' (method), 'x' (hash), and 'expiration'
cJSON* tag = NULL;
cJSON_ArrayForEach(tag, tags) {
if (!cJSON_IsArray(tag)) continue;
cJSON* tag_name = cJSON_GetArrayItem(tag, 0);
if (!tag_name || !cJSON_IsString(tag_name)) continue;
const char* tag_name_str = cJSON_GetStringValue(tag_name);
if (strcmp(tag_name_str, "t") == 0) {
// Method tag
cJSON* method_value = cJSON_GetArrayItem(tag, 1);
if (method_value && cJSON_IsString(method_value)) {
const char* event_method = cJSON_GetStringValue(method_value);
if (strcmp(event_method, method) == 0) {
found_method = 1;
printf("DEBUG: Found matching method tag: %s\r\n", event_method);
}
}
} else if (strcmp(tag_name_str, "x") == 0) {
// Hash tag
cJSON* hash_value = cJSON_GetArrayItem(tag, 1);
if (hash_value && cJSON_IsString(hash_value)) {
const char* event_hash = cJSON_GetStringValue(hash_value);
if (expected_hash && strcmp(event_hash, expected_hash) == 0) {
found_hash = 1;
printf("DEBUG: Found matching hash tag: %s\r\n", event_hash);
}
}
} else if (strcmp(tag_name_str, "expiration") == 0) {
// Expiration tag
cJSON* exp_value = cJSON_GetArrayItem(tag, 1);
if (exp_value && cJSON_IsString(exp_value)) {
expiration = (time_t)atol(cJSON_GetStringValue(exp_value));
printf("DEBUG: Found expiration tag: %ld\r\n", expiration);
}
}
}
// Check if method matches (required)
if (!found_method) {
printf("DEBUG: Event missing or invalid method tag\r\n");
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
// Check if hash matches (if provided)
if (expected_hash && !found_hash) {
printf("DEBUG: Event hash doesn't match expected hash\r\n");
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
// Check expiration
time_t now = time(NULL);
if (expiration > 0 && now > expiration) {
printf("DEBUG: Event expired (now: %ld, exp: %ld)\r\n", now, expiration);
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
printf("DEBUG: Blossom event validation passed\r\n");
return NOSTR_SUCCESS;
}
// Main authentication function
int authenticate_request(const char* auth_header, const char* method, const char* file_hash) {
if (!auth_header) {
printf("DEBUG: No authorization header provided\r\n");
return NOSTR_ERROR_INVALID_INPUT;
}
printf("DEBUG: Authenticating request - method: %s, hash: %s\r\n",
method ? method : "null", file_hash ? file_hash : "null");
// Parse authorization header
char event_json[4096];
int parse_result = parse_authorization_header(auth_header, event_json, sizeof(event_json));
if (parse_result != NOSTR_SUCCESS) {
printf("DEBUG: Authorization header parsing failed: %d\r\n", parse_result);
return parse_result;
}
// Parse JSON event
cJSON* event = cJSON_Parse(event_json);
if (!event) {
printf("DEBUG: Failed to parse JSON event\r\n");
return NOSTR_ERROR_EVENT_INVALID_CONTENT;
}
// Validate event structure and signature using nostr_core_lib
int validation_result = nostr_validate_event(event);
if (validation_result != NOSTR_SUCCESS) {
printf("DEBUG: Nostr event validation failed: %d (%s)\r\n",
validation_result, nostr_strerror(validation_result));
cJSON_Delete(event);
return validation_result;
}
// Validate Blossom-specific requirements
int blossom_result = validate_blossom_event(event, file_hash, method);
if (blossom_result != NOSTR_SUCCESS) {
printf("DEBUG: Blossom event validation failed: %d\r\n", blossom_result);
cJSON_Delete(event);
return blossom_result;
}
cJSON_Delete(event);
printf("DEBUG: Authentication successful\r\n");
return NOSTR_SUCCESS;
}
// Handle PUT /upload requests
void handle_upload_request(void) {
printf("DEBUG: handle_upload_request called\r\n");
// Get HTTP headers
const char* auth_header = getenv("HTTP_AUTHORIZATION");
const char* content_type = getenv("CONTENT_TYPE");
const char* content_length_str = getenv("CONTENT_LENGTH");
printf("DEBUG: auth_header=%s\r\n", auth_header ? auth_header : "NULL");
printf("DEBUG: content_type=%s\r\n", content_type ? content_type : "NULL");
printf("DEBUG: content_length=%s\r\n", content_length_str ? content_length_str : "NULL");
// Validate required headers
if (!auth_header) {
printf("Status: 401 Unauthorized\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Authorization header required\n");
return;
}
if (!content_type) {
printf("Status: 400 Bad Request\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Content-Type header required\n");
return;
}
if (!content_length_str) {
printf("Status: 400 Bad Request\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Content-Length header required\n");
return;
}
long content_length = atol(content_length_str);
if (content_length <= 0 || content_length > 100 * 1024 * 1024) { // 100MB limit
printf("Status: 413 Payload Too Large\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("File size must be between 1 byte and 100MB\n");
return;
}
// Authenticate the request
int auth_result = authenticate_request(auth_header, "PUT", NULL);
if (auth_result != NOSTR_SUCCESS) {
printf("DEBUG: Authentication failed: %d\r\n", auth_result);
printf("Status: 401 Unauthorized\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Authentication failed\n");
return;
}
printf("DEBUG: Authentication successful, proceeding with upload\r\n");
// For now, return a simple response indicating the upload endpoint is working
// In a full implementation, you would:
// 1. Read the file data from stdin
// 2. Calculate SHA-256 hash
// 3. Save file to blobs/ directory with proper extension
// 4. Store metadata in database
// 5. Return blob descriptor JSON
printf("Status: 501 Not Implemented\r\n");
printf("Content-Type: application/json\r\n\r\n");
printf("{\n");
printf(" \"message\": \"Upload endpoint authenticated successfully\",\n");
printf(" \"note\": \"Full file upload implementation pending\"\n");
printf("}\n");
}
int main(void) {
while (FCGI_Accept() >= 0) {
// DEBUG: Log every request received
@@ -249,6 +513,9 @@ int main(void) {
printf("Content-Type: text/plain\r\n\r\n");
printf("Invalid SHA-256 hash in URI\n");
}
} else if (strcmp(request_method, "PUT") == 0 && strcmp(request_uri, "/upload") == 0) {
// Handle PUT /upload requests with authentication
handle_upload_request();
} else {
// Other methods not implemented yet
printf("Status: 501 Not Implemented\r\n");