#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" // Standard Library #include #include struct ap_object* account_activity_pub( struct account* a ) { // Pass thru remote activity pub actor data if present char filename[512]; struct ap_object* obj = ap_object_from_file( format( filename, 512, "data/accounts/%d/ap.json", a->id ) ); if( obj ) { return obj; } 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)); ap_object_add_context( act, "https://www.w3.org/ns/activitystreams"); ap_object_add_context( act, "https://apogee.polaris-1.work/schemas/litepub-0.1.jsonld"); act->published = time(NULL); act->type = ap_Person; act->id = aformat( "https://%s/owner/actor", g_server->domain ); act->name = strdup(a->display_name); act->summary = strdup(a->note); act->endpoints.shared_inbox = aformat("https://%s/inbox", g_server->domain); act->inbox = aformat("https://%s/inbox", g_server->domain); act->outbox = aformat("https://%s/outbox", g_server->domain ); act->preferred_username = strdup(a->handle); act->featured = aformat("https://%s/owner/collections/featured", g_server->domain ); act->followers = aformat("https://%s/owner/followers", g_server->domain); act->following = aformat("https://%s/owner/following", g_server->domain); act->avatar = aformat( "https://%s/owner/avatar.blob", g_server->domain ); act->banner = aformat( "https://%s/owner/banner.blob", g_server->domain ); act->url = aformat( "https://%s/@%s@%s", g_server->domain, a->handle, g_server->domain ); act->capabilities.show = true; struct ap_public_key* key; key = malloc(sizeof(*key)); memset(key,0,sizeof(*key)); key->id = aformat( "https://%s/owner/actor#main-key", g_server->domain ); key->owner = strdup(act->id); FILE* f = fopen( "data/owner/public.pem", "r" ); fseek( f, 0, SEEK_END ); int size = ftell(f); fseek( f, 0, SEEK_SET ); char* pem = malloc( size + 1 ); fread( pem, 1, size, f ); pem[size] = 0; key->public_key = pem; fclose(f); act->public_key = key; for( int i = 0; i < a->fields.count; ++i ) { struct ap_object* at; at = malloc(sizeof(*at)); memset(at,0,sizeof(*at)); at->name = strdup(a->fields.items[i].key); at->value = strdup(a->fields.items[i].value); at->type = schema_PropertyValue; array_append( &act->attachments, sizeof(at), &at ); } return act; } struct ap_object* account_ap_outbox( struct account* a ) { struct ap_object* outbox; outbox = malloc(sizeof(*outbox)); memset(outbox,0,sizeof(*outbox)); ap_object_add_context( outbox, "https://www.w3.org/ns/activitystreams"); ap_object_add_context( outbox, "https://apogee.polaris-1.work/schemas/litepub-0.1.jsonld"); outbox->type = ap_OrderedCollection; outbox->published = time(NULL); outbox->id = aformat( "https://%s/outbox", g_server->domain ); outbox->first.tag = apaot_ref; outbox->first.ref = aformat( "https://%s/outbox/page-0", g_server->domain ); 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)); ap_object_add_context( outbox, "https://www.w3.org/ns/activitystreams"); ap_object_add_context( outbox, "https://apogee.polaris-1.work/schemas/litepub-0.1.jsonld"); 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], "act-%d", &activity_id ) ) { // Direct activity } else 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; } status_free(s); } if( activity_id ) { struct ap_object* act = activity_from_local_id( 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 ); } 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->domain, activity_id ); } else { o->id = aformat( "https://%s/activity/tombstone", g_server->domain ); } o->type = ap_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 = ap_OrderedCollectionPage; outbox->published = time(NULL); outbox->part_of = aformat( "https://%s/outbox", g_server->domain ); outbox->id = aformat( "https://%s/outbox/page-%d", g_server->domain, page ); if( page > 0 ) { outbox->prev = aformat( "https://%s/outbox/page-%d", g_server->domain, page - 1 ); } if( page < page_count - 1 ) { outbox->next = aformat( "https://%s/outbox/page-%d", g_server->domain, 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)); ap_object_add_context( o, "https://www.w3.org/ns/activitystreams"); ap_object_add_context( o, "https://apogee.polaris-1.work/schemas/litepub-0.1.jsonld"); 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 = ap_OrderedCollectionPage; 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)); ap_object_add_context( o, "https://www.w3.org/ns/activitystreams"); ap_object_add_context( o, "https://apogee.polaris-1.work/schemas/litepub-0.1.jsonld"); o->type = ap_OrderedCollection; o->published = time(NULL); o->id = aformat( "https://%s/owner/followers", g_server->domain ); 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->domain ); char page_format[512]; snprintf( page_format, 512, "https://%s/owner/followers/page-%%d", g_server->domain ); 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)); ap_object_add_context( o, "https://www.w3.org/ns/activitystreams"); ap_object_add_context( o, "https://apogee.polaris-1.work/schemas/litepub-0.1.jsonld"); o->type = ap_OrderedCollection; o->published = time(NULL); o->id = aformat( "https://%s/owner/following", g_server->domain ); 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->domain ); char page_format[512]; snprintf( page_format, 512, "https://%s/owner/following/page-%%d", g_server->domain ); 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 ); }