#include "model/account.h" // Submodules #include "util/format.h" #include "ffdb/trie.h" #include "collections/array.h" #include "ap/object.h" // Model #include "model/server.h" #include "model/status.h" #include "model/activity.h" #include "model/outbox_envelope.h" // Standard Library #include #include #include void account_deliver_activity( struct account* a, struct ap_object* act, struct outbox_envelope_list* oel ) { if( a->defunct ) { printf( "Account %s id defunct, not delivering activity\n", a->account_url ); return; } 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 = activity_create_Note(s); struct ap_object* create = activity_create_Create(note); 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 ); //account_deliver_activity_to_followers( a, create, &oel ); activity_deliver( create ); ap_object_free(create); ap_object_free(note); } struct status* account_announce( struct account* a, struct status* original_post, struct status* local_repost, struct ap_object* act ) { if( !local_repost ) { local_repost = status_new_repost( original_post, a ); // Flag the status as reposted original_post->reposted_status_id = local_repost->id; status_save(original_post); // Add repost to timelines status_add_to_timeline( local_repost, a->id ); if( a->id == owner_account_id ) { status_add_to_timeline( local_repost, home_timeline_id ); } status_add_to_timeline( local_repost, public_timeline_id ); } if( !act ) { act = ap_object_new(); activity_allocate_local_id(act); act->id = aformat( "https://%s/activity/%d", g_server->domain, act->local_id ); act->published = time(NULL); act->type = ap_Announce; act->object.tag = apaot_ref; act->object.ref = strdup(original_post->url); act->actor = strdup(a->account_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->domain ); 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 ); account_free(origin_post_account); } activity_deliver( act ); // 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); goto cleanup; cleanup: ap_object_free(act); return local_repost; } 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 = 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 = activity_from_local_id( atoi(res) ); free(res); // Federate Undo Activity 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)); activity_allocate_local_id(act); act->id = aformat( "https://%s/activity/%d", g_server->domain, act->id ); act->type = ap_Update; act->actor = strdup(a->account_url); act->object.tag = apaot_activity; act->object.ptr = account_activity_pub(a); char* str = aformat("https://%s/owner/followers", g_server->domain ); array_append( &act->to, sizeof(str), &str ); activity_deliver( act ); activity_save(act); ap_object_free(act); }