Split apart application-specific AcitivytPub code and generic ap_object code, rename ap_object type enumeration values, start work to ingest external Activities

master
teknomunk 1 year ago
parent c5445ca9a5
commit f2a61e0b71

@ -4,12 +4,13 @@
#include "http/server/request.h"
#include "collections/array.h"
#include "format.h"
#include "model/ap/object.h"
// Model
#include "model/server.h"
#include "model/status.h"
#include "model/account.h"
#include "model/ap/activity.h"
#include "model/activity.h"
#include <string.h>
#include <stdlib.h>
@ -31,7 +32,7 @@ bool route_ap_note( struct http_request* req )
if( !s ) { return false; }
if( s->remote ) { return false; }
struct ap_object* act = ap_activity_create_note( s );
struct ap_object* act = activity_create_Note( s );
http_request_send_headers( req, 200, "application/ld+json", true );
FILE* f = http_request_get_response_body( req );

@ -77,19 +77,28 @@ failed:
goto cleanup;
}
bool check_bearer_token( struct http_request* req )
bool check_bearer_token( const char* auth_token )
{
// Check bearer token
const char* auth_token = http_request_get_header( req, "Authorization" );
if( !auth_token ) { return false; }
if( 0 != strncmp( auth_token, "Bearer ", 7 ) ) { return false; }
char* client_id = strndup( &auth_token[7], 32 );
struct client_app* app = client_app_from_id( client_id );
free(client_id);
if( !app ) { return false; }
if( 0 != strcmp( &auth_token[7], app->access_token ) ) { return false; }
if( 0 != strcmp( &auth_token[7], app->access_token ) ) {
client_app_free(app);
return false;
}
client_app_free(app);
return true;
}
bool check_authentication_header( struct http_request* req )
{
// Check bearer token
const char* auth_token = http_request_get_header( req, "Authorization" );
if( !auth_token ) { return false; }
return check_bearer_token( auth_token );
}

@ -5,5 +5,6 @@
struct http_request;
bool handle_mastodon_api_apps( struct http_request* req );
bool check_bearer_token( struct http_request* req );
bool check_authentication_header( struct http_request* req );
bool check_bearer_token( const char* auth_header );

@ -6,16 +6,20 @@
#include "ffdb/fs_list.h"
#include "collections/array.h"
#include "format.h"
#include "model/ap/object.h"
// Model
#include "model/server.h"
#include "model/status.h"
#include "model/account.h"
#include "model/notification.h"
#include "model/ap/activity.h"
#include "model/activity.h"
#include "model/ap/inbox_envelope.h"
#include "model/crypto/http_sign.h"
#include "controller/inbox/follow.h"
#include "src/controller/api/client_apps.h"
// Stdlib
#include <stdio.h>
#include <stdint.h>
@ -25,6 +29,7 @@
extern bool terminate;
// Route: /inbox
bool route_inbox( struct http_request* req )
{
// No subroutes
@ -40,54 +45,6 @@ bool route_inbox( struct http_request* req )
return true;
}
static bool route_follow( struct ap_object* act )
{
struct account* follower = NULL;
bool res = false;
struct ap_object* accept = NULL;
const char* target = act->object.ref;
struct account* a = account_from_uri( target );
// Don't process follows for remote users
if( !a || 0 != strcmp( a->server, g_server->domain ) ) {
printf( "Unfollow not targeted at local account. Discarding.\n" );
goto success;
}
// Get account for the follower
follower = account_from_uri( act->actor );
if( !follower ) {
follower = account_fetch_from_uri( act->actor );
}
if( follower->id == owner_account_id ) {
// Something went wrong, bail
goto failed;
}
if( !follower ) {
printf( "Unable to fetch account for %s\n", act->actor );
goto failed;
}
// Add the follower
account_add_follower( a, follower );
// Create Accept activity
ap_activity_accept( act, follower->id );
success:
res = true;
goto cleanup;
cleanup:
account_free(a);
account_free(follower);
return res;
failed:
res = false;
goto cleanup;
}
static bool route_undo_Announce( struct ap_object* act )
{
struct status* s = NULL;
@ -122,9 +79,9 @@ static bool route_undo_activity( struct ap_object* act )
}
switch( act->object.ptr->type ) {
case apat_follow: return route_undo_follow( act );
case apat_announce: return route_undo_Announce( act );
case apat_like: return route_undo_Like( act );
case ap_Follow: return route_undo_follow( act );
case ap_Announce: return route_undo_Announce( act );
case ap_Like: return route_undo_Like( act );
default:
printf( "Unhandled object activity type %d in undo\n", act->object.ptr->type );
return false;
@ -262,19 +219,19 @@ static bool route_update( struct ap_object* act )
if( act->object.tag == apaot_activity ) {
switch( act->object.ptr->type ) {
case apat_note:
case ap_Note:
{
s = status_from_uri( act->object.ptr->id );
if( !s ) { return true; } // Status not available locally, discard
// TODO: update status
}; break;
case apat_person:
case ap_Person:
return route_update_Person(act);
case apat_question:
case ap_Question:
// TODO: update Poll
return true;
case apot_service:
case ap_Service:
// discard
return true;
}
@ -474,24 +431,68 @@ bool route_activity( struct ap_object* act )
ap_object_write_to_FILE( act, stdout );
switch( act->type ) {
case apat_undo: return route_undo_activity(act);
case apat_follow: return route_follow(act);
case apat_like: return route_like(act);
case apat_create: return route_create(act);
case apat_accept: return route_accept(act);
case apat_announce: return route_announce(act);
case apat_add: return route_add(act);
case apat_emoji_react: return route_emoji_react(act);
case apat_update: return route_update(act);
case apat_move: return route_move(act);
case apat_block: return route_block(act);
case apat_remove: return true;
case ap_Undo: return route_undo_activity(act);
case ap_Follow: return route_follow(act);
case ap_Like: return route_like(act);
case ap_Create: return route_create(act);
case ap_Accept: return route_accept(act);
case ap_Announce: return route_announce(act);
case ap_Add: return route_add(act);
case pleroma_EmojiReact: return route_emoji_react(act);
case ap_Update: return route_update(act);
case ap_Move: return route_move(act);
case ap_Block: return route_block(act);
case ap_Remove: return true;
default:
printf( "Unhandled activity type: %d\n", act->type );
}
return false;
}
bool handle_forward( struct ap_envelope* env, struct ap_object* act )
{
char* authentication = NULL;
struct account* a = NULL;
bool result = false;
for( int i = 0; i < env->headers.count; ++i ) {
char* key = env->headers.items[i].key;
char* value = env->headers.items[i].value;
if( 0 == strcasecmp( "Authentication", key ) ) {
free(authentication);
authentication = strdup(value);
}
}
// Make sure this action is allowed
if( !authentication ) { return false; }
if( !check_bearer_token( authentication ) ) { goto failed; }
a = account_from_uri( act->actor );
if( !a->local ) { goto failed; };
// Save to disk
activity_allocate_local_id(act);
activity_save(act);
// Debug
printf( "Forwarding activity. act=" );
ap_object_write_to_FILE( act, stdout );
// TODO: try to inject to create a status
// Deliver activity
activity_deliver( act );
goto failed;
cleanup:
account_free(a);
free(authentication);
return result;
failed:
result = false;
goto cleanup;
}
static bool process_one()
{
// Items requiring cleanup
@ -522,6 +523,8 @@ static bool process_one()
act = ap_object_from_FILE(f);
if( !act ) { goto failed; }
if( handle_forward( env, act ) ) { goto discard; }
// Sanitize actor
if( !act->actor ) {
if( !act->attributed_to ) {
@ -532,13 +535,15 @@ static bool process_one()
}
// Discard delete requests
if( act->type == apat_delete ) {
if( act->type == ap_Delete ) {
goto discard;
}
// Validate signature
env->validated = http_signature_validate( env, "post /inbox", act->actor );
if( !env->validated ) { goto discard; }
printf( "Processing %d\n", id );

@ -0,0 +1,58 @@
#include "follow.h"
#include "model/ap/object.h"
#include "model/activity.h"
#include "model/account.h"
#include "model/server.h"
#include <string.h>
bool route_follow( struct ap_object* act )
{
struct account* follower = NULL;
bool res = false;
struct ap_object* accept = NULL;
const char* target = act->object.ref;
struct account* a = account_from_uri( target );
// Don't process follows for remote users
if( !a || 0 != strcmp( a->server, g_server->domain ) ) {
printf( "Unfollow not targeted at local account. Discarding.\n" );
goto success;
}
// Get account for the follower
follower = account_from_uri( act->actor );
if( !follower ) {
follower = account_fetch_from_uri( act->actor );
}
if( follower->id == owner_account_id ) {
// Something went wrong, bail
goto failed;
}
if( !follower ) {
printf( "Unable to fetch account for %s\n", act->actor );
goto failed;
}
// Add the follower
account_add_follower( a, follower );
// Create Accept activity
activity_accept( act, follower->id );
success:
res = true;
goto cleanup;
cleanup:
account_free(a);
account_free(follower);
return res;
failed:
res = false;
goto cleanup;
}

@ -0,0 +1,8 @@
#pragma once
#include <stdbool.h>
struct ap_object;
bool route_follow( struct ap_object* act );

@ -205,7 +205,7 @@ bool route_mastodon_api( struct http_request* req )
return route_custom_emojis(req);
}
if( !check_bearer_token(req) ) { return false; }
if( !check_authentication_header(req) ) { return false; }
if( http_request_route( req, "pleroma" ) ) {
return route_pleroma_api( req );

@ -4,13 +4,14 @@
#include "ffdb/fs_list.h"
#include "collections/array.h"
#include "http/client/client.h"
#include "model/ap/object.h"
// Model
#include "model/server.h"
#include "model/crypto/keys.h"
#include "model/crypto/http_sign.h"
#include "model/account.h"
#include "model/ap/activity.h"
#include "model/activity.h"
#include "model/ap/outbox_envelope.h"
#include "model/ap/activity/rsa_signature_2017.h"
@ -47,7 +48,7 @@ static bool process_envelope( struct outbox_envelope* env )
}
// Load target account
act = ap_activity_from_local_id( env->activity_id );
act = activity_from_local_id( env->activity_id );
if( !act ) {
printf( "! No activity\n" );
goto failed;

@ -5,7 +5,8 @@
#include "collections/array.h"
#include "model/account.h"
#include "model/ap/activity.h"
#include "model/activity.h"
#include "model/ap/object.h"
#include "model/ap/activity/rsa_signature_2017.h"
#include "model/ap/outbox_envelope.h"
#include "model/status.h"
@ -20,7 +21,7 @@
void develop()
{
// TODO: remove memory leak
struct ap_object* act = ap_activity_from_local_id(27);
struct ap_object* act = activity_from_local_id(27);
ap_object_free(act);
}

@ -1 +1 @@
Subproject commit f887e970303d2602ef9cdf9a51756fc46631964d
Subproject commit 7eb2ff95d464fba91d7ba4e496aa37a80cbec1de

@ -64,6 +64,7 @@ static struct json_field_type string_pair_type = {
#define OBJ_TYPE struct account
static struct json_object_field account_layout[] = {
JSON_FIELD_STRING( handle, true ),
JSON_FIELD_BOOL( local, false ),
JSON_FIELD_STRING( server, true ),
JSON_FIELD_STRING( display_name, true ),
JSON_FIELD_BOOL( stub, false ),
@ -170,6 +171,10 @@ struct account* account_from_id( int id )
a->banner = aformat( "https://%s/server/default-banner.blob", g_server->domain );
}
if( a->id == owner_account_id ) {
a->local = true;
}
return a;
}

@ -35,6 +35,7 @@ enum account_type
struct account
{
unsigned int id;
bool local;
char* handle;
char* server;
char* display_name;

@ -6,7 +6,8 @@
#include "model/server.h"
#include "model/status.h"
#include "model/ap/activity.h"
#include "model/activity.h"
#include "model/ap/object.h"
#include <stdlib.h>
#include <string.h>
@ -28,7 +29,7 @@ struct ap_object* account_ap_actor( struct account* a )
memset(act,0,sizeof(*act));
act->published = time(NULL);
act->type = apot_person;
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);
@ -69,7 +70,7 @@ struct ap_object* account_ap_actor( struct account* a )
at->name = strdup(a->fields.items[i].key);
at->value = strdup(a->fields.items[i].value);
at->type = apot_property_value;
at->type = schema_PropertyValue;
array_append( &act->attachments, sizeof(at), &at );
}
@ -83,7 +84,7 @@ struct ap_object* account_ap_outbox( struct account* a )
outbox = malloc(sizeof(*outbox));
memset(outbox,0,sizeof(*outbox));
outbox->type = apot_ordered_collection;
outbox->type = ap_OrderedCollection;
outbox->published = time(NULL);
outbox->id = aformat( "https://%s/outbox", g_server->domain );
outbox->first.tag = apaot_ref;
@ -115,7 +116,9 @@ struct ap_object* account_ap_outbox_page( struct account* a, int page )
for( int i = 0; i < values.count; ++i ) {
int id;
int activity_id = 0;
if( 1 == sscanf( values.items[i], "%d", &id ) ) {
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; }
@ -123,7 +126,11 @@ struct ap_object* account_ap_outbox_page( struct account* a, int page )
if( s->activity_id == 0 ) {
goto include_tombstone;
}
struct ap_object* act = ap_activity_from_local_id( s->activity_id );
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;
@ -131,8 +138,6 @@ struct ap_object* account_ap_outbox_page( struct account* a, int page )
r.ptr = act;
array_append( &outbox->collection_items, sizeof(r), &r );
status_free(s);
} else {
include_tombstone:
// include Tombstone
@ -145,7 +150,7 @@ struct ap_object* account_ap_outbox_page( struct account* a, int page )
} else {
o->id = aformat( "https://%s/activity/tombstone", g_server->domain );
}
o->type = apot_tombstone;
o->type = ap_Tombstone;
struct ap_object_ptr_or_ref r;
r.tag = apaot_activity;
@ -157,7 +162,7 @@ struct ap_object* account_ap_outbox_page( struct account* a, int page )
}
free( values.items );
outbox->type = apot_ordered_collection_page;
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 );
@ -205,7 +210,7 @@ static struct ap_object* account_list_page( int page, char* part_of, const char*
}
free( values.items );
o->type = apot_ordered_collection_page;
o->type = ap_OrderedCollectionPage;
o->published = time(NULL);
o->part_of = part_of;
o->id = aformat( page_format, page );
@ -227,7 +232,7 @@ struct ap_object* account_ap_followers( struct account* a )
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
o->type = apot_ordered_collection;
o->type = ap_OrderedCollection;
o->published = time(NULL);
o->id = aformat( "https://%s/owner/followers", g_server->domain );
o->first.tag = apaot_object;
@ -260,7 +265,7 @@ struct ap_object* account_ap_following( struct account* a )
o = malloc(sizeof(*o));
memset(o,0,sizeof(*o));
o->type = apot_ordered_collection;
o->type = ap_OrderedCollection;
o->published = time(NULL);
o->id = aformat( "https://%s/owner/following", g_server->domain );
o->first.tag = apaot_object;

@ -45,7 +45,7 @@ bool account_sync_from_activity_pub( unsigned int account_id )
if( obj->banner ) {
a->banner = strdup(obj->banner);
}
a->bot = ( obj->type != apot_person );
a->bot = ( obj->type != ap_Person );
a->account_type = at_remote_activity_pub;
if( obj->url ) {
a->account_url = strdup(obj->url);

@ -1,13 +1,13 @@
#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/activity.h"
#include "model/ap/object.h"
#include "model/ap/outbox_envelope.h"
#include <stdlib.h>
@ -72,9 +72,9 @@ void account_deliver_activity_to_followers( struct account* a, struct ap_object*
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);
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;
@ -100,13 +100,10 @@ void account_create( struct account* a, struct status* s )
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->domain, id );
act->local_id = id;
activity_allocate_local_id(act);
act->id = aformat( "https://%s/activity/%d", g_server->domain, act->local_id );
act->published = time(NULL);
act->type = apat_announce;
act->type = ap_Announce;
act->actor = strdup(a->account_url);
act->object.tag = apaot_ref;
act->object.ref = strdup(original_post->url);
@ -132,7 +129,7 @@ void account_announce( struct account* a, struct status* original_post, struct s
outbox_envelope_list_save(&oel);
outbox_envelope_list_free_composite(&oel);
ap_activity_save(act);
activity_save(act);
ap_object_write_to_FILE( act, stdout );
// Link status to activity
@ -154,7 +151,7 @@ void account_follow( struct account* a, struct account* to_follow )
char value[32];
// Federate Follow Activity
int act_id = ap_activity_follow( a, to_follow );
int act_id = activity_follow( a, to_follow );
// Update following list
account_add_follower( to_follow, a );
@ -185,11 +182,11 @@ void account_unfollow( struct account* a, struct account* to_unfollow )
}
// Lookup the Activity used to federate following this account
struct ap_object* act = ap_activity_from_local_id( atoi(res) );
struct ap_object* act = activity_from_local_id( atoi(res) );
free(res);
// Federate Undo Activity
ap_activity_undo(act,to_unfollow->id);
activity_undo(act,to_unfollow->id);
ap_object_free(act);
// Update following list
@ -201,16 +198,13 @@ void account_unfollow( struct account* a, struct account* to_unfollow )
void account_update( struct account* a )
{
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set("data/activities/HEAD", id );
struct ap_object* act;
act = malloc(sizeof(*act));
memset(act,0,sizeof(*act));
act->local_id = id;
act->id = aformat( "https://%s/activity/%d", g_server->domain, id );
act->type = apat_update;
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;
@ -228,7 +222,7 @@ void account_update( struct account* a )
outbox_envelope_list_save(&oel);
outbox_envelope_list_free_composite(&oel);
ap_activity_save(act);
activity_save(act);
ap_object_free(act);
}

@ -0,0 +1,332 @@
#define _GNU_SOURCE
#include "activity.h"
#include "format.h"
#include "ffdb/fs_list.h"
#include "collections/array.h"
// TODO: change to submodule
#include "model/ap/object.h"
#include "model/server.h"
#include "model/account.h"
#include "model/status.h"
#include "model/emoji.h"
#include "model/media.h"
#include "model/ap/outbox_envelope.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void activity_allocate_local_id( struct ap_object* obj )
{
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set( "data/activities/HEAD", id );
obj->local_id = id;
}
void activity_save( struct ap_object* act )
{
char filename[512];
snprintf( filename, sizeof(filename), "data/activities/%d.json", act->local_id );
json_write_object_layout_to_file( filename, "\t", ap_object_layout, act );
}
struct ap_object* activity_from_local_id( int id )
{
struct ap_object* act = ap_activity_new();
char filename[512];
snprintf( filename, sizeof(filename), "data/activities/%d.json", id );
if( !json_read_object_layout_from_file( filename, ap_object_layout, act ) ) {
ap_object_free(act);
return NULL;
}
return act;
}
struct ap_object* activity_create_Accept( struct ap_object* act )
{
struct ap_object* accept = malloc(sizeof(struct ap_activity));
memset(accept,0,sizeof(*accept));
activity_allocate_local_id(accept);
asprintf( &accept->id,"https://%s/activity/%d", g_server->domain, accept->id );
accept->type = ap_Accept;
asprintf( &accept->actor, "https://%s/owner/actor", g_server->domain );
char* new_act_actor = strdup(act->actor);
array_append( &accept->to, sizeof(char*), &new_act_actor );
accept->object.tag = apaot_activity;
accept->object.ptr = ap_activity_dup(act);
return accept;
}
void activity_accept( struct ap_object* act, int deliver_to_account_id )
{
struct ap_object* accept = activity_create_Accept(act);
activity_save(accept);
struct outbox_envelope* env = outbox_envelope_new();
env->activity_id = accept->local_id;
env->account_id = deliver_to_account_id;
outbox_envelope_save( env );
outbox_envelope_free( env );
ap_object_free(accept);
}
struct ap_object* activity_create_EmojiReact( struct status* s, const char* react )
{
if( !*react ) {
return NULL;
}
struct account* a = account_from_id( s->account_id );
struct ap_object* act = ap_activity_new();
activity_allocate_local_id(act);
act->id = aformat( "https://%s/activity/%d", g_server->domain, act->local_id );
act->actor = aformat( "https://%s/owner/actor", g_server->domain );
act->type = pleroma_EmojiReact;
act->content.content = safe_strdup(react);
act->published = time(NULL);
act->object.tag = apaot_ref;
act->object.ref = strdup( s->url );
char* to = aformat( "%s/followers", a->account_url );
array_append( &act->to, sizeof(to), &to );
char* cc = strdup( "https://www.w3.org/ns/activitystreams#Public" );
array_append( &act->cc, sizeof(cc), &cc );
cc = strdup( a->account_url );
array_append( &act->cc, sizeof(cc), &cc );
account_free(a);
return act;
}
void activity_react( struct status* s, const char* react )
{
struct ap_object* act = activity_create_EmojiReact(s,react);
if( !act ) { return; }
activity_save(act);
struct outbox_envelope* env = outbox_envelope_new();
env->activity_id = act->local_id;
env->account_id = s->account_id;
outbox_envelope_save( env );
outbox_envelope_free( env );
ap_object_free(act);
}
struct ap_object* activity_create_Follow( struct account* follower, struct account* following )
{
struct ap_object* act = ap_activity_new();
activity_allocate_local_id(act);
act->id = aformat( "https://%s/activity/%d", g_server->domain, act->local_id );
act->actor = strdup( follower->account_url );
act->type = ap_Follow;
act->published = time(NULL);
act->object.tag = apaot_ref;
act->state = strdup("pending");
act->object.ref = strdup( following->account_url );
char* to = strdup(following->account_url);
array_append( &act->to, sizeof(to), &to );
return act;
}
int activity_follow( struct account* follower, struct account* following )
{
int res = -1;
struct ap_object* act = activity_create_Follow( follower, following );
if( !act ) { goto failed; }
activity_save(act);
res = act->local_id;
struct outbox_envelope* env = outbox_envelope_new();
if( !env ) { goto failed; }
env->activity_id = act->local_id;
env->account_id = following->id;
outbox_envelope_save( env );
outbox_envelope_free( env );
cleanup:
ap_object_free(act);
return res;
failed:
res = -1;
goto cleanup;
}
struct ap_object* activity_create_Undo( struct ap_activity* act_to_undo )
{
struct ap_object* act = ap_activity_new();
activity_allocate_local_id(act);
act->id = aformat( "https://%s/activity/%d", g_server->domain, act->local_id );
act->actor = strdup( act_to_undo->actor );
act->type = ap_Undo;
act->published = time(NULL);
act->object.tag = apaot_activity;
act->object.ptr = ap_activity_dup(act_to_undo);
char* to = strdup(act_to_undo->object.ref);
array_append( &act->to, sizeof(to), &to );
return act;
}
void activity_undo( struct ap_object* act, int deliver_to_account_id )
{
struct ap_object* undo_act = activity_create_Undo( act );
if( !undo_act ) { goto failed; }
activity_save(undo_act);
/*
printf( "act=" );
ap_activity_write_to_FILE( act, stdout );
printf( "\nundo_act=");
ap_activity_write_to_FILE( undo_act, stdout );
printf( "\n" );
*/
struct outbox_envelope* env = outbox_envelope_new();
if( !env ) { goto failed; }
env->activity_id = undo_act->local_id;
env->account_id = deliver_to_account_id;
outbox_envelope_save( env );
outbox_envelope_free( env );
cleanup:
ap_object_free(undo_act);
return;
failed:
goto cleanup;
}
struct ap_object* activity_create_Like( struct status* s )
{
return NULL;
}
int activity_like( struct status* s )
{
printf( "TODO: like\n" );
return -1;
}
struct ap_object* activity_create_Note( struct status* s )
{
struct ap_object* act = ap_activity_new();
act->id = aformat( "https://%s/note/%d", g_server->domain, s->id );
act->type = ap_Note;
act->published = s->published;
act->source.content = strdup(s->source);
act->content.content = strdup(status_render_source(s));
if( s->in_reply_to ) {
struct status* s_in_reply_to = status_from_id( s->in_reply_to );
act->in_reply_to = strdup( s_in_reply_to->url );
status_free(s_in_reply_to);
}
/* set account related parameters */ {
struct account* a = account_from_id( s->account_id );
act->actor = strdup( a->account_url );
act->attributed_to = strdup( a->account_url );
account_free(a);
}
char* str = strdup("https://www.w3.org/ns/activitystreams#Public");
array_append( &act->to, sizeof(str), &str );
for( int i = 0; i < s->media.count; ++i ) {
struct media* m = media_from_local_uri( s->media.items[i] );
if( m ) {
struct ap_attachment* att;
att = malloc(sizeof(*att));
memset(att,0,sizeof(*att));
att->type = ap_Document;
att->url = strdup(s->media.items[i]);
att->mediaType = strdup(m->content_type);
att->name = strdup("");
array_append( &act->attachments, sizeof(att), &att );
}
media_free(m);
}
for( int i = 0; i < s->emoji.count; ++i ) {
struct emoji* e = s->emoji.items[i];
struct ap_activity_tag* tag;
tag = malloc(sizeof(*tag));
memset(tag,0,sizeof(*tag));
tag->type = aptag_emoji;
tag->icon.url = strdup(e->url);
tag->id = strdup(e->url);
tag->icon.type = ap_Image;
tag->name = aformat(":%s:", e->shortcode );
array_append( &act->tags, sizeof(tag), &tag );
}
str = aformat( "https://%s/owner/followers", g_server->domain );
array_append( &act->cc, sizeof(str), &str );
for( int i = 0; i < s->mentions.count; ++i ) {
struct account* mentioned = account_from_id( s->mentions.items[i] );
str = strdup(mentioned->account_url);
array_append( &act->to, sizeof(str), &str );
struct ap_activity_tag* tag;
tag = malloc(sizeof(*tag));
memset(tag,0,sizeof(*tag));
tag->type = aptag_mention;
tag->href = strdup(mentioned->account_url);
tag->name = aformat( "%s@%s", mentioned->handle, mentioned->server );
array_append( &act->tags, sizeof(tag), &tag );
account_free(mentioned);
}
return act;
}
struct ap_object* activity_create_Create( struct ap_activity* object )
{
struct ap_object* act = ap_activity_new();
activity_allocate_local_id(act);
act->type = ap_Create;
act->id = aformat( "https://%s/activity/%d", g_server->domain, act->local_id );
act->object.tag = apaot_activity;
act->object.ptr = ap_activity_dup(object);
act->actor = strdup( object->actor );
act->published = object->published;
char* str;
for( int i = 0; i < object->to.count; ++i ) {
str = strdup( object->to.items[i] );
array_append( &act->to, sizeof(str), &str );
}
for( int i = 0; i < object->cc.count; ++i ) {
str = strdup( object->cc.items[i] );
array_append( &act->cc, sizeof(str), &str );
}
return act;
}
void activity_deliver( struct ap_object* obj )
{
// TODO: handle delivery
}

@ -0,0 +1,30 @@
#pragma once
struct ap_object;
struct status;
struct account;
// Database management
struct ap_object* activity_from_local_id( int id );
void activity_save( struct ap_object* o );
void activity_allocate_local_id( struct ap_object* obj );
// creation
struct ap_object* activity_create_Accept( struct ap_object* act );
struct ap_object* activity_create_EmojiReact( struct status* s, const char* react );
struct ap_object* activity_create_Undo( struct ap_object* act );
struct ap_object* activity_create_Like( struct status* s );
struct ap_object* activity_create_Note( struct status* s );
struct ap_object* activity_create_Create( struct ap_object* act );
struct ap_object* activity_create_Accept( struct ap_object* act );
// Actions
void activity_react( struct status* s, const char* react );
int activity_follow( struct account* follower, struct account* following );
void activity_unfollow( struct account* follower, struct account* to_unfollow );
void activity_undo( struct ap_object* act, int deliver_to_account_id );
int activity_like( struct status* s );
void activity_accept( struct ap_object* act, int deliver_to_account_id );
void activity_deliver( struct ap_object* obj );

@ -237,294 +237,3 @@ void ap_object_ptr_or_ref_free_composite( struct ap_object_ptr_or_ref* o )
};
}
struct ap_activity* ap_activity_create_accept( struct ap_activity* act )
{
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set( "data/activities/HEAD", id );
struct ap_activity* accept = malloc(sizeof(struct ap_activity));
memset(accept,0,sizeof(*accept));
asprintf( &accept->id,"https://%s/activity/%d", g_server->domain, id );
accept->local_id = id;
accept->type = apat_accept;
asprintf( &accept->actor, "https://%s/owner/actor", g_server->domain );
char* new_act_actor = strdup(act->actor);
array_append( &accept->to, sizeof(char*), &new_act_actor );
accept->object.tag = apaot_activity;
accept->object.ptr = ap_activity_dup(act);
return accept;
}
void ap_activity_accept( struct ap_activity* act, int deliver_to_account_id )
{
struct ap_activity* accept = ap_activity_create_accept(act);
ap_activity_save(accept);
struct outbox_envelope* env = outbox_envelope_new();
env->activity_id = accept->local_id;
env->account_id = deliver_to_account_id;
outbox_envelope_save( env );
outbox_envelope_free( env );
ap_object_free(accept);
}
struct ap_activity* ap_activity_create_emoji_react( struct status* s, const char* react )
{
if( !*react ) {
return NULL;
}
struct account* a = account_from_id( s->account_id );
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set( "data/activities/HEAD", id );
struct ap_activity* act = ap_activity_new();
act->local_id = id;
act->id = aformat( "https://%s/activity/%d", g_server->domain, id );
act->actor = aformat( "https://%s/owner/actor", g_server->domain );
act->type = apat_emoji_react;
act->content.content = safe_strdup(react);
act->published = time(NULL);
act->object.tag = apaot_ref;
act->object.ref = strdup( s->url );
char* to = aformat( "%s/followers", a->account_url );
array_append( &act->to, sizeof(to), &to );
char* cc = strdup( "https://www.w3.org/ns/activitystreams#Public" );
array_append( &act->cc, sizeof(cc), &cc );
cc = strdup( a->account_url );
array_append( &act->cc, sizeof(cc), &cc );
account_free(a);
return act;
}
void ap_activity_react( struct status* s, const char* react )
{
struct ap_activity* act = ap_activity_create_emoji_react(s,react);
if( !act ) { return; }
ap_activity_save(act);
struct outbox_envelope* env = outbox_envelope_new();
env->activity_id = act->local_id;
env->account_id = s->account_id;
outbox_envelope_save( env );
outbox_envelope_free( env );
ap_object_free(act);
}
struct ap_activity* ap_activity_create_follow( struct account* follower, struct account* following )
{
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set( "data/activities/HEAD", id );
struct ap_activity* act = ap_activity_new();
act->local_id = id;
act->id = aformat( "https://%s/activity/%d", g_server->domain, id );
act->actor = strdup( follower->account_url );
act->type = apat_follow;
act->published = time(NULL);
act->object.tag = apaot_ref;
act->state = strdup("pending");
act->object.ref = strdup( following->account_url );
char* to = strdup(following->account_url);
array_append( &act->to, sizeof(to), &to );
return act;
}
int ap_activity_follow( struct account* follower, struct account* following )
{
int res = -1;
struct ap_activity* act = ap_activity_create_follow( follower, following );
if( !act ) { goto failed; }
ap_activity_save(act);
res = act->local_id;
struct outbox_envelope* env = outbox_envelope_new();
if( !env ) { goto failed; }
env->activity_id = act->local_id;
env->account_id = following->id;
outbox_envelope_save( env );
outbox_envelope_free( env );
cleanup:
ap_object_free(act);
return res;
failed:
res = -1;
goto cleanup;
}
struct ap_activity* ap_activity_create_undo( struct ap_activity* act_to_undo )
{
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set( "data/activities/HEAD", id );
struct ap_activity* act = ap_activity_new();
act->local_id = id;
act->id = aformat( "https://%s/activity/%d", g_server->domain, id );
act->actor = strdup( act_to_undo->actor );
act->type = apat_undo;
act->published = time(NULL);
act->object.tag = apaot_activity;
act->object.ptr = ap_activity_dup(act_to_undo);
char* to = strdup(act_to_undo->object.ref);
array_append( &act->to, sizeof(to), &to );
return act;
}
void ap_activity_undo( struct ap_activity* act, int deliver_to_account_id )
{
struct ap_activity* undo_act = ap_activity_create_undo( act );
if( !undo_act ) { goto failed; }
ap_activity_save(undo_act);
/*
printf( "act=" );
ap_activity_write_to_FILE( act, stdout );
printf( "\nundo_act=");
ap_activity_write_to_FILE( undo_act, stdout );
printf( "\n" );
*/
struct outbox_envelope* env = outbox_envelope_new();
if( !env ) { goto failed; }
env->activity_id = undo_act->local_id;
env->account_id = deliver_to_account_id;
outbox_envelope_save( env );
outbox_envelope_free( env );
cleanup:
ap_object_free(undo_act);
return;
failed:
goto cleanup;
}
struct ap_activity* ap_activity_create_like( struct status* s )
{
}
int ap_activity_like( struct status* s )
{
printf( "TODO: like\n" );
return -1;
}
struct ap_activity* ap_activity_create_note( struct status* s )
{
struct ap_activity* act = ap_activity_new();
act->id = aformat( "https://%s/note/%d", g_server->domain, s->id );
act->type = apat_note;
act->published = s->published;
act->source.content = strdup(s->source);
act->content.content = strdup(status_render_source(s));
if( s->in_reply_to ) {
struct status* s_in_reply_to = status_from_id( s->in_reply_to );
act->in_reply_to = strdup( s_in_reply_to->url );
status_free(s_in_reply_to);
}
/* set account related parameters */ {
struct account* a = account_from_id( s->account_id );
act->actor = strdup( a->account_url );
act->attributed_to = strdup( a->account_url );
account_free(a);
}
char* str = strdup("https://www.w3.org/ns/activitystreams#Public");
array_append( &act->to, sizeof(str), &str );
for( int i = 0; i < s->media.count; ++i ) {
struct media* m = media_from_local_uri( s->media.items[i] );
if( m ) {
struct ap_attachment* att;
att = malloc(sizeof(*att));
memset(att,0,sizeof(*att));
att->type = apot_document;
att->url = strdup(s->media.items[i]);
att->mediaType = strdup(m->content_type);
att->name = strdup("");
array_append( &act->attachments, sizeof(att), &att );
}
media_free(m);
}
for( int i = 0; i < s->emoji.count; ++i ) {
struct emoji* e = s->emoji.items[i];
struct ap_activity_tag* tag;
tag = malloc(sizeof(*tag));
memset(tag,0,sizeof(*tag));
tag->type = aptag_emoji;
tag->icon.url = strdup(e->url);
tag->id = strdup(e->url);
tag->icon.type = apot_image;
tag->name = aformat(":%s:", e->shortcode );
array_append( &act->tags, sizeof(tag), &tag );
}
str = aformat( "https://%s/owner/followers", g_server->domain );
array_append( &act->cc, sizeof(str), &str );
for( int i = 0; i < s->mentions.count; ++i ) {
struct account* mentioned = account_from_id( s->mentions.items[i] );
str = strdup(mentioned->account_url);
array_append( &act->to, sizeof(str), &str );
struct ap_activity_tag* tag;
tag = malloc(sizeof(*tag));
memset(tag,0,sizeof(*tag));
tag->type = aptag_mention;
tag->href = strdup(mentioned->account_url);
tag->name = aformat( "%s@%s", mentioned->handle, mentioned->server );
array_append( &act->tags, sizeof(tag), &tag );
account_free(mentioned);
}
return act;
}
struct ap_activity* ap_activity_create_Create( struct ap_activity* object )
{
int id = fs_list_get("data/activities/HEAD") + 1;
fs_list_set( "data/activities/HEAD", id );
struct ap_activity* act = ap_activity_new();
act->local_id = id;
act->type = apat_create;
act->id = aformat( "https://%s/activity/%d", g_server->domain, id );
act->object.tag = apaot_activity;
act->object.ptr = ap_activity_dup(object);
act->actor = strdup( object->actor );
act->published = object->published;
char* str;
for( int i = 0; i < object->to.count; ++i ) {
str = strdup( object->to.items[i] );
array_append( &act->to, sizeof(str), &str );
}
for( int i = 0; i < object->cc.count; ++i ) {
str = strdup( object->cc.items[i] );
array_append( &act->cc, sizeof(str), &str );
}
return act;
}

@ -1,257 +1,4 @@
#pragma once
#include "json/layout.h"
#include "object.h"
#include "activity/context.h"
#include "activity/tag.h"
#include <time.h>
// Reference: https://www.w3.org/TR/activitystreams-vocabulary/
// TODO: rename this to ap_object, as this is really an Object base and not an Activity base
struct ap_object;
#define ap_activity ap_object
struct ap_activity_source
{
char* mime_type;
char* content;
// TODO: add map;
};
enum ap_signature_type
{
apst_rsa_signature_2017 = 1,
};
struct ap_signature
{
int type;
char* creator;
time_t created;
char* value;
};
enum ap_object_type
{
// Activity Types
apat_undo = 1,
apat_follow = 2,
apat_delete = 3,
apat_accept = 4,
apat_create = 5,
apat_tentative_accept = 6,
apat_add = 7,
apat_arrive = 8,
apat_ignore = 9,
apat_join = 10,
apat_leave = 11,
apat_like = 12,
apat_offer = 13,
apat_invite = 14,
apat_reject = 15,
apat_tentative_reject = 16,
apat_remove = 17,
apat_update = 18,
apat_view = 19,
apat_listen = 20,
apat_read = 21,
apat_move = 22,
apat_travel = 23,
apat_announce = 24,
apat_block = 25,
apat_flag = 26,
apat_dislike = 27,
apat_emoji_react = 28,
// Object Types
apat_note = 100,
apat_question = 101,
apot_article = 102,
apot_audio = 103,
apot_document = 104,
apot_event = 105,
apot_image = 106,
apot_page = 107,
apot_place = 108,
apot_profile = 109,
apot_relationship = 110,
apot_tombstone = 111,
apot_video = 112,
// Actor Types
apot_person = 200, apat_person = 200,
apot_service = 201,
apot_application = 202,
apot_group = 203,
apot_organization = 204,
// Collection Types
apot_ordered_collection = 301,
apot_ordered_collection_page = 302,
apot_collection = 303,
apot_collection_page = 304,
// Other
apot_property_value = 401,
};
extern struct json_enum ap_object_type_enum[];
enum ap_activity_object_type {
apaot_ref = 1,
apaot_activity = 2,
apaot_object = 2,
};
struct ap_attachment
{
int type;
char* mediaType;
char* name;
char* url;
char* value;
};
void ap_attachment_free( struct ap_attachment* a );
extern struct json_field_type ap_attachment_type;
struct ap_object_ptr_or_ref
{
int tag;
union {
char* ref;
struct ap_object* ptr;
};
};
struct ap_public_key
{
char* id;
char* owner;
char* public_key;
};
void ap_object_ptr_or_ref_free( struct ap_object_ptr_or_ref* oor );
void ap_object_ptr_or_ref_free_composite( struct ap_object_ptr_or_ref* oor );
struct ap_object
{
struct ap_activity_context ap_context;
char* id;
char* actor;
char* name;
int local_id;
int type;
// Actor-specific fields
char* inbox;
char* outbox;
char* preferred_username;
char* url;
struct {
char* shared_inbox;
} endpoints;
char* featured;
char* followers;
char* following;
struct {
bool show;
bool accepts_chat_messages;
} capabilities;
char* avatar;
char* banner;
bool discoverable;
bool manually_approves_followers;
struct ap_public_key* public_key;
char* context;
int context_id;
bool direct_message;
char* attributed_to;
char* target;
char* in_reply_to;
struct ap_activity_source content;
char* conversation;
time_t published;
struct ap_activity_source source;
char* summary;
bool sensitive;
struct {
struct ap_activity_tag** items;
int count;
} tags;
struct {
struct ap_attachment** items;
int count;
} attachments;
struct {
char** items;
int count;
} also_known_as;
struct {
struct ap_object_ptr_or_ref* items;
int count;
} collection_items;
struct {
char** items;
int count;
} to, cc, bcc;
struct ap_object_ptr_or_ref object;
char* state;
int has_signature;
struct ap_signature signature;
// Collection fields
struct ap_object_ptr_or_ref first;
char* next;
char* prev;
char* part_of;
int total_items;
};
struct json_object_field;
extern struct json_field_type ap_object_type;
extern struct json_object_field ap_object_layout[];
struct ap_object* ap_activity_new();
struct ap_object* ap_activity_dup( struct ap_object* o );
struct ap_object* ap_object_from_FILE( FILE* f );
struct ap_object* ap_object_from_file( const char* filename );
struct ap_object* ap_activity_from_local_id( int id );
void ap_object_free( struct ap_object* o );
void ap_activity_free_composite( struct ap_object* o );
void ap_object_write_to_FILE( struct ap_object* o, FILE* f );
void ap_activity_save( struct ap_object* o );
struct ap_object* ap_activity_create_accept( struct ap_object* act );
void ap_activity_accept( struct ap_object* act, int deliver_to_account_id );
struct status;
struct account;
struct ap_object* ap_activity_create_emoji_react( struct status* s, const char* react );
struct ap_object* ap_activity_create_undo( struct ap_object* act );
struct ap_object* ap_activity_create_like( struct status* s );
struct ap_object* ap_activity_create_note( struct status* s );
struct ap_object* ap_activity_create_Create( struct ap_object* act );
// These should be elsewhere
void ap_activity_react( struct status* s, const char* react );
int ap_activity_follow( struct account* follower, struct account* following );
void ap_activity_unfollow( struct account* follower, struct account* to_unfollow );
void ap_activity_undo( struct ap_activity* act, int deliver_to_account_id );
int ap_activity_like( struct status* s );

@ -12,53 +12,61 @@ extern struct json_field_type ap_activity_source_type;
// https://www.w3.org/TR/activitystreams-vocabulary/#h-activity-types
struct json_enum ap_object_type_enum[] = {
{ "Undo", apat_undo },
{ "Follow", apat_follow },
{ "Delete", apat_delete },
{ "Accept", apat_accept },
{ "Create", apat_create },
{ "TentativeAccept", apat_tentative_accept },
{ "Add", apat_add },
{ "Arrive", apat_arrive },
{ "Ignore", apat_ignore },
{ "Join", apat_join },
{ "Leave", apat_leave },
{ "Like", apat_like },
{ "Offer", apat_offer },
{ "Invite", apat_invite },
{ "Reject", apat_reject },
{ "TentativeReject", apat_tentative_reject },
{ "Remove", apat_remove },
{ "Update", apat_update },
{ "View", apat_view },
{ "Listen", apat_listen },
{ "Read", apat_read },
{ "Move", apat_move },
{ "Travel", apat_travel },
{ "Announce", apat_announce },
{ "Block", apat_block },
{ "Flag", apat_flag },
{ "Dislike", apat_dislike },
{ "EmojiReact", apat_emoji_react },
{ "Note", apat_note },
{ "Question", apat_question },
{ "Document", apot_document },
{ "Image", apot_image },
{ "Tombstone", apot_tombstone },
{ "Person", apat_person },
{ "Service", apot_service },
{ "Application", apot_application },
{ "Group", apot_group },
{ "Organization", apot_organization },
{ "Collection", apot_collection },
{ "CollectionPage", apot_collection_page },
{ "OrderedCollection", apot_ordered_collection },
{ "OrderedCollectionPage", apot_ordered_collection_page },
{ "PropertyValue", apot_property_value },
{ "Undo", ap_Undo },
{ "Follow", ap_Follow },
{ "Delete", ap_Delete },
{ "Accept", ap_Accept },
{ "Create", ap_Create },
{ "TentativeAccept", ap_TentativeAccept },
{ "Add", ap_Add },
{ "Arrive", ap_Arrive },
{ "Ignore", ap_Ignore },
{ "Join", ap_Join },
{ "Leave", ap_Leave },
{ "Like", ap_Like },
{ "Offer", ap_Offer },
{ "Invite", ap_Invite },
{ "Reject", ap_Reject },
{ "TentativeReject", ap_TentativeReject },
{ "Remove", ap_Remove },
{ "Update", ap_Update },
{ "View", ap_View },
{ "Listen", ap_Listen },
{ "Read", ap_Read },
{ "Move", ap_Move },
{ "Travel", ap_Travel },
{ "Announce", ap_Announce },
{ "Block", ap_Block },
{ "Flag", ap_Flag },
{ "Dislike", ap_Dislike },
{ "EmojiReact", pleroma_EmojiReact },
{ "Note", ap_Note },
{ "Question", ap_Question },
{ "Document", ap_Document },
{ "Image", ap_Image },
{ "Tombstone", ap_Tombstone },
{ "Person", ap_Person },
{ "Service", ap_Service },
{ "Application", ap_Application },
{ "Group", ap_Group },
{ "Organization", ap_Organization },
{ "Collection", ap_Collection },
{ "CollectionPage", ap_CollectionPage },
{ "OrderedCollection", ap_OrderedCollection },
{ "OrderedCollectionPage", ap_OrderedCollectionPage },
// Musicbox Types
{ "Track", mb_Track },
{ "Album", mb_Album },
// Forge Types
// Schema Types
{ "PropertyValue", schema_PropertyValue },
{ NULL, 0 },
};
@ -347,25 +355,4 @@ void ap_object_write_to_FILE( struct ap_object* obj, FILE* f )
}
json_write_pretty_object_layout( &jw, ap_object_layout, obj );
}
void ap_activity_save( struct ap_object* act )
{
char filename[512];
snprintf( filename, sizeof(filename), "data/activities/%d.json", act->local_id );
json_write_object_layout_to_file( filename, "\t", ap_object_layout, act );
}
struct ap_object* ap_activity_from_local_id( int id )
{
struct ap_object* act = ap_activity_new();
char filename[512];
snprintf( filename, sizeof(filename), "data/activities/%d.json", id );
if( !json_read_object_layout_from_file( filename, ap_object_layout, act ) ) {
ap_object_free(act);
return NULL;
}
return act;
}

@ -0,0 +1,256 @@
#pragma once
#include "json/layout.h"
#include "activity/context.h"
#include "activity/tag.h"
#include <time.h>
// Reference: https://www.w3.org/TR/activitystreams-vocabulary/
// TODO: rename this to ap_object, as this is really an Object base and not an Activity base
struct ap_object;
#define ap_activity ap_object
struct ap_activity_source
{
char* mime_type;
char* content;
// TODO: add map;
};
enum ap_signature_type
{
apst_rsa_signature_2017 = 1,
};
struct ap_signature
{
int type;
char* creator;
time_t created;
char* value;
};
enum ap_object_type
{
// Activity Types
ap_Undo = 1,
ap_Follow = 2,
ap_Delete = 3,
ap_Accept = 4,
ap_Create = 5,
ap_TentativeAccept = 6,
ap_Add = 7,
ap_Arrive = 8,
ap_Ignore = 9,
ap_Join = 10,
ap_Leave = 11,
ap_Like = 12,
ap_Offer = 13,
ap_Invite = 14,
ap_Reject = 15,
ap_TentativeReject = 16,
ap_Remove = 17,
ap_Update = 18,
ap_View = 19,
ap_Listen = 20,
ap_Read = 21,
ap_Move = 22,
ap_Travel = 23,
ap_Announce = 24,
ap_Block = 25,
ap_Flag = 26,
ap_Dislike = 27,
// Object Types
ap_Note = 100,
ap_Question = 101,
ap_Article = 102,
ap_Audio = 103,
ap_Document = 104,
ap_Event = 105,
ap_Image = 106,
ap_Page = 107,
ap_Place = 108,
ap_Profile = 109,
ap_Relationship = 110,
ap_Tombstone = 111,
ap_Video = 112,
// Actor Types
ap_Person = 200,
ap_Service = 201,
ap_Application = 202,
ap_Group = 203,
ap_Organization = 204,
// Collection Types
ap_OrderedCollection = 301,
ap_OrderedCollectionPage = 302,
ap_Collection = 303,
ap_CollectionPage = 304,
/// Pleroma Types
pleroma_EmojiReact = 28,
/// Musicbox Types
mb_Track = 501,
mb_Album = 502,
/// Forge Types (https://forgefed.org/vocabulary.html)
forge_Grant = 601,
forge_Push = 602,
forge_Repository = 603,
forge_TicketTracker = 604,
forge_PatchTracker = 605,
forge_Project = 606,
forge_Team = 607,
forge_Branch = 608,
forge_Commit = 609,
forge_TicketDependency = 610,
forge_Ticket = 611,
/// Schema.org types
schema_PropertyValue = 401, // https://schema.org/PropertyValue
};
extern struct json_enum ap_object_type_enum[];
enum ap_activity_object_type {
apaot_ref = 1,
apaot_activity = 2,
apaot_object = 2,
};
struct ap_attachment
{
int type;
char* mediaType;
char* name;
char* url;
char* value;
};
void ap_attachment_free( struct ap_attachment* a );
extern struct json_field_type ap_attachment_type;
struct ap_object_ptr_or_ref
{
int tag;
union {
char* ref;
struct ap_object* ptr;
};
};
struct ap_public_key
{
char* id;
char* owner;
char* public_key;
};
void ap_object_ptr_or_ref_free( struct ap_object_ptr_or_ref* oor );
void ap_object_ptr_or_ref_free_composite( struct ap_object_ptr_or_ref* oor );
struct ap_object
{
struct ap_activity_context ap_context;
char* id;
char* actor;
char* name;
int local_id;
int type;
// Actor-specific fields
char* inbox;
char* outbox;
char* preferred_username;
char* url;
struct {
char* shared_inbox;
} endpoints;
char* featured;
char* followers;
char* following;
struct {
bool show;
bool accepts_chat_messages;
} capabilities;
char* avatar;
char* banner;
bool discoverable;
bool manually_approves_followers;
struct ap_public_key* public_key;
char* context;
int context_id;
bool direct_message;
char* attributed_to;
char* target;
char* in_reply_to;
struct ap_activity_source content;
char* conversation;
time_t published;
struct ap_activity_source source;
char* summary;
bool sensitive;
struct {
struct ap_activity_tag** items;
int count;
} tags;
struct {
struct ap_attachment** items;
int count;
} attachments;
struct {
char** items;
int count;
} also_known_as;
struct {
struct ap_object_ptr_or_ref* items;
int count;
} collection_items;
struct {
char** items;
int count;
} to, cc, bcc;
struct ap_object_ptr_or_ref object;
char* state;
int has_signature;
struct ap_signature signature;
// Collection fields
struct ap_object_ptr_or_ref first;
char* next;
char* prev;
char* part_of;
int total_items;
};
struct json_object_field;
extern struct json_field_type ap_object_type;
extern struct json_object_field ap_object_layout[];
struct ap_object* ap_activity_new();
struct ap_object* ap_activity_dup( struct ap_object* o );
struct ap_object* ap_object_from_FILE( FILE* f );
struct ap_object* ap_object_from_file( const char* filename );
void ap_object_free( struct ap_object* o );
void ap_activity_free_composite( struct ap_object* o );
void ap_object_write_to_FILE( struct ap_object* o, FILE* f );

@ -4,7 +4,8 @@
#include "model/server.h"
#include "model/account.h"
#include "model/ap/activity.h"
#include "model/activity.h"
#include "model/ap/object.h"
#include "model/notification.h"
#include "model/timeline.h"
#include "model/emoji.h"
@ -653,7 +654,7 @@ void status_add_react( struct status* s, const char* react, struct account* a )
} else {
// Deliver react to post owner
//printf( "TODO: generate outbox activity for adding reaction '%s' to status #%d by account #%d, deliver to account #%d\n", react, s->id, a->id, s->account_id );
ap_activity_react( s, react );
activity_react( s, react );
}
}
@ -759,7 +760,7 @@ void status_add_like( struct status* s, struct account* a )
}
}
if( a->id == owner_account_id ) {
ap_activity_like( s );
activity_like( s );
}
}

Loading…
Cancel
Save