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.
294 lines
7.2 KiB
C
294 lines
7.2 KiB
C
#include "accounts.h"
|
|
|
|
#include "http/server/request.h"
|
|
#include "json/layout.h"
|
|
#include "json/json.h"
|
|
#include "http/query.h"
|
|
|
|
#include "model/account.h"
|
|
|
|
#include "view/api/Relationship.h"
|
|
#include "view/api/Account.h"
|
|
|
|
#include "controller/api/timeline.h"
|
|
|
|
// Submodules
|
|
#include "collections/array.h"
|
|
|
|
// Platform libraries
|
|
#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; }
|
|
if( 1 != sscanf( id_str, "%d", id ) ) {
|
|
struct account* a = account_from_webfinger(id_str,NULL);
|
|
if( a ) {
|
|
*id = a->id;
|
|
account_free(a);
|
|
goto success;
|
|
}
|
|
|
|
goto failed;
|
|
}
|
|
|
|
success:
|
|
result = true;
|
|
cleanup:
|
|
free(id_str);
|
|
return result;
|
|
failed:
|
|
result = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
bool http_request_is_tor_request( struct http_request* req );
|
|
|
|
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, http_request_is_tor_request(req) );
|
|
|
|
return true;
|
|
}
|
|
|
|
struct search_params
|
|
{
|
|
const char* q;
|
|
int limit;
|
|
bool resolve;
|
|
bool following;
|
|
};
|
|
|
|
static void search_accounts( struct search_params* params, struct account_list* al )
|
|
{
|
|
if( strstr( params->q, "https://" ) || strstr( params->q, "http://" ) ) {
|
|
//// Is URL
|
|
struct account* a = NULL;
|
|
|
|
// TODO: try to fetch RSS account from url
|
|
a = account_from_rss( params->q );
|
|
if( a ) {
|
|
array_append( al, sizeof(a), &a );
|
|
return;
|
|
}
|
|
|
|
// TODO: try to fetch account from url
|
|
a = account_from_uri_or_fetch( params->q );
|
|
if( a ) {
|
|
array_append( al, sizeof(a), &a );
|
|
}
|
|
} else if( strstr( params->q, "@" ) ) {
|
|
// Has @, assume this is a webfinger search
|
|
struct account* a = account_from_webfinger( params->q, NULL );
|
|
if( a ) {
|
|
array_append( al, sizeof(a), &a );
|
|
}
|
|
|
|
if( !a && params->resolve ) {
|
|
printf( "TODO: perform webfinger lookup\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Route: /api/v1/accounts/search?
|
|
static bool handle_search( struct http_request* req )
|
|
{
|
|
struct search_params params = {0};
|
|
|
|
#define OBJ_TYPE struct search_params
|
|
static struct http_query_fields fields[] = {
|
|
{ "q", offsetof(OBJ_TYPE,q), qf_string },
|
|
{ "limit", offsetof(OBJ_TYPE,limit), qf_integer },
|
|
{ "resolve", offsetof(OBJ_TYPE,resolve), qf_bool },
|
|
{ "following", offsetof(OBJ_TYPE,following), qf_bool },
|
|
{ NULL },
|
|
};
|
|
#undef OBJ_TYPE
|
|
|
|
http_query_parse( req, fields, ¶ms );
|
|
|
|
struct account_list al = {0};
|
|
search_accounts( ¶ms, &al );
|
|
|
|
http_request_send_headers( req, 200, "application/json", true );
|
|
FILE* f = http_request_get_response_body( req );
|
|
|
|
fprintf( f, "[" );
|
|
for( int i = 0; i < al.count; ++i ) {
|
|
if( i != 0 ) {
|
|
fprintf( f, ",\n" );
|
|
}
|
|
|
|
api_Account_write(al.items[i],f,1,false);
|
|
}
|
|
fprintf( f, "]" );
|
|
|
|
account_list_free(&al);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Route: /api/v1/accounts/relationships?
|
|
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/update_credentials
|
|
bool route_api_account_update_credentials( struct http_request* req )
|
|
{
|
|
if( !http_request_route_method( req, "PATCH" ) ) {
|
|
return false;
|
|
}
|
|
|
|
FILE* f = http_request_get_request_data( req );
|
|
|
|
printf( "Request body:\n" );
|
|
int c;
|
|
while( (c=fgetc(f)) != EOF ) {
|
|
fputc(c,stdout);
|
|
}
|
|
printf( "\nEnd of body (last=%d)\n", c );
|
|
|
|
struct account* owner = account_from_id(owner_account_id);
|
|
bool res = handle_mastodon_api_show_account( req, owner );
|
|
account_free(owner);
|
|
return res;
|
|
}
|
|
|
|
// Route: /api/v1/accounts/
|
|
bool route_api_account( struct http_request* req )
|
|
{
|
|
if( http_request_route_term( req, "verify_credentials" ) ) {
|
|
struct account* owner = account_from_id(owner_account_id);
|
|
bool res = handle_mastodon_api_show_account( req, owner );
|
|
account_free(owner);
|
|
return res;
|
|
} else if( http_request_route_term( req, "update_credentials" ) ) {
|
|
return route_api_account_update_credentials(req);
|
|
} 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;
|
|
}
|
|
|
|
// Route: /api/v1/accounts/%d{id}
|
|
int id = 0;
|
|
if( !get_local_account_id( req, &id ) ) {
|
|
return false;
|
|
}
|
|
|
|
// Make sure the account exists before allowing anything to be done with it
|
|
struct account* a = account_from_id( id );
|
|
if( !a ) { return false; }
|
|
|
|
// Account-specific routes
|
|
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, 100, &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, 100, &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, "unfollow" ) ) {
|
|
struct account* owner_account = account_from_id( owner_account_id );
|
|
account_unfollow( 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;
|
|
}
|