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
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 );
|
|
}
|
|
|