Add relationship and search handling, fix posting, add format(...)

master
teknomunk 1 year ago
parent 0325fc898c
commit 27180caf5d

@ -79,7 +79,7 @@ bool handle_post( struct http_request* req, struct account* a )
json_pull_parser_read_bool(jpp,&s->sensitive);
} else if( 0 == strcmp(key,"status") ) {
s->content = json_pull_parser_read_string(jpp);
s->source = json_pull_parser_read_string(jpp);
} else if( 0 == strcmp(key,"visibility") ) {
free(json_pull_parser_read_string(jpp));

@ -105,6 +105,118 @@ bool handle_mastodon_api_show_account( struct http_request* req, struct account*
return true;
}
struct query_fields
{
const char* field_name;
int offset;
void (*handle)( void* ptr, const char* value );
};
#include <stddef.h>
void qf_string( void* ptr, const char* value )
{
*(const char**)ptr = value;
}
void qf_integer( void* ptr, const char* value )
{
*(int*)ptr = atoi(value);
}
void qf_bool( void* ptr, const char* value )
{
if( 0 == strcmp(value,"true") ) {
*(bool*)ptr = true;
}
}
void parse_query( struct http_request* req, struct query_fields* fields, void* ptr )
{
char* data = ptr;
const char* key;
while( key = http_request_route_query_key(req) ) {
const char* value = http_request_route_query_value(req);
printf( "%s = %s\n", key, value );
for( struct query_fields* i = fields; i->field_name; ++i ) {
if( 0 == strcmp(i->field_name,key) ) {
i->handle( &data[i->offset], value );
}
}
}
}
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 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 },
};
parse_query( req, fields, &params );
printf( "params = {\n"
"\t.q = %s\n"
"\t.limit = %d\n"
"\t.resolve = %c\n"
"\t.following = %c\n",
params.q,
params.limit,
params.resolve ? 'T' : 'F',
params.following ? 'T' : 'F'
);
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_as_json(a,f);
fprintf( f, "]" );
return true;
}
static bool handle_relationships( struct http_request* req )
{
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
fprintf( f, "[" );
bool first = true;
const char* key;
while( (key=http_request_route_query_key(req)) && (0 == strcmp(key,"id[]") ) ) {
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" : ",\n" ); first = false;
#include "src/view/api/relationship.json.inc"
account_free(a);
}
}
fprintf( f, "]" );
return true;
}
bool http_request_route_id( struct http_request* req, int* id )
{
char* id_str = http_request_route_get_dir_or_file(req);
@ -191,14 +303,12 @@ bool route_mastodon_api( struct http_request* req )
bool res = handle_mastodon_api_show_account( req, owner );
account_free(owner);
return res;
} else if( http_request_route( req, "relationships" ) ) {
// TODO: implement stub
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, "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 );

@ -1,37 +1,17 @@
#include "pleroma_api.h"
#include "http/cgi.h"
#include "http_server/http_request.h"
#include "model/account.h"
#include "model/status.h"
#include "src/controller/api/client_apps.h"
#include "src/controller/api/status.h"
#include <stdlib.h>
#include <string.h>
char* cgi_unescape( const char* str )
{
int size = strlen(str);
char* result = malloc(size+1);
char* o = result;
for( const char* i = str; *i; ++i ) {
if( *i == '%' ) {
int code;
sscanf( &i[1], "%02X", &code );
*o = code;
++o;
i += 2;
} else {
*o = *i;
}
}
*o = '\0';
return result;
}
bool http_request_route_id( struct http_request* req, int* id );
static bool route_status( struct http_request* req, struct status* s )
@ -46,6 +26,9 @@ static bool route_status( struct http_request* req, struct status* s )
status_add_react( s, react, owner );
free(react);
account_free(owner);
show_status( req, s );
return true;
} else if( http_request_route_method( req, "GET" ) ) {
printf( "TODO: get who react information\n" );

@ -1 +1 @@
Subproject commit 5cc08a251f63cf6e88d1d68d62e7ea82599976c8
Subproject commit 3408ca78fe4323e95addb18675569a714f7ae66a

@ -0,0 +1,15 @@
#include "format.h"
#include <stdarg.h>
#include <stdio.h>
const char* format( char* buffer, int size, const char* f, ... )
{
va_list args;
va_start(args,f);
vsnprintf( buffer, size, f, args );
va_end(args);
return buffer;
}

@ -0,0 +1,4 @@
#pragma once
const char* format( char* buffer, int size, const char* f, ... );

@ -0,0 +1,27 @@
#include "cgi.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char* cgi_unescape( const char* str )
{
int size = strlen(str);
char* result = malloc(size+1);
char* o = result;
for( const char* i = str; *i; ++i ) {
if( *i == '%' ) {
int code;
sscanf( &i[1], "%02X", &code );
*o = code;
++o;
i += 2;
} else {
*o = *i;
}
}
*o = '\0';
return result;
}

@ -0,0 +1,4 @@
#pragma once
char* cgi_unescape( const char* str );

@ -8,6 +8,7 @@
#include "ffdb/fs_list.h"
#include "ffdb/hash_index.h"
#include "ffdb/trie.h"
#include "format.h"
// Model
#include "model/server.h"
@ -163,9 +164,9 @@ struct account* account_from_webfinger( const char* handle )
bool account_sync_from_acitvity_pub( unsigned int account_id )
{
char filename[512];
snprintf( filename, 512, "data/accounts/%d/ap.json", account_id );
struct ap_account* ap = ap_account_from_file( filename );
struct ap_account* ap = ap_account_from_file(
format( filename, 512, "data/accounts/%d/ap.json", account_id )
);
if( !ap ) {
printf( "! Failed to sync account %d from %s\n", account_id, filename );
return false;
@ -198,8 +199,7 @@ bool account_sync_from_acitvity_pub( unsigned int account_id )
char* id = strdup(ap->public_key.id);
char* key_id = NULL;
strtok_r( id, "#", &key_id );
snprintf( filename, sizeof(filename), "data/accounts/%d/%s.pem", a->id, key_id );
FILE* key_pem = fopen(filename,"w");
FILE* key_pem = fopen( format(filename,sizeof(filename),"data/accounts/%d/%s.pem", a->id, key_id), "w" );
fprintf( key_pem, "%s", ap->public_key.pem );
fclose(key_pem);
@ -213,8 +213,7 @@ bool account_sync_from_acitvity_pub( unsigned int account_id )
struct crypto_keys* account_get_public_key( struct account* a, const char* key_name )
{
char filename[512];
snprintf( filename, sizeof(filename), "data/accounts/%d/%s.pem", a->id, key_name );
FILE* f = fopen( filename, "r" );
FILE* f = fopen( format( filename, sizeof(filename), "data/accounts/%d/%s.pem", a->id, key_name ), "r" );
if( !f ) {
printf( "Failed to open file %s\n", filename );
return NULL;
@ -237,16 +236,13 @@ struct crypto_keys* account_get_private_key( struct account* a )
static void create_account_skeleton( int account_id )
{
char b[512];
// Make sure the account directory exists
char dirname[512];
snprintf( dirname, 512, "data/accounts/%d", account_id );
mkdir( dirname, 0755 );
snprintf( dirname, 512, "data/accounts/%d/timeline", account_id );
mkdir( dirname, 0755 );
mkdir( format( b, 512, "data/accounts/%d", account_id ), 0755 );
mkdir( format( b, 512, "data/accounts/%d/timeline", account_id ), 0755 );
char filename[512];
snprintf( filename, 512, "data/accounts/%d/timeline/HEAD", account_id );
fs_list_set( filename, 0 );
fs_list_set( format( b, 512, "data/accounts/%d/timeline/HEAD", account_id ), 0 );
}
struct account* account_fetch_from_uri( const char* uri )
@ -266,8 +262,7 @@ struct account* account_fetch_from_uri( const char* uri )
// Fetch the ActivityPub actor data if we don't already have it
char filename[512];
snprintf( filename, 512, "data/accounts/%d/ap.json", account_id );
FILE* f = fopen(filename,"r");
FILE* f = fopen( format( filename, 512, "data/accounts/%d/ap.json", account_id ), "r" );
if( !f ) {
char tmp_filename[512];
snprintf( tmp_filename, 512, "%s.tmp", filename );
@ -325,15 +320,22 @@ void account_save( struct account* a )
json_write_object_layout_to_file( filename, "\t", account_layout, a );
}
void account_add_follower( struct account* a, struct account* follower )
{
char filename[512];
snprintf( filename, sizeof(filename), "data/accounts/%d/followers", a->id );
// Insert an entry for this follower (only does something if not already set)
char filename[512];
char key[32];
snprintf( key, sizeof(key), "%d", follower->id );
ffdb_trie_set( filename, key, "T" );
ffdb_trie_set(
format( filename, sizeof(filename), "data/accounts/%d/followers", a->id ),
format(key,sizeof(key),"%d", follower->id),
"T"
);
ffdb_trie_set(
format( filename, sizeof(filename), "data/accounts/%d/following", follower->id ),
format(key,sizeof(key),"%d", a->id),
"T"
);
// Update follower count
a->followers_count = ffdb_trie_count(filename);
@ -348,13 +350,17 @@ void account_add_follower( struct account* a, struct account* follower )
}
void account_remove_follower( struct account* a, struct account* follower )
{
char filename[512];
snprintf( filename, sizeof(filename), "data/accounts/%d/followers", a->id );
// Remove the follow
char filename[512];
char key[32];
snprintf( key, sizeof(key), "%d", follower->id );
ffdb_trie_remove( filename, key );
ffdb_trie_remove(
format( filename, sizeof(filename), "data/accounts/%d/followers", a->id ),
format( key, sizeof(key), "%d", follower->id )
);
ffdb_trie_remove(
format( filename, sizeof(filename), "data/accounts/%d/following", follower->id ),
format( key, sizeof(key), "%d", a->id )
);
// Update follower count
a->followers_count = ffdb_trie_count(filename);

@ -1,48 +1,49 @@
{
"acct": "%s{a->handle}%( if( a->server ) { )@%s{a->server}%( } )",
"avatar": "%s{safe(a->avatar.url,"")}",
"avatar_static": "%s{safe(a->avatar.static_url,"")}",
"bot": %s{ b(a->bot) },
"created_at": "2022-03-18T19:33:06.000Z",
"display_name": "%s{ safe( a->display_name, a->handle )}",
"emojis": [],
"fields": [],
"followers_count": %d{ a->followers_count },
"following_count": %d{ a->following_count },
"fqn": "%s{a->handle}%( if( a->server ) { )@%s{a->server}%( } )",
"header": "https://pl.polaris-1.work/images/banner.png",
"header_static": "https://pl.polaris-1.work/images/banner.png",
"id": "%d{ a->id }",
"locked": %s{b(a->locked)},
"note": "%s{a->note}",
"pleroma": {
"accepts_chat_messages": true,
"also_known_as": [],
"ap_id": "%s{a->account_url}",
"background_image": null,
"favicon": null,
"hide_favorites": true,
"hide_followers": false,
"hide_followers_count": false,
"hide_follows": false,
"hide_follows_count": false,
"is_admin": false,
"is_confirmed": true,
"is_moderator": false,
"relationship": {},
"skip_thread_containment": false,
"tags": []
"acct":"%s{a->handle}%( if( a->server ) { )@%s{a->server}%( } )",
"avatar":"%s{safe(a->avatar.url,"")}",
"avatar_static":"%s{safe(a->avatar.static_url,"")}",
"bot":%s{ b(a->bot) },
"created_at":"2022-03-18T19:33:06.000Z",
"display_name":"%s{ safe( a->display_name, a->handle )}",
"emojis":[],
"fields":[],
"followers_count":%d{ a->followers_count },
"following_count":%d{ a->following_count },
"fqn":"%s{a->handle}%( if( a->server ) { )@%s{a->server}%( } )",
"header":"https://pl.polaris-1.work/images/banner.png",
"header_static":"https://pl.polaris-1.work/images/banner.png",
"id":"%d{ a->id }",
"locked":%s{b(a->locked)},
"note":%( json_write_string( f, a->note ? a->note : "" ); ),
"pleroma":{
"accepts_chat_messages":true,
"also_known_as":[],
"ap_id":"%s{a->account_url}",
"background_image":null,
"deactivated":false,
"favicon":null,
"hide_favorites":true,
"hide_followers":false,
"hide_followers_count":false,
"hide_follows":false,
"hide_follows_count":false,
"is_admin":%s{ a->id == owner_account_id ? "true" : "false" },
"is_confirmed":true,
"is_moderator":false,
"relationship":{},
"skip_thread_containment":false,
"tags":[]
},
"source": {
"fields": [],
"note": "",
"pleroma": {
"actor_type": "Person",
"discoverable": true
"source":{
"fields":[],
"note":"",
"pleroma":{
"actor_type":"Person",
"discoverable":false
},
"sensitive": false
"sensitive":false
},
"statuses_count": 0,
"url": "%s{a->account_url}",
"username": "%s{a->handle}"
"statuses_count":0,
"url":"%s{a->account_url}",
"username":"%s{a->handle}"
}

@ -0,0 +1,14 @@
{
"id": "%d{ a->id }",
"following": false
"showing_reblogs": false,
"notifying": false,
"followed_by": false,
"blocking": false,
"blocked_by": false,
"muting": false,
"muting_notifications": false,
"requested": false,
"domain_blocking": false,
"endorsed": false
}

@ -1,4 +1,5 @@
{
"access_token": "%s{app->access_token}",
"token_type": "Bearer"
"token_type": "Bearer",
"scope": "read write follow"
}

Loading…
Cancel
Save