Implement /outbox, add status->activity indexing for local statuses

master
teknomunk 1 year ago
parent df7bb10cd8
commit 3af41c25d3

@ -31,7 +31,7 @@ bool route_ap_note( struct http_request* req )
if( !s ) { return false; }
if( s->remote ) { return false; }
struct ap_activity* act = ap_activity_create_note( s );
struct ap_object* act = ap_activity_create_note( s );
http_request_send_headers( req, 200, "application/ld+json", true );
FILE* f = http_request_get_response_body( req );
@ -44,3 +44,45 @@ bool route_ap_note( struct http_request* req )
return true;
}
// Route: /outbox
bool route_ap_outbox( struct http_request* req )
{
bool result = false;
struct account* owner_account = account_from_id( owner_account_id );
if( http_request_route_term(req,"") ) {
struct ap_object* outbox = account_ap_outbox( owner_account );
http_request_send_headers( req, 200, "application/ld+json", true );
FILE* f = http_request_get_response_body( req );
ap_activity_write_to_FILE( outbox, f );
fflush(f);
ap_object_free(outbox);
} else if( http_request_route( req, "/page-" ) ) {
char* page_str = http_request_route_get_dir_or_file(req);
int page = -1;
if( !page_str || (1 != sscanf(page_str,"%d",&page) ) || page < 0 ) { goto failed; }
struct ap_object* outbox_page = account_ap_outbox_page( owner_account, page );
http_request_send_headers( req, 200, "application/ld+json", true );
FILE* f = http_request_get_response_body( req );
ap_activity_write_to_FILE( outbox_page, f );
fflush(f);
ap_object_free(outbox_page);
}
failed:
result = false;
goto cleanup;
success:
result = true;
goto cleanup;
cleanup:
account_free(owner_account);
return true;
}

@ -5,4 +5,5 @@ struct http_request;
#include <stdbool.h>
bool route_ap_note( struct http_request* req );
bool route_ap_outbox( struct http_request* req );

@ -187,7 +187,7 @@ bool handle_repost( struct http_request* req, struct status* s )
status_add_to_timeline( repost, public_timeline_id );
// Federate
account_announce( owner, s );
account_announce( owner, s, repost );
// Show the new status as the response
show_status( req, repost );

@ -151,40 +151,17 @@ static bool handle_command_test( struct cli_request* req )
int res = cli_route_command( req, "test", 0, "" );
if( res != 1 ) { return !!res; }
for( int i = 0; i < 10; ++i ) {
struct notification* n = notification_from_id( 320 - i );
api_Notification_write( n, stdout, 0 );
notification_free(n);
printf( "\n\n----------------\n\n" );
}
/*
struct account* owner_account = account_from_id( owner_account_id );
struct status* s = status_from_id( 1164 );
struct ap_activity* note = ap_activity_create_note( s );
struct ap_activity* note2 = ap_activity_dup( note );
struct ap_activity* create = ap_activity_create_Create( note );
ap_activity_save(create);
//account_deliver_activity_to_followers( owner_account, create );
printf( "note = " );
ap_activity_write_to_FILE( note, stdout );
printf( "\nnote2 = " );
ap_activity_write_to_FILE( note2, stdout );
printf( "\ncreate = " );
ap_activity_write_to_FILE( create, stdout );
printf( "\n" );
ap_object_free(create);
ap_object_free(note);
ap_object_free(note2);
status_free(s);
struct ap_object* outbox = account_ap_outbox( owner_account );
ap_activity_write_to_FILE( outbox, stdout );
printf( "\n" );
ap_object_free(outbox);
outbox = account_ap_outbox_page( owner_account, 0 );
ap_activity_write_to_FILE( outbox, stdout );
printf( "\n" );
ap_object_free(outbox);
account_free(owner_account);
*/
return true;
}

@ -162,6 +162,8 @@ bool route_request( struct http_request* req )
return route_owner( req );
} else if( http_request_route( req, "/inbox" ) ) {
return route_inbox( req );
} else if( http_request_route( req, "/outbox" ) ) {
return route_ap_outbox( req );
} else if( http_request_route( req, "/note/" ) ) {
return route_ap_note( req );
} else if( http_request_route( req, "/media/" ) ) {

@ -224,14 +224,110 @@ struct ap_account* account_activity_pub_data( struct account* a )
format( filename, 512, "data/accounts/%d/ap.json", a->id )
);
}
struct ap_activity* account_activity_pub( struct account* a )
struct ap_object* account_ap_actor( struct account* a )
{
struct ap_activity* act;
struct ap_object* act;
act = malloc(sizeof(*act));
memset(act,0,sizeof(*act));
act->type = apot_person;
return act;
}
struct ap_object* account_ap_outbox( struct account* a )
{
struct ap_object* outbox;
outbox = malloc(sizeof(*outbox));
memset(outbox,0,sizeof(*outbox));
outbox->type = apot_ordered_collection;
outbox->published = time(NULL);
outbox->id = aformat( "https://%s/outbox", g_server_name );
outbox->first = aformat( "https://%s/outbox/page-0", g_server_name );
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));
struct {
char** items;
int count;
} values;
memset( &values, 0, sizeof(values) );
ffdb_trie_list( buffer, page * items_per_page, items_per_page, NULL, &values );
printf( "items: %d\n", values.count );
for( int i = 0; i < values.count; ++i ) {
int id;
int activity_id = 0;
if( 1 == sscanf( values.items[i], "%d", &id ) ) {
struct status* s = status_from_id(id);
activity_id = s->activity_id;
if( s->activity_id == 0 ) {
goto include_tombstone;
}
struct ap_object* act = ap_activity_from_local_id( s->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 );
status_free(s);
} 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_name, activity_id );
} else {
o->id = aformat( "https://%s/activity/tombstone", g_server_name );
}
o->type = apot_tombstone;
struct ap_object_ptr_or_ref r;
r.tag = apaot_activity;
r.ptr = o;
array_append( &outbox->collection_items, sizeof(r), &r );
}
}
outbox->type = apot_ordered_collection_page;
outbox->published = time(NULL);
outbox->part_of = aformat( "https://%s/outbox", g_server_name );
outbox->id = aformat( "https://%s/outbox/page-%d", g_server_name, page );
if( page > 0 ) {
outbox->prev = aformat( "https://%s/outbox/page-%d", g_server_name, page - 1 );
}
if( page < page_count - 1 ) {
outbox->next = aformat( "https://%s/outbox/page-%d", g_server_name, page + 1 );
}
return outbox;
}
struct ap_activity* account_activity_pub( struct account* a )
{
return account_ap_actor(a);
}
bool account_sync_from_activity( struct account* a, struct ap_activity* act )
{
@ -515,10 +611,11 @@ void account_create( struct account* a, struct status* s )
ap_object_free(create);
ap_object_free(note);
}
void account_announce( struct account* a, struct status* s )
void account_announce( struct account* a, struct status* original_post, struct status* local_repost )
{
struct ap_activity* 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_name, id );
act->local_id = id;
@ -526,7 +623,7 @@ void account_announce( struct account* a, struct status* s )
act->type = apat_announce;
act->actor = strdup(a->account_url);
act->object.tag = apaot_ref;
act->object.ref = strdup(s->url);
act->object.ref = strdup(original_post->url);
// Create To: list
char* str = strdup("https://www.w3.org/ns/activitystreams#Public");
@ -535,7 +632,7 @@ void account_announce( struct account* a, struct status* s )
str = aformat("https://%s/owner/followers", g_server_name );
array_append( &act->to, sizeof(str), &str );
struct account* origin_post_account = account_from_id(s->account_id);
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 );
@ -553,8 +650,11 @@ void account_announce( struct account* a, struct status* s )
ap_activity_write_to_FILE( act, stdout );
// Link status to activity
s->activity_id = act->local_id;
status_save(s);
local_repost->activity_id = act->local_id;
status_save(local_repost);
status_add_repost( original_post, local_repost );
status_save(original_post);
cleanup:
account_free(origin_post_account);

@ -84,6 +84,9 @@ void account_list_followers( struct account* a, int offset, int limit, void* id_
void account_list_following( struct account* a, int offset, int limit, void* id_array );
struct ap_account* account_activity_pub_data( struct account* a );
struct ap_object* account_activity_pub( struct account* a );
struct ap_object* account_ap_actor( struct account* a );
struct ap_object* account_ap_outbox( struct account* a );
struct ap_object* account_ap_outbox_page( struct account* a, int page );
// Local actions
void account_add_follower( struct account* a, struct account* follower );
@ -95,7 +98,7 @@ bool account_does_follow( struct account* a, int account_id );
void account_deliver_activity( struct account* a, struct ap_object* act, struct outbox_envelope_list* oel );
void account_deliver_activity_to_followers( struct account* a, struct ap_object* act, struct outbox_envelope_list* oel );
void account_create( struct account* a, struct status* s );
void account_announce( struct account* a, struct status* s );
void account_announce( struct account* a, struct status* original_post, struct status* local_repost );
void account_follow( struct account* a, struct account* to_follow );
void account_unfollow( struct account* a, struct account* to_unfollow );
void account_update( struct account* a );

@ -56,6 +56,7 @@ struct ap_activity* ap_activity_dup( struct ap_activity* act )
new_tag->href = safe_strdup(old_tag->href);
new_tag->name = safe_strdup(old_tag->name);
new_tag->updated = old_tag->updated;
new_tag->id = safe_strdup(old_tag->id);
new_tag->icon.type = old_tag->icon.type;
new_tag->icon.url = safe_strdup( old_tag->icon.url );
@ -157,7 +158,7 @@ void ap_activity_free_composite( struct ap_activity* act )
free(act->also_known_as.items);
for( int i = 0; i < act->collection_items.count; ++i ) {
ap_object_ptr_or_ref_free( act->collection_items.items[i] );
ap_object_ptr_or_ref_free_composite( &act->collection_items.items[i] );
}
free( act->collection_items.items );
@ -182,6 +183,11 @@ void ap_activity_free_composite( struct ap_activity* act )
free( act->signature.creator );
free( act->signature.value );
free( act->first );
free( act->next );
free( act->prev );
free( act->part_of );
}
void ap_object_ptr_or_ref_free( struct ap_object_ptr_or_ref* o )
{

@ -81,7 +81,7 @@ enum ap_object_type
apot_video = 112,
// Actor Types
apat_person = 200,
apot_person = 200, apat_person = 200,
apot_service = 201,
apot_application = 202,
apot_group = 203,
@ -166,7 +166,7 @@ struct ap_object
} also_known_as;
struct {
struct ap_object_ptr_or_ref** items;
struct ap_object_ptr_or_ref* items;
int count;
} collection_items;
@ -183,6 +183,7 @@ struct ap_object
struct ap_signature signature;
// Collection fields
char* first;
char* next;
char* prev;
char* part_of;

@ -51,22 +51,12 @@ struct json_enum ap_object_type_enum[] = {
{ "CollectionPage", apot_collection_page },
{ "OrderedCollection", apot_ordered_collection },
{ "OrderedCollectionPage", apot_ordered_collection_page },
{ "Tombstone", apot_tombstone },
{ NULL, 0 },
};
struct json_tagged_union_typelist activity_ref_types[] = {
{
.tag_value = apaot_ref,
.offset = offsetof( struct ap_object, object.ref ),
.type = &json_field_string
},
{
.tag_value = apaot_activity,
.offset = offsetof( struct ap_object, object.ptr ),
.type = &ap_object_type
},
JSON_TAGGED_UNION_TYPELIST_END,
};
extern struct json_field_type ap_object_ptr_or_ref_type;
struct json_tagged_union_typelist signature_types[] = {
{
.tag_value = 1,
@ -185,10 +175,9 @@ struct json_object_field ap_object_layout[] = {
},
{
.key = "object",
.offset = offsetof( OBJ_TYPE, object.tag ),
.offset = offsetof( OBJ_TYPE, object ),
.required = false,
.type = &json_field_tagged_union,
.tagged_item_types = activity_ref_types
.type = &ap_object_ptr_or_ref_type,
},
{
@ -199,6 +188,7 @@ struct json_object_field ap_object_layout[] = {
.tagged_item_types = signature_types
},
JSON_FIELD_STRING( first, false ),
JSON_FIELD_STRING( next, false ),
JSON_FIELD_STRING( prev, false ),
{
@ -213,6 +203,13 @@ struct json_object_field ap_object_layout[] = {
.required = false,
.type = &json_field_integer,
},
{
.key = "items",
.offset = offsetof( OBJ_TYPE, collection_items ),
.allow_drop_empty_array = true,
.type = &json_field_array_of,
.array_item_type = &ap_object_ptr_or_ref_type,
},
JSON_FIELD_ENUM( type, ap_object_type_enum, true ),

@ -31,18 +31,19 @@ bool ap_activity_source_writer( struct json_writer* jw, const char* field_name,
{
struct ap_activity_source* src = field_data;
json_write_field_name( jw, field_name );
// Collapse to simple string if no additional properties are present
if( !src->mime_type ) {
if( !src->content ) {
json_write_string( jw->f, "" );
return false;
} else {
json_write_field_name( jw, field_name );
json_write_string( jw->f, src->content );
}
return true;
}
json_write_field_name( jw, field_name );
json_write_pretty_object_layout( jw, ap_activity_source_layout, src );
return true;
}

@ -0,0 +1,70 @@
#include "json/json.h"
#include "json/layout.h"
#include "model/ap/activity.h"
#include <stdlib.h>
#include <string.h>
bool ap_object_ptr_or_ref_reader( struct json_pull_parser* jpp, void* field_data, struct json_object_field* layout_field_data )
{
struct ap_object_ptr_or_ref* o = field_data;
char* ref = json_pull_parser_read_string(jpp);
if( ref ) {
o->tag = apaot_ref;
o->ref = ref;
return true;
} else {
o->tag = apaot_activity;
o->ptr = malloc(sizeof(*o->ptr));
memset(o->ptr,0,sizeof(*o->ptr));
struct json_object_field f = {
.key = layout_field_data->key,
.offset = 0,
.required = true,
.type = &ap_object_type,
};
if( !ap_object_type.reader( jpp, &o->ptr, &f ) ) {
ap_object_free(o->ptr);
o->ptr = malloc(sizeof(*o->ptr));
memset(o->ptr,0,sizeof(*o->ptr));
o->ptr = NULL;
return false;
}
return true;
}
return false;
}
bool ap_object_ptr_or_ref_writer( struct json_writer* jw, const char* field_name, void* field_data, struct json_object_field* layout_field_data )
{
struct ap_object_ptr_or_ref* o = field_data;
if( o->tag == apaot_ref ) {
if( o->ref ) {
json_write_field_name( jw, field_name );
json_write_string( jw->f, o->ref );
return true;
} else {
return false;
}
} else if( o->tag == apaot_activity ) {
struct json_object_field f = {
.key = layout_field_data->key,
.offset = 0,
.required = true,
.type = &ap_object_type,
};
return ap_object_type.writer( jw, field_name, &o->ptr, &f );
}
return false;
}
void ap_object_ptr_or_ref_free_shim( void* ptr )
{
ap_object_ptr_or_ref_free(ptr);
}
struct json_field_type ap_object_ptr_or_ref_type = {
.reader = ap_object_ptr_or_ref_reader,
.writer = ap_object_ptr_or_ref_writer,
.size = sizeof(struct ap_object_ptr_or_ref),
.free = ap_object_ptr_or_ref_free_shim,
};

@ -198,6 +198,10 @@ void status_add_mention( struct status* s, int id )
array_append( &s->mentions, sizeof(id), &id );
}
void status_add_repost( struct status* s, struct status* repost )
{
// TODO: implement
}
bool status_sync_from_activity_pub( struct status* s, struct ap_activity* act )
{
printf( "Syncing status from activity %s\n", act->id );

@ -92,6 +92,7 @@ void status_get_context( struct status* s, void* ancestors, void* replies );
void status_add_reply( struct status* s, struct status* child );
void status_add_mention( struct status* s, int id );
void status_add_repost( struct status* s, struct status* repost );
void status_make_reply_to( struct status* s, int in_reply_to );
void status_add_react( struct status* s, const char* react, struct account* a );
void status_remove_react( struct status* s, const char* react, struct account* a );

Loading…
Cancel
Save