Start upload functionality
This commit is contained in:
84
src/ginxsom.h
Normal file
84
src/ginxsom.h
Normal 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
|
||||
267
src/main.c
267
src/main.c
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user