Get multipart post data for media parsing and writing to disk, provide Soapbox index.html instead of 404 message, fix several resource leaks

master
teknomunk 1 year ago
parent 9f9b93a263
commit 6d3b608925

@ -120,31 +120,37 @@ cleanup:
bool route_request( struct http_request* req )
{
if( http_request_route( req, "/api/v1/" ) ) {
return route_mastodon_api( req );
} else if( http_request_route( req, "/api/pleroma" ) ) {
return route_pleroma_api2( req );
} else if( http_request_route( req, "/oauth" ) ) {
return route_oauth( req );
} else if( http_request_route( req, "/owner" ) ) {
return route_owner( req );
} else if( http_request_route( req, "/inbox" ) ) {
return route_inbox( req );
} else if( http_request_route( req, "/note/" ) ) {
return route_ap_note( req );
} else if( http_request_route( req, "/.well-known" ) ) {
if( http_request_route( req, "/webfinger?" ) ) {
return route_wellknown_webfinger( req );
bool inner( struct http_request* req ) {
if( http_request_route( req, "/api/v1/" ) ) {
return route_mastodon_api( req );
} else if( http_request_route( req, "/api/pleroma" ) ) {
return route_pleroma_api2( req );
} else if( http_request_route( req, "/oauth" ) ) {
return route_oauth( req );
} else if( http_request_route( req, "/owner" ) ) {
return route_owner( req );
} else if( http_request_route( req, "/inbox" ) ) {
return route_inbox( req );
} else if( http_request_route( req, "/note/" ) ) {
return route_ap_note( req );
} else if( http_request_route( req, "/.well-known" ) ) {
if( http_request_route( req, "/webfinger?" ) ) {
return route_wellknown_webfinger( req );
} else if( http_request_route( req, "/nodeinfo" ) ) {
printf( "Nodeinfo\n" );
return route_wellknown_nodeinfo( req );
}
} else if( http_request_route( req, "/nodeinfo" ) ) {
printf( "Nodeinfo\n" );
return route_wellknown_nodeinfo( req );
return route_nodeinfo(req);
} else {
return route_asset(req);
}
} else if( http_request_route( req, "/nodeinfo" ) ) {
return route_nodeinfo(req);
} else {
return route_asset(req);
}
return false;
if( inner( req ) ) {
return true;
} else {
return send_asset( req, "assets/soapbox/index.html" );
}
}

@ -6,6 +6,7 @@
#include "json/json.h"
#include "http/query.h"
#include "ffdb/fs_list.h"
#include "format.h"
#include "model/status.h"
#include "model/account.h"
@ -80,6 +81,126 @@ success:
goto cleanup;
}
ssize_t getline_stripped( char** restrict lineptr, size_t* restrict n, FILE* restrict stream);
bool http_request_write_multipart_to_FILE( struct http_request* req, FILE* f )
{
char* line = NULL;
char* boundary = NULL;
FILE* data = http_request_get_request_data( req );
size_t n;
// lineptr should match the contents of the Content-Type boundary= parametera
ssize_t res = getline_stripped( &line, &n, data );
boundary = aformat( "\r\n%s", line );
int boundary_size = strlen(boundary);
struct ring_buffer {
char* data;
int count;
int limit;
int head;
};
struct ring_buffer buffer = {
.data = malloc(boundary_size + 20),
.count = 0,
.limit = boundary_size + 20,
.head = 0
};
bool ring_buffer_push_back( struct ring_buffer* rb, char ch )
{
if( rb->count >= rb->limit ) { return false; }
rb->data[ ( rb->head + rb->count ) % rb->limit ] = ch;
rb->count += 1;
}
bool ring_buffer_pop_front( struct ring_buffer* rb, char* ch )
{
if( rb->count == 0 ) { return false; }
*ch = rb->data[ rb->head ];
rb->head = ( rb->head + 1 ) % rb->limit;
rb->count -= 1;
return true;
}
bool ring_buffer_compare_prefix( struct ring_buffer* rb, const char* str )
{
int i = 0;
for( int i = 0; i < rb->count; ++i, ++str ) {
if( !*str ) {
// String matches start of buffer
return true;
}
char ch = rb->data[ ( i + rb->head ) % rb->limit ];
if( *str != ch ) {
return false;
}
}
return true;
}
memset( buffer.data, 0, boundary_size );
int pos = 0;
bool filled = false;
// Eat all header data
char* content_type = NULL;
while( getline_stripped( &line, &n, data ) > 0 ) {
if( 0 == strncasecmp( line, "Content-Type: ", 14 ) ) {
content_type = strdup( &line[14] );
printf( "content_type=%s\n", content_type );
} else {
printf( "Header: %s\n", line );
}
}
free(line);
// Read the line
int filesize = 0;
int ch;
do {
ch = fgetc(data);
if( ch != EOF ) {
ring_buffer_push_back(&buffer,ch );
}
if( ring_buffer_compare_prefix(&buffer,boundary) ) {
break;
}
if( buffer.count > boundary_size || ch == EOF ) {
char c;
if( ring_buffer_pop_front( &buffer, &c ) ) {
filesize += 1;
fputc( c, f );
}
}
} while( buffer.count > 0 );
//printf( "%d bytes uploaded\n", filesize );
free(content_type);
free(boundary);
free(buffer.data);
return true;
}
bool handle_media( struct http_request* req )
{
char filename[512];
int id = fs_list_get( "data/media/HEAD" );
FILE* f = fopen(format(filename,sizeof(filename), "data/media/%d.blob",id), "w" );
bool result = http_request_write_multipart_to_FILE( req, f );
fclose(f);
if( !result ) { return false; }
return false;
}
bool route_mastodon_api( struct http_request* req )
{
if( http_request_route_term( req, "apps" ) ) {
@ -116,6 +237,8 @@ bool route_mastodon_api( struct http_request* req )
} else if( http_request_route( req, "public" ) ) {
return handle_timeline( req, tli_public );
}
} else if( http_request_route_term( req, "media" ) ) {
return handle_media(req);
} else if( http_request_route( req, "apps/" ) ) {
if( http_request_route( req, "verify_credentials" ) ) {

@ -1 +1 @@
Subproject commit 93740b63638063a131736b1394bc99f957e24f39
Subproject commit 8e678021ff7804778ad32d0c11dd9ceb47b647fa

@ -344,6 +344,7 @@ static void create_account_skeleton( int account_id )
fs_list_set( format( b, 512, "data/accounts/%d/timeline/HEAD", account_id ), 0 );
}
bool pull_remote_file( const char* filename, const char* uri );
struct account* account_fetch_from_uri( const char* uri )
{
if( !uri ) { return NULL; }
@ -362,31 +363,9 @@ struct account* account_fetch_from_uri( const char* uri )
char filename[512];
FILE* f = fopen( format( filename, 512, "data/accounts/%d/ap.json", account_id ), "r" );
if( !f ) {
char tmp_filename[512];
snprintf( tmp_filename, 512, "%s.tmp", filename );
printf( "tmp_filename = %s\n", tmp_filename );
FILE* tmp = fopen(tmp_filename,"w");
if( !tmp ) {
printf( "! Unable to open %s\n", tmp_filename );
return NULL;
}
long status_code = -1;
const void* request[] = {
HTTP_REQ_URL, uri,
HTTP_REQ_HEADER, "Accept: application/ld+json",
HTTP_REQ_OUTFILE, tmp,
HTTP_REQ_RESULT_STATUS, &status_code,
NULL,
};
if( !http_client_do( request ) ) {
printf( "! Unable to fetch %s\n", uri );
return NULL;
}
fflush(tmp);
printf( "status_code = %d\n", status_code );
rename(tmp_filename,filename);
if( !pull_remote_file( filename, uri ) ) { return NULL; }
} else {
fclose(f);
}
// Fail if we can't sync
@ -403,12 +382,15 @@ void account_free( struct account* a )
free(a->handle);
free(a->server);
free(a->inbox);
free(a->shared_inbox);
free(a->display_name);
free(a->account_url);
free(a->inbox);
free(a->shared_inbox);
free(a->avatar.url);
free(a->avatar.static_url);
free(a->banner);
for( int i = 0; i < a->aliases.count; ++i ) {
free( a->aliases.items[i] );

@ -26,15 +26,6 @@ static struct json_object_field http_header_layout[] = {
#undef OBJ_TYPE
JSON_FIELD_TYPE_COMPOSITE( http_header )
/*
static struct json_field_type http_header_type = {
.reader = json_field_object_composite_reader,
.writer = json_field_object_composite_writer,
.size = sizeof( struct http_header ),
.layout = http_header_layout,
.alloc = NULL,
.free = NULL,
};*/
#define OBJ_TYPE struct ap_envelope
static struct json_object_field envelope_layout[] = {
@ -65,6 +56,11 @@ struct ap_envelope* ap_envelope_from_id( int id )
void ap_envelope_free_composite( struct ap_envelope* env )
{
free(env->body);
for( int i = 0; i < env->headers.count; ++i ) {
struct http_header* h = &env->headers.items[i];
free( h->key );
free( h->value );
}
free(env->headers.items);
free(env->when);
}
@ -116,6 +112,13 @@ bool envelope_create_from_request( struct http_request* req )
};
http_request_copy_headers( req, c );
// Deep copy header values
for( int i = 0; i < env.headers.count; ++i ) {
struct http_header* h = & env.headers.items[i];
h->key = strdup(h->key);
h->value = strdup(h->value);
}
// Get a space in the inbox
int head = fs_list_get( "data/inbox/HEAD" );
head += 1;

@ -161,6 +161,7 @@ bool http_signature_validate( struct ap_envelope* env, const char* request_targe
struct crypto_keys* keys = NULL;
bool result = false;
char* hash_line = NULL;
struct account* actor = NULL;
// Get the Signature header
for( int i = 0; i < env->headers.count; ++i ) {
@ -241,7 +242,7 @@ bool http_signature_validate( struct ap_envelope* env, const char* request_targe
}
// Get the account
struct account* actor = account_from_uri_or_fetch(actor_uri);
actor = account_from_uri_or_fetch(actor_uri);
if( !actor ) {
printf( "! failed to load account for %s\n", actor_uri );
goto failed;
@ -291,6 +292,7 @@ bool http_signature_validate( struct ap_envelope* env, const char* request_targe
result = true;
cleanup:
crypto_keys_free(keys);
account_free(actor);
free(signature_header);
free(date_header);
free(hash_line);

@ -245,54 +245,64 @@ failed:
result = false;
goto cleanup;
};
bool pull_remote_file( const char* filename, const char* uri )
{
printf( "* Fetching %s\n", uri );
char tmp_filename[512];
FILE* f = fopen(format(tmp_filename,512,"%s.tmp",filename),"w");
long status_code = -1;
const void* request[] = {
HTTP_REQ_URL, uri,
HTTP_REQ_HEADER, "Accept: application/ld+json",
HTTP_REQ_OUTFILE, f,
HTTP_REQ_RESULT_STATUS, &status_code,
NULL,
};
if( !http_client_do( request ) ) {
printf( "! Unable to fetch %s\n", uri );
fclose(f);
return false;
}
printf( "status_code = %d\n", status_code );
if( status_code != 200 ) {
fclose(f);
return false;
}
fclose(f);
rename(tmp_filename,filename);
return true;
}
bool status_sync_from_uri( struct status* s, const char* uri )
{
struct ap_activity* act = NULL;
FILE* f = NULL;
bool result = false;
// Fetch the object from the remote server
char filename[512];
snprintf( filename, sizeof(filename), "data/statuses/ap/%d.json", s->id );
f = fopen(filename,"r");
if( !f ) {
printf( "* Fetching %s\n", uri );
char tmp_filename[512];
FILE* f = fopen(format(tmp_filename,512,"%s.tmp",filename),"w");
long status_code = -1;
const void* request[] = {
HTTP_REQ_URL, uri,
HTTP_REQ_HEADER, "Accept: application/ld+json",
HTTP_REQ_OUTFILE, f,
HTTP_REQ_RESULT_STATUS, &status_code,
NULL,
};
if( !http_client_do( request ) ) {
printf( "! Unable to fetch %s\n", uri );
return NULL;
}
printf( "status_code = %d\n", status_code );
if( status_code != 200 ) {
return NULL;
}
fclose(f);
rename(tmp_filename,filename);
if( !pull_remote_file( filename, uri ) ) { goto failed; }
f = fopen(filename,"r");
if( !f ) { goto failed; }
}
f = fopen(filename,"r");
if( !f ) { return NULL; }
// Load the activity and sync status
act = ap_activity_from_FILE(f); f = NULL;
if( !act ) { goto failed; }
if( !status_sync_from_activity_pub(s,act) ) { goto failed; }
result = true;
cleanup:
if( f ) { fclose(f); }
ap_activity_free(act);
return s;
return result;
failed:
if( s ) {
printf( "Creating stub status for later sync\n" );
@ -301,7 +311,7 @@ failed:
}
status_free(s);
s = NULL;
result = false;
goto cleanup;
}
bool status_sync( struct status* s )
@ -406,6 +416,7 @@ struct status* status_from_activity( struct ap_activity* act )
bool status_save_new( struct status* s )
{
// TODO: change to fs_list_get/set
int head = -1;
FILE* f = fopen("data/statuses/HEAD","r");
fscanf(f,"%d",&head);
@ -462,6 +473,7 @@ void status_free( struct status* s )
free(s->likes.items);
free(s->reposts.items);
free(s->reposts.items);
free(s->mentions.items);
free(s);

Loading…
Cancel
Save