rework status handling to support replies, add handling for /api/pleroma/frontend_configurations, rework instance data, rework status response

master
teknomunk 1 year ago
parent 9b0c820e52
commit 30feff8246

@ -1,6 +1,7 @@
#include "status.h"
#include "json/json.h"
#include "json/layout.h"
#include "http/server/request.h"
#include "model/status.h"
@ -11,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
void write_json_escaped( FILE* f, const char* str );
@ -18,10 +20,18 @@ void api_status_write_as_json( struct status* s, FILE* f )
{
struct account* account = account_from_id( s->account_id );
struct status* in_reply_to = NULL;
struct account* in_reply_to_account = NULL;
if( s->in_reply_to ) {
in_reply_to = status_from_id( s->in_reply_to );
in_reply_to_account = account_from_id( in_reply_to->account_id );
}
#include "src/view/api/status.json.inc"
cleanup:
account_free(account);
status_free(in_reply_to);
}
void show_status( struct http_request* req, struct status* s )
@ -54,50 +64,56 @@ void show_statuses( struct http_request* req, struct status** ss, int count )
fprintf( f, "]" );
}
// Route: POST /api/v1/statuses
bool handle_post( struct http_request* req, struct account* a )
{
bool result = false;
struct status* s = NULL;
FILE* data = http_request_get_request_data( req );
struct params_t
{
struct {
int* items;
int count;
} media_ids;
bool sensitive;
char* status;
char* visibility;
char* in_reply_to_id;
char* spoiler_text;
} params;
memset(&params,0,sizeof(params));
#define OBJ_TYPE struct params_t
static struct json_object_field layout[] = {
JSON_FIELD_ARRAY_OF_INTS( media_ids, false ),
JSON_FIELD_BOOL( sensitive, false ),
JSON_FIELD_STRING( status, true ),
JSON_FIELD_STRING( visibility, true ),
JSON_FIELD_STRING( in_reply_to_id, false ),
JSON_FIELD_STRING( spoiler_text, false ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
// {"media_ids":[],"sensitive":false,"status":"Test","visibility":"public","spoiler_text":""}
struct json_pull_parser* jpp = json_pull_parser_new( data );
if( !jpp ) { return false; }
int save;
if( !json_pull_parser_begin_object( jpp, &save ) ) { return false; }
FILE* data = http_request_get_request_data( req );
if( !json_read_object_layout_from_FILE( data, layout, &params ) ) {
return false;
}
s = malloc(sizeof(struct status));
memset(s,0,sizeof(*s));
s->published = time(NULL);
s->source = strdup( params.status );
status_save_new(s);
char* key;
while( key = json_pull_parser_read_object_key(jpp) ) {
if( 0 == strcmp(key,"media_ids") ) {
json_pull_parser_read_value(jpp);
} else if( 0 == strcmp(key,"sensitive") ) {
json_pull_parser_read_bool(jpp,&s->sensitive);
} else if( 0 == strcmp(key,"status") ) {
s->source = json_pull_parser_read_string(jpp);
} else if( 0 == strcmp(key,"visibility") ) {
free(json_pull_parser_read_string(jpp));
} else if( 0 == strcmp(key,"spoiler_text") ) {
free(json_pull_parser_read_string(jpp));
} else {
json_pull_parser_read_value(jpp);
}
if( params.in_reply_to_id ) {
status_make_reply_to( s, atoi( params.in_reply_to_id ) );
}
free(key);
if( !json_pull_parser_end_object(jpp, &save ) ) { goto failed; }
status_save_new(s);
status_save(s);
// Add to owner timeline, public timeline and home timelines
for( int i = 0; i < 3; ++i ) {
@ -111,8 +127,11 @@ bool handle_post( struct http_request* req, struct account* a )
api_status_write_as_json(s,f);
result = true;
cleanup:
free(s->content);
free(s);
free(params.status);
free(params.visibility);
free(params.in_reply_to_id);
free(params.spoiler_text);
status_free(s);
return result;
failed:
result = false;

@ -1,6 +1,7 @@
#include "http/server/request.h"
#include "controller/mastodon_api.h"
#include "controller/pleroma_api.h"
#include "controller/oauth.h"
#include "controller/webfinger.h"
#include "controller/nodeinfo.h"
@ -120,6 +121,8 @@ bool route_request( struct http_request* req )
{
if( http_request_route( req, "/api/v1/" ) ) {
return route_mastodon_api( req );
} else if( http_request_route( req, "/api/pleroma" ) ) {
return route_pleroma_api2( req );
} else if( http_request_route( req, "/oauth" ) ) {
return route_oauth( req );
} else if( http_request_route( req, "/owner" ) ) {

@ -1,9 +1,11 @@
#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 "model/status.h"
#include "model/account.h"

@ -14,42 +14,71 @@
bool http_request_route_id( struct http_request* req, int* id );
// Route: /api/v1/pleroma/statuses/reactions/
static bool handle_reactions( struct http_request* req, struct status* s )
{
int method = -1;
if( http_request_route_method( req, "PUT" ) ) {
method = 1;
} else if( http_request_route_method( req, "DELETE" ) ) {
method = 0;
}
if( method != -1 ) {
char* react_cgi = http_request_route_get_dir_or_file( req );
char* react = cgi_unescape(react_cgi);
free(react_cgi);
struct account* owner = account_from_id( owner_account_id );
if( method ) {
status_add_react( s, react, owner );
} else {
status_remove_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" );
return false;
}
}
// Route: /api/v1/pleroma/statuses
static bool route_status( struct http_request* req, struct status* s )
{
if( http_request_route( req, "reactions/" ) ) {
int method = -1;
if( http_request_route_method( req, "PUT" ) ) {
method = 1;
} else if( http_request_route_method( req, "DELETE" ) ) {
method = 0;
}
return handle_reactions( req, s );
}
if( method != -1 ) {
char* react_cgi = http_request_route_get_dir_or_file( req );
char* react = cgi_unescape(react_cgi);
free(react_cgi);
return false;
}
struct account* owner = account_from_id( owner_account_id );
if( method ) {
status_add_react( s, react, owner );
} else {
status_remove_react( s, react, owner );
}
free(react);
account_free(owner);
// Route: /api/pleroma/frontend_configurations
static bool handle_frontend_configurations( struct http_request* req )
{
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
show_status( req, s );
#include "src/view/api/frontend_config.json.inc"
return true;
} else if( http_request_route_method( req, "GET" ) ) {
printf( "TODO: get who react information\n" );
return false;
}
return true;
}
// Route: /api/pleroma
bool route_pleroma_api2( struct http_request* req )
{
if( http_request_route( req, "/frontend_configurations" ) ) {
return handle_frontend_configurations(req);
}
return false;
}
// Route: /api/v1/pleroma
bool route_pleroma_api( struct http_request* req )
{
if( http_request_route( req, "/statuses" ) ) {

@ -4,4 +4,5 @@
struct http_request;
bool route_pleroma_api( struct http_request* req );
bool route_pleroma_api2( struct http_request* req );

@ -1 +1 @@
Subproject commit b792543d754290e2c3b1b83ffe0123f8dc90fdbd
Subproject commit 9882a630cace9308c6f7c902c722b504894de597

@ -1 +1 @@
Subproject commit 49b21a92cef63f7e35bdc1c78079310d78cd7fac
Subproject commit add9b7ab24f7db20009565596e526eb64764a3ca

@ -18,22 +18,31 @@
#include <stdlib.h>
#include <stddef.h>
#define OBJ_TYPE struct status
static struct json_object_field status_layout[] = {
{ "account_id", offsetof( struct status, account_id ), true, &json_field_integer },
JSON_FIELD_INTEGER( account_id, true ),
{ "url", offsetof( struct status, url ), false, &json_field_string },
//{ "content", offsetof( struct status, source ), false, &json_field_string },
{ "source", offsetof( struct status, source ), false, &json_field_string },
{ "sensitive", offsetof( struct status, sensitive ), true, &json_field_bool },
{ "published", offsetof( struct status, published ), false, &json_field_date_time },
JSON_FIELD_STRING( url, false ),
JSON_FIELD_BOOL( stub, false ),
JSON_FIELD_BOOL( remote, false ),
{ "media", offsetof( struct status, media ), false, &json_field_array_of, &json_field_string },
{ "reacts", offsetof( struct status, reacts ), false, &json_field_array_of, &status_react_type },
JSON_FIELD_STRING( source, false ),
JSON_FIELD_BOOL( sensitive, true ),
JSON_FIELD_DATETIME( published, false ),
{ "likes", offsetof( struct status, likes ), false, &json_field_array_of, &json_field_integer },
{ "reposts", offsetof( struct status, reposts ), false, &json_field_array_of, &json_field_integer },
{ NULL }
JSON_FIELD_INTEGER( in_reply_to, false ),
JSON_FIELD_INTEGER( root_status_id, false ),
JSON_FIELD_ARRAY_OF_STRINGS( media, false ),
JSON_FIELD_ARRAY_OF_TYPE( media, false, status_react_type ),
JSON_FIELD_ARRAY_OF_INTS( likes, false ),
JSON_FIELD_ARRAY_OF_INTS( replies, false ),
JSON_FIELD_ARRAY_OF_INTS( reposts, false ),
JSON_FIELD_END
};
#undef OBJ_TYPE
void* allocate( size_t s )
{
@ -117,6 +126,7 @@ struct status* status_from_activity( struct ap_activity* act )
struct account* a = account_from_uri(act->actor);
s->account_id = a->id;
s->published = act->published;
s->remote = true;
s->source = strdup(act->source);
//s->content = status_render_source(s);
@ -134,6 +144,7 @@ bool status_save_new( struct status* s )
fclose(f);
s->id = head + 1;
s->root_status_id = s->id;
f = fopen("data/statuses/HEAD.tmp","w");
fprintf( f, "%d", s->id );
@ -183,6 +194,20 @@ void status_free( struct status* s )
free(s);
}
void status_make_reply_to( struct status* s, int in_reply_to_id )
{
// Add this status to the other
struct status* in_reply_to = status_from_id( in_reply_to_id );
if( !in_reply_to ) {
s->in_reply_to = 0;
} else {
s->in_reply_to = in_reply_to_id;
s->root_status_id = in_reply_to->root_status_id;
array_append( &in_reply_to->replies, sizeof(s->id), &s->id );
status_save(in_reply_to);
}
}
void status_add_react( struct status* s, const char* react, struct account* a )
{
// generate outbox element

@ -12,11 +12,17 @@ struct status
int account_id;
char* url;
bool stub;
bool remote;
char* content; // Deprecate from file system data and render when loading
char* source;
bool sensitive;
time_t published;
int in_reply_to;
int root_status_id;
struct {
char** items;
int count;
@ -32,6 +38,11 @@ struct status
int count;
} likes;
struct {
int* items;
int count;
} replies;
struct {
int* items;
int count;
@ -50,6 +61,8 @@ bool status_save_new( struct status* s );
void status_save( struct status* s );
void status_free( struct status* s );
void status_make_reply_to( struct status* s, int in_reply_to );
void status_add_react( struct status* s, const char* react, struct account* a );
void status_remove_react( struct status* s, const char* react, struct account* a );
void status_add_like( struct status* s, struct account* a );

@ -0,0 +1,34 @@
{
"masto_fe": {
"showInstanceSpecificPanel": true
},
"pleroma_fe": {
"alwaysShowSubjectInput": false,
"background": "/images/city.jpg",
"collapseMessageWithSubject": false,
"disableChat": false,
"greentext": false,
"hideFilteredStatuses": false,
"hideMutedPosts": false,
"hidePostStats": false,
"hideSitename": false,
"hideUserStats": false,
"loginMethod": "password",
"logo": "https://pl.polaris-1.work/media/828039ffb1e9bf4dc2b0fbf943f8620613adec095fb633ab96304d28c14becbb.png",
"logoMargin": ".1em",
"logoMask": false,
"minimalScopesMode": false,
"noAttachmentLinks": false,
"nsfwCensorImage": "https://pl.polaris-1.work/media/c9486e190ac6969842b5746e6cd72d865edd1c123108f552ef4c5af718cdd4b3.png",
"postContentType": "text/plain",
"redirectRootLogin": "/",
"redirectRootNoLogin": "/",
"scopeCopy": true,
"showFeaturesPanel": true,
"showInstanceSpecificPanel": false,
"sidebarRight": false,
"subjectLineBehavior": "email",
"theme": "pleroma-dark",
"webPushNotifications": false
}
}

@ -14,23 +14,21 @@
"account_activation_required":false,
"features":[
"pleroma_api",
"mastodon_api"
%( /*
"mastodon_api_streaming",
"mastodon_api",
%(/* "mastodon_api_streaming", */)
"polls",
"pleroma_explicit_addressing",
"shareable_emoji_packs",
"multifetch",
%(/*"multifetch",*/)
"pleroma:api/v1/notifications:include_types_filter",
"chat",
"shout",
"relay",
"pleroma_emoji_reactions",
"pleroma_chat_messages"
*/ )
],
"federation":{
"enabled":false,
"enabled":true,
"exclusions":false,
"mrf_hashtag":{
"federated_timeline_removal":[],
@ -38,10 +36,17 @@
"sensitive":["nsfw"]
},
"mrf_object_age":{
"actions":["delist","strip_followers"],
"actions":[
"delist",
"strip_followers"
],
"threshold":604800
},
"mrf_policies":["ObjectAgePolicy","TagPolicy","HashtagPolicy"],
"mrf_policies":[
"ObjectAgePolicy",
"TagPolicy",
"HashtagPolicy"
],
"quarantined_instances":[]
},
"fields_limits":{
@ -58,7 +63,7 @@
]
},
"stats":{"mau":1},
"vapid_public_key":"BIAQBuhbJJ-kSDSy3lfx9amo0iMk4jG4e4OSwJi4-N908lHWspxLy2p7sACjyW8dtJkLUSP00midXnaOIZxOVTo"
"vapid_public_key":"%08X{rand()}%08X{rand()}%08X{rand()}"
},
"poll_limits":{
"max_expiration":31536000,
@ -70,7 +75,7 @@
"shout_limit":5000,
"stats":{
"domain_count":0,
"status_count":5,
"status_count":%d{ fs_list_get("data/statuses/HEAD") },
"user_count":1
},
"thumbnail":"https://pl.polaris-1.work/media/27313e491a834971b8d868e121fcf3e279b109dca0351b87310a2a367b3a6237.png",
@ -80,5 +85,5 @@
"urls":{
%(/*"streaming_api":"wss://pl.polaris-1.work"*/)
},
"version":"0.1alpha"
"version":"2.7.2 (compatible; Pleroma 2.4.52+soapbox%(/*Apogee 0.1.0*/))"
}

@ -1,16 +1,21 @@
{
"account": %( api_account_write_as_json(account,f); ),
"content": %( json_write_string(f,status_render_source(s)); ),
"application": null,
"bookmarked": false,
"card": null,
"created_at": %( json_write_date_time_string( s->published, f ); ),
"content": %( json_write_string(f,status_render_source(s)); ),
"emojis": [],
"favourited": false,
"favourites_count": %d{ s->likes.count },
"id": "%d{ s->id }",
"id": "%d{ s->id }",%(
if( in_reply_to ) { )
"in_reply_to_account_id": "%d{ in_reply_to->account_id }",
"in_reply_to_id": "%d{in_reply_to->id}",%(
} else { )
"in_reply_to_account_id": null,
"in_reply_to_id": null,
"in_reply_to_id": null,%(
} )
"language": null,
"media_attachments": [%( for( int i = 0; i < s->media.count; ++i ) { )
{
@ -39,9 +44,9 @@
"pinned": false,
"pleroma": {
"content": {
"text/plain": ""
"text/plain": %( json_write_string(f,s->source ? s->source : ""); )
},
"conversation_id": 2935699,
"conversation_id": %d{s->root_status_id},
"direct_conversation_id": null,
"emoji_reactions": [%( for( int i = 0; i < s->reacts.count; ++i ) { struct status_react* sr = s->reacts.items[i];)
%(
@ -58,10 +63,16 @@
"name": %( json_write_string( f, sr->code ); )
}%s{ i != s->reacts.count-1 ? "," : "" }
%( } )],
"expires_at": null,
"expires_at": null,%(
if( in_reply_to_account ) { )
"in_reply_to_account_acct": "%s{in_reply_to_account->handle}@%s{in_reply_to_account->server}",
"local": %s{ s->remote ? "false" : "true" },
"parent_visible": true,%(
} else { )
"in_reply_to_account_acct": null,
"local": false,
"parent_visible": false,
"local": %s{ s->remote ? "false" : "true" },
"parent_visible": false,%(
} )
"pinned_at": null,
"spoiler_text": {
"text/plain": ""

Loading…
Cancel
Save