Start upload functionality
This commit is contained in:
@@ -36,6 +36,8 @@ This document outlines the implementation plan for ginxsom, a FastCGI-based Blos
|
|||||||
- [x] Handle 404s gracefully when blob doesn't exist
|
- [x] Handle 404s gracefully when blob doesn't exist
|
||||||
- [x] Configure FastCGI pass-through for HEAD and non-GET requests
|
- [x] Configure FastCGI pass-through for HEAD and non-GET requests
|
||||||
|
|
||||||
|
**Future Enhancement Note**: Consider implementing nginx Lua extension for true Blossom compliance with dynamic file discovery. The current approach uses explicit extension lists in `try_files`, which works well for common extensions but may not serve files with unusual extensions. Lua module would allow runtime directory scanning for hash-matching files regardless of extension.
|
||||||
|
|
||||||
### 1.4 Basic HEAD Endpoint
|
### 1.4 Basic HEAD Endpoint
|
||||||
- [x] Implement FastCGI handler for `HEAD /<sha256>`
|
- [x] Implement FastCGI handler for `HEAD /<sha256>`
|
||||||
- [x] Query database for blob metadata (single source of truth)
|
- [x] Query database for blob metadata (single source of truth)
|
||||||
@@ -58,12 +60,32 @@ This document outlines the implementation plan for ginxsom, a FastCGI-based Blos
|
|||||||
|
|
||||||
### 2.1 Nostr Authentication Setup
|
### 2.1 Nostr Authentication Setup
|
||||||
- [ ] Integrate nostr_core_lib submodule
|
- [ ] Integrate nostr_core_lib submodule
|
||||||
- [ ] Implement nostr event validation
|
- [ ] Update Makefile to include nostr_core_lib paths and static library
|
||||||
- [ ] Verify event signature (schnorr)
|
- [ ] Build libnostr_core_x64.a using provided build.sh script
|
||||||
- [ ] Validate event structure (kind 24242)
|
- [ ] Add system dependencies: -lsecp256k1 -lssl -lcrypto -lcurl -lz -ldl -lpthread -lm
|
||||||
- [ ] Check required fields (t, expiration, x tags)
|
|
||||||
- [ ] Implement expiration checking
|
- [ ] Implement authentication functions in main.c (BUD-02 section):
|
||||||
- [ ] Create authentication middleware
|
- [ ] `parse_authorization_header()` - Extract JSON from "Nostr base64(event)" header
|
||||||
|
- [ ] `validate_blossom_event()` - Validate Blossom-specific requirements (kind 24242, content hash, method, expiration)
|
||||||
|
- [ ] `authenticate_request()` - Main orchestrator function
|
||||||
|
|
||||||
|
- [ ] Leverage existing nostr_core_lib functions:
|
||||||
|
- [ ] Use `nostr_validate_event()` for structure + signature validation (from nip001.h)
|
||||||
|
- [ ] Use standardized error codes from nostr_common.h (NOSTR_SUCCESS, NOSTR_ERROR_EVENT_INVALID_SIGNATURE, etc.)
|
||||||
|
- [ ] Use `nostr_strerror()` for error message translation
|
||||||
|
|
||||||
|
**Function Specifications:**
|
||||||
|
```c
|
||||||
|
// Custom functions to implement:
|
||||||
|
int parse_authorization_header(const char* auth_header, char* event_json, size_t json_size);
|
||||||
|
int validate_blossom_event(struct cJSON* event, const char* expected_hash, const char* method);
|
||||||
|
int authenticate_request(const char* auth_header, const char* method, const char* file_hash);
|
||||||
|
|
||||||
|
// Existing nostr_core_lib functions to use directly:
|
||||||
|
// - nostr_validate_event(cJSON* event) - handles structure + signature validation
|
||||||
|
// - nostr_validate_event_structure(cJSON* event) - if separate validation needed
|
||||||
|
// - nostr_verify_event_signature(cJSON* event) - if separate signature check needed
|
||||||
|
```
|
||||||
|
|
||||||
### 2.2 Upload Endpoint Implementation
|
### 2.2 Upload Endpoint Implementation
|
||||||
- [ ] Implement `PUT /upload` endpoint
|
- [ ] Implement `PUT /upload` endpoint
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -1,8 +1,8 @@
|
|||||||
# Ginxsom Blossom Server Makefile
|
# Ginxsom Blossom Server Makefile
|
||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -std=c99 -O2
|
CFLAGS = -Wall -Wextra -std=c99 -O2 -Inostr_core_lib/nostr_core -Inostr_core_lib/cjson
|
||||||
LIBS = -lfcgi -lsqlite3
|
LIBS = -lfcgi -lsqlite3 nostr_core_lib/libnostr_core_x64.a -lz -ldl -lpthread -lm -L/usr/local/lib -lsecp256k1 -lssl -lcrypto -lcurl
|
||||||
SRCDIR = src
|
SRCDIR = src
|
||||||
BUILDDIR = build
|
BUILDDIR = build
|
||||||
TARGET = $(BUILDDIR)/ginxsom-fcgi
|
TARGET = $(BUILDDIR)/ginxsom-fcgi
|
||||||
|
|||||||
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
24
config/fastcgi_params
Normal file
24
config/fastcgi_params
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
fastcgi_param QUERY_STRING $query_string;
|
||||||
|
fastcgi_param REQUEST_METHOD $request_method;
|
||||||
|
fastcgi_param CONTENT_TYPE $content_type;
|
||||||
|
fastcgi_param CONTENT_LENGTH $content_length;
|
||||||
|
|
||||||
|
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||||
|
fastcgi_param REQUEST_URI $request_uri;
|
||||||
|
fastcgi_param DOCUMENT_URI $document_uri;
|
||||||
|
fastcgi_param DOCUMENT_ROOT $document_root;
|
||||||
|
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||||
|
fastcgi_param REQUEST_SCHEME $scheme;
|
||||||
|
fastcgi_param HTTPS $https if_not_empty;
|
||||||
|
|
||||||
|
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
|
||||||
|
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||||
|
|
||||||
|
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||||
|
fastcgi_param REMOTE_PORT $remote_port;
|
||||||
|
fastcgi_param SERVER_ADDR $server_addr;
|
||||||
|
fastcgi_param SERVER_PORT $server_port;
|
||||||
|
fastcgi_param SERVER_NAME $server_name;
|
||||||
|
|
||||||
|
# PHP only, required if PHP was built with --enable-force-cgi-redirect
|
||||||
|
fastcgi_param REDIRECT_STATUS 200;
|
||||||
@@ -18,8 +18,8 @@ http {
|
|||||||
keepalive_timeout 65;
|
keepalive_timeout 65;
|
||||||
types_hash_max_size 2048;
|
types_hash_max_size 2048;
|
||||||
|
|
||||||
# MIME types
|
# MIME types (local)
|
||||||
include /etc/nginx/mime.types;
|
include mime.types;
|
||||||
default_type application/octet-stream;
|
default_type application/octet-stream;
|
||||||
|
|
||||||
# Logging (relative to prefix directory)
|
# Logging (relative to prefix directory)
|
||||||
@@ -58,9 +58,8 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# GET requests served directly with explicit file extensions
|
# GET requests served directly with explicit file extensions
|
||||||
# try_files /$1 =404;
|
# Potentially in the future look at a LUA extension
|
||||||
# try_files /$1.webp =404;
|
try_files /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.txt /$1.md=404;
|
||||||
try_files /$1.pdf /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.mp4 /$1.mp3 =404;
|
|
||||||
|
|
||||||
# Set appropriate headers for blobs
|
# Set appropriate headers for blobs
|
||||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
@@ -68,33 +67,7 @@ http {
|
|||||||
add_header X-Frame-Options DENY;
|
add_header X-Frame-Options DENY;
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
}
|
}
|
||||||
|
|
||||||
# Commented out problematic regex for reference
|
|
||||||
# location ~ "^/([a-f0-9]{64}).*$" {
|
|
||||||
# limit_except HEAD GET {
|
|
||||||
# deny all;
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# # Debug headers to see what nginx is capturing
|
|
||||||
# add_header X-Debug-Hash "$1" always;
|
|
||||||
# add_header X-Debug-TryFiles "$1*" always;
|
|
||||||
# add_header X-Debug-URI "$uri" always;
|
|
||||||
# add_header X-Debug-Root "$document_root" always;
|
|
||||||
#
|
|
||||||
# # Route HEAD requests to FastCGI via rewrite
|
|
||||||
# if ($request_method = HEAD) {
|
|
||||||
# rewrite ^/(.*)$ /fcgi-head/$1 last;
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# # GET requests served directly with hash-only lookup
|
|
||||||
# try_files $1* =404;
|
|
||||||
#
|
|
||||||
# # Set appropriate headers for blobs
|
|
||||||
# add_header Cache-Control "public, max-age=31536000, immutable";
|
|
||||||
# add_header X-Content-Type-Options nosniff;
|
|
||||||
# add_header X-Frame-Options DENY;
|
|
||||||
# add_header X-XSS-Protection "1; mode=block";
|
|
||||||
# }
|
|
||||||
|
|
||||||
# FastCGI handler for HEAD requests
|
# FastCGI handler for HEAD requests
|
||||||
location ~ "^/fcgi-head/([a-f0-9]{64}).*$" {
|
location ~ "^/fcgi-head/([a-f0-9]{64}).*$" {
|
||||||
@@ -116,6 +89,19 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Upload endpoint - requires authentication
|
||||||
|
location /upload {
|
||||||
|
# Pass to FastCGI application for processing
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||||
|
fastcgi_pass fastcgi_backend;
|
||||||
|
|
||||||
|
# Only allow PUT method for uploads
|
||||||
|
if ($request_method !~ ^(PUT)$ ) {
|
||||||
|
return 405;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Health check endpoint
|
# Health check endpoint
|
||||||
location /health {
|
location /health {
|
||||||
access_log off;
|
access_log off;
|
||||||
|
|||||||
95
config/mime.types
Normal file
95
config/mime.types
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
types {
|
||||||
|
text/html html htm shtml;
|
||||||
|
text/css css;
|
||||||
|
text/xml xml;
|
||||||
|
image/gif gif;
|
||||||
|
image/jpeg jpeg jpg;
|
||||||
|
image/png png;
|
||||||
|
image/webp webp;
|
||||||
|
application/javascript js;
|
||||||
|
application/atom+xml atom;
|
||||||
|
application/rss+xml rss;
|
||||||
|
|
||||||
|
text/mathml mml;
|
||||||
|
text/plain txt;
|
||||||
|
text/vnd.sun.j2me.app-descriptor jad;
|
||||||
|
text/vnd.wap.wml wml;
|
||||||
|
text/x-component htc;
|
||||||
|
|
||||||
|
image/avif avif;
|
||||||
|
image/svg+xml svg svgz;
|
||||||
|
image/tiff tif tiff;
|
||||||
|
image/vnd.wap.wbmp wbmp;
|
||||||
|
image/x-icon ico;
|
||||||
|
image/x-jng jng;
|
||||||
|
image/x-ms-bmp bmp;
|
||||||
|
|
||||||
|
font/woff woff;
|
||||||
|
font/woff2 woff2;
|
||||||
|
|
||||||
|
application/java-archive jar war ear;
|
||||||
|
application/json json;
|
||||||
|
application/mac-binhex40 hqx;
|
||||||
|
application/msword doc;
|
||||||
|
application/pdf pdf;
|
||||||
|
application/postscript ps eps ai;
|
||||||
|
application/rtf rtf;
|
||||||
|
application/vnd.apple.mpegurl m3u8;
|
||||||
|
application/vnd.google-earth.kml+xml kml;
|
||||||
|
application/vnd.google-earth.kmz kmz;
|
||||||
|
application/vnd.ms-excel xls;
|
||||||
|
application/vnd.ms-fontobject eot;
|
||||||
|
application/vnd.ms-powerpoint ppt;
|
||||||
|
application/vnd.oasis.opendocument.graphics odg;
|
||||||
|
application/vnd.oasis.opendocument.presentation odp;
|
||||||
|
application/vnd.oasis.opendocument.spreadsheet ods;
|
||||||
|
application/vnd.oasis.opendocument.text odt;
|
||||||
|
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
|
||||||
|
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
|
||||||
|
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
|
||||||
|
application/vnd.wap.wmlc wmlc;
|
||||||
|
application/wasm wasm;
|
||||||
|
application/x-7z-compressed 7z;
|
||||||
|
application/x-cocoa cco;
|
||||||
|
application/x-java-archive-diff jardiff;
|
||||||
|
application/x-java-jnlp-file jnlp;
|
||||||
|
application/x-makeself run;
|
||||||
|
application/x-perl pl pm;
|
||||||
|
application/x-pilot prc pdb;
|
||||||
|
application/x-rar-compressed rar;
|
||||||
|
application/x-redhat-package-manager rpm;
|
||||||
|
application/x-sea sea;
|
||||||
|
application/x-shockwave-flash swf;
|
||||||
|
application/x-stuffit sit;
|
||||||
|
application/x-tcl tcl tk;
|
||||||
|
application/x-x509-ca-cert der pem crt;
|
||||||
|
application/x-xpinstall xpi;
|
||||||
|
application/xhtml+xml xhtml;
|
||||||
|
application/xspf+xml xspf;
|
||||||
|
application/zip zip;
|
||||||
|
|
||||||
|
application/octet-stream bin exe dll;
|
||||||
|
application/octet-stream deb;
|
||||||
|
application/octet-stream dmg;
|
||||||
|
application/octet-stream iso img;
|
||||||
|
application/octet-stream msi msp msm;
|
||||||
|
|
||||||
|
audio/midi mid midi kar;
|
||||||
|
audio/mpeg mp3;
|
||||||
|
audio/ogg ogg;
|
||||||
|
audio/x-m4a m4a;
|
||||||
|
audio/x-realaudio ra;
|
||||||
|
|
||||||
|
video/3gpp 3gpp 3gp;
|
||||||
|
video/mp2t ts;
|
||||||
|
video/mp4 mp4;
|
||||||
|
video/mpeg mpeg mpg;
|
||||||
|
video/quicktime mov;
|
||||||
|
video/webm webm;
|
||||||
|
video/x-flv flv;
|
||||||
|
video/x-m4v m4v;
|
||||||
|
video/x-mng mng;
|
||||||
|
video/x-ms-asf asx asf;
|
||||||
|
video/x-ms-wmv wmv;
|
||||||
|
video/x-msvideo avi;
|
||||||
|
}
|
||||||
24
fastcgi_params
Normal file
24
fastcgi_params
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
fastcgi_param QUERY_STRING $query_string;
|
||||||
|
fastcgi_param REQUEST_METHOD $request_method;
|
||||||
|
fastcgi_param CONTENT_TYPE $content_type;
|
||||||
|
fastcgi_param CONTENT_LENGTH $content_length;
|
||||||
|
|
||||||
|
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||||
|
fastcgi_param REQUEST_URI $request_uri;
|
||||||
|
fastcgi_param DOCUMENT_URI $document_uri;
|
||||||
|
fastcgi_param DOCUMENT_ROOT $document_root;
|
||||||
|
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||||
|
fastcgi_param REQUEST_SCHEME $scheme;
|
||||||
|
fastcgi_param HTTPS $https if_not_empty;
|
||||||
|
|
||||||
|
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
|
||||||
|
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||||
|
|
||||||
|
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||||
|
fastcgi_param REMOTE_PORT $remote_port;
|
||||||
|
fastcgi_param SERVER_ADDR $server_addr;
|
||||||
|
fastcgi_param SERVER_PORT $server_port;
|
||||||
|
fastcgi_param SERVER_NAME $server_name;
|
||||||
|
|
||||||
|
# PHP only, required if PHP was built with --enable-force-cgi-redirect
|
||||||
|
fastcgi_param REDIRECT_STATUS 200;
|
||||||
@@ -105,3 +105,9 @@
|
|||||||
127.0.0.1 - - [18/Aug/2025:22:40:32 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 564 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
127.0.0.1 - - [18/Aug/2025:22:40:32 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 564 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||||
127.0.0.1 - - [18/Aug/2025:23:00:31 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 203886 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
127.0.0.1 - - [18/Aug/2025:23:00:31 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 203886 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||||
127.0.0.1 - - [18/Aug/2025:23:01:14 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
127.0.0.1 - - [18/Aug/2025:23:01:14 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||||
|
127.0.0.1 - - [19/Aug/2025:06:00:05 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||||
|
127.0.0.1 - - [19/Aug/2025:07:49:21 -0400] "PUT /upload HTTP/1.1" 405 166 "-" "curl/8.15.0"
|
||||||
|
127.0.0.1 - - [19/Aug/2025:07:49:21 -0400] "GET /21fc30dac4c95ad4b26eb78d97cb31a0cb4bc69f30c0d4195f7685ac13b22ce6 HTTP/1.1" 404 162 "-" "curl/8.15.0"
|
||||||
|
127.0.0.1 - - [19/Aug/2025:07:51:54 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||||
|
127.0.0.1 - - [19/Aug/2025:07:57:54 -0400] "PUT /upload HTTP/1.1" 501 38 "-" "curl/8.15.0"
|
||||||
|
127.0.0.1 - - [19/Aug/2025:07:57:54 -0400] "GET /ae9f59c7ac386b7fe6343d669fc27f37d7b66256824be655d29a256908f154e9 HTTP/1.1" 404 162 "-" "curl/8.15.0"
|
||||||
|
|||||||
1492
logs/error.log
1492
logs/error.log
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
|||||||
296457
|
349770
|
||||||
|
|||||||
95
mime.types
Normal file
95
mime.types
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
types {
|
||||||
|
text/html html htm shtml;
|
||||||
|
text/css css;
|
||||||
|
text/xml xml;
|
||||||
|
image/gif gif;
|
||||||
|
image/jpeg jpeg jpg;
|
||||||
|
image/png png;
|
||||||
|
image/webp webp;
|
||||||
|
application/javascript js;
|
||||||
|
application/atom+xml atom;
|
||||||
|
application/rss+xml rss;
|
||||||
|
|
||||||
|
text/mathml mml;
|
||||||
|
text/plain txt;
|
||||||
|
text/vnd.sun.j2me.app-descriptor jad;
|
||||||
|
text/vnd.wap.wml wml;
|
||||||
|
text/x-component htc;
|
||||||
|
|
||||||
|
image/avif avif;
|
||||||
|
image/svg+xml svg svgz;
|
||||||
|
image/tiff tif tiff;
|
||||||
|
image/vnd.wap.wbmp wbmp;
|
||||||
|
image/x-icon ico;
|
||||||
|
image/x-jng jng;
|
||||||
|
image/x-ms-bmp bmp;
|
||||||
|
|
||||||
|
font/woff woff;
|
||||||
|
font/woff2 woff2;
|
||||||
|
|
||||||
|
application/java-archive jar war ear;
|
||||||
|
application/json json;
|
||||||
|
application/mac-binhex40 hqx;
|
||||||
|
application/msword doc;
|
||||||
|
application/pdf pdf;
|
||||||
|
application/postscript ps eps ai;
|
||||||
|
application/rtf rtf;
|
||||||
|
application/vnd.apple.mpegurl m3u8;
|
||||||
|
application/vnd.google-earth.kml+xml kml;
|
||||||
|
application/vnd.google-earth.kmz kmz;
|
||||||
|
application/vnd.ms-excel xls;
|
||||||
|
application/vnd.ms-fontobject eot;
|
||||||
|
application/vnd.ms-powerpoint ppt;
|
||||||
|
application/vnd.oasis.opendocument.graphics odg;
|
||||||
|
application/vnd.oasis.opendocument.presentation odp;
|
||||||
|
application/vnd.oasis.opendocument.spreadsheet ods;
|
||||||
|
application/vnd.oasis.opendocument.text odt;
|
||||||
|
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
|
||||||
|
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
|
||||||
|
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
|
||||||
|
application/vnd.wap.wmlc wmlc;
|
||||||
|
application/wasm wasm;
|
||||||
|
application/x-7z-compressed 7z;
|
||||||
|
application/x-cocoa cco;
|
||||||
|
application/x-java-archive-diff jardiff;
|
||||||
|
application/x-java-jnlp-file jnlp;
|
||||||
|
application/x-makeself run;
|
||||||
|
application/x-perl pl pm;
|
||||||
|
application/x-pilot prc pdb;
|
||||||
|
application/x-rar-compressed rar;
|
||||||
|
application/x-redhat-package-manager rpm;
|
||||||
|
application/x-sea sea;
|
||||||
|
application/x-shockwave-flash swf;
|
||||||
|
application/x-stuffit sit;
|
||||||
|
application/x-tcl tcl tk;
|
||||||
|
application/x-x509-ca-cert der pem crt;
|
||||||
|
application/x-xpinstall xpi;
|
||||||
|
application/xhtml+xml xhtml;
|
||||||
|
application/xspf+xml xspf;
|
||||||
|
application/zip zip;
|
||||||
|
|
||||||
|
application/octet-stream bin exe dll;
|
||||||
|
application/octet-stream deb;
|
||||||
|
application/octet-stream dmg;
|
||||||
|
application/octet-stream iso img;
|
||||||
|
application/octet-stream msi msp msm;
|
||||||
|
|
||||||
|
audio/midi mid midi kar;
|
||||||
|
audio/mpeg mp3;
|
||||||
|
audio/ogg ogg;
|
||||||
|
audio/x-m4a m4a;
|
||||||
|
audio/x-realaudio ra;
|
||||||
|
|
||||||
|
video/3gpp 3gpp 3gp;
|
||||||
|
video/mp2t ts;
|
||||||
|
video/mp4 mp4;
|
||||||
|
video/mpeg mpeg mpg;
|
||||||
|
video/quicktime mov;
|
||||||
|
video/webm webm;
|
||||||
|
video/x-flv flv;
|
||||||
|
video/x-m4v m4v;
|
||||||
|
video/x-mng mng;
|
||||||
|
video/x-ms-asf asx asf;
|
||||||
|
video/x-ms-wmv wmv;
|
||||||
|
video/x-msvideo avi;
|
||||||
|
}
|
||||||
Submodule nostr_core_lib updated: 1da4f6751e...77d92dbcf9
249
put_test.sh
Executable file
249
put_test.sh
Executable file
@@ -0,0 +1,249 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# put_test.sh - Test script for Ginxsom Blossom server upload functionality
|
||||||
|
# This script simulates a user uploading a blob to ginxsom using proper Blossom authentication
|
||||||
|
|
||||||
|
set -e # Exit on any error
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
SERVER_URL="http://localhost:9001"
|
||||||
|
UPLOAD_ENDPOINT="${SERVER_URL}/upload"
|
||||||
|
TEST_FILE="test_blob_$(date +%s).txt"
|
||||||
|
CLEANUP_FILES=()
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Cleanup function
|
||||||
|
cleanup() {
|
||||||
|
echo -e "${YELLOW}Cleaning up temporary files...${NC}"
|
||||||
|
for file in "${CLEANUP_FILES[@]}"; do
|
||||||
|
if [[ -f "$file" ]]; then
|
||||||
|
rm -f "$file"
|
||||||
|
echo "Removed: $file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up cleanup on exit
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
# Helper functions
|
||||||
|
log_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check prerequisites
|
||||||
|
check_prerequisites() {
|
||||||
|
log_info "Checking prerequisites..."
|
||||||
|
|
||||||
|
# Check if nak is installed
|
||||||
|
if ! command -v nak &> /dev/null; then
|
||||||
|
log_error "nak command not found. Please install nak first."
|
||||||
|
log_info "Install with: go install github.com/fiatjaf/nak@latest"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "nak is installed"
|
||||||
|
|
||||||
|
# Check if curl is available
|
||||||
|
if ! command -v curl &> /dev/null; then
|
||||||
|
log_error "curl command not found. Please install curl."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "curl is available"
|
||||||
|
|
||||||
|
# Check if sha256sum is available
|
||||||
|
if ! command -v sha256sum &> /dev/null; then
|
||||||
|
log_error "sha256sum command not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "sha256sum is available"
|
||||||
|
|
||||||
|
# Check if base64 is available
|
||||||
|
if ! command -v base64 &> /dev/null; then
|
||||||
|
log_error "base64 command not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log_success "base64 is available"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if server is running
|
||||||
|
check_server() {
|
||||||
|
log_info "Checking if server is running..."
|
||||||
|
|
||||||
|
if curl -s -f "${SERVER_URL}/health" > /dev/null 2>&1; then
|
||||||
|
log_success "Server is running at ${SERVER_URL}"
|
||||||
|
else
|
||||||
|
log_error "Server is not responding at ${SERVER_URL}"
|
||||||
|
log_info "Please start the server with: ./scripts/start-fcgi.sh && nginx -p . -c config/local-nginx.conf"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create test file
|
||||||
|
create_test_file() {
|
||||||
|
log_info "Creating test file: ${TEST_FILE}"
|
||||||
|
|
||||||
|
# Create test content with timestamp and random data
|
||||||
|
cat > "${TEST_FILE}" << EOF
|
||||||
|
Test blob content for Ginxsom Blossom server
|
||||||
|
Timestamp: $(date -Iseconds)
|
||||||
|
Random data: $(openssl rand -hex 32)
|
||||||
|
Test message: Hello from put_test.sh!
|
||||||
|
|
||||||
|
This file is used to test the upload functionality
|
||||||
|
of the Ginxsom Blossom server implementation.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
CLEANUP_FILES+=("${TEST_FILE}")
|
||||||
|
log_success "Created test file with $(wc -c < "${TEST_FILE}") bytes"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Calculate file hash
|
||||||
|
calculate_hash() {
|
||||||
|
log_info "Calculating SHA-256 hash..."
|
||||||
|
|
||||||
|
HASH=$(sha256sum "${TEST_FILE}" | cut -d' ' -f1)
|
||||||
|
log_success "File hash: ${HASH}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate nostr event
|
||||||
|
generate_nostr_event() {
|
||||||
|
log_info "Generating kind 24242 nostr event with nak..."
|
||||||
|
|
||||||
|
# Calculate expiration time (1 hour from now)
|
||||||
|
EXPIRATION=$(date -d '+1 hour' +%s)
|
||||||
|
|
||||||
|
# Generate the event using nak
|
||||||
|
EVENT_JSON=$(nak event -k 24242 -c "" \
|
||||||
|
-t "t=upload" \
|
||||||
|
-t "x=${HASH}" \
|
||||||
|
-t "expiration=${EXPIRATION}")
|
||||||
|
|
||||||
|
if [[ -z "$EVENT_JSON" ]]; then
|
||||||
|
log_error "Failed to generate nostr event"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Generated nostr event"
|
||||||
|
echo "Event JSON: $EVENT_JSON"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create authorization header
|
||||||
|
create_auth_header() {
|
||||||
|
log_info "Creating authorization header..."
|
||||||
|
|
||||||
|
# Base64 encode the event (without newlines)
|
||||||
|
AUTH_B64=$(echo -n "$EVENT_JSON" | base64 -w 0)
|
||||||
|
AUTH_HEADER="Nostr ${AUTH_B64}"
|
||||||
|
|
||||||
|
log_success "Created authorization header"
|
||||||
|
echo "Auth header length: ${#AUTH_HEADER} characters"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Perform upload
|
||||||
|
perform_upload() {
|
||||||
|
log_info "Performing upload to ${UPLOAD_ENDPOINT}..."
|
||||||
|
|
||||||
|
# Create temporary file for response
|
||||||
|
RESPONSE_FILE=$(mktemp)
|
||||||
|
CLEANUP_FILES+=("${RESPONSE_FILE}")
|
||||||
|
|
||||||
|
# Perform the upload with verbose output
|
||||||
|
HTTP_STATUS=$(curl -s -w "%{http_code}" \
|
||||||
|
-X PUT \
|
||||||
|
-H "Authorization: ${AUTH_HEADER}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary "@${TEST_FILE}" \
|
||||||
|
"${UPLOAD_ENDPOINT}" \
|
||||||
|
-o "${RESPONSE_FILE}")
|
||||||
|
|
||||||
|
echo "HTTP Status: ${HTTP_STATUS}"
|
||||||
|
echo "Response body:"
|
||||||
|
cat "${RESPONSE_FILE}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Check response
|
||||||
|
case "${HTTP_STATUS}" in
|
||||||
|
200)
|
||||||
|
log_success "Upload successful!"
|
||||||
|
;;
|
||||||
|
201)
|
||||||
|
log_success "Upload successful (created)!"
|
||||||
|
;;
|
||||||
|
400)
|
||||||
|
log_error "Bad request - check the event format"
|
||||||
|
;;
|
||||||
|
401)
|
||||||
|
log_error "Unauthorized - authentication failed"
|
||||||
|
;;
|
||||||
|
405)
|
||||||
|
log_error "Method not allowed - check nginx configuration"
|
||||||
|
;;
|
||||||
|
413)
|
||||||
|
log_error "Payload too large"
|
||||||
|
;;
|
||||||
|
501)
|
||||||
|
log_warning "Upload endpoint not yet implemented (expected for now)"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log_error "Upload failed with HTTP status: ${HTTP_STATUS}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test file retrieval
|
||||||
|
test_retrieval() {
|
||||||
|
log_info "Testing file retrieval..."
|
||||||
|
|
||||||
|
RETRIEVAL_URL="${SERVER_URL}/${HASH}"
|
||||||
|
|
||||||
|
if curl -s -f "${RETRIEVAL_URL}" > /dev/null 2>&1; then
|
||||||
|
log_success "File can be retrieved at: ${RETRIEVAL_URL}"
|
||||||
|
else
|
||||||
|
log_warning "File not yet available for retrieval (expected if upload processing not implemented)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
main() {
|
||||||
|
echo "=== Ginxsom Blossom Upload Test ==="
|
||||||
|
echo "Timestamp: $(date -Iseconds)"
|
||||||
|
echo
|
||||||
|
|
||||||
|
check_prerequisites
|
||||||
|
check_server
|
||||||
|
create_test_file
|
||||||
|
calculate_hash
|
||||||
|
generate_nostr_event
|
||||||
|
create_auth_header
|
||||||
|
perform_upload
|
||||||
|
test_retrieval
|
||||||
|
|
||||||
|
echo
|
||||||
|
log_info "Test completed!"
|
||||||
|
echo "Summary:"
|
||||||
|
echo " Test file: ${TEST_FILE}"
|
||||||
|
echo " File hash: ${HASH}"
|
||||||
|
echo " Server: ${SERVER_URL}"
|
||||||
|
echo " Upload endpoint: ${UPLOAD_ENDPOINT}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
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
|
* Handles HEAD requests and other dynamic operations
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcgi_stdio.h>
|
#include <fcgi_stdio.h>
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "ginxsom.h"
|
||||||
|
|
||||||
#define MAX_SHA256_LEN 65
|
#define MAX_SHA256_LEN 65
|
||||||
#define MAX_PATH_LEN 512
|
#define MAX_PATH_LEN 512
|
||||||
@@ -218,6 +222,266 @@ const char* extract_sha256_from_uri(const char* uri) {
|
|||||||
return sha256_buffer;
|
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) {
|
int main(void) {
|
||||||
while (FCGI_Accept() >= 0) {
|
while (FCGI_Accept() >= 0) {
|
||||||
// DEBUG: Log every request received
|
// DEBUG: Log every request received
|
||||||
@@ -249,6 +513,9 @@ int main(void) {
|
|||||||
printf("Content-Type: text/plain\r\n\r\n");
|
printf("Content-Type: text/plain\r\n\r\n");
|
||||||
printf("Invalid SHA-256 hash in URI\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 {
|
} else {
|
||||||
// Other methods not implemented yet
|
// Other methods not implemented yet
|
||||||
printf("Status: 501 Not Implemented\r\n");
|
printf("Status: 501 Not Implemented\r\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user