Implement emoji processing and display for federated posts, fix /api/v1/account/update_credentials

master
teknomunk 1 year ago
parent eb432b6ea0
commit c66f92924d

@ -133,7 +133,7 @@ static bool handle_relationships( struct http_request* req )
// Route: /api/v1/accounts/
bool route_api_account( struct http_request* req )
{
if( http_request_route( req, "verify_credentials" ) ) {
if( http_request_route_term( req, "verify_credentials" ) || http_request_route_term( req, "update_credentials" ) ) {
struct account* owner = account_from_id(0);
bool res = handle_mastodon_api_show_account( req, owner );
account_free(owner);

@ -0,0 +1,73 @@
#include "emoji.h"
#include "http/server/request.h"
#include "format.h"
#include "model/server.h"
#include "model/emoji.h"
#include "view/api/Emoji.h"
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <dirent.h>
// Route: /api/v1/custom_emojis
bool route_custom_emojis( struct http_request* req )
{
http_request_send_headers( req, 200, "application/json", true );
FILE* f = http_request_get_response_body( req );
fprintf( f, "[" );
DIR* d = opendir( "data/emoji" );
struct dirent* ent;
bool first = true;
while( ent = readdir(d) ) {
if( ent->d_name[0] != '.' && !index(ent->d_name,' ') ) {
if( !first ) {
fprintf( f, "," );
} else {
fprintf( f, "\n" );
}
char* filename = strdup(ent->d_name);
char* remain = NULL;
char* shortcode = strtok_r(filename, ".", &remain );
struct emoji e;
e.shortcode = shortcode;
e.url = aformat( "https://%s/emoji/%s", g_server_name, shortcode );
api_Emoji_write( &e, f, 1 );
free(e.url);
free(filename);
first = false;
}
}
closedir(d);
fprintf( f, "\n]" );
return true;
}
const char* mime_type_for_filename( const char* filename );
bool route_emoji_asset( struct http_request* req )
{
char* id_str = http_request_route_get_dir_or_file(req);
char* filename = filename_for_shortcode( id_str );
free(id_str);
if( !filename ) { return false; }
const char* mime_type = mime_type_for_filename( filename );
char fs_path[512];
snprintf( fs_path, 512, "data/emoji/%s", filename );
free(filename);
return http_request_send_file( req, fs_path, mime_type );
}

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

@ -11,6 +11,7 @@
#include "controller/owner.h"
#include "controller/inbox.h"
#include "controller/activity_pub.h"
#include "controller/api/emoji.h"
#include <string.h>
#include <stdlib.h>
@ -163,6 +164,8 @@ bool route_request( struct http_request* req )
return route_ap_note( req );
} else if( http_request_route( req, "/media/" ) ) {
return route_media( req );
} else if( http_request_route( req, "/emoji/" ) ) {
return route_emoji_asset( req );
} else if( http_request_route( req, "/.well-known" ) ) {
if( http_request_route( req, "/webfinger?" ) ) {
return route_wellknown_webfinger( req );

@ -21,6 +21,7 @@
#include "controller/api/status.h"
#include "controller/api/notice.h"
#include "controller/api/accounts.h"
#include "controller/api/emoji.h"
#include "view/api/Attachement.h"
@ -246,6 +247,8 @@ bool route_mastodon_api( struct http_request* req )
FILE* f = http_request_get_response_body( req );
#include "src/view/api/instance_data.json.inc"
return true;
} else if( http_request_route_term( req, "custom_emojis" ) ) {
return route_custom_emojis(req);
}
if( !check_bearer_token(req) ) { return false; }

@ -1 +1 @@
Subproject commit c2f69469881dbc5b4a4cd976f5d58d7700f83ccb
Subproject commit 49765d54359b3537465ea432ba92fa955cf4a385

@ -404,6 +404,7 @@ struct ap_activity* ap_activity_create_note( struct status* s )
struct ap_activity_tag* tag;
tag = malloc(sizeof(*tag));
memset(tag,0,sizeof(*tag));
tag->type = aptag_mention;
tag->href = strdup(mentioned->account_url);
tag->name = aformat( "%s@%s", mentioned->handle, mentioned->server );

@ -45,6 +45,7 @@ struct json_enum ap_object_type_enum[] = {
{ "Person", apat_person },
{ "Service", apot_service },
{ "Document", apot_document },
{ "Image", apot_image },
{ NULL, 0 },
};

@ -8,14 +8,38 @@
static struct json_enum ap_activity_tag_enum[] = {
{ "Mention", aptag_mention },
{ "Emoji", aptag_emoji },
{ NULL, 0 },
};
extern struct json_enum ap_object_type_enum[];
#define OBJ_TYPE struct ap_activity_tag
static struct json_object_field tag_icon_layout[] = {
{
.key = "url",
.offset = offsetof( OBJ_TYPE, icon.url ),
.type = &json_field_string,
},
{
.key = "type",
.offset = offsetof( OBJ_TYPE, icon.type ),
.type = &json_field_enum,
.enum_list = ap_object_type_enum,
},
JSON_FIELD_END,
};
static struct json_object_field ap_activity_tag_layout[] = {
JSON_FIELD_ENUM( type, ap_activity_tag_enum, true ),
JSON_FIELD_STRING( href, false ),
JSON_FIELD_STRING( name, false ),
JSON_FIELD_STRING( id, false ),
JSON_FIELD_DATETIME( updated, false ),
{
.key = "icon",
.offset = 0,
.type = &json_field_object_composite,
.composite_layout = tag_icon_layout,
},
JSON_FIELD_ENUM( type, ap_activity_tag_enum, true ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
@ -26,6 +50,7 @@ void ap_activity_tag_free( struct ap_activity_tag* tag )
free(tag->href);
free(tag->name);
free(tag->icon.url);
memset(tag,0,sizeof(*tag));
free(tag);
}

@ -1,14 +1,24 @@
#pragma once
#include <time.h>
enum {
aptag_mention = 1,
aptag_emoji = 2,
};
struct ap_activity_tag
{
int type;
struct {
char* url;
int type;
} icon;
char* href;
char* id;
time_t updated;
char* name;
};

@ -0,0 +1,42 @@
#include "emoji.h"
#include "json/layout.h"
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#define OBJ_TYPE struct emoji
struct json_object_field emoji_layout[] = {
JSON_FIELD_STRING( url, true ),
JSON_FIELD_STRING( shortcode, true ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
JSON_FIELD_TYPE_OBJECT_LAYOUT_WITH_DEFAULTS( emoji );
char* filename_for_shortcode( const char* shortcode )
{
int len = strlen(shortcode);
DIR* d = opendir( "data/emoji" );
struct dirent* ent;
char* filename = NULL;
while( ent = readdir(d) ) {
if( 0 == strncmp( shortcode, ent->d_name, len ) && ent->d_name[len] == '.' ) {
closedir(d);
return strdup(ent->d_name);
}
}
closedir(d);
return NULL;
}
void emoji_free( struct emoji* e )
{
if( !e ) { return; }
free(e->shortcode);
free(e->url);
free(e);
}

@ -0,0 +1,17 @@
#pragma once
#include "json/layout.h"
char* filename_for_shortcode( const char* shortcode );
struct emoji
{
char* shortcode;
char* url;
};
void emoji_free( struct emoji* e );
extern struct json_object_field emoji_layout[];
extern struct json_field_type emoji_type;

@ -7,6 +7,7 @@
#include "model/ap/activity.h"
#include "model/notification.h"
#include "model/timeline.h"
#include "model/emoji.h"
// Submodules
#include "json/json.h"
@ -48,6 +49,7 @@ static struct json_object_field status_layout[] = {
JSON_FIELD_ARRAY_OF_INTS( replies, false ),
JSON_FIELD_ARRAY_OF_INTS( reposts, false ),
JSON_FIELD_ARRAY_OF_INTS( mentions, false ),
JSON_FIELD_ARRAY_OF_TYPE( emoji, false, emoji_type ),
JSON_FIELD_BOOL( sensitive, true ),
@ -166,9 +168,35 @@ struct status* status_from_uri( const char* uri )
return status_from_id(id);
}
void status_add_reply( struct status* s, struct status* child )
{
bool is_already_parent_reply = false;
for( int i = 0; i < s->replies.count; ++i ) {
if( s->replies.items[i] == child->id ) {
is_already_parent_reply = true;
}
}
if( !is_already_parent_reply ) {
int id = child->id;
array_append( &s->replies, sizeof(id), &id );
status_save(s);
}
}
void status_add_mention( struct status* s, int id )
{
for( int i = 0; i < s->mentions.count; ++i ) {
if( s->mentions.items[i] == id ) {
// Already mentioned, don't add a second time
return;
}
}
array_append( &s->mentions, sizeof(id), &id );
}
bool status_sync_from_activity_pub( struct status* s, struct ap_activity* act )
{
printf( "Syncing status from activity %s\n", act->id );
ap_activity_write_to_FILE( act, stdout );
bool result = false;
struct account* a = account_from_uri_or_fetch(act->actor);
if( !a ) {
@ -185,21 +213,15 @@ bool status_sync_from_activity_pub( struct status* s, struct ap_activity* act )
if( parent ) {
s->in_reply_to = parent->id;
s->root_status_id = parent->root_status_id;
bool is_already_parent_reply = false;
for( int i = 0; i < parent->replies.count; ++i ) {
if( parent->replies.items[i] == s->id ) {
is_already_parent_reply = true;
}
}
if( !is_already_parent_reply ) {
int id = s->id;
array_append( &parent->replies, sizeof(id), &id );
status_save(parent);
}
status_add_reply( parent, s );
status_free(parent);
}
}
for( int i = 0; i < s->emoji.count; ++i ) {
emoji_free( s->emoji.items[i] );
}
s->emoji.count = 0;
s->mentions.count = 0;
for( int i = 0; i < act->tags.count; ++i ) {
struct ap_activity_tag* tag = act->tags.items[i];
@ -210,6 +232,16 @@ bool status_sync_from_activity_pub( struct status* s, struct ap_activity* act )
array_append( &s->mentions, sizeof(item), &item );
account_free(a);
}
} else if( tag->type == aptag_emoji ) {
struct emoji* e;
e = malloc(sizeof(*e));
memset(e,0,sizeof(*e));
e->url = safe_strdup( tag->icon.url );
e->shortcode = safe_strdup( &tag->name[1] );
e->shortcode[ strlen(e->shortcode) - 1 ] = '\0';
array_append( &s->emoji, sizeof(e), &e );
}
}
@ -525,13 +557,15 @@ void status_make_reply_to( struct status* s, int 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_add_reply( in_reply_to, s );
// TODO: full mention handling
int id = in_reply_to->account_id;
array_append( &s->mentions, sizeof(id), &id );
// Mention the account that made the post being replied to
status_add_mention( s, in_reply_to->account_id );
// Mention everytone else in that post
for( int i = 0; i < in_reply_to->mentions.count; ++i ) {
status_add_mention( s, in_reply_to->mentions.items[i] );
}
status_save(in_reply_to);
}

@ -5,6 +5,7 @@
#include <time.h>
struct account;
struct emoji;
struct status
{
@ -55,6 +56,11 @@ struct status
int* items;
int count;
} mentions;
struct {
struct emoji** items;
int count;
} emoji;
};
struct status* status_from_id( unsigned int id );
@ -79,6 +85,7 @@ void status_free( struct status* s );
void status_flag_for_async_fetch( struct status* s );
void status_add_reply( struct status* s, struct status* child );
void status_make_reply_to( struct status* s, int in_reply_to );
void status_get_context( struct status* s, void* ancestors, void* replies );

@ -0,0 +1,46 @@
#include "Emoji.h"
#include "json/layout.h"
#include "model/emoji.h"
#include <string.h>
#include <stdlib.h>
#define OBJ_TYPE struct emoji
struct json_object_field api_Emoji_layout[] = {
JSON_FIELD_FIXED_STRING( category, "", false ),
JSON_FIELD_STRING( shortcode, true ),
JSON_FIELD_STRING( url, true ),
{
.key = "static_url",
.offset = offsetof( OBJ_TYPE, url ),
.type = &json_field_string,
},
JSON_FIELD_FIXED_BOOL( visible_in_picker, true ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
void api_Emoji_write( struct emoji* e, FILE* f, int indent )
{
struct json_writer jw = {
.f = f,
.indentation = "\t",
.indent = indent,
};
if( !e ) {
fprintf( f, "null" );
return;
}
json_write_pretty_object_layout( &jw, api_Emoji_layout, e );
}
struct json_field_type api_Emoji_type = {
.reader = json_field_object_type_reader,
.writer = json_field_object_type_writer,
.size = sizeof(struct emoji*),
.layout = api_Emoji_layout,
};

@ -0,0 +1,7 @@
#pragma once
#include <stdio.h>
struct emoji;
void api_Emoji_write( struct emoji* e, FILE* f, int indent );

@ -13,6 +13,9 @@
#include <string.h>
extern struct json_object_field api_Status_layout[];
extern struct json_field_type api_Emoji_type;
bool int_to_string_callback( void* field_data, bool is_read, char** res );
bool post_reposted( void* field_data, bool is_read, bool* val )
{
@ -23,7 +26,6 @@ bool post_reposted( void* field_data, bool is_read, bool* val )
}
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 )
{
@ -339,7 +341,13 @@ struct json_object_field api_Status_layout[] = {
.type = &json_field_date_time
},
JSON_FIELD_FIXED_NULL( edited_at ),
JSON_FIELD_EMPTY_ARRAY( emoji, true ),
{
.key = "emojis",
.offset = offsetof( OBJ_TYPE, emoji ),
.required = true,
.type = &json_field_array_of,
.array_item_type = &api_Emoji_type,
},
{
.key = "favourited",
.required = true,

Loading…
Cancel
Save