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