Bud 2 mostly done
This commit is contained in:
366
src/main.c
366
src/main.c
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user