Convert all api responses from text templates to JSON layouts, fix memory leaks, discard more unsupported Activities, server owner banner

master
teknomunk 1 year ago
parent aec2cd8ad8
commit cc9e32b7dc

@ -8,7 +8,10 @@
#include "collections/array.h"
// Model
#include "status.h"
#include "model/status.h"
// View
#include "view/api/Notification.h"
// Stdlib
#include <stdio.h>
@ -26,7 +29,7 @@ void show_notifications( struct http_request* req, struct notification** ns, int
if( i > 0 ) {
fprintf( f, "," );
}
notification_write_as_json(ns[i],f);
api_Notification_write( ns[i], f, 1 );
}
fprintf( f, "]" );
}

@ -12,6 +12,7 @@
#include "model/ap/outbox_envelope.h"
#include "view/api/Account.h"
#include "view/api/Status.h"
#include <stdio.h>
#include <stdlib.h>
@ -20,41 +21,12 @@
void write_json_escaped( FILE* f, const char* str );
void api_status_write_as_json( struct status* s, FILE* f )
{
if( s->stub ) {
struct status* system_status = status_new_system_stub( s );
s = system_status;
}
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 );
}
struct status* repost = NULL;
if( s->repost_id ) {
repost = status_from_id( s->repost_id );
}
#include "src/view/api/status.json.inc"
cleanup:
account_free(account);
account_free(in_reply_to_account);
status_free(in_reply_to);
}
void show_status( struct http_request* req, struct status* s )
{
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
api_status_write_as_json(s,f);
api_Status_write( s, f, 0 );
}
// Route: /api/v1/statuses/%d{s->id}/context
@ -72,7 +44,7 @@ void show_status_context( struct http_request* req, struct status* s )
fprintf( f, "{\"ancestors\":[" );
for( int i = 0; i < ancestors.count; ++i ) {
struct status* s2 = ancestors.items[i];
api_status_write_as_json(s2,f);
api_Status_write( s2, f, 1 );
status_free(s2);
if( i != ancestors.count - 1 ) {
@ -84,7 +56,7 @@ void show_status_context( struct http_request* req, struct status* s )
fprintf( f, "],\"descendants\":[" );
for( int i = 0; i < replies.count; ++i ) {
struct status* s2 = replies.items[i];
api_status_write_as_json(s2,f);
api_Status_write( s2, f, 1 );
status_free(s2);
if( i != replies.count - 1 ) {
@ -105,7 +77,7 @@ void show_statuses( struct http_request* req, struct status** ss, int count )
if( i > 0 ) {
fprintf( f, "," );
}
api_status_write_as_json(ss[i],f);
api_Status_write( ss[i], f, 1 );
}
fprintf( f, "]" );
}
@ -184,7 +156,7 @@ bool handle_post( 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_status_write_as_json(s,f);
api_Status_write( s, f, 0 );
result = true;
cleanup:
free(params.status);

@ -14,5 +14,3 @@ void show_statuses( struct http_request* req, struct status** ss, int count );
bool handle_post( struct http_request* req, struct account* a );
bool handle_repost( struct http_request* req, struct status* s );
void api_status_write_as_json( struct status* s, FILE* f );

@ -1,10 +1,15 @@
#include "cli.h"
#include "format.h"
#include "model/account.h"
#include "model/status.h"
#include "model/notification.h"
#include "model/ap/activity.h"
#include "controller/inbox.h"
#include "format.h"
#include "view/api/Status.h"
#include "view/api/Notification.h"
#include <stdio.h>
#include <stdlib.h>
@ -21,7 +26,7 @@ struct cli_request
static int cli_route_command( struct cli_request* req, const char* cmd, int count, const char* usage_args )
{
if( 0 != strcmp(req->argv[0], cmd ) ) {
printf( "No match for %s==%s\n", req->argv[0], cmd );
//printf( "No match for %s==%s\n", req->argv[0], cmd );
return 0;
}
req->argv += 1;
@ -142,6 +147,14 @@ static bool handle_command_reindex( struct cli_request* req )
}
static bool handle_command_test( struct cli_request* req )
{
for( int i = 0; i < 10; ++i ) {
struct notification* n = notification_from_id( 320 - i );
api_Notification_write( n, stdout, 0 );
notification_free(n);
printf( "\n\n----------------\n\n" );
}
/*
int res = cli_route_command( req, "test", 0, "" );
if( res != 1 ) { return !!res; }
@ -170,13 +183,13 @@ static bool handle_command_test( struct cli_request* req )
ap_activity_free(note2);
status_free(s);
account_free(owner_account);
*/
return true;
}
bool handle_command_account_sync( struct cli_request* req, int account_id )
{
printf( "sync cmd = %s\n", req->argv[0] );
int res = cli_route_command( req, "sync", 0, "" );
if( res != 1 ) { return !!res; }
@ -198,7 +211,6 @@ bool handle_command_update( struct cli_request* req )
bool handle_command_account( struct cli_request* req )
{
printf( "cmd = %s\n", req->argv[0] );
int res = cli_route_command( req, "account", 1, "account (id)" );
if( res != 1 ) { return !!res; }

@ -91,8 +91,10 @@ static bool route_undo_Announce( struct ap_activity* act )
s = status_from_uri( act->object.ptr->id );
if( !s ) { return true; } // Status not local, discard
printf( "TODO: delete post\n" );
status_free(s);
return false;
return true;
}
static bool route_undo_Like( struct ap_activity* act )
{
@ -239,6 +241,9 @@ static bool route_update( struct ap_activity* act )
if( !s ) { return true; } // Status not available locally, discard
} else if( act->object.ptr->type == apat_person ) {
return route_update_Person(act);
} else if( act->object.ptr->type == apat_question ) {
// TODO: update Poll
return true;
} else {
return false;
}
@ -447,6 +452,7 @@ bool route_activity( struct ap_activity* act )
case apat_update: return route_update(act);
case apat_move: return route_move(act);
case apat_block: return route_block(act);
case apat_remove: return true;
default:
printf( "Unhandled activity type: %d\n", act->type );
}

@ -24,7 +24,7 @@ static bool process_envelope( struct outbox_envelope* env )
{
bool result = false;
char* postdata = NULL;
struct crypto_keys* keys = crypto_keys_new();
struct crypto_keys* keys = NULL;
FILE* f = NULL;
struct ap_activity* act = NULL;
@ -39,6 +39,7 @@ static bool process_envelope( struct outbox_envelope* env )
struct account* to_account = account_from_id( env->account_id );
// Load crypto keys
keys = crypto_keys_new();
if( !crypto_keys_load_private( keys, "data/owner/private.pem" ) ) {
printf( "Failed to load private key\n" );
return false;

@ -88,6 +88,16 @@ static bool handle_avatar( struct http_request* req )
return false;
}
static bool handle_banner( struct http_request* req )
{
FILE* f = fopen("data/owner/banner.png","r");
if( f ) {
fclose(f);
return http_request_send_file( req, "data/owner/banner.png", "image/png" );
}
return false;
}
bool route_owner( struct http_request* req )
{
@ -103,6 +113,8 @@ bool route_owner( struct http_request* req )
return handle_featured(req);
} else if( http_request_route_term( req, "/avatar.blob" ) ) {
return handle_avatar(req);
} else if( http_request_route_term( req, "/banner.blob" ) ) {
return handle_banner(req);
} else if( http_request_route_term( req, "" ) ) {
return show_owner_profile_page(req);
}

@ -66,6 +66,7 @@ static struct json_object_field account_layout[] = {
.required = false,
.type = &json_field_string
},
JSON_FIELD_STRING( banner, false ),
JSON_FIELD_ARRAY_OF_STRINGS( aliases, false ),
JSON_FIELD_ENUM( account_type, account_types_enum, true ),
JSON_FIELD_STRING( inbox, false ),
@ -142,6 +143,10 @@ struct account* account_from_id( int id )
return NULL;
}
if( !a->banner ) {
a->banner = aformat( "https://%s/server/default-banner.blob", g_server_name );
}
return a;
}

@ -41,6 +41,7 @@ struct account
char* url;
char* static_url;
} avatar;
char* banner;
struct {
char** items;

@ -7,8 +7,7 @@
#include "model/account.h"
#include "model/status.h"
#include "controller/api/status.h"
#include "view/api/Status.h"
#include "view/api/Account.h"
#include <stdlib.h>
@ -32,6 +31,7 @@ static struct json_object_field notification_layout[] = {
JSON_FIELD_INTEGER( account_id, true ),
JSON_FIELD_INTEGER( status_id, false ),
JSON_FIELD_INTEGER( ref_account_id, false ),
JSON_FIELD_DATETIME( created_at, false ),
JSON_FIELD_STRING( react, false ),
JSON_FIELD_ENUM( type, notification_type_enum, true ),
JSON_FIELD_END,
@ -60,6 +60,7 @@ struct notification* notification_new()
struct notification* note = malloc(sizeof(struct notification));
memset(note,0,sizeof(*note));
note->id = id;
note->created_at = time(NULL);
fs_list_set( "data/notices/HEAD", id );
return note;
@ -80,26 +81,3 @@ void notification_free( struct notification* note )
free(note);
}
void notification_write_as_json( struct notification* n, FILE* f )
{
// TODO: move to controller/view
struct account* a = account_from_id( n->account_id );
struct account* owner = account_from_id( 0 );
struct status* s = status_from_id( n->status_id );
switch( n->type ) {
case nt_unfollow:
s = status_new_system_unfollow( n->ref_account_id );
break;
case nt_block:
s = status_new_system_block( n->ref_account_id );
break;
}
#include "src/model/notification.json.inc"
status_free(s);
account_free(owner);
account_free(a);
}

@ -37,5 +37,5 @@ void notification_save( struct notification* note );
void notification_free( struct notification* note );
// TODO: move to controler/view
void notification_write_as_json( struct notification* n, FILE* f );
//void notification_write_as_json( struct notification* n, FILE* f );

@ -1,26 +0,0 @@
{
"account": %( api_Account_write( a, f, 1 ); ),
"created_at": "2022-12-12T15:31:54.000Z",
"id": "%d{n->id}",
"pleroma": {
"is_muted": false,
"is_seen": false
},
%( if(s) { )
"status": %( api_status_write_as_json(s,f); ),
%( } )
%( if( n->type == nt_react ) { )
"emoji": %( json_write_string( f, n->react ); ),
%( } )
"type": "%(
switch(n->type) {
case nt_favorite: fprintf(f,"favourite"); break;
case nt_follow: fprintf(f,"follow"); break;
case nt_like: fprintf(f,"favourite"); break;
case nt_unfollow:
case nt_block:
case nt_mention: fprintf(f,"mention"); break;
case nt_react: fprintf(f,"pleroma:emoji_reaction"); break;
default: fprintf(f,"unknown-%d",n->type); break;
}; )"
}

@ -93,6 +93,8 @@ static FILE* open_status_data_file( unsigned int id, const char* mode )
struct status* status_from_id( unsigned int id )
{
if( id == 0 ) { return NULL; }
struct status* s = NULL;
FILE* f = open_status_data_file( id, "r" );

@ -29,8 +29,8 @@ static struct json_object_field pleroma_layout[] = {
.offset = offsetof( OBJ_TYPE, account_url ),
.type = &json_field_string,
},
// background_image: null,
// favicon: null,
JSON_FIELD_FIXED_NULL( background_image ),
JSON_FIELD_FIXED_NULL( favicon ),
JSON_FIELD_FIXED_BOOL( deactivated, false ),
JSON_FIELD_FIXED_BOOL( hide_favorites, true ),
JSON_FIELD_FIXED_BOOL( hide_followers, false ),
@ -92,7 +92,7 @@ static bool fqn_callback( void* field_data, bool is_read, char** res )
return true;
}
}
static bool int_to_string_callback( void* field_data, bool is_read, char** res )
bool int_to_string_callback( void* field_data, bool is_read, char** res )
{
int* field = field_data;
if( is_read ) {
@ -102,7 +102,7 @@ static bool int_to_string_callback( void* field_data, bool is_read, char** res )
return true;
}
}
static struct json_object_field api_Account_layout[] = {
struct json_object_field api_Account_layout[] = {
{
.key = "acct",
.offset = 0,
@ -139,8 +139,16 @@ static struct json_object_field api_Account_layout[] = {
.type = &json_field_callback_string,
.string_callback = fqn_callback,
},
JSON_FIELD_FIXED_STRING( header, "https://pl.polaris-1.work/images/banner.png", true ),
JSON_FIELD_FIXED_STRING( header_static, "https://pl.polaris-1.work/images/banner.png", true ),
{
.key = "header",
.offset = offsetof( OBJ_TYPE, banner ),
.type = &json_field_string
},
{
.key = "header_static",
.offset = offsetof( OBJ_TYPE, banner ),
.type = &json_field_string
},
{
.key = "id",
.offset = offsetof( OBJ_TYPE, id ),
@ -189,12 +197,11 @@ static bool api_Account_writer( struct json_writer* jw, const char* field_name,
return false;
}
FILE* f = jw->f;
struct account* owner_account = account_from_id( owner_account_id );
//#include "view/api/account.json.inc"
//struct account* owner_account = account_from_id( owner_account_id );
json_write_field_name(jw,field_name);
json_write_pretty_object_layout( jw, api_Account_layout, a );
account_free(owner_account);
//account_free(owner_account);
account_free(a);
return true;

@ -0,0 +1,88 @@
#include "Notification.h"
#include "json/json.h"
#include "json/layout.h"
#include "format.h"
#include "model/notification.h"
#include "view/api/Status.h"
#include "view/api/Account.h"
bool int_to_string_callback( void* field_data, bool is_read, char** res );
static struct json_enum type_enum[] = {
{ "favourite", nt_favorite },
{ "follow", nt_follow },
{ "favourite", nt_like },
{ "mention", nt_unfollow },
{ "mention", nt_block },
{ "mention", nt_mention },
{ "pleroma:emoji_raction", nt_react },
{ NULL, 0 },
};
#define OBJ_TYPE struct notification
static struct json_object_field Notification_Pleroma_layout[] = {
JSON_FIELD_FIXED_BOOL( is_muted, false ),
JSON_FIELD_FIXED_BOOL( is_seen, false ),
JSON_FIELD_END,
};
static struct json_object_field api_Notification_layout[] = {
{
.key = "account",
.offset = offsetof( OBJ_TYPE, account_id ),
.type = &api_Account_type,
},
JSON_FIELD_DATETIME( created_at, true ),
{
.key = "id",
.offset = offsetof( OBJ_TYPE, id ),
.type = &json_field_callback_string,
.string_callback = int_to_string_callback
},
{
.key = "pleroma",
.offset = 0,
.type = &json_field_object_composite,
.composite_layout = Notification_Pleroma_layout,
},
{
.key = "status",
.offset = offsetof( OBJ_TYPE, status_id ),
.required = false,
.type = &api_Status_type,
},
{
.key = "emoji",
.offset = offsetof( OBJ_TYPE, react ),
.required = false,
.type = &json_field_string,
},
JSON_FIELD_ENUM( type, type_enum, true ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
void api_Notification_write( struct notification* note, FILE* f, int indent )
{
struct json_writer jw = {
.f = f,
.indentation = "\t",
.indent = indent,
};
/*
switch( n->type ) {
case nt_unfollow:
s = status_new_system_unfollow( n->ref_account_id );
break;
case nt_block:
s = status_new_system_block( n->ref_account_id );
break;
}
*/
json_write_pretty_object_layout( &jw, api_Notification_layout, note );
}

@ -0,0 +1,8 @@
#pragma once
struct notification;
#include <stdio.h>
void api_Notification_write( struct notification* note, FILE* f, int indent );

@ -0,0 +1,430 @@
#include "Status.h"
#include "json/json.h"
#include "json/layout.h"
#include "format.h"
#include "model/status.h"
#include "model/status/react.h"
#include "model/account.h"
#include "view/api/Account.h"
#include <string.h>
bool int_to_string_callback( void* field_data, bool is_read, char** res );
bool int_non_zero( void* field_data, bool is_read, bool* val )
{
if( !is_read ) {
*val = *(int*)field_data > 0;
return true;
}
}
extern struct json_object_field api_Status_layout[];
static bool write_in_reply_to( struct json_writer* jw, const char* field_name, void* field_data, struct json_object_field* layout_field_data )
{
struct status* s = field_data;
struct status* in_reply_to = status_from_id( s->in_reply_to );
if( in_reply_to ) {
json_write_field_name(jw,"in_reply_to_account_id");
fprintf( jw->f, "\"%d\"", in_reply_to->account_id );
jw->need_comma = true;
json_write_indention(jw);
json_write_field_name(jw,"in_reply_to_id");
fprintf( jw->f, "%d", in_reply_to->id );
} else {
json_write_field_name(jw,"in_reply_to_account_id");
fprintf( jw->f, "null" );
jw->need_comma = true;
json_write_indention(jw);
json_write_field_name(jw,"in_reply_to_id");
fprintf( jw->f, "null" );
}
return true;
}
static struct json_field_type in_reply_to = {
.writer = write_in_reply_to,
};
static bool write_status_reference( struct json_writer* jw, const char* field_name, void* field_data, struct json_object_field* layout_field_data )
{
json_write_field_name(jw,field_name);
int id = *(int*)field_data;
struct status* s = status_from_id(id);
if( !s ) {
fprintf( jw->f, "null" );
} else {
json_write_pretty_object_layout( jw, api_Status_layout, s );
status_free(s);
}
return true;
}
static struct json_field_type Status_reference_type = {
.writer = write_status_reference,
};
bool does_react_include_me( void* field_data, bool is_read, bool* val )
{
struct status_react* sr = field_data;
for( int i = 0; i < sr->accounts.count; ++i ) {
if( sr->accounts.items[i] == owner_account_id ) {
*val = true;
return true;
}
}
*val = false;
return true;
}
static struct json_object_field MediaAttachment_Pleroma_layout[] = {
JSON_FIELD_FIXED_STRING( mime_type, "image/png", true ),
JSON_FIELD_END
};
static struct json_object_field MediaAttachment_layout[] = {
JSON_FIELD_FIXED_STRING( blurhash, "eRH.A}xs0Kxv00xYR,R+t5R+9Gt6xaNG%%2-;xaM{NGRjD%%Rjs:xaxu", true ),
JSON_FIELD_FIXED_NULL( description ),
{
.key = "id",
.offset = 0,
.type = &json_field_string,
},
{
.key = "pleroma",
.offset = 0,
.type = &json_field_object_composite,
.composite_layout = MediaAttachment_Pleroma_layout,
},
{
.key = "preview_url",
.offset = 0,
.type = &json_field_string,
},
{
.key = "remote_url",
.offset = 0,
.type = &json_field_string,
},
{
.key = "text_url",
.offset = 0,
.type = &json_field_string,
},
JSON_FIELD_FIXED_STRING( type, "image", true ),
{
.key = "url",
.offset = 0,
.type = &json_field_string,
},
JSON_FIELD_END,
};
static struct json_field_type MediaAttachment_type = {
.layout = MediaAttachment_layout,
.reader = json_field_object_composite_reader,
.writer = json_field_object_composite_writer,
.size = sizeof(char*),
};
#define OBJ_TYPE struct account
bool account_acct_callback( void* field_data, bool is_read, char** res )
{
if( is_read ) { return false; }
struct account* a = field_data;
*res = aformat( "%s@%s", a->handle, a->server );
return true;
}
static struct json_object_field api_Mention_layout[] = {
{
.key = "acct",
.offset = 0,
.type = &json_field_callback_string,
.string_callback = account_acct_callback,
},
{
.key = "id",
.offset = offsetof( OBJ_TYPE, id ),
.type = &json_field_callback_string,
.string_callback = int_to_string_callback,
},
{
.key = "url",
.offset = offsetof( OBJ_TYPE, account_url ),
.type = &json_field_string
},
{
.key = "username",
.offset = offsetof( OBJ_TYPE, handle ),
.type = &json_field_string,
},
JSON_FIELD_END,
};
#undef OBJ_TYPE
static bool Mention_from_id_writer( struct json_writer* jw, const char* field_name, void* field_data, struct json_object_field* layout_field_data )
{
json_write_field_name(jw,field_name);
int id = *(int*)field_data;
struct account* a = account_from_id(id);
if( !a ) {
fprintf( jw->f, "null" );
} else {
json_write_pretty_object_layout( jw, api_Mention_layout, a);
account_free(a);
}
return true;
}
static struct json_field_type Mention_type = {
.writer = Mention_from_id_writer,
.size = sizeof(int),
};
#define OBJ_TYPE struct status_react
static struct json_object_field emoji_react_layout[] = {
{
.key = "count",
.offset = offsetof( OBJ_TYPE, accounts.count ),
.type = &json_field_integer,
},
{
.key = "me",
.offset = 0,
.type = &json_field_bool_callback,
.bool_callback = does_react_include_me,
},
{
.key = "name",
.offset = offsetof( OBJ_TYPE, code ),
.type = &json_field_string,
},
JSON_FIELD_END,
};
#undef OBJ_TYPE
static struct json_field_type EmojiReact_type = {
.layout = emoji_react_layout,
.reader = json_field_object_type_reader,
.writer = json_field_object_type_writer,
.size = sizeof(struct status_react*),
};
bool render_source_callback( void* field_data, bool is_read, char** res )
{
struct status* s = field_data;
if( !s->content ) {
s->content = status_render_source(s);
}
*res = strdup(s->content);
return true;
}
#define OBJ_TYPE struct status
static struct json_object_field content_layout[] = {
{
.key = "text/plain",
.offset = 0,
.type = &json_field_callback_string,
.string_callback = render_source_callback,
},
JSON_FIELD_END,
};
static struct json_object_field pleroma_layout[] = {
{
.key = "content",
.offset = 0,
.type = &json_field_object_composite,
.composite_layout = content_layout,
},
{
.key = "conversation_id",
.offset = offsetof( OBJ_TYPE, root_status_id ),
.type = &json_field_integer,
},
JSON_FIELD_FIXED_NULL( direct_conversation_id ), // Maybe this is breaking things in the home feed?
{
.key = "emoji_reactions",
.offset = offsetof( OBJ_TYPE, reacts ),
.type = &json_field_array_of,
.array_item_type = &EmojiReact_type,
},
JSON_FIELD_FIXED_NULL( expires_at ),
/*%(
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": %s{ s->remote ? "false" : "true" },
"parent_visible": false,%(
} )
*/
JSON_FIELD_FIXED_NULL( pinned_at ),
/*
"spoiler_text": {
"text/plain": ""
},
*/
JSON_FIELD_FIXED_BOOL( thread_muted, false ),
JSON_FIELD_END,
};
struct json_object_field api_Status_layout[] = {
{
.key = "account",
.required = true,
.offset = offsetof( OBJ_TYPE, account_id ),
.type = &api_Account_type,
},
JSON_FIELD_FIXED_NULL( application ),
JSON_FIELD_FIXED_BOOL( bookmarked, false ),
JSON_FIELD_FIXED_NULL( card ),
{
.key = "content",
.offset = 0,
.type = &json_field_callback_string,
.string_callback = render_source_callback,
},
{
.key = "crated_at",
.required = true,
.offset = offsetof( OBJ_TYPE, published ),
.type = &json_field_date_time
},
JSON_FIELD_FIXED_NULL( edited_at ),
JSON_FIELD_EMPTY_ARRAY( emoji, true ),
{
.key = "favourited",
.required = true,
.offset = 0,
.type = &json_field_fixed_bool,
.fixed_bool = false,
},
{
.key = "favourited_count",
.required = true,
.offset = offsetof( OBJ_TYPE, likes.count ),
.type = &json_field_integer,
},
{
.key = "id",
.required = true,
.offset = offsetof( OBJ_TYPE, id ),
.type = &json_field_callback_string,
.string_callback = int_to_string_callback,
},
{
.key = "@in_reply_to_inline",
.required = true,
.offset = 0,
.type = &in_reply_to,
},
JSON_FIELD_FIXED_NULL( language ),
{
.key = "media_attachments",
.offset = offsetof( OBJ_TYPE, media ),
.type = &json_field_array_of,
.array_item_type = &MediaAttachment_type,
},
{
.key = "mentions",
.offset = offsetof( OBJ_TYPE, mentions ),
.type = &json_field_array_of,
.array_item_type = &Mention_type,
},
JSON_FIELD_FIXED_BOOL( muted, false ),
JSON_FIELD_FIXED_BOOL( pinned, false ),
{
.key = "pleroma",
.offset = 0,
.type = &json_field_object_composite,
.composite_layout = pleroma_layout,
},
JSON_FIELD_FIXED_NULL( poll ),
{
.key = "reblog",
.offset = offsetof(OBJ_TYPE, repost_id),
.type = &Status_reference_type,
},
{
.key = "reblogged",
.offset = offsetof( OBJ_TYPE, reposts.count ),
.type = &json_field_bool_callback,
.bool_callback = int_non_zero,
},
{
.key = "reblogs_count",
.offset = offsetof( OBJ_TYPE, reposts.count ),
.type = &json_field_integer,
},
{
.key = "replies_count",
.offset = offsetof( OBJ_TYPE, replies.count ),
.type = &json_field_integer,
},
JSON_FIELD_FIXED_BOOL( sensitive, false ),
JSON_FIELD_FIXED_STRING( spoiler_text, "", true ),
JSON_FIELD_EMPTY_ARRAY( tags, true ),
JSON_FIELD_FIXED_NULL( text ),
{
.key = "uri",
.offset = offsetof( OBJ_TYPE, url ),
.type = &json_field_string,
},
JSON_FIELD_STRING( url, true ),
JSON_FIELD_FIXED_STRING( visibility, "public", true ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
static bool api_Status_writer( struct json_writer* jw, const char* field_name, void* field_data, struct json_object_field* layout_field_data )
{
int status_id = *(int*)field_data;
struct status* s = status_from_id(status_id);
struct status* system_status = NULL;
if( !s ) {
return false;
}
json_write_field_name(jw,field_name);
if( s->stub ) {
system_status = status_new_system_stub( s );
json_write_pretty_object_layout( jw, api_Status_layout, system_status );
status_free(system_status);
} else {
json_write_pretty_object_layout( jw, api_Status_layout, s );
}
status_free(s);
return true;
}
struct json_field_type api_Status_type = {
.writer = api_Status_writer,
.size = sizeof(int),
.layout = api_Status_layout,
};
void api_Status_write( struct status* s, FILE* f, int indent )
{
struct json_writer jw = {
.f = f,
.indentation = "\t",
.indent = indent,
};
struct status* system_status = NULL;
if( s->stub ) {
system_status = status_new_system_stub( s );
s = system_status;
}
json_write_pretty_object_layout( &jw, api_Status_layout, s );
status_free(system_status);
}

@ -0,0 +1,10 @@
#pragma once
#include <stdio.h>
struct json_field_type;
struct status;
extern struct json_field_type api_Status_type;
void api_Status_write( struct status* s, FILE* f, int indent );

@ -1,102 +0,0 @@
{
"account": %( api_Account_write(account,f,1); ),
"application": null,
"bookmarked": false,
"card": null,
"content": %( json_write_string(f,status_render_source(s)); ),
"created_at": %( json_write_date_time_string( s->published, f ); ),
"edited_at": null,
"emojis": [],
"favourited": false,
"favourites_count": %d{ s->likes.count },
"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,%(
} )
"language": null,
"media_attachments": [%( for( int i = 0; i < s->media.count; ++i ) { )
{
"blurhash": "eRH.A}xs0Kxv00xYR,R+t5R+9Gt6xaNG%%2-;xaM{NGRjD%%Rjs:xaxu",
"description": null,
"id": "%d{s->id}.media[%d{i}]",
"pleroma": {
"mime_type": "image/png"
},
"preview_url": "%s{s->media.items[i]}",
"remote_url": "%s{s->media.items[i]}",
"text_url": "%s{s->media.items[i]}",
"type": "image",
"url": "%s{s->media.items[i]}"
}%s{ i < s->media.count - 1 ? "," : "" }
%( } )],
"mentions": [%( for( int i = 0; i < s->mentions.count; ++i ) { )
%( struct account* mention = account_from_id( s->mentions.items[i] ); )
%s{ i != 0 ? "," : "" }{
"acct": "%s{mention->handle}@%s{mention->server}",
"id": "%d{mention->id}",
"url": "%s{mention->account_url}",
"username": "%s{mention->handle}"
}
%( account_free(mention); )
%( } )],
"muted": false,
"pinned": false,
"pleroma": {
"content": {
"text/plain": %( json_write_string(f,s->source ? s->source : ""); )
},
"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];)
%(
bool includes_me( struct status_react* sr ) {
for( int i = 0; i < sr->accounts.count; ++i ) {
if( sr->accounts.items[i] == owner_account_id ) { return true; }
}
return false;
}
)
{
"count": %d{ sr->accounts.count },
"me": %s{ includes_me(sr) ? "true" : "false" },
"name": %( json_write_string( f, sr->code ); )
}%s{ i != s->reacts.count-1 ? "," : "" }
%( } )],
"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": %s{ s->remote ? "false" : "true" },
"parent_visible": false,%(
} )
"pinned_at": null,
"spoiler_text": {
"text/plain": ""
},
"thread_muted": false
},
"poll": null,
%(
if( repost ) { )
"reblog": %( api_status_write_as_json( repost, f ); ),%(
} else { )
"reblog": null,%(
} )
"reblogged": %s{ s->reposts.count > 0 ? "true": "false" },
"reblogs_count": %d{ s->reposts.count },
"replies_count": 0,
"sensitive": false,
"spoiler_text": "",
"tags": [],
"text": null,
"uri": "%s{s->url}",
"url": "%s{s->url}",
"visibility": "public"
}
Loading…
Cancel
Save