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.

416 lines
11 KiB
C

#define _GNU_SOURCE
#include "object.h"
#include "object/context.h"
// Submodules
#include "json/json.h"
#include "ffdb/fs_list.h"
#include "collections/array.h"
#include "util/format.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static __thread struct ap_object* (*fetch_ap_object_ref)( const char* uri ) = NULL;
struct ap_object_vtable* factories = &ap_object_vtable;
void ap_object_set_fetch_callback( struct ap_object* (*callback)( const char* uri ) )
{
fetch_ap_object_ref = callback;
}
struct ap_object* ap_object_fetch( const char* uri )
{
if( !fetch_ap_object_ref ) { return NULL; }
return fetch_ap_object_ref(uri);
}
void register_ap_object_type( struct ap_object_vtable* vtable )
{
// Check if already registered
for( struct ap_object_vtable* i = factories; i; i = i->next ) {
if( i == vtable ) { return; }
}
vtable->next = factories;
factories = vtable;
}
struct ap_object* ap_object_new()
{
struct ap_object* act;
act = malloc(sizeof(*act));
memset(act,0,sizeof(*act));
return act;
}
struct ap_object_ptr_or_ref ap_object_ptr_or_ref_dup( struct ap_object_ptr_or_ref por )
{
struct ap_object_ptr_or_ref res;
res.tag = por.tag;
switch( por.tag ) {
case apaot_ref:
res.ref = strdup(por.ref);
break;
case apaot_activity:
res.ptr = ap_object_dup(por.ptr);
break;
}
return res;
}
void ap_object_ptr_or_ref_force_embedded( struct ap_object_ptr_or_ref* oor )
{
if( oor->tag == apaot_ref ) {
char* uri = oor->ref;
oor->ptr = ap_object_fetch(uri);
free(uri);
oor->tag = apaot_activity;
}
}
struct ap_object* ap_object_dup( struct ap_object* act )
{
if( !act ) { return NULL; }
struct ap_object* new_act = ap_object_new();
memset(new_act,0,sizeof(*new_act));
new_act->local_id = act->local_id;
// JSON-LD Fields
new_act->type = act->type;
new_act->id = safe_strdup(act->id);
for( int i = 0; i < act->ap_context.extra.count; ++i ) {
ap_object_add_context( new_act, act->ap_context.extra.items[i] );
}
// Object-specific fields. See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object
for( int i = 0; i < act->attachments.count; ++i ) {
struct ap_object* old_att = act->attachments.items[i];
struct ap_object* new_att = ap_object_dup(old_att);
array_append( &new_act->attachments, sizeof(new_att), &new_att );
}
new_act->attributed_to = safe_strdup(act->attributed_to);
new_act->content.content = safe_strdup(act->content.content);
new_act->content.mime_type = safe_strdup(act->content.mime_type);
new_act->name = safe_strdup(act->name);
new_act->in_reply_to = safe_strdup( act->in_reply_to );
new_act->quote_url = safe_strdup( act->quote_url );
new_act->published = act->published;
new_act->summary = safe_strdup(act->summary);
for( int i = 0; i < act->tags.count; ++i ) {
struct ap_activity_tag* old_tag = act->tags.items[i];
struct ap_activity_tag* new_tag;
new_tag = malloc(sizeof(*new_tag));
memset(new_tag,0,sizeof(*new_tag));
new_tag->type = old_tag->type;
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 );
array_append( &new_act->tags, sizeof(new_tag), &new_tag );
}
for( int i = 0; i < act->url.count; ++i ) {
struct ap_object_ptr_or_ref item = ap_object_ptr_or_ref_dup(
act->url.items[i]
);
array_append( &new_act->url, sizeof(item), &item );
}
array_dup( &new_act->to, sizeof(char*), &act->to );
for( int i = 0; i < new_act->to.count; ++i ) {
new_act->to.items[i] = strdup(new_act->to.items[i]);
}
array_dup( &new_act->cc, sizeof(char*), &act->cc );
for( int i = 0; i < new_act->cc.count; ++i ) {
new_act->cc.items[i] = strdup(new_act->cc.items[i]);
}
array_dup( &new_act->bcc, sizeof(char*), &act->bcc );
for( int i = 0; i < new_act->bcc.count; ++i ) {
new_act->bcc.items[i] = strdup(new_act->bcc.items[i]);
}
array_dup( &new_act->bto, sizeof(char*), &act->bto );
for( int i = 0; i < new_act->bto.count; ++i ) {
new_act->bto.items[i] = strdup(new_act->bto.items[i]);
}
new_act->media_type = safe_strdup(act->media_type);
// PropertyValue-specific fields
new_act->value = safe_strdup(act->value);
// Link-specific fields
new_act->href = safe_strdup(act->href);
new_act->rel = safe_strdup(act->rel);
// Activity-specific fields
new_act->actor = safe_strdup(act->actor);
new_act->object = ap_object_ptr_or_ref_dup( act->object );
new_act->target = safe_strdup(act->target);
new_act->replies = ap_object_ptr_or_ref_dup( act->replies );
// Actor-specific fields
new_act->inbox = safe_strdup(act->inbox);
new_act->outbox = safe_strdup(act->outbox);
new_act->preferred_username = safe_strdup(act->preferred_username);
new_act->endpoints.shared_inbox = safe_strdup(act->endpoints.shared_inbox);
new_act->featured = safe_strdup(act->featured);
new_act->followers = safe_strdup(act->followers);
new_act->following = safe_strdup(act->following);
new_act->icon = ap_object_dup(act->icon);
new_act->image = ap_object_dup(act->image);
new_act->discoverable = act->discoverable;
new_act->manually_approves_followers = act->manually_approves_followers;
// TODO: deep-copy public_key field
new_act->context = safe_strdup(act->context);
new_act->source.content = safe_strdup(act->source.content);
new_act->source.mime_type = safe_strdup(act->source.mime_type);
new_act->state = safe_strdup( act->state );
if( act->has_signature ) {
new_act->has_signature = true;
new_act->signature.type = act->signature.type;
new_act->signature.creator = strdup(act->signature.creator);
new_act->signature.created = act->signature.created;
new_act->signature.value = strdup(act->signature.value);
}
// Collection Fields
new_act->first = ap_object_ptr_or_ref_dup( act->first );
new_act->next = ap_object_ptr_or_ref_dup(act->next);
new_act->prev = safe_strdup(act->prev);
new_act->part_of = safe_strdup(act->part_of);
for( int i = 0; i < act->collection_items.count; ++i ) {
struct ap_object_ptr_or_ref item = ap_object_ptr_or_ref_dup(
act->collection_items.items[i]
);
array_append( &new_act->collection_items, sizeof(item), &item );
}
for( int i = 0; i < act->ordered_items.count; ++i ) {
struct ap_object_ptr_or_ref item = ap_object_ptr_or_ref_dup(
act->ordered_items.items[i]
);
array_append( &new_act->ordered_items, sizeof(item), &item );
}
// Question fields
for( int i = 0; i < act->one_of.count; ++i ) {
struct ap_object_ptr_or_ref item = ap_object_ptr_or_ref_dup(
act->one_of.items[i]
);
array_append( &new_act->one_of, sizeof(item), &item );
}
for( int i = 0; i < act->any_of.count; ++i ) {
struct ap_object_ptr_or_ref item = ap_object_ptr_or_ref_dup(
act->any_of.items[i]
);
array_append( &new_act->any_of, sizeof(item), &item );
}
return new_act;
}
void ap_public_key_free( struct ap_public_key* pk );
void ap_object_free( struct ap_object* act )
{
if( !act ) { return; }
ap_object_free_composite(act);
free(act);
}
void ap_object_free_composite( struct ap_object* act )
{
// TODO: reorganize this to match header file
free(act->id);
free(act->actor);
free(act->name);
free(act->value);
free(act->href);
free(act->rel);
ap_object_ptr_or_ref_free_composite( &act->object );
free(act->inbox);
free(act->outbox);
free(act->preferred_username);
for( int i = 0; i < act->url.count; ++i ) {
ap_object_ptr_or_ref_free_composite( &act->url.items[i] );
}
free(act->endpoints.shared_inbox);
free(act->featured);
free(act->followers);
free(act->following);
ap_object_free(act->icon);
ap_object_free(act->image);
ap_public_key_free( act->public_key );
free(act->context);
for( int i = 0; i < act->ap_context.extra.count; ++i ) {
free( act->ap_context.extra.items[i] );
}
free( act->ap_context.extra.items );
free(act->attributed_to);
free(act->target);
free(act->in_reply_to);
free(act->quote_url);
free(act->content.content);
free(act->source.content);
free(act->summary);
for( int i = 0; i < act->tags.count; ++i ) {
ap_activity_tag_free(act->tags.items[i]);
}
free(act->tags.items);
for( int i = 0; i < act->attachments.count; ++i ) {
ap_object_free( act->attachments.items[i] );
}
free( act->attachments.items );
for( int i = 0; i < act->also_known_as.count; ++i ) {
free( act->also_known_as.items[i] );
}
free(act->also_known_as.items);
for( int i = 0; i < act->to.count; ++i ) {
free(act->to.items[i]);
}
free(act->to.items);
for( int i = 0; i < act->cc.count; ++i ) {
free(act->cc.items[i]);
}
free(act->cc.items);
for( int i = 0; i < act->bcc.count; ++i ) {
free(act->bcc.items[i]);
}
free(act->bcc.items);
for( int i = 0; i < act->bto.count; ++i ) {
free(act->bto.items[i]);
}
free(act->bto.items);
free( act->media_type );
free(act->conversation);
// Other
free( act->state );
ap_object_ptr_or_ref_free_composite( &act->replies );
// Signature
free( act->signature.creator );
free( act->signature.value );
// Collection fields
ap_object_ptr_or_ref_free_composite( &act->first );
ap_object_ptr_or_ref_free_composite( &act->next );
free( act->prev );
free( act->part_of );
for( int i = 0; i < act->collection_items.count; ++i ) {
ap_object_ptr_or_ref_free_composite( &act->collection_items.items[i] );
}
free( act->collection_items.items );
for( int i = 0; i < act->ordered_items.count; ++i ) {
ap_object_ptr_or_ref_free_composite( &act->ordered_items.items[i] );
}
free( act->ordered_items.items );
// Question fields
for( int i = 0; i < act->one_of.count; ++i ) {
ap_object_ptr_or_ref_free_composite( &act->one_of.items[i] );
}
free( act->one_of.items );
for( int i = 0; i < act->any_of.count; ++i ) {
ap_object_ptr_or_ref_free_composite( &act->any_of.items[i] );
}
free( act->any_of.items );
}
void ap_object_ptr_or_ref_free( struct ap_object_ptr_or_ref* o )
{
if( !o ) { return; }
ap_object_ptr_or_ref_free_composite(o);
free(o);
}
void ap_object_ptr_or_ref_free_composite( struct ap_object_ptr_or_ref* o )
{
switch( o->tag ) {
case apaot_ref:
free( o->ref );
break;
case apaot_activity:
ap_object_free( o->ptr );
o->ptr = NULL;
break;
};
}
bool ap_object_has_context( struct ap_object* o, const char* ctx )
{
//printf( "context count: %d, ctx=%s\n", o->ap_context.extra.count, ctx );
for( int i = 0; i < o->ap_context.extra.count; ++i ) {
//printf( "context[%d]: %s\n", i, o->ap_context.extra.items[i] );
if( strstr(o->ap_context.extra.items[i],ctx) ) {
return true;
}
}
//printf( "Unable to find context %s\n", ctx );
return false;
}
void ap_object_add_context( struct ap_object* o, const char* ctx )
{
for( int i = 0; i < o->ap_context.extra.count; ++i ) {
if( 0 == strcmp(o->ap_context.extra.items[i],ctx) ) {
return ;
}
}
char* new_ctx = strdup(ctx);
array_append( &o->ap_context.extra, sizeof(new_ctx), &new_ctx );
}
void ap_object_write_to_FILE( struct ap_object* obj, FILE* f )
{
struct json_writer jw = {
.f = f,
.indentation = "\t",
.indent = 0,
};
if( !obj ) {
fprintf( f, "null" );
return;
}
if( obj->vtable && obj->vtable->layout ) {
json_write_pretty_layout( &jw, obj->vtable->layout, obj );
} else {
json_write_pretty_object_layout( &jw, ap_object_layout, obj );
}
}
struct ap_object_vtable ap_object_vtable = {
.type_string = "Object",
.type = ap_Object,
};
void ap_object_array_append_ref( void* ptr, char* ref_url )
{
struct ap_object_ptr_or_ref ref;
ref.tag = apaot_ref;
ref.ref = ref_url;
array_append( ptr, sizeof(ref), &ref );
}