Add route for unfollow, handle Update activities, add self mentions to statuses during Create handling, replace purely linear outbox handling with one that can skip failed deliveries

master
teknomunk 1 year ago
parent 4110a986c7
commit 8cd52febba

@ -93,6 +93,7 @@ static bool handle_search( struct http_request* req )
return true;
}
// Route: /api/v1/accounts/relationships?
static bool handle_relationships( struct http_request* req )
{
http_request_send_headers( req, 200, "application/json", true );
@ -208,6 +209,13 @@ bool route_api_account( struct http_request* req )
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);

@ -4,6 +4,7 @@
#include "http/server/request.h"
#include "json/json.h"
#include "ffdb/fs_list.h"
#include "collections/array.h"
// Model
#include "model/server.h"
@ -116,6 +117,10 @@ static struct status* lookup_object_status( struct ap_activity* act, int* error
s = status_from_uri( act->object.ref );
if( !s ) {
s = status_fetch_from_uri( act->object.ref );
if( !s ) {
*error = error_lookup_failed;
return NULL;
}
status_save_new(s);
}
break;
@ -183,6 +188,50 @@ static bool route_emoji_react( struct ap_activity* act )
return true;
}
static bool route_update_Person( struct ap_activity* act )
{
bool result = false;
struct account* a = account_fetch_from_uri( act->object.ptr->id );
if( !a ) { return true; } // We don't have this user locally, we can safely discard
if( !account_sync_from_activity( a, act->object.ptr ) ) { goto failed; }
account_save(a);
result = true;
cleanup:
account_free(a);
return result;
failed:
result = false;
goto cleanup;
}
static bool route_update( struct ap_activity* act )
{
struct status* s = NULL;
if( act->object.tag == apaot_activity ) {
if( act->object.ptr->type == apat_note ) {
s = status_from_uri( act->object.ptr->id );
if( !s ) { return false; }
} else if( act->object.ptr->type == apat_person ) {
return route_update_Person(act);
} else {
return false;
}
} else if( act->object.tag == apaot_ref ) {
s = status_from_uri( act->object.ref );
if( !s ) { return false; }
}
status_sync_from_uri( s, s->url );
status_save(s);
status_free(s);
return true;
}
static bool route_create( struct ap_activity* act )
{
struct status* s = NULL;
@ -207,6 +256,7 @@ static bool route_create( struct ap_activity* act )
if( tag->type != aptag_mention ) { continue; }
if( 0 == strcmp(tag->href, owner_url) ) {
mentions_me = true;
int id = owner_account_id;
goto check_is_follower;
}
}
@ -243,8 +293,13 @@ check_is_follower:
// TODO: create notification if user notifications are on or this is part of a watched conversation
}
if( mentions_me ) {
// Add me to mention lists
int id = owner_account_id;
array_append( &s->mentions, sizeof(id), &id );
// Create notification
struct notification* note = notification_new();
note->debug = 6;
note->type = nt_mention;
note->status_id = s->id;
note->account_id = actor_account->id;
@ -288,6 +343,7 @@ bool route_activity( struct ap_activity* act )
case apat_announce: return route_announce(act);
case apat_add: return route_add(act);
case apat_emoji_react: return route_emoji_react(act);
case apat_update: return route_update(act);
default:
printf( "Unhandled activity type: %d\n", act->type );
}

@ -29,12 +29,15 @@ static bool process_envelope( int id )
struct ap_activity* act = NULL;
// Get next outbox item
struct outbox_envelope* env = outbox_envelope_load_next();
struct outbox_envelope* env = outbox_envelope_from_id( id );
if( !env ) {
printf( "? No envelope\n" );
goto failed;
}
if( env->sent ) { return false; }
printf( "Processing outbox/%d.json\n", id );
printf( "account_id=%d\n", env->account_id );
printf( "activity_id=%d\n", env->activity_id );
@ -88,6 +91,7 @@ static bool process_envelope( int id )
char user_agent[512];
snprintf( user_agent, sizeof(user_agent), "User-Agent: curl (Apogee/0.1; +https://%s/)", g_server_name );
printf( "Performing post to %s\n", to_account->inbox );
long status_code = -1;
const void* request[] = {
HTTP_REQ_URL, to_account->inbox,
@ -109,6 +113,8 @@ static bool process_envelope( int id )
if( status_code == 200 ) {
printf( "Submitted successfully\n" );
env->sent = true;
outbox_envelope_save(env);
goto discard;
}
@ -127,7 +133,6 @@ cleanup:
return result;
failed:
sleep(10);
result = false;
goto cleanup;
discard:
@ -139,17 +144,16 @@ static bool process_one()
{
int head = fs_list_get("data/outbox/HEAD");
int tail = fs_list_get("data/outbox/TAIL");
if( tail < head ) {
printf( "Processing outbox/%d.json\n", tail+1 );
if( process_envelope(tail+1) ) {
printf( "Done with outbox/%d.json\n", tail );
fs_list_set( "data/outbox/TAIL", tail + 1 );
bool result = false;
for( int i = tail + 1; i <= head; ++i ) {
if( process_envelope(i) ) {
printf( "Done with outbox/%d.json\n", i );
result = true;
//fs_list_set( "data/outbox/TAIL", tail + 1 );
}
printf("after\n" );
return true;
}
return false;
return result;
}
extern bool terminate;
@ -161,8 +165,9 @@ void process_outbox()
//activity |= cleanup_outbox();
if( !activity ) {
printf( "Sleeping for 10 seconds\n" );
fflush(stdout);
sleep(1);
sleep(10);
}
}
}

@ -1,8 +0,0 @@
src/controller/test/.crypt-f203bfe5.o: src/controller/test/crypt.c \
/usr/include/stdc-predef.h src/controller/test/crypto.h \
/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/include/stdbool.h \
src/model/crypto/http_sign.h
/usr/include/stdc-predef.h:
src/controller/test/crypto.h:
/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/include/stdbool.h:
src/model/crypto/http_sign.h:

@ -1 +1 @@
Subproject commit 730c340d66cd2c6775a95d297ad1c97ff29126c1
Subproject commit a71ffab0d0131cac8904dff715a1fc863ef07c48

@ -1 +1 @@
Subproject commit 1fc62a25ca5377c8f2e5d1691b6c312715607104
Subproject commit b35006d02377bb9eded418627d87e652cc2e6d37

@ -66,6 +66,7 @@ static struct json_object_field account_layout[] = {
.required = false,
.type = &json_field_string
},
JSON_FIELD_ARRAY_OF_STRINGS( aliases, false ),
JSON_FIELD_ENUM( account_type, account_types_enum, true ),
JSON_FIELD_STRING( inbox, false ),
JSON_FIELD_STRING( note, false ),
@ -211,6 +212,21 @@ struct ap_account* account_activity_pub_data( struct account* a )
);
}
bool account_sync_from_activity( struct account* a, struct ap_activity* act )
{
for( int i = 0; i < a->aliases.count; ++i ) {
free( a->aliases.items[i] );
}
a->aliases.count = 0;
for( int i = 0; i < act->also_known_as.count; ++i ) {
char* str = strdup(act->also_known_as.items[i]);
array_append( &a->aliases, sizeof(str), &str );
}
return true;
}
bool account_sync_from_acitvity_pub( unsigned int account_id )
{
char filename[512];
@ -359,6 +375,12 @@ void account_free( struct account* a )
free(a->account_url);
free(a->avatar.url);
free(a->avatar.static_url);
for( int i = 0; i < a->aliases.count; ++i ) {
free( a->aliases.items[i] );
}
free(a->aliases.items);
free(a->note);
free(a);
@ -396,6 +418,7 @@ void account_add_follower( struct account* a, struct account* follower )
// Create notification for follow
struct notification* note = notification_new();
note->debug = 4;
note->type = nt_follow;
note->account_id = follower->id;
note->created_at = time(NULL);
@ -421,6 +444,7 @@ void account_remove_follower( struct account* a, struct account* follower )
// create a notification for unfollow
struct notification* note = notification_new();
note->debug = 5;
note->type = nt_unfollow;
note->account_id = -1;
note->ref_account_id = follower->id;

@ -37,6 +37,11 @@ struct account
char* static_url;
} avatar;
struct {
char** items;
int count;
} aliases;
int followers_count;
int following_count;
int status_count;
@ -61,6 +66,8 @@ void account_free( struct account* a );
void account_save( struct account* a );
bool account_sync_from_acitvity_pub( unsigned int id );
bool account_sync_from_activity( struct account* a, struct ap_activity* act );
struct ap_account;
struct ap_account* account_activity_pub_data( struct account* a );

@ -101,6 +101,11 @@ void ap_activity_free_composite( struct ap_activity* act )
}
free(act->tags.items);
for( int i = 0; i < act->also_known_as.count; ++i ) {
free( act->also_known_as.items[i] );
}
free(act->also_known_as.items);
for( int i = 0; i < act->attachments.count; ++i ) {
ap_attachement_free( act->attachments.items[i] );
}

@ -60,6 +60,7 @@ enum ap_activity_type
apat_question = 28,
apat_emoji_react = 29,
apat_note = 30,
apat_person = 31,
};
enum ap_activity_object_type {
apaot_ref = 1,
@ -113,6 +114,11 @@ struct ap_activity
int count;
} attachments;
struct {
char** items;
int count;
} also_known_as;
struct {
char** items;
int count;

@ -42,6 +42,7 @@ struct json_enum ap_activity_type_enum[] = {
{ "Question", apat_question },
{ "EmojiReact", apat_emoji_react },
{ "Note", apat_note },
{ "Person", apat_person },
{ NULL, 0 },
};
@ -109,6 +110,13 @@ struct json_object_field ap_activity_layout[] = {
.type = &json_field_array_of,
.array_item_type = &ap_activity_tag_type
},
{
.key = "alsoKnownAs",
.offset = offsetof( struct ap_activity, also_known_as ),
.required = false,
.type = &json_field_array_of,
.array_item_type = &json_field_string,
},
JSON_FIELD_STRING(context,false),
{

@ -11,6 +11,7 @@
#define OBJ_TYPE struct outbox_envelope
static struct json_object_field layout[] = {
JSON_FIELD_BOOL( use_shared_inbox, false ),
JSON_FIELD_BOOL( sent, false ),
{
.key = "account",
.offset = offsetof( struct outbox_envelope, account_id ),
@ -42,22 +43,23 @@ void outbox_envelope_free( struct outbox_envelope* env )
void outbox_envelope_save( struct outbox_envelope* env )
{
int id = fs_list_get( "data/outbox/HEAD" ) + 1;
fs_list_set( "data/outbox/HEAD", id );
if( env->id == 0 ) {
int id = fs_list_get( "data/outbox/HEAD" ) + 1;
fs_list_set( "data/outbox/HEAD", id );
env->id = id;
}
char filename[512];
snprintf( filename, sizeof(filename), "data/outbox/%d.json", id );
snprintf( filename, sizeof(filename), "data/outbox/%d.json", env->id );
json_write_object_layout_to_file( filename, "\t", layout, env );
}
struct outbox_envelope* outbox_envelope_load_next()
struct outbox_envelope* outbox_envelope_from_id( int id )
{
struct outbox_envelope* env;
if( !( env = outbox_envelope_new()) ) { return NULL; }
int id = fs_list_get( "data/outbox/TAIL" ) + 1;
env->id = id;
char filename[512];
@ -71,6 +73,11 @@ struct outbox_envelope* outbox_envelope_load_next()
return env;
}
struct outbox_envelope* outbox_envelope_load_next()
{
return outbox_envelope_from_id( fs_list_get( "data/outbox/TAIL" ) + 1 );
}
void outbox_envelope_delete( struct outbox_envelope* env )
{
char filename[512];

@ -8,13 +8,16 @@ struct outbox_envelope
int activity_id;
int account_id;
bool use_shared_inbox;
bool sent;
};
struct outbox_envelope* outbox_envelope_new();
void outbox_envelope_free( struct outbox_envelope* env );
struct outbox_envelope* outbox_envelope_from_id( int id );
void outbox_envelope_save( struct outbox_envelope* env );
struct outbox_envelope* outbox_envelope_load_next();
//struct outbox_envelope* outbox_envelope_load_next();
void outbox_envelope_delete( struct outbox_envelope* env );

@ -358,8 +358,10 @@ void status_free( struct status* s )
status_react_free(s->reacts.items[i]);
}
free(s->reacts.items);
free(s->likes.items);
free(s->reposts.items);
free(s->mentions.items);
free(s);
}
@ -466,6 +468,7 @@ void status_add_react( struct status* s, const char* react, struct account* a )
if( s->id != 0 ) {
// Create notification for liking the owner's post
struct notification* note = notification_new();
note->debug = 1;
note->type = nt_react;
note->status_id = s->id;
note->account_id = a->id;
@ -512,6 +515,7 @@ void status_remove_react( struct status* s, const char* react, struct account* a
if( s->account_id == owner_account_id && a->id != owner_account_id ) {
// Create notification for liking the owner's post
struct notification* note = notification_new();
note->debug = 2;
note->type = nt_react;
note->account_id = a->id;
note->react = strdup(react);
@ -552,6 +556,7 @@ void status_add_like( struct status* s, struct account* a )
if( a->id != owner_account_id ) {
// Create notification for liking the owner's post
struct notification* note = notification_new();
note->debug = 3;
note->type = nt_like;
note->account_id = a->id;
note->status_id = s->id;

@ -0,0 +1,10 @@
#include "time.h"
time_t get_utc_time()
{
time_t local_time = time(NULL);
struct tm utc_time;
gmtime_r( &local_time, &utc_time );
return mktime(&utc_time);
}

@ -0,0 +1,6 @@
#pragma once
#include <time.h>
time_t get_utc_time();
Loading…
Cancel
Save