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