You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

308 lines
9.4 KiB
C

#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 <stdlib.h>
#include <string.h>
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 );
}