You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
6.4 KiB
C

#include "mastodon_api.h"
#include "http/server/request.h"
// Submodules
#include "form.h"
#include "json/json.h"
#include "http/query.h"
#include "ffdb/fs_list.h"
#include "util/format.h"
#include "ap/object.h"
// Model
#include "model/status.h"
#include "model/account.h"
#include "model/notification.h"
#include "model/server.h"
#include "model/media.h"
// View
#include "view/api/Attachement.h"
// Controller
#include "controller/pleroma_api.h"
#include "controller/api/timeline.h"
#include "controller/api/client_apps.h"
#include "controller/api/status.h"
#include "controller/api/notice.h"
#include "controller/api/accounts.h"
#include "controller/api/emoji.h"
#include <stddef.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
bool handle_scheduled_statuses( struct http_request* req )
{
http_request_send_headers( req, 200, "application/json", true );
FILE* body = http_request_get_response_body( req );
fprintf( body, "[]" );
return true;
}
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** content_type )
{
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;
return true;
}
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 )
{
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 );
// Eat all header data
while( getline_stripped( &line, &n, data ) > 0 ) {
if( 0 == strncasecmp( line, "Content-Type: ", 14 ) ) {
if( content_type ) {
*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(boundary);
free(buffer.data);
return true;
}
bool handle_media( struct http_request* req )
{
bool result = false;
char filename[512];
FILE* f = NULL;
struct media* m = NULL;
int id = fs_list_get( "data/media/HEAD" ) + 1;
fs_list_set( "data/media/HEAD", id );
m = malloc(sizeof(*m));
memset(m,0,sizeof(*m));
m->id = id;
f = fopen(format(filename,sizeof(filename), "data/media/%d.blob",id), "w" );
if( !http_request_write_multipart_to_FILE( req, f, &m->content_type ) ) { goto failed; }
fclose(f); f = NULL;
if( 0 == strncmp("image/",m->content_type,6) ) {
printf( "This is an image, create preview\n" );
char buffer[512];
system( "set -x; pwd" );
int res = system( format( buffer,sizeof(buffer),"set -x; /usr/bin/convert '%s' -thumbnail '250x80>' '%s.preview'", filename, filename ) );
printf( "$ %s -> %d\n", buffer, res );
}
media_save(m);
http_request_send_headers( req, 200, "application/json", true );
FILE* body = http_request_get_response_body( req );
api_Attachement_write( m, body, 0 );
result = true;
cleanup:
if( f ) { fclose(f); }
media_free(m);
return result;
failed:
result = false;
goto cleanup;
}
bool route_mastodon_api( struct http_request* req )
{
if( http_request_route_term( req, "apps" ) ) {
if( http_request_route_method( req, "POST" ) ) {
return handle_mastodon_api_apps(req);
}
} else if( http_request_route( req, "timelines/public" ) ) {
return handle_timeline( req, tli_public );
} else if( http_request_route( req, "instance" ) ) {
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
#include "src/view/api/instance_data.json.inc"
return true;
} else if( http_request_route_term( req, "custom_emojis" ) ) {
return route_custom_emojis(req);
}
if( !check_authentication_header(req) ) { return false; }
if( http_request_route( req, "pleroma" ) ) {
return route_pleroma_api( req );
}
if( http_request_route( req, "notifications" ) ) {
return handle_notifications(req);
} else if( http_request_route( req, "filters" ) ) {
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
fprintf( f, "[]" );
return true;
} else if( http_request_route( req, "statuses" ) ) {
return route_statuses(req);
} else if( http_request_route_term( req, "scheduled_statuses" ) ) {
return handle_scheduled_statuses(req);
} else if( http_request_route( req, "timelines/" ) ) {
if( http_request_route( req, "home" ) ) {
return handle_timeline( req, tli_home );
} 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_term( req, "bookmarks" ) ) {
return handle_show_bookmarks( req );
} else if( http_request_route( req, "apps/" ) ) {
if( http_request_route( req, "verify_credentials" ) ) {
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
#include "src/view/api/verify_credentials.json.inc"
return true;
}
} else if( http_request_route( req, "accounts/" ) ) {
return route_api_account( req );
}
return false;
}