Bud 2 mostly done

This commit is contained in:
Your Name
2025-08-19 10:23:40 -04:00
parent d8803b1ad0
commit ec976ab090
16 changed files with 6071 additions and 85 deletions

View File

@@ -33,6 +33,99 @@ typedef struct {
int found;
} blob_metadata_t;
// Insert blob metadata into database
int insert_blob_metadata(const char* sha256, long size, const char* type,
long uploaded_at, const char* uploader_pubkey,
const char* filename) {
sqlite3* db;
sqlite3_stmt* stmt;
int rc;
printf("DEBUG: insert_blob_metadata() called for sha256='%s'\r\n", sha256);
printf("DEBUG: Opening database at path: %s\r\n", DB_PATH);
rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READWRITE, NULL);
if (rc) {
printf("DEBUG: Database open FAILED: %s\r\n", sqlite3_errmsg(db));
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
return 0;
}
printf("DEBUG: Database opened successfully for writing\r\n");
const char* sql = "INSERT INTO blobs (sha256, size, type, uploaded_at, uploader_pubkey, filename) VALUES (?, ?, ?, ?, ?, ?)";
printf("DEBUG: Preparing SQL: %s\r\n", sql);
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
printf("DEBUG: SQL prepare FAILED: %s\r\n", sqlite3_errmsg(db));
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 0;
}
printf("DEBUG: SQL prepared successfully, binding parameters\r\n");
printf("DEBUG: Parameter values to bind:\r\n");
printf("DEBUG: 1. sha256 = '%s'\r\n", sha256 ? sha256 : "NULL");
printf("DEBUG: 2. size = %ld\r\n", size);
printf("DEBUG: 3. type = '%s'\r\n", type ? type : "NULL");
printf("DEBUG: 4. uploaded_at = %ld\r\n", uploaded_at);
printf("DEBUG: 5. uploader_pubkey = '%s'\r\n", uploader_pubkey ? uploader_pubkey : "NULL");
printf("DEBUG: 6. filename = '%s'\r\n", filename ? filename : "NULL");
// Bind parameters
printf("DEBUG: Binding parameter 1 (sha256)\r\n");
sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
printf("DEBUG: Binding parameter 2 (size)\r\n");
sqlite3_bind_int64(stmt, 2, size);
printf("DEBUG: Binding parameter 3 (type)\r\n");
sqlite3_bind_text(stmt, 3, type, -1, SQLITE_STATIC);
printf("DEBUG: Binding parameter 4 (uploaded_at)\r\n");
sqlite3_bind_int64(stmt, 4, uploaded_at);
printf("DEBUG: Binding parameter 5 (uploader_pubkey)\r\n");
if (uploader_pubkey) {
printf("DEBUG: Binding uploader_pubkey as text: '%s'\r\n", uploader_pubkey);
sqlite3_bind_text(stmt, 5, uploader_pubkey, -1, SQLITE_STATIC);
} else {
printf("DEBUG: Binding uploader_pubkey as NULL\r\n");
sqlite3_bind_null(stmt, 5);
}
printf("DEBUG: Binding parameter 6 (filename)\r\n");
if (filename) {
printf("DEBUG: Binding filename as text: '%s'\r\n", filename);
sqlite3_bind_text(stmt, 6, filename, -1, SQLITE_STATIC);
} else {
printf("DEBUG: Binding filename as NULL\r\n");
sqlite3_bind_null(stmt, 6);
}
printf("DEBUG: Parameters bound, executing INSERT\r\n");
rc = sqlite3_step(stmt);
int success = 0;
if (rc == SQLITE_DONE) {
printf("DEBUG: INSERT successful\r\n");
success = 1;
} else if (rc == SQLITE_CONSTRAINT) {
printf("DEBUG: INSERT failed - blob already exists (duplicate sha256)\r\n");
// This is actually OK - blob already exists with same hash
success = 1;
} else {
printf("DEBUG: INSERT failed: %s\r\n", sqlite3_errmsg(db));
success = 0;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
printf("DEBUG: Database closed, returning %d\r\n", success);
return success;
}
// Get blob metadata from database
int get_blob_metadata(const char* sha256, blob_metadata_t* metadata) {
sqlite3* db;
@@ -245,18 +338,25 @@ int parse_authorization_header(const char* auth_header, char* event_json, size_t
// Extract base64 encoded event after "Nostr "
const char* base64_event = auth_header + prefix_len;
printf("DEBUG: Base64 event from header: %.100s...\r\n", base64_event);
// 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");
// Decode base64 to JSON using nostr_core_lib base64 decode
unsigned char decoded_buffer[4096];
size_t decoded_len = base64_decode(base64_event, decoded_buffer);
if (decoded_len == 0) {
printf("DEBUG: Failed to decode base64 event\r\n");
return NOSTR_ERROR_INVALID_INPUT;
}
strncpy(event_json, base64_event, json_size - 1);
event_json[json_size - 1] = '\0';
if (decoded_len >= json_size) {
printf("DEBUG: Decoded JSON too large for buffer\r\n");
return NOSTR_ERROR_INVALID_INPUT;
}
// Copy decoded JSON to output buffer
memcpy(event_json, decoded_buffer, decoded_len);
event_json[decoded_len] = '\0';
printf("DEBUG: Parsed authorization header, extracted JSON: %.100s...\r\n", event_json);
return NOSTR_SUCCESS;
@@ -416,22 +516,13 @@ 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");
@@ -454,32 +545,239 @@ void handle_upload_request(void) {
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");
// Get Authorization header for authentication
const char* auth_header = getenv("HTTP_AUTHORIZATION");
printf("DEBUG: Raw Authorization header: %s\r\n", auth_header ? auth_header : "NULL");
// Parse the authorization header to extract the Nostr event
const char* uploader_pubkey = NULL;
if (auth_header) {
printf("DEBUG: Authorization header present, length=%zu\r\n", strlen(auth_header));
// Parse authorization header to get JSON
char event_json[4096];
int parse_result = parse_authorization_header(auth_header, event_json, sizeof(event_json));
printf("DEBUG: parse_authorization_header returned: %d\r\n", parse_result);
if (parse_result == NOSTR_SUCCESS) {
printf("DEBUG: Successfully parsed authorization header\r\n");
printf("DEBUG: Event JSON: %.200s...\r\n", event_json);
// Parse the JSON event to extract pubkey
cJSON* event = cJSON_Parse(event_json);
if (event) {
printf("DEBUG: Successfully parsed JSON event\r\n");
cJSON* pubkey_json = cJSON_GetObjectItem(event, "pubkey");
if (pubkey_json && cJSON_IsString(pubkey_json)) {
const char* temp_pubkey = cJSON_GetStringValue(pubkey_json);
printf("DEBUG: Found pubkey in JSON: %.16s...\r\n", temp_pubkey ? temp_pubkey : "NULL");
// Copy the pubkey to a static buffer to preserve it after cJSON_Delete
static char pubkey_buffer[256];
if (temp_pubkey) {
strncpy(pubkey_buffer, temp_pubkey, sizeof(pubkey_buffer)-1);
pubkey_buffer[sizeof(pubkey_buffer)-1] = '\0';
uploader_pubkey = pubkey_buffer;
printf("DEBUG: Copied pubkey to static buffer: %.16s...\r\n", uploader_pubkey);
}
} else {
printf("DEBUG: No valid 'pubkey' field found in JSON event\r\n");
}
cJSON_Delete(event);
} else {
printf("DEBUG: Failed to parse JSON event\r\n");
}
} else {
printf("DEBUG: Failed to parse authorization header, error: %d\r\n", parse_result);
}
} else {
printf("DEBUG: No authorization header provided\r\n");
}
printf("DEBUG: Final uploader_pubkey after auth parsing: %s\r\n", uploader_pubkey ? uploader_pubkey : "NULL");
// Read file data from stdin
unsigned char* file_data = malloc(content_length);
if (!file_data) {
printf("Status: 500 Internal Server Error\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Authentication failed\n");
printf("Memory allocation failed\n");
return;
}
printf("DEBUG: Authentication successful, proceeding with upload\r\n");
size_t bytes_read = fread(file_data, 1, content_length, stdin);
if (bytes_read != (size_t)content_length) {
printf("DEBUG: Expected %ld bytes, read %zu bytes\r\n", content_length, bytes_read);
free(file_data);
printf("Status: 400 Bad Request\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Failed to read complete file data\n");
return;
}
// 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("DEBUG: Successfully read %zu bytes from stdin\r\n", bytes_read);
printf("Status: 501 Not Implemented\r\n");
// Calculate SHA-256 hash using nostr_core function
unsigned char hash[32];
if (nostr_sha256(file_data, content_length, hash) != NOSTR_SUCCESS) {
free(file_data);
printf("Status: 500 Internal Server Error\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Hash calculation failed\n");
return;
}
// Convert hash to hex string
char sha256_hex[65];
nostr_bytes_to_hex(hash, 32, sha256_hex);
printf("DEBUG: Calculated SHA-256: %s\r\n", sha256_hex);
// Determine file extension from Content-Type
const char* extension = "";
if (strstr(content_type, "image/jpeg")) {
extension = ".jpg";
} else if (strstr(content_type, "image/webp")) {
extension = ".webp";
} else if (strstr(content_type, "image/png")) {
extension = ".png";
} else if (strstr(content_type, "image/gif")) {
extension = ".gif";
} else if (strstr(content_type, "video/mp4")) {
extension = ".mp4";
} else if (strstr(content_type, "video/webm")) {
extension = ".webm";
} else if (strstr(content_type, "audio/mpeg")) {
extension = ".mp3";
} else if (strstr(content_type, "audio/ogg")) {
extension = ".ogg";
} else if (strstr(content_type, "text/plain")) {
extension = ".txt";
} else {
// Default to binary extension for unknown types
extension = ".bin";
}
// Save file to blobs directory with SHA-256 + extension
char filepath[MAX_PATH_LEN];
snprintf(filepath, sizeof(filepath), "blobs/%s%s", sha256_hex, extension);
printf("DEBUG: Saving file to: %s\r\n", filepath);
FILE* outfile = fopen(filepath, "wb");
if (!outfile) {
free(file_data);
printf("Status: 500 Internal Server Error\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Failed to create file\n");
return;
}
size_t bytes_written = fwrite(file_data, 1, content_length, outfile);
fclose(outfile);
free(file_data);
if (bytes_written != (size_t)content_length) {
// Clean up partial file
unlink(filepath);
printf("Status: 500 Internal Server Error\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Failed to write complete file\n");
return;
}
printf("DEBUG: Successfully saved %zu bytes to %s\r\n", bytes_written, filepath);
// Extract filename from Content-Disposition header if present
const char* filename = NULL;
const char* content_disposition = getenv("HTTP_CONTENT_DISPOSITION");
printf("DEBUG: Content-Disposition header: %s\r\n", content_disposition ? content_disposition : "NULL");
if (content_disposition) {
printf("DEBUG: Looking for filename= in Content-Disposition header\r\n");
// Look for filename= in Content-Disposition header
const char* filename_start = strstr(content_disposition, "filename=");
if (filename_start) {
printf("DEBUG: Found filename= at position %ld\r\n", filename_start - content_disposition);
filename_start += 9; // Skip "filename="
printf("DEBUG: Filename value starts with: %.20s\r\n", filename_start);
// Handle quoted filenames
if (*filename_start == '"') {
printf("DEBUG: Processing quoted filename\r\n");
filename_start++; // Skip opening quote
// Find closing quote
const char* filename_end = strchr(filename_start, '"');
if (filename_end) {
// Extract filename between quotes
static char filename_buffer[256];
size_t filename_len = filename_end - filename_start;
printf("DEBUG: Quoted filename length: %zu\r\n", filename_len);
if (filename_len < sizeof(filename_buffer)) {
strncpy(filename_buffer, filename_start, filename_len);
filename_buffer[filename_len] = '\0';
filename = filename_buffer;
printf("DEBUG: Extracted quoted filename: '%s'\r\n", filename);
} else {
printf("DEBUG: Quoted filename too long, skipping\r\n");
}
} else {
printf("DEBUG: No closing quote found for filename\r\n");
}
} else {
printf("DEBUG: Processing unquoted filename\r\n");
// Unquoted filename - extract until space or end
const char* filename_end = filename_start;
while (*filename_end && *filename_end != ' ' && *filename_end != ';') {
filename_end++;
}
static char filename_buffer[256];
size_t filename_len = filename_end - filename_start;
printf("DEBUG: Unquoted filename length: %zu\r\n", filename_len);
if (filename_len < sizeof(filename_buffer)) {
strncpy(filename_buffer, filename_start, filename_len);
filename_buffer[filename_len] = '\0';
filename = filename_buffer;
printf("DEBUG: Extracted unquoted filename: '%s'\r\n", filename);
} else {
printf("DEBUG: Unquoted filename too long, skipping\r\n");
}
}
} else {
printf("DEBUG: No filename= found in Content-Disposition header\r\n");
}
} else {
printf("DEBUG: No Content-Disposition header provided\r\n");
}
printf("DEBUG: Final filename after extraction: %s\r\n", filename ? filename : "NULL");
// Store blob metadata in database
time_t uploaded_time = time(NULL);
if (!insert_blob_metadata(sha256_hex, content_length, content_type, uploaded_time, uploader_pubkey, filename)) {
// Database insertion failed - clean up the physical file to maintain consistency
printf("DEBUG: Database insertion failed, removing physical file\r\n");
unlink(filepath);
printf("Status: 500 Internal Server Error\r\n");
printf("Content-Type: text/plain\r\n\r\n");
printf("Failed to store blob metadata\n");
return;
}
printf("DEBUG: Blob metadata successfully stored in database\r\n");
// Return success response with blob descriptor
printf("Status: 200 OK\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(" \"sha256\": \"%s\",\n", sha256_hex);
printf(" \"size\": %ld,\n", content_length);
printf(" \"type\": \"%s\",\n", content_type);
printf(" \"uploaded\": %ld,\n", uploaded_time);
printf(" \"url\": \"http://localhost:9001/%s%s\"\n", sha256_hex, extension);
printf("}\n");
printf("DEBUG: Upload completed successfully with database storage\r\n");
}
int main(void) {