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.
300 lines
7.8 KiB
C
300 lines
7.8 KiB
C
#include "model/account.h"
|
|
|
|
// Model
|
|
#include "model/fetch.h"
|
|
|
|
// Submodules
|
|
#include "collections/array.h"
|
|
#include "util/format.h"
|
|
#include "ap/object.h"
|
|
#include "ap/collection.h"
|
|
|
|
// Standard Library
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
struct ap_object* account_get_activity_pub_data( struct account* a )
|
|
{
|
|
if( !a ) { return NULL; }
|
|
if( !a->account_url ) {
|
|
printf( "No account url\n" );
|
|
goto skip_pull;
|
|
}
|
|
|
|
char filename[512];
|
|
snprintf( filename, 512, "data/accounts/%d/ap.json", a->id );
|
|
|
|
printf( "ap_object_from_file( %s )\n", filename );
|
|
pull_remote_file_if_older( filename, a->account_url, 60*60*24*3 );
|
|
skip_pull:
|
|
return ap_object_from_file( filename );
|
|
}
|
|
|
|
bool account_sync_from_activity_pub( unsigned int account_id )
|
|
{
|
|
bool result = false;
|
|
struct account* a = account_from_id(account_id);
|
|
struct ap_object* obj = NULL;
|
|
if( !a ) {
|
|
a = account_new();
|
|
a->id = account_id;
|
|
} else if( a->next_update > time(NULL) ) {
|
|
goto succeeded;
|
|
} else if( a->local ) {
|
|
goto succeeded;
|
|
}
|
|
|
|
obj = account_get_activity_pub_data(a);
|
|
if( !obj ) {
|
|
printf( "Failed to load AP data for %s (%d)\n", a->account_url, a->id );
|
|
goto failed;
|
|
}
|
|
|
|
if( !account_sync_from_activity( a, obj ) ) { goto failed; }
|
|
|
|
// If we got here, this account can't be a stub any more
|
|
a->stub = false;
|
|
a->next_update = time(NULL) + (60*60*24*3) + (rand() % (60*60*24*2)); // Next update in 3-5 days
|
|
|
|
account_save(a);
|
|
account_index_webfinger(a);
|
|
|
|
goto succeeded;
|
|
succeeded:
|
|
result = true;
|
|
goto cleanup;
|
|
cleanup:
|
|
if( a ) {
|
|
a->next_update = time(NULL) + (60*60*24*3) + (rand() % (60*60*24*2)); // Next update in 3-5 days
|
|
account_save(a);
|
|
}
|
|
ap_object_free(obj);
|
|
account_free(a);
|
|
|
|
return result;
|
|
failed:
|
|
result = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
bool account_sync_from_activity( struct account* a, struct ap_object* obj )
|
|
{
|
|
if( obj->url.count > 0 ) {
|
|
a->account_url = strdup(obj->url.items[0].ref); // TODO: make this more robust
|
|
} else if( obj->id ) {
|
|
a->account_url = strdup(obj->id);
|
|
} else {
|
|
printf( "No id or url in AP data\n" );
|
|
return false;
|
|
}
|
|
|
|
for( int i = 0; i < a->aliases.count; ++i ) {
|
|
free( a->aliases.items[i] );
|
|
}
|
|
a->aliases.count = 0;
|
|
|
|
for( int i = 0; i < obj->also_known_as.count; ++i ) {
|
|
char* str = strdup(obj->also_known_as.items[i]);
|
|
array_append( &a->aliases, sizeof(str), &str );
|
|
}
|
|
|
|
a->handle = strdup(obj->preferred_username);
|
|
if( obj->name ) {
|
|
a->display_name = strdup(obj->name);
|
|
} else {
|
|
a->display_name = strdup(obj->preferred_username);
|
|
}
|
|
if( obj->icon && obj->icon->url.count > 0 ) {
|
|
a->avatar.url = strdup(obj->icon->url.items[0].ref); // TODO: make this more robust
|
|
a->avatar.static_url = strdup(a->avatar.url);
|
|
}
|
|
if( obj->image && obj->image->url.count > 0 ) {
|
|
a->banner = strdup(obj->image->url.items[0].ref); // TODO: make this more robust
|
|
}
|
|
a->bot = ( obj->type != ap_Person );
|
|
a->account_type = at_remote_activity_pub;
|
|
|
|
if( obj->inbox ) {
|
|
a->inbox = strdup(obj->inbox);
|
|
}
|
|
if( obj->summary ) {
|
|
a->note = strdup(obj->summary);
|
|
}
|
|
if( obj->endpoints.shared_inbox ) {
|
|
a->shared_inbox = strdup(obj->endpoints.shared_inbox);
|
|
}
|
|
|
|
if( 0 == strncmp( obj->id, "https://", 8 ) ) {
|
|
char* server_name = strdup(&obj->id[8]);
|
|
char* discard;
|
|
strtok_r(server_name,"/",&discard);
|
|
|
|
a->server = server_name;
|
|
}
|
|
|
|
// Clear out existing emoji
|
|
for( int i = 0; i < a->emoji.count; ++i ) {
|
|
free( a->emoji.items[i].key );
|
|
free( a->emoji.items[i].value );
|
|
}
|
|
a->emoji.count = 0;
|
|
|
|
// Pull in emoji
|
|
for( int i = 0; i < obj->tags.count; ++i ) {
|
|
struct ap_activity_tag* tag = obj->tags.items[i];
|
|
|
|
if( tag->type == aptag_emoji ) {
|
|
struct string_pair item;
|
|
memset(&item,0,sizeof(item));
|
|
item.key = strndup(&tag->name[1],strlen(tag->name)-2);
|
|
item.value = strdup(tag->icon.url);
|
|
array_append( &a->emoji, sizeof(item), &item );
|
|
}
|
|
}
|
|
|
|
// Extract out the public key if present
|
|
if( obj->public_key ) {
|
|
char filename[512];
|
|
char* id = strdup(obj->public_key->id);
|
|
char* key_id = NULL;
|
|
strtok_r( id, "#", &key_id );
|
|
FILE* key_pem = fopen( format(filename,sizeof(filename),"data/accounts/%d/%s.pem", a->id, key_id), "w" );
|
|
if( !key_pem ) {
|
|
printf( "Unable to save public key to %s\n", filename );
|
|
} else {
|
|
printf( "Writing public key to %s\n", filename );
|
|
fprintf( key_pem, "%s", obj->public_key->public_key );
|
|
fclose(key_pem);
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static struct ap_object* fetch_ap_object_with_delay( const char* uri )
|
|
{
|
|
// Sleep for 150 milliseconds to reduce load on remote servers
|
|
usleep( 150 * 1000 );
|
|
return fetch_ap_object_ref(uri);
|
|
}
|
|
|
|
void account_sync_following_list( struct account* a )
|
|
{
|
|
ap_object_set_fetch_callback( fetch_ap_object_with_delay );
|
|
|
|
struct ap_object* obj = NULL;
|
|
struct ap_object* following = NULL;
|
|
if( a->local ) { goto cleanup; }
|
|
|
|
obj = account_get_activity_pub_data(a);
|
|
if( !obj ) { goto cleanup; }
|
|
|
|
following = ap_collection_from_uri( obj->following );
|
|
if( !following ) { goto cleanup; }
|
|
|
|
struct {
|
|
int* items;
|
|
int count;
|
|
} existing;
|
|
account_list_following( a, 0, INT_MAX, &existing );
|
|
|
|
for( struct ap_object* page = following->first.ptr; page; ap_collection_iterate(&page) ) {
|
|
for( int i = 0; i < page->ordered_items.count; ++i ) {
|
|
// ap_object_force_to_ref( &page->ordered_items.items[i] );
|
|
const char* account_uri = page->ordered_items.items[i].ref;
|
|
|
|
struct account* following = account_from_uri_or_fetch( account_uri );
|
|
if( !following ) { continue; }
|
|
|
|
int account_id = following->id;
|
|
printf( "Follower local account at %d\n", account_id );
|
|
|
|
if( -1 != array_find( &existing, sizeof(account_id), &account_id ) ) {
|
|
array_delete( &existing, sizeof(account_id), &account_id );
|
|
} else {
|
|
printf( "Adding following %s (%d)\n", account_uri, account_id );
|
|
account_add_follower( following, a );
|
|
}
|
|
|
|
account_free(following);
|
|
}
|
|
}
|
|
|
|
printf( "existing.count = %d\n", existing.count );
|
|
for( int i = 0; i < existing.count; ++i ) {
|
|
printf( "Need to remove %d as following\n", existing.items[i] );
|
|
}
|
|
free(existing.items);
|
|
cleanup:
|
|
ap_object_free(obj);
|
|
ap_object_free(following);
|
|
}
|
|
|
|
void account_sync_followers_list( struct account* a )
|
|
{
|
|
ap_object_set_fetch_callback( fetch_ap_object_with_delay );
|
|
|
|
struct {
|
|
int* items;
|
|
int count;
|
|
} existing;
|
|
account_list_followers( a, 0, INT_MAX, &existing );
|
|
|
|
struct ap_object* obj = account_get_activity_pub_data(a);
|
|
if( !obj->followers ) { goto cleanup; }
|
|
struct ap_object* followers = ap_collection_from_uri( obj->followers );
|
|
if( !followers ) { goto cleanup; }
|
|
|
|
for( struct ap_object* page = followers->first.ptr; page; ap_collection_iterate(&page) ) {
|
|
for( int i = 0; i < page->ordered_items.count; ++i ) {
|
|
// ap_object_force_to_ref( &page->ordered_items.items[i] );
|
|
const char* account_uri = page->ordered_items.items[i].ref;
|
|
|
|
struct account* follower = account_from_uri_or_fetch( account_uri );
|
|
if( !follower ) { continue; }
|
|
|
|
int account_id = follower->id;
|
|
printf( "Follower local account at %d\n", follower->id );
|
|
|
|
if( -1 != array_find( &existing, sizeof(account_id), &account_id ) ) {
|
|
array_delete( &existing, sizeof(account_id), &account_id );
|
|
} else {
|
|
printf( "Adding follower %s (%d)\n", account_uri, account_id );
|
|
account_add_follower( a, follower );
|
|
}
|
|
|
|
account_free(follower);
|
|
}
|
|
}
|
|
|
|
printf( "existing.count = %d\n", existing.count );
|
|
for( int i = 0; i < existing.count; ++i ) {
|
|
printf( "Need to remove %d as follower\n", existing.items[i] );
|
|
}
|
|
free(existing.items);
|
|
|
|
cleanup:
|
|
ap_object_free(followers);
|
|
}
|
|
void account_pull_friends_of_friends( struct account* a )
|
|
{
|
|
struct {
|
|
int* items;
|
|
int count;
|
|
} following;
|
|
account_list_following( a, 0, INT_MAX, &following );
|
|
|
|
for( int i = 0; i < following.count; ++i ) {
|
|
struct account* friend = account_from_id( following.items[i] );
|
|
account_sync_following_list( friend );
|
|
account_sync_followers_list( friend );
|
|
account_free(friend);
|
|
}
|
|
|
|
free( following.items );
|
|
}
|
|
|