#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 #include #include #include #include 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; }