Split up model/account.c to reduce line count and make it easier to find code

master
teknomunk 1 year ago
parent 808dbecb88
commit aca54e746f

@ -1 +1 @@
Subproject commit e991549cae6ef7c9c50e62cbc3fac86515360220
Subproject commit b5165d7553f1b247212f4ee4d095d8eadb77d70f

@ -13,9 +13,8 @@
// Model
#include "model/server.h"
#include "model/status.h"
#include "model/ap/account.h"
#include "model/ap/activity.h"
#include "model/ap/outbox_envelope.h"
#include "model/ap/account.h"
#include "model/crypto/keys.h"
#include "model/notification.h"
@ -228,317 +227,6 @@ struct account* account_from_webfinger( const char* handle )
return a;
}
struct ap_account* account_activity_pub_data( struct account* a )
{
char filename[512];
return ap_account_from_file(
format( filename, 512, "data/accounts/%d/ap.json", a->id )
);
}
struct ap_object* account_ap_actor( struct account* a )
{
struct ap_object* act;
act = malloc(sizeof(*act));
memset(act,0,sizeof(*act));
act->type = apot_person;
return act;
}
struct ap_object* account_ap_outbox( struct account* a )
{
struct ap_object* outbox;
outbox = malloc(sizeof(*outbox));
memset(outbox,0,sizeof(*outbox));
outbox->type = apot_ordered_collection;
outbox->published = time(NULL);
outbox->id = aformat( "https://%s/outbox", g_server_name );
outbox->first.tag = apaot_ref;
outbox->first.ref = aformat( "https://%s/outbox/page-0", g_server_name );
char buffer[512];
outbox->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/timeline", a->id ) );
return outbox;
}
struct ap_object* account_ap_outbox_page( struct account* a, int page )
{
enum { items_per_page = 10 };
char buffer[512];
int total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/timeline", a->id ) );
int page_count = ( total_items + items_per_page - 1 ) / items_per_page;
if( page >= page_count ) { return NULL; }
struct ap_object* outbox;
outbox = malloc(sizeof(*outbox));
memset(outbox,0,sizeof(*outbox));
struct {
char** items;
int count;
} values;
memset( &values, 0, sizeof(values) );
ffdb_trie_list( buffer, page * items_per_page, items_per_page, NULL, &values );
for( int i = 0; i < values.count; ++i ) {
int id;
int activity_id = 0;
if( 1 == sscanf( values.items[i], "%d", &id ) ) {
struct status* s = status_from_id(id);
if( !s ) { goto include_tombstone; }
activity_id = s->activity_id;
if( s->activity_id == 0 ) {
goto include_tombstone;
}
struct ap_object* act = ap_activity_from_local_id( s->activity_id );
if( !act ) { goto include_tombstone; }
struct ap_object_ptr_or_ref r;
r.tag = apaot_activity;
r.ptr = act;
array_append( &outbox->collection_items, sizeof(r), &r );
status_free(s);
} else {
include_tombstone:
// include Tombstone
struct ap_object* o;
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
if( activity_id != 0 ) {
o->id = aformat( "https://%s/activity/%d", g_server_name, activity_id );
} else {
o->id = aformat( "https://%s/activity/tombstone", g_server_name );
}
o->type = apot_tombstone;
struct ap_object_ptr_or_ref r;
r.tag = apaot_activity;
r.ptr = o;
array_append( &outbox->collection_items, sizeof(r), &r );
}
free( values.items[i] );
}
free( values.items );
outbox->type = apot_ordered_collection_page;
outbox->published = time(NULL);
outbox->part_of = aformat( "https://%s/outbox", g_server_name );
outbox->id = aformat( "https://%s/outbox/page-%d", g_server_name, page );
if( page > 0 ) {
outbox->prev = aformat( "https://%s/outbox/page-%d", g_server_name, page - 1 );
}
if( page < page_count - 1 ) {
outbox->next = aformat( "https://%s/outbox/page-%d", g_server_name, page + 1 );
}
return outbox;
}
static struct ap_object* account_list_page( int page, char* part_of, const char* page_format, const char* trie_filename )
{
enum { items_per_page = 10 };
char buffer[512];
int total_items = ffdb_trie_count( trie_filename );
int page_count = ( total_items + items_per_page - 1 ) / items_per_page;
if( page >= page_count ) { return NULL; }
struct ap_object* o;
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
struct {
char** items;
int count;
} values;
memset( &values, 0, sizeof(values) );
ffdb_trie_list( buffer, page * items_per_page, items_per_page, &values, NULL );
for( int i = 0; i < values.count; ++i ) {
int id;
if( 1 != sscanf( values.items[i], "%d", &id ) ) { continue; }
free( values.items[i] );
struct account* follower = account_from_id(id);
if( !follower ) { continue; }
struct ap_object_ptr_or_ref r;
r.tag = apaot_ref;
r.ref = strdup( follower->account_url );
array_append( &o->collection_items, sizeof(r), &r );
account_free(follower);
}
free( values.items );
o->type = apot_ordered_collection_page;
o->published = time(NULL);
o->part_of = part_of;
o->id = aformat( page_format, page );
if( page > 0 ) {
o->prev = aformat( page_format, page - 1 );
}
if( page < page_count - 1 ) {
o->next = aformat( page_format, page + 1 );
}
return o;
}
struct ap_object* account_ap_followers( struct account* a )
{
if( a->id != 0 ) { return NULL; }
struct ap_object* o;
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
o->type = apot_ordered_collection;
o->published = time(NULL);
o->id = aformat( "https://%s/owner/followers", g_server_name );
o->first.tag = apaot_object;
o->first.ptr = account_ap_followers_page( a, 0 );
char buffer[512];
o->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/followers", a->id ) );
return o;
}
struct ap_object* account_ap_followers_page( struct account* a, int page )
{
if( a->id != 0 ) { return NULL; }
char* part_of = aformat( "https://%s/owner/followers", g_server_name );
char page_format[512];
snprintf( page_format, 512, "https://%s/owner/followers/page-%%d", g_server_name );
char trie_filename[512];
snprintf( trie_filename, 512, "data/accounts/%d/followers", a->id );
return account_list_page( page, part_of, page_format, trie_filename );
}
struct ap_object* account_ap_following( struct account* a )
{
if( a->id != 0 ) { return NULL; }
struct ap_object* o;
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
o->type = apot_ordered_collection;
o->published = time(NULL);
o->id = aformat( "https://%s/owner/following", g_server_name );
o->first.tag = apaot_object;
o->first.ptr = account_ap_following_page( a, 0 );
char buffer[512];
o->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/following", a->id ) );
return o;
}
struct ap_object* account_ap_following_page( struct account* a, int page )
{
if( a->id != 0 ) { return NULL; }
char* part_of = aformat( "https://%s/owner/following", g_server_name );
char page_format[512];
snprintf( page_format, 512, "https://%s/owner/following/page-%%d", g_server_name );
char trie_filename[512];
snprintf( trie_filename, 512, "data/accounts/%d/following", a->id );
return account_list_page( page, part_of, page_format, trie_filename );
}
struct ap_object* account_activity_pub( struct account* a )
{
return account_ap_actor(a);
}
bool account_sync_from_activity( struct account* a, struct ap_object* 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_activity_pub( unsigned int account_id )
{
char filename[512];
struct ap_account* ap = ap_account_from_file(
format( filename, 512, "data/accounts/%d/ap.json", account_id )
);
struct account* a = malloc(sizeof(struct account));
memset(a,0,sizeof(*a));
a->id = account_id;
if( !ap ) {
printf( "? Failed to sync account %d from %s, creating stub\n", account_id, filename );
return false;
}
printf( "ap = " ); ap_account_debug_dump(ap);
a->handle = strdup(ap->preferredUsername);
if( ap->name ) {
a->display_name = strdup(ap->name);
} else {
a->display_name = strdup(ap->preferredUsername);
}
if( ap->avatar ) {
a->avatar.url = strdup(ap->avatar);
a->avatar.static_url = strdup(ap->avatar);
}
a->bot = ( ap->type != apacct_Person );
a->account_type = at_remote_activity_pub;
a->account_url = strdup(ap->url);
a->inbox = strdup(ap->inbox);
if( ap->shared_inbox ) {
a->shared_inbox = strdup(ap->shared_inbox);
}
if( 0 == strncmp( ap->id, "https://", 8 ) ) {
char* server_name = strdup(&ap->id[8]);
char* discard;
strtok_r(server_name,"/",&discard);
a->server = server_name;
}
// Extract out the public key
char* id = strdup(ap->public_key.id);
char* key_id = NULL;
strtok_r( id, "#", &key_id );
FILE* key_pem = fopen( format(filename,sizeof(filename),"data/accounts/%d/%s.pem", a->id, key_id), "w" );
if( !key_pem ) {
printf( "Unable to save public key to %s\n", filename );
} else {
printf( "Writing public key to %s\n", filename );
fprintf( key_pem, "%s", ap->public_key.pem );
fclose(key_pem);
}
free(id);
account_save(a);
account_index_webfinger(a);
ap_account_free(ap);
account_free(a);
return true;
}
struct crypto_keys* account_get_public_key( struct account* a, const char* key_name )
{
char filename[512];
@ -702,13 +390,13 @@ void account_remove_follower( struct account* a, struct account* follower )
format( filename, sizeof(filename), "data/accounts/%d/followers", a->id ),
format( key, sizeof(key), "%d", follower->id )
);
a->followers_count = ffdb_trie_count(filename);
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);
follower->following_count = ffdb_trie_count(filename);
// create a notification for unfollow
struct notification* note = notification_new();
@ -720,187 +408,6 @@ void account_remove_follower( struct account* a, struct account* follower )
notification_save( note );
notification_free( note );
}
void account_follow( struct account* a, struct account* to_follow )
{
char index[512];
char key[32];
char value[32];
// Federate Follow Activity
int act_id = ap_activity_follow( a, to_follow );
// Update following list
ffdb_trie_set(
format(index,512,"data/accounts/%d/following", a->id),
format(key,32,"%d", to_follow->id ),
format(value,32,"%d", act_id )
);
a->following_count = ffdb_trie_count(index);
// Save account data
account_save(a);
}
void account_create( struct account* a, struct status* s )
{
struct ap_object* note = ap_activity_create_note(s);
struct ap_object* create = ap_activity_create_Create(note);
ap_activity_save(create);
// Link status to activity
s->activity_id = create->local_id;
status_save(s);
// Add to owner timeline, public timeline and home timelines
status_add_to_timeline( s, a->id );
status_add_to_timeline( s, public_timeline_id );
status_add_to_timeline( s, home_timeline_id );
//status_add_post_to_timeline( s, federated_timeline_id );
struct outbox_envelope_list oel;
memset(&oel,0,sizeof(oel));
account_deliver_activity_to_followers( a, create, &oel );
printf( "Delivering to %d inboxes\n", oel.count );
outbox_envelope_list_save(&oel);
outbox_envelope_list_free_composite(&oel);
ap_object_free(create);
ap_object_free(note);
}
void account_announce( struct account* a, struct status* original_post, struct status* local_repost )
{
struct ap_object* act = ap_activity_new();
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set("data/activities/HEAD", id );
act->id = aformat( "https://%s/activity/%d", g_server_name, id );
act->local_id = id;
act->published = time(NULL);
act->type = apat_announce;
act->actor = strdup(a->account_url);
act->object.tag = apaot_ref;
act->object.ref = strdup(original_post->url);
// Create To: list
char* str = strdup("https://www.w3.org/ns/activitystreams#Public");
array_append( &act->to, sizeof(str), &str );
str = aformat("https://%s/owner/followers", g_server_name );
array_append( &act->to, sizeof(str), &str );
struct account* origin_post_account = account_from_id(original_post->account_id);
str = strdup(origin_post_account->account_url);
array_append( &act->to, sizeof(str), &str );
struct outbox_envelope_list oel;
memset(&oel,0,sizeof(oel));
account_deliver_activity_to_followers( a, act, &oel );
account_deliver_activity( origin_post_account, act, &oel );
printf( "Delivering to %d inboxes\n", oel.count );
outbox_envelope_list_save(&oel);
outbox_envelope_list_free_composite(&oel);
ap_activity_save(act);
ap_activity_write_to_FILE( act, stdout );
// Link status to activity
local_repost->activity_id = act->local_id;
status_save(local_repost);
status_add_repost( original_post, local_repost );
status_save(original_post);
cleanup:
account_free(origin_post_account);
ap_object_free(act);
}
void account_deliver_activity( struct account* a, struct ap_object* act, struct outbox_envelope_list* oel )
{
printf( "Delivering activity %s to account %s\n", act->id, a->account_url );
if( a->shared_inbox ) {
// Check if the sh
for( int i = 0; i < oel->count; ++i ) {
struct outbox_envelope* e = oel->items[i];
if( e->shared_inbox && a->shared_inbox && 0 == strcmp(e->shared_inbox,a->shared_inbox) ) {
printf( "\tUsing shared inbox %s already in delivery list\n", a->shared_inbox );
// This account will get the message delivered thru the shared inbox
return;
}
}
}
struct outbox_envelope* env = outbox_envelope_new();
env->activity_id = act->local_id;
if( a->shared_inbox ) {
env->shared_inbox = strdup(a->shared_inbox);
}
env->account_id = a->id;
array_append( oel, sizeof(env), &env );
}
void account_deliver_activity_to_followers( struct account* a, struct ap_object* act, struct outbox_envelope_list* oel )
{
printf( "Delivering activity %s to followers of account %s\n", act->id, a->account_url );
struct {
char** items;
int count;
} keys;
memset( &keys, 0, sizeof(keys) );
char filename[512];
snprintf( filename, sizeof(filename), "data/accounts/%d/followers", a->id );
// TODO: handle shared inbox delivery
int pages = (a->followers_count+31) / 32;
printf( "\tpages = %d, followers = %d\n", pages, a->followers_count );
for( int i = 0; i < pages; ++i ) {
keys.count = 0;
ffdb_trie_list( filename, i * 32, 32, &keys, NULL );
printf( "keys.count = %d\n", keys.count );
for( int j = 0; j < keys.count; ++j ) {
int account_id = atoi(keys.items[j]);
free(keys.items[j]);
printf( "account_id = %d\n", account_id );
struct account* follower_account = account_from_id(account_id);
account_deliver_activity( follower_account, act, oel );
account_free(follower_account);
}
}
free(keys.items);
}
void account_unfollow( struct account* a, struct account* to_unfollow )
{
char index[512];
char key[32];
// Make sure the account to unfollow has previously been followed
char* res = ffdb_trie_get(
format(index,512,"data/accounts/%d/following", a->id),
format(key,32,"%d", to_unfollow->id)
);
if( !res ) {
printf( "%s is not following %s\n", a->account_url, to_unfollow->account_url );
return;
}
// Lookup the Activity used to federate following this account
struct ap_object* act = ap_activity_from_local_id( atoi(res) );
free(res);
// Federate Undo Activity
ap_activity_undo(act,to_unfollow->id);
ap_object_free(act);
// Update following list
ffdb_trie_remove( index, key );
a->following_count = ffdb_trie_count(index);
// Save account data
account_save(a);
}
struct int_array {
int* items;
@ -959,20 +466,4 @@ bool account_does_follow( struct account* a, int account_id )
return result;
}
void account_update( struct account* a )
{
struct ap_object* act;
act = malloc(sizeof(*act));
memset(act,0,sizeof(*act));
act->type = apat_update;
act->actor = strdup(a->account_url);
act->object.tag = apaot_activity;
act->object.ptr = account_activity_pub(a);
ap_activity_write_to_FILE( act, stdout );
ap_object_free(act);
}

@ -79,13 +79,16 @@ void account_save( struct account* a );
// Update from external activity pub data
bool account_sync_from_activity_pub( unsigned int id );
bool account_sync_from_activity( struct account* a, struct ap_object* act );
void account_sync_following_list( struct account* a );
// Data requests
struct crypto_keys* account_get_public_key( struct account* a, const char* key_name );
struct crypto_keys* account_get_private_key( struct account* a );
void account_list_followers( struct account* a, int offset, int limit, void* id_array );
void account_list_following( struct account* a, int offset, int limit, void* id_array );
struct ap_account* account_activity_pub_data( struct account* a );
// Activity pub data
struct ap_account* account_activity_pub_data( struct account* a ); // TODO: convert this to struct ap_object*
struct ap_object* account_activity_pub( struct account* a );
struct ap_object* account_ap_actor( struct account* a );
struct ap_object* account_ap_outbox( struct account* a );

@ -0,0 +1,247 @@
#include "model/account.h"
#include "format.h"
#include "ffdb/trie.h"
#include "collections/array.h"
#include "model/server.h"
#include "model/status.h"
#include "model/ap/activity.h"
#include "model/ap/account.h"
#include <stdlib.h>
#include <string.h>
// TODO: convert this to struct ap_object*
struct ap_account* account_activity_pub_data( struct account* a )
{
char filename[512];
return ap_account_from_file(
format( filename, 512, "data/accounts/%d/ap.json", a->id )
);
}
struct ap_object* account_activity_pub( struct account* a )
{
return account_ap_actor(a);
}
struct ap_object* account_ap_actor( struct account* a )
{
struct ap_object* act;
act = malloc(sizeof(*act));
memset(act,0,sizeof(*act));
act->type = apot_person;
return act;
}
struct ap_object* account_ap_outbox( struct account* a )
{
struct ap_object* outbox;
outbox = malloc(sizeof(*outbox));
memset(outbox,0,sizeof(*outbox));
outbox->type = apot_ordered_collection;
outbox->published = time(NULL);
outbox->id = aformat( "https://%s/outbox", g_server_name );
outbox->first.tag = apaot_ref;
outbox->first.ref = aformat( "https://%s/outbox/page-0", g_server_name );
char buffer[512];
outbox->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/timeline", a->id ) );
return outbox;
}
struct ap_object* account_ap_outbox_page( struct account* a, int page )
{
enum { items_per_page = 10 };
char buffer[512];
int total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/timeline", a->id ) );
int page_count = ( total_items + items_per_page - 1 ) / items_per_page;
if( page >= page_count ) { return NULL; }
struct ap_object* outbox;
outbox = malloc(sizeof(*outbox));
memset(outbox,0,sizeof(*outbox));
struct {
char** items;
int count;
} values;
memset( &values, 0, sizeof(values) );
ffdb_trie_list( buffer, page * items_per_page, items_per_page, NULL, &values );
for( int i = 0; i < values.count; ++i ) {
int id;
int activity_id = 0;
if( 1 == sscanf( values.items[i], "%d", &id ) ) {
struct status* s = status_from_id(id);
if( !s ) { goto include_tombstone; }
activity_id = s->activity_id;
if( s->activity_id == 0 ) {
goto include_tombstone;
}
struct ap_object* act = ap_activity_from_local_id( s->activity_id );
if( !act ) { goto include_tombstone; }
struct ap_object_ptr_or_ref r;
r.tag = apaot_activity;
r.ptr = act;
array_append( &outbox->collection_items, sizeof(r), &r );
status_free(s);
} else {
include_tombstone:
// include Tombstone
struct ap_object* o;
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
if( activity_id != 0 ) {
o->id = aformat( "https://%s/activity/%d", g_server_name, activity_id );
} else {
o->id = aformat( "https://%s/activity/tombstone", g_server_name );
}
o->type = apot_tombstone;
struct ap_object_ptr_or_ref r;
r.tag = apaot_activity;
r.ptr = o;
array_append( &outbox->collection_items, sizeof(r), &r );
}
free( values.items[i] );
}
free( values.items );
outbox->type = apot_ordered_collection_page;
outbox->published = time(NULL);
outbox->part_of = aformat( "https://%s/outbox", g_server_name );
outbox->id = aformat( "https://%s/outbox/page-%d", g_server_name, page );
if( page > 0 ) {
outbox->prev = aformat( "https://%s/outbox/page-%d", g_server_name, page - 1 );
}
if( page < page_count - 1 ) {
outbox->next = aformat( "https://%s/outbox/page-%d", g_server_name, page + 1 );
}
return outbox;
}
static struct ap_object* account_list_page( int page, char* part_of, const char* page_format, const char* trie_filename )
{
enum { items_per_page = 10 };
char buffer[512];
int total_items = ffdb_trie_count( trie_filename );
int page_count = ( total_items + items_per_page - 1 ) / items_per_page;
if( page >= page_count ) { return NULL; }
struct ap_object* o;
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
struct {
char** items;
int count;
} values;
memset( &values, 0, sizeof(values) );
ffdb_trie_list( buffer, page * items_per_page, items_per_page, &values, NULL );
for( int i = 0; i < values.count; ++i ) {
int id;
if( 1 != sscanf( values.items[i], "%d", &id ) ) { continue; }
free( values.items[i] );
struct account* follower = account_from_id(id);
if( !follower ) { continue; }
struct ap_object_ptr_or_ref r;
r.tag = apaot_ref;
r.ref = strdup( follower->account_url );
array_append( &o->collection_items, sizeof(r), &r );
account_free(follower);
}
free( values.items );
o->type = apot_ordered_collection_page;
o->published = time(NULL);
o->part_of = part_of;
o->id = aformat( page_format, page );
if( page > 0 ) {
o->prev = aformat( page_format, page - 1 );
}
if( page < page_count - 1 ) {
o->next = aformat( page_format, page + 1 );
}
return o;
}
struct ap_object* account_ap_followers( struct account* a )
{
if( a->id != 0 ) { return NULL; }
struct ap_object* o;
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
o->type = apot_ordered_collection;
o->published = time(NULL);
o->id = aformat( "https://%s/owner/followers", g_server_name );
o->first.tag = apaot_object;
o->first.ptr = account_ap_followers_page( a, 0 );
char buffer[512];
o->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/followers", a->id ) );
return o;
}
struct ap_object* account_ap_followers_page( struct account* a, int page )
{
if( a->id != 0 ) { return NULL; }
char* part_of = aformat( "https://%s/owner/followers", g_server_name );
char page_format[512];
snprintf( page_format, 512, "https://%s/owner/followers/page-%%d", g_server_name );
char trie_filename[512];
snprintf( trie_filename, 512, "data/accounts/%d/followers", a->id );
return account_list_page( page, part_of, page_format, trie_filename );
}
struct ap_object* account_ap_following( struct account* a )
{
if( a->id != 0 ) { return NULL; }
struct ap_object* o;
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
o->type = apot_ordered_collection;
o->published = time(NULL);
o->id = aformat( "https://%s/owner/following", g_server_name );
o->first.tag = apaot_object;
o->first.ptr = account_ap_following_page( a, 0 );
char buffer[512];
o->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/following", a->id ) );
return o;
}
struct ap_object* account_ap_following_page( struct account* a, int page )
{
if( a->id != 0 ) { return NULL; }
char* part_of = aformat( "https://%s/owner/following", g_server_name );
char page_format[512];
snprintf( page_format, 512, "https://%s/owner/following/page-%%d", g_server_name );
char trie_filename[512];
snprintf( trie_filename, 512, "data/accounts/%d/following", a->id );
return account_list_page( page, part_of, page_format, trie_filename );
}

@ -0,0 +1,100 @@
#include "model/account.h"
// Submodules
#include "collections/array.h"
#include "format.h"
// Model
#include "model/ap/activity.h"
#include "model/ap/account.h"
// Standard Library
#include <string.h>
#include <stdlib.h>
bool account_sync_from_activity_pub( unsigned int account_id )
{
char filename[512];
struct ap_account* ap = ap_account_from_file(
format( filename, 512, "data/accounts/%d/ap.json", account_id )
);
struct account* a = malloc(sizeof(struct account));
memset(a,0,sizeof(*a));
a->id = account_id;
if( !ap ) {
printf( "? Failed to sync account %d from %s, creating stub\n", account_id, filename );
return false;
}
printf( "ap = " ); ap_account_debug_dump(ap);
a->handle = strdup(ap->preferredUsername);
if( ap->name ) {
a->display_name = strdup(ap->name);
} else {
a->display_name = strdup(ap->preferredUsername);
}
if( ap->avatar ) {
a->avatar.url = strdup(ap->avatar);
a->avatar.static_url = strdup(ap->avatar);
}
a->bot = ( ap->type != apacct_Person );
a->account_type = at_remote_activity_pub;
a->account_url = strdup(ap->url);
a->inbox = strdup(ap->inbox);
if( ap->shared_inbox ) {
a->shared_inbox = strdup(ap->shared_inbox);
}
if( 0 == strncmp( ap->id, "https://", 8 ) ) {
char* server_name = strdup(&ap->id[8]);
char* discard;
strtok_r(server_name,"/",&discard);
a->server = server_name;
}
// Extract out the public key
char* id = strdup(ap->public_key.id);
char* key_id = NULL;
strtok_r( id, "#", &key_id );
FILE* key_pem = fopen( format(filename,sizeof(filename),"data/accounts/%d/%s.pem", a->id, key_id), "w" );
if( !key_pem ) {
printf( "Unable to save public key to %s\n", filename );
} else {
printf( "Writing public key to %s\n", filename );
fprintf( key_pem, "%s", ap->public_key.pem );
fclose(key_pem);
}
free(id);
account_save(a);
account_index_webfinger(a);
ap_account_free(ap);
account_free(a);
return true;
}
bool account_sync_from_activity( struct account* a, struct ap_object* 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;
}
void account_sync_following_list( struct account* a )
{
// TODO: implement
}

@ -0,0 +1,218 @@
#include "model/account.h"
#include "format.h"
#include "ffdb/fs_list.h"
#include "ffdb/trie.h"
#include "collections/array.h"
#include "model/server.h"
#include "model/status.h"
#include "model/ap/activity.h"
#include "model/ap/outbox_envelope.h"
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
void account_deliver_activity( struct account* a, struct ap_object* act, struct outbox_envelope_list* oel )
{
printf( "Delivering activity %s to account %s\n", act->id, a->account_url );
if( a->shared_inbox ) {
// Check if the sh
for( int i = 0; i < oel->count; ++i ) {
struct outbox_envelope* e = oel->items[i];
if( e->shared_inbox && a->shared_inbox && 0 == strcmp(e->shared_inbox,a->shared_inbox) ) {
printf( "\tUsing shared inbox %s already in delivery list\n", a->shared_inbox );
// This account will get the message delivered thru the shared inbox
return;
}
}
}
struct outbox_envelope* env = outbox_envelope_new();
env->activity_id = act->local_id;
if( a->shared_inbox ) {
env->shared_inbox = strdup(a->shared_inbox);
}
env->account_id = a->id;
array_append( oel, sizeof(env), &env );
}
void account_deliver_activity_to_followers( struct account* a, struct ap_object* act, struct outbox_envelope_list* oel )
{
printf( "Delivering activity %s to followers of account %s\n", act->id, a->account_url );
struct {
char** items;
int count;
} keys;
memset( &keys, 0, sizeof(keys) );
char filename[512];
snprintf( filename, sizeof(filename), "data/accounts/%d/followers", a->id );
// TODO: handle shared inbox delivery
int pages = (a->followers_count+31) / 32;
printf( "\tpages = %d, followers = %d\n", pages, a->followers_count );
for( int i = 0; i < pages; ++i ) {
keys.count = 0;
ffdb_trie_list( filename, i * 32, 32, &keys, NULL );
printf( "keys.count = %d\n", keys.count );
for( int j = 0; j < keys.count; ++j ) {
int account_id = atoi(keys.items[j]);
free(keys.items[j]);
printf( "account_id = %d\n", account_id );
struct account* follower_account = account_from_id(account_id);
account_deliver_activity( follower_account, act, oel );
account_free(follower_account);
}
}
free(keys.items);
}
void account_create( struct account* a, struct status* s )
{
struct ap_object* note = ap_activity_create_note(s);
struct ap_object* create = ap_activity_create_Create(note);
ap_activity_save(create);
// Link status to activity
s->activity_id = create->local_id;
status_save(s);
// Add to owner timeline, public timeline and home timelines
status_add_to_timeline( s, a->id );
status_add_to_timeline( s, public_timeline_id );
status_add_to_timeline( s, home_timeline_id );
//status_add_post_to_timeline( s, federated_timeline_id );
struct outbox_envelope_list oel;
memset(&oel,0,sizeof(oel));
account_deliver_activity_to_followers( a, create, &oel );
printf( "Delivering to %d inboxes\n", oel.count );
outbox_envelope_list_save(&oel);
outbox_envelope_list_free_composite(&oel);
ap_object_free(create);
ap_object_free(note);
}
void account_announce( struct account* a, struct status* original_post, struct status* local_repost )
{
struct ap_object* act = ap_activity_new();
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set("data/activities/HEAD", id );
act->id = aformat( "https://%s/activity/%d", g_server_name, id );
act->local_id = id;
act->published = time(NULL);
act->type = apat_announce;
act->actor = strdup(a->account_url);
act->object.tag = apaot_ref;
act->object.ref = strdup(original_post->url);
// Create To: list
char* str = strdup("https://www.w3.org/ns/activitystreams#Public");
array_append( &act->to, sizeof(str), &str );
str = aformat("https://%s/owner/followers", g_server_name );
array_append( &act->to, sizeof(str), &str );
struct account* origin_post_account = account_from_id(original_post->account_id);
str = strdup(origin_post_account->account_url);
array_append( &act->to, sizeof(str), &str );
struct outbox_envelope_list oel;
memset(&oel,0,sizeof(oel));
account_deliver_activity_to_followers( a, act, &oel );
account_deliver_activity( origin_post_account, act, &oel );
printf( "Delivering to %d inboxes\n", oel.count );
outbox_envelope_list_save(&oel);
outbox_envelope_list_free_composite(&oel);
ap_activity_save(act);
ap_activity_write_to_FILE( act, stdout );
// Link status to activity
local_repost->activity_id = act->local_id;
status_save(local_repost);
status_add_repost( original_post, local_repost );
status_save(original_post);
cleanup:
account_free(origin_post_account);
ap_object_free(act);
}
void account_follow( struct account* a, struct account* to_follow )
{
char filename[512];
char key[32];
char value[32];
// Federate Follow Activity
int act_id = ap_activity_follow( a, to_follow );
// Update following list
account_add_follower( to_follow, a );
// Record the activity so it an Undo can be issued in the future
mkdir( format(filename,sizeof(filename),"data/accounts/%d/follow-activities", a->id), 0755 );
ffdb_trie_set(filename, format(key,32,"%d", to_follow->id ), format(value,32,"%d", act_id ) );
// Save account data
account_save(a);
account_save(to_follow);
}
void account_unfollow( struct account* a, struct account* to_unfollow )
{
char index[512];
char key[32];
// Make sure the account to unfollow has previously been followed
char* res = ffdb_trie_get(
format(index,512,"data/accounts/%d/follow-activities", a->id),
format(key,32,"%d", to_unfollow->id)
);
if( !res ) {
printf( "Unable to find activity to undo follow\n" );
printf( "%s is trying to unfollow %s\n", a->account_url, to_unfollow->account_url );
return;
}
// Lookup the Activity used to federate following this account
struct ap_object* act = ap_activity_from_local_id( atoi(res) );
free(res);
// Federate Undo Activity
ap_activity_undo(act,to_unfollow->id);
ap_object_free(act);
// Update following list
account_remove_follower( to_unfollow, a );
// Save account data
account_save(a);
}
void account_update( struct account* a )
{
struct ap_object* act;
act = malloc(sizeof(*act));
memset(act,0,sizeof(*act));
act->type = apat_update;
act->actor = strdup(a->account_url);
act->object.tag = apaot_activity;
act->object.ptr = account_activity_pub(a);
ap_activity_write_to_FILE( act, stdout );
ap_object_free(act);
}
Loading…
Cancel
Save