Break timeline and account routes out of controler/mastodon_api.c, connect /api/v1/accounts/%d{id}/follow to account_follow

master
teknomunk 1 year ago
parent d8ad309e08
commit 31bc4e73be

@ -0,0 +1,218 @@
#include "accounts.h"
#include "http/server/request.h"
#include "json/layout.h"
#include "http/query.h"
#include "model/account.h"
#include "view/api/Relationship.h"
#include "view/api/Account.h"
#include "controller/api/timeline.h"
#include <stdlib.h>
#include <string.h>
bool get_local_account_id( struct http_request* req, int* id )
{
bool result = false;
char* id_str = http_request_route_get_dir_or_file(req);
if( !id_str || !*id_str ) { goto failed; }
*id = -1;
sscanf( id_str, "%d", id );
if( *id == -1 ) {
struct account* a = account_from_webfinger(id_str);
if( a ) {
*id = a->id;
account_free(a);
goto success;
}
goto failed;
}
success:
result = true;
cleanup:
free(id_str);
return true;
failed:
result = false;
goto cleanup;
}
bool handle_mastodon_api_show_account( struct http_request* req, struct account* a )
{
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
api_Account_write(a,f,0);
return true;
}
static bool handle_search( struct http_request* req )
{
struct params_t
{
const char* q;
int limit;
bool resolve;
bool following;
} params;
memset(&params,0,sizeof(params));
static struct http_query_fields fields[] = {
{ "q", offsetof(struct params_t,q), qf_string },
{ "limit", offsetof(struct params_t,limit), qf_integer },
{ "resolve", offsetof(struct params_t,resolve), qf_bool },
{ "following", offsetof(struct params_t,following), qf_bool },
{ NULL },
};
http_query_parse( req, fields, &params );
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
struct account* a = account_from_webfinger( params.q );
if( !a && params.resolve ) {
printf( "TODO: perform webfinger lookup\n" );
return false;
}
fprintf( f, "[" );
api_Account_write(a,f,1);
fprintf( f, "]" );
account_free(a);
return true;
}
static bool handle_relationships( struct http_request* req )
{
http_request_send_headers( req, 200, "application/json", true );
struct account* owner_account = account_from_id( owner_account_id );
FILE* f = http_request_get_response_body( req );
fprintf( f, "[" );
bool first = true;
for(;;) {
const char* key = http_request_route_query_key(req); if( !key ) { break; }
if( 0 == strcmp(key,"id[]" ) ) {
} else if( 0 == strcmp(key,"id%5B%5D") ) {
} else {
break;
}
int id = atoi( http_request_route_query_value(req) );
if( id > 0 ) {
struct account* a = account_from_id(id);
if( !a ) { continue; }
fprintf( f, first ? "\n\t" : ",\n\t" ); first = false;
api_Relationship_write( owner_account, a, f, 1 );
account_free(a);
}
}
fprintf( f, "\n]" );
account_free(owner_account);
return true;
}
// Route: /api/v1/accounts/
bool route_api_account( struct http_request* req )
{
if( http_request_route( req, "verify_credentials" ) ) {
struct account* owner = account_from_id(0);
bool res = handle_mastodon_api_show_account( req, owner );
account_free(owner);
return res;
} else if( http_request_route( req, "relationships?" ) ) {
return handle_relationships(req);
} else if( http_request_route( req, "statuses" ) ) {
return handle_timeline( req, tli_owner );
} else if( http_request_route( req, "search?" ) ) {
return handle_search( req );
} else if( http_request_route_term(req,"") ) {
struct account* owner = account_from_id(0);
bool res = handle_mastodon_api_show_account( req, owner );
account_free(owner);
return res;
}
int id = 0;
if( !get_local_account_id( req, &id ) ) {
return false;
}
struct account* a = account_from_id( id );
if( !a ) { return false; }
// Route: /api/v1/accounts/%d{id}
if( http_request_route( req, "statuses" ) ) {
bool res = handle_timeline( req, id );
account_free(a);
return res;
} else if( http_request_route( req, "followers" ) ) {
struct {
int* items;
int count;
} accounts;
account_list_followers( a, 0, 32, &accounts );
struct json_writer jw = {
.f = http_request_get_response_body( req ),
.indentation = "\t",
.indent = 0,
};
http_request_send_headers( req, 200, "application/json", true );
json_write_array( &jw, &api_Account_type, &accounts );
free(accounts.items);
return true;
} else if( http_request_route( req, "following" ) ) {
struct {
int* items;
int count;
} accounts;
account_list_following( a, 0, 32, &accounts );
struct json_writer jw = {
.f = http_request_get_response_body( req ),
.indentation = "\t",
.indent = 0,
};
http_request_send_headers( req, 200, "application/json", true );
json_write_array( &jw, &api_Account_type, &accounts );
free(accounts.items);
return true;
} else if( http_request_route_term( req, "follow" ) ) {
struct account* owner_account = account_from_id( owner_account_id );
account_follow( owner_account, a );
account_free(owner_account);
bool res = handle_mastodon_api_show_account( req, a );
account_free(a);
return res;
} else if( http_request_route_term( req, "" ) ) {
bool res = handle_mastodon_api_show_account( req, a );
account_free(a);
return res;
}
return false;
}

@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
struct http_request;
bool route_api_account( struct http_request* req );

@ -0,0 +1,113 @@
#include "timeline.h"
// Submodules
#include "http/server/request.h"
#include "http/query.h"
// Model
#include "model/timeline.h"
#include "model/status.h"
// Controller
#include "controller/api/status.h"
bool handle_timeline( struct http_request* req, int timeline_id )
{
struct params_t {
int since_id;
int max_id;
int limit;
bool hide_muted;
bool pinned;
} params = {
.since_id = 0,
.limit = 32,
.max_id = 2147483647,
.hide_muted = true,
.pinned = false,
};
#define OBJ_TYPE struct params_t
static struct http_query_fields fields[] = {
HTTP_QUERY_FIELD_INT( since_id )
HTTP_QUERY_FIELD_INT( limit )
HTTP_QUERY_FIELD_INT( max_id )
HTTP_QUERY_FIELD_BOOL( hide_muted )
HTTP_QUERY_FIELD_BOOL( pinned )
HTTP_QUERY_FIELD_END
};
#undef OBJ_TYPE
// handle query parameters
if( http_request_route( req, "?" ) ) {
http_query_parse( req, fields, &params );
}
if( params.limit > 32 ) { params.limit = 32; }
struct timeline* tl = timeline_from_id( timeline_id );
if( !tl ) {
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
fprintf( f, "[]" );
return true;
}
struct status* ss[params.limit];
struct status* items[params.limit];
struct {
struct status** items;
int count;
} show = {
.items = items,
.count = 0
};
int offset = 0;
int count = 0;
int max_loops = 5;
load_statuses:
printf( "loading offset=%d,limit=%d\n", offset, params.limit );
count = timeline_load_statuses( tl, offset, params.limit, ss );
printf( "count=%d\n", count );
if( count == 0 ) { goto done; }
// Filter
for( int i = 0; i < count; ++i ) {
struct status* s = ss[i];
if( s->id <= params.since_id ) { goto filtered; }
if( s->id >= params.max_id ) { goto filtered; }
if( s->pinned != params.pinned ) { goto filtered; }
show.items[show.count] = s;
show.count += 1;
if( 0 ) {
filtered:
//printf( "Filtered out %d\n", s->id );
}
}
// Try to load more statuses
if( count < params.limit ) {
offset += params.limit;
params.limit -= show.count;
if( max_loops > 0 ) {
max_loops -= 1;
goto load_statuses;
}
}
done:
show_statuses( req, show.items, show.count );
for( int i = 0; i < count; ++i ) {
status_free( ss[i] );
}
timeline_free(tl);
return true;
}

@ -0,0 +1,14 @@
#pragma once
#include <stdbool.h>
struct http_request;
enum timeline_ids {
tli_owner = 0,
tli_public = 1,
tli_home = 2,
};
bool handle_timeline( struct http_request* req, int timeline_id );

@ -10,18 +10,15 @@
#include "model/status.h"
#include "model/account.h"
#include "model/notification.h"
#include "model/timeline.h"
#include "model/server.h"
#include "model/ap/activity.h"
#include "controller/pleroma_api.h"
#include "api/client_apps.h"
#include "api/status.h"
#include "api/notice.h"
#include "view/api/Relationship.h"
#include "view/api/Account.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 <stddef.h>
#include <limits.h>
@ -29,194 +26,6 @@
#include <stdlib.h>
#include <string.h>
enum timeline_ids {
tli_owner = 0,
tli_public = 1,
tli_home = 2,
};
bool handle_timeline( struct http_request* req, int timeline_id )
{
struct params_t {
int since_id;
int max_id;
int limit;
bool hide_muted;
bool pinned;
} params = {
.since_id = 0,
.limit = 32,
.max_id = 2147483647,
.hide_muted = true,
.pinned = false,
};
#define OBJ_TYPE struct params_t
static struct http_query_fields fields[] = {
HTTP_QUERY_FIELD_INT( since_id )
HTTP_QUERY_FIELD_INT( limit )
HTTP_QUERY_FIELD_INT( max_id )
HTTP_QUERY_FIELD_BOOL( hide_muted )
HTTP_QUERY_FIELD_BOOL( pinned )
HTTP_QUERY_FIELD_END
};
#undef OBJ_TYPE
// handle query parameters
if( http_request_route( req, "?" ) ) {
http_query_parse( req, fields, &params );
}
if( params.limit > 32 ) { params.limit = 32; }
struct timeline* tl = timeline_from_id( timeline_id );
if( !tl ) {
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
fprintf( f, "[]" );
return true;
}
struct status* ss[params.limit];
struct status* items[params.limit];
struct {
struct status** items;
int count;
} show = {
.items = items,
.count = 0
};
int offset = 0;
int count = 0;
int max_loops = 5;
load_statuses:
printf( "loading offset=%d,limit=%d\n", offset, params.limit );
count = timeline_load_statuses( tl, offset, params.limit, ss );
printf( "count=%d\n", count );
if( count == 0 ) { goto done; }
// Filter
for( int i = 0; i < count; ++i ) {
struct status* s = ss[i];
if( s->id <= params.since_id ) { goto filtered; }
if( s->id >= params.max_id ) { goto filtered; }
if( s->pinned != params.pinned ) { goto filtered; }
show.items[show.count] = s;
show.count += 1;
if( 0 ) {
filtered:
//printf( "Filtered out %d\n", s->id );
}
}
// Try to load more statuses
if( count < params.limit ) {
offset += params.limit;
params.limit -= show.count;
if( max_loops > 0 ) {
max_loops -= 1;
goto load_statuses;
}
}
done:
show_statuses( req, show.items, show.count );
for( int i = 0; i < count; ++i ) {
status_free( ss[i] );
}
timeline_free(tl);
return true;
}
bool handle_mastodon_api_show_account( struct http_request* req, struct account* a )
{
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
api_Account_write(a,f,0);
return true;
}
static bool handle_search( struct http_request* req )
{
struct params_t
{
const char* q;
int limit;
bool resolve;
bool following;
} params;
memset(&params,0,sizeof(params));
static struct http_query_fields fields[] = {
{ "q", offsetof(struct params_t,q), qf_string },
{ "limit", offsetof(struct params_t,limit), qf_integer },
{ "resolve", offsetof(struct params_t,resolve), qf_bool },
{ "following", offsetof(struct params_t,following), qf_bool },
{ NULL },
};
http_query_parse( req, fields, &params );
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
struct account* a = account_from_webfinger( params.q );
if( !a && params.resolve ) {
printf( "TODO: perform webfinger lookup\n" );
return false;
}
fprintf( f, "[" );
api_Account_write(a,f,1);
fprintf( f, "]" );
account_free(a);
return true;
}
static bool handle_relationships( struct http_request* req )
{
http_request_send_headers( req, 200, "application/json", true );
struct account* owner_account = account_from_id( owner_account_id );
FILE* f = http_request_get_response_body( req );
fprintf( f, "[" );
bool first = true;
for(;;) {
const char* key = http_request_route_query_key(req); if( !key ) { break; }
if( 0 == strcmp(key,"id[]" ) ) {
} else if( 0 == strcmp(key,"id%5B%5D") ) {
} else {
break;
}
int id = atoi( http_request_route_query_value(req) );
if( id > 0 ) {
struct account* a = account_from_id(id);
if( !a ) { continue; }
fprintf( f, first ? "\n\t" : ",\n\t" ); first = false;
api_Relationship_write( owner_account, a, f, 1 );
account_free(a);
}
}
fprintf( f, "\n]" );
account_free(owner_account);
return true;
}
bool http_request_route_id( struct http_request* req, int* id )
{
char* id_str = http_request_route_get_dir_or_file(req);
@ -228,35 +37,6 @@ bool http_request_route_id( struct http_request* req, int* id )
return true;
}
bool get_local_account_id( struct http_request* req, int* id )
{
bool result = false;
char* id_str = http_request_route_get_dir_or_file(req);
if( !id_str || !*id_str ) { goto failed; }
*id = -1;
sscanf( id_str, "%d", id );
if( *id == -1 ) {
struct account* a = account_from_webfinger(id_str);
if( a ) {
*id = a->id;
account_free(a);
goto success;
}
goto failed;
}
success:
result = true;
cleanup:
free(id_str);
return true;
failed:
result = false;
goto cleanup;
}
// Route: /api/v1/statuses/%d{id}/
bool route_statuses_1f( struct http_request* req )
{
@ -343,80 +123,7 @@ bool route_mastodon_api( struct http_request* req )
}
} else if( http_request_route( req, "accounts/" ) ) {
if( http_request_route( req, "verify_credentials" ) ) {
struct account* owner = account_from_id(0);
bool res = handle_mastodon_api_show_account( req, owner );
account_free(owner);
return res;
} else if( http_request_route( req, "relationships?" ) ) {
return handle_relationships(req);
} else if( http_request_route( req, "statuses" ) ) {
return handle_timeline( req, tli_owner );
} else if( http_request_route( req, "search?" ) ) {
return handle_search( req );
} else if( http_request_route_term(req,"") ) {
struct account* owner = account_from_id(0);
bool res = handle_mastodon_api_show_account( req, owner );
account_free(owner);
return res;
}
int id = 0;
if( !get_local_account_id( req, &id ) ) {
return false;
}
struct account* a = account_from_id( id );
if( !a ) { return false; }
if( http_request_route( req, "statuses" ) ) {
bool res = handle_timeline( req, id );
account_free(a);
return res;
} else if( http_request_route( req, "followers" ) ) {
struct {
int* items;
int count;
} accounts;
account_list_followers( a, 0, 32, &accounts );
struct json_writer jw = {
.f = http_request_get_response_body( req ),
.indentation = "\t",
.indent = 0,
};
http_request_send_headers( req, 200, "application/json", true );
json_write_array( &jw, &api_Account_type, &accounts );
free(accounts.items);
return true;
} else if( http_request_route( req, "following" ) ) {
struct {
int* items;
int count;
} accounts;
account_list_following( a, 0, 32, &accounts );
struct json_writer jw = {
.f = http_request_get_response_body( req ),
.indentation = "\t",
.indent = 0,
};
http_request_send_headers( req, 200, "application/json", true );
json_write_array( &jw, &api_Account_type, &accounts );
free(accounts.items);
return true;
} else {
bool res = handle_mastodon_api_show_account( req, a );
account_free(a);
return res;
}
return route_api_account( req );
}
return false;

Loading…
Cancel
Save