Finished BUD 1
This commit is contained in:
261
src/main.c
Normal file
261
src/main.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Ginxsom Blossom Server - FastCGI Application
|
||||
* Handles HEAD requests and other dynamic operations
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcgi_stdio.h>
|
||||
#include <sqlite3.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MAX_SHA256_LEN 65
|
||||
#define MAX_PATH_LEN 512
|
||||
#define MAX_MIME_LEN 128
|
||||
|
||||
// Database path
|
||||
#define DB_PATH "db/ginxsom.db"
|
||||
|
||||
// Blob metadata structure
|
||||
typedef struct {
|
||||
char sha256[MAX_SHA256_LEN];
|
||||
long size;
|
||||
char type[MAX_MIME_LEN];
|
||||
long uploaded_at;
|
||||
char filename[256];
|
||||
int found;
|
||||
} blob_metadata_t;
|
||||
|
||||
// Get blob metadata from database
|
||||
int get_blob_metadata(const char* sha256, blob_metadata_t* metadata) {
|
||||
sqlite3* db;
|
||||
sqlite3_stmt* stmt;
|
||||
int rc;
|
||||
|
||||
printf("DEBUG: get_blob_metadata() called with 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_READONLY, 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\r\n");
|
||||
|
||||
const char* sql = "SELECT sha256, size, type, uploaded_at, filename FROM blobs WHERE sha256 = ?";
|
||||
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\r\n");
|
||||
printf("DEBUG: Binding parameter sha256='%s'\r\n", sha256);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
|
||||
|
||||
printf("DEBUG: Executing SQL query...\r\n");
|
||||
rc = sqlite3_step(stmt);
|
||||
printf("DEBUG: sqlite3_step() returned: %d (SQLITE_ROW=%d, SQLITE_DONE=%d)\r\n",
|
||||
rc, SQLITE_ROW, SQLITE_DONE);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
printf("DEBUG: Row found! Extracting metadata...\r\n");
|
||||
strncpy(metadata->sha256, (char*)sqlite3_column_text(stmt, 0), MAX_SHA256_LEN-1);
|
||||
metadata->size = sqlite3_column_int64(stmt, 1);
|
||||
strncpy(metadata->type, (char*)sqlite3_column_text(stmt, 2), MAX_MIME_LEN-1);
|
||||
metadata->uploaded_at = sqlite3_column_int64(stmt, 3);
|
||||
const char* filename = (char*)sqlite3_column_text(stmt, 4);
|
||||
if (filename) {
|
||||
strncpy(metadata->filename, filename, 255);
|
||||
} else {
|
||||
metadata->filename[0] = '\0';
|
||||
}
|
||||
metadata->found = 1;
|
||||
printf("DEBUG: Metadata extracted - size=%ld, type='%s'\r\n",
|
||||
metadata->size, metadata->type);
|
||||
} else {
|
||||
printf("DEBUG: No row found for sha256='%s'\r\n", sha256);
|
||||
metadata->found = 0;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
printf("DEBUG: Database closed, returning %d\r\n", metadata->found);
|
||||
return metadata->found;
|
||||
}
|
||||
|
||||
// Check if physical file exists (with extension based on MIME type)
|
||||
int file_exists_with_type(const char* sha256, const char* mime_type) {
|
||||
char filepath[MAX_PATH_LEN];
|
||||
const char* extension = "";
|
||||
|
||||
// Determine file extension based on MIME type
|
||||
if (strstr(mime_type, "image/jpeg")) {
|
||||
extension = ".jpg";
|
||||
} else if (strstr(mime_type, "image/webp")) {
|
||||
extension = ".webp";
|
||||
} else if (strstr(mime_type, "image/png")) {
|
||||
extension = ".png";
|
||||
} else if (strstr(mime_type, "image/gif")) {
|
||||
extension = ".gif";
|
||||
} else if (strstr(mime_type, "video/mp4")) {
|
||||
extension = ".mp4";
|
||||
} else if (strstr(mime_type, "video/webm")) {
|
||||
extension = ".webm";
|
||||
} else if (strstr(mime_type, "audio/mpeg")) {
|
||||
extension = ".mp3";
|
||||
} else if (strstr(mime_type, "audio/ogg")) {
|
||||
extension = ".ogg";
|
||||
} else if (strstr(mime_type, "text/plain")) {
|
||||
extension = ".txt";
|
||||
}
|
||||
|
||||
snprintf(filepath, sizeof(filepath), "blobs/%s%s", sha256, extension);
|
||||
|
||||
printf("DEBUG: file_exists_with_type() checking path: '%s' (MIME: %s)\r\n", filepath, mime_type);
|
||||
|
||||
struct stat st;
|
||||
int result = stat(filepath, &st);
|
||||
printf("DEBUG: stat() returned: %d (0=success, -1=fail)\r\n", result);
|
||||
|
||||
if (result == 0) {
|
||||
printf("DEBUG: File exists! Size: %ld bytes\r\n", st.st_size);
|
||||
return 1;
|
||||
} else {
|
||||
printf("DEBUG: File does not exist or stat failed\r\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle HEAD request for blob
|
||||
void handle_head_request(const char* sha256) {
|
||||
blob_metadata_t metadata = {0};
|
||||
|
||||
printf("DEBUG: handle_head_request called with sha256=%s\r\n", sha256);
|
||||
|
||||
// Validate SHA-256 format (64 hex characters)
|
||||
if (strlen(sha256) != 64) {
|
||||
printf("DEBUG: SHA-256 length validation failed: %zu\r\n", strlen(sha256));
|
||||
printf("Status: 400 Bad Request\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Invalid SHA-256 hash format\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("DEBUG: SHA-256 length validation passed\r\n");
|
||||
|
||||
// Check if blob exists in database - this is the single source of truth
|
||||
if (!get_blob_metadata(sha256, &metadata)) {
|
||||
printf("DEBUG: Database lookup failed for sha256=%s\r\n", sha256);
|
||||
printf("Status: 404 Not Found\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Blob not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("DEBUG: Database lookup succeeded - blob exists\r\n");
|
||||
|
||||
// Return successful HEAD response with metadata from database
|
||||
printf("Status: 200 OK\r\n");
|
||||
printf("Content-Type: %s\r\n", metadata.type);
|
||||
printf("Content-Length: %ld\r\n", metadata.size);
|
||||
printf("Cache-Control: public, max-age=31536000, immutable\r\n");
|
||||
printf("ETag: \"%s\"\r\n", metadata.sha256);
|
||||
|
||||
// Add timing header for debugging
|
||||
printf("X-Ginxsom-Server: FastCGI\r\n");
|
||||
printf("X-Ginxsom-Timestamp: %ld\r\n", time(NULL));
|
||||
|
||||
if (strlen(metadata.filename) > 0) {
|
||||
printf("X-Original-Filename: %s\r\n", metadata.filename);
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
// HEAD request - no body content
|
||||
}
|
||||
|
||||
// Extract SHA-256 from request URI (Blossom compliant - ignores any extension)
|
||||
const char* extract_sha256_from_uri(const char* uri) {
|
||||
static char sha256_buffer[MAX_SHA256_LEN];
|
||||
|
||||
if (!uri || uri[0] != '/') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* start = uri + 1; // Skip leading '/'
|
||||
|
||||
// Extract exactly 64 hex characters, ignoring anything after (extensions, etc.)
|
||||
int len = 0;
|
||||
for (int i = 0; i < 64 && start[i] != '\0'; i++) {
|
||||
char c = start[i];
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
|
||||
// If we hit a non-hex character before 64 chars, it's invalid
|
||||
if (len < 64) {
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
sha256_buffer[i] = c;
|
||||
len = i + 1;
|
||||
}
|
||||
|
||||
// Must be exactly 64 hex characters
|
||||
if (len != 64) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sha256_buffer[64] = '\0';
|
||||
return sha256_buffer;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
while (FCGI_Accept() >= 0) {
|
||||
// DEBUG: Log every request received
|
||||
printf("DEBUG: FastCGI received request\r\n");
|
||||
|
||||
const char* request_method = getenv("REQUEST_METHOD");
|
||||
const char* request_uri = getenv("REQUEST_URI");
|
||||
|
||||
// DEBUG: Log request details
|
||||
printf("DEBUG: METHOD=%s, URI=%s\r\n",
|
||||
request_method ? request_method : "NULL",
|
||||
request_uri ? request_uri : "NULL");
|
||||
|
||||
if (!request_method || !request_uri) {
|
||||
printf("Status: 400 Bad Request\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Invalid request\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle HEAD requests for blob metadata
|
||||
if (strcmp(request_method, "HEAD") == 0) {
|
||||
const char* sha256 = extract_sha256_from_uri(request_uri);
|
||||
printf("DEBUG: Extracted SHA256=%s\r\n", sha256 ? sha256 : "NULL");
|
||||
if (sha256) {
|
||||
handle_head_request(sha256);
|
||||
} else {
|
||||
printf("Status: 400 Bad Request\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Invalid SHA-256 hash in URI\n");
|
||||
}
|
||||
} else {
|
||||
// Other methods not implemented yet
|
||||
printf("Status: 501 Not Implemented\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Method %s not implemented\n", request_method);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user