|
|
|
@ -166,6 +166,40 @@ void account_reindex()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char* get_account_path( unsigned int id, char* filename, int size )
|
|
|
|
|
{
|
|
|
|
|
struct stat s;
|
|
|
|
|
|
|
|
|
|
// Check old-style path
|
|
|
|
|
snprintf( filename, size, "data/accounts/%d", id );
|
|
|
|
|
if( 0 == stat( filename, &s ) ) {
|
|
|
|
|
//printf( "Using path %s for account %d\n", filename, id );
|
|
|
|
|
return filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check new-style path
|
|
|
|
|
int millions = id / 1000000;
|
|
|
|
|
int thousands = ( id % 1000000 ) / 1000;
|
|
|
|
|
int ones = id % 1000;
|
|
|
|
|
|
|
|
|
|
mkdir( format(filename,size, "data/accounts/%03dm", millions ), 0755 );
|
|
|
|
|
mkdir( format(filename,size, "data/accounts/%03dm/%03dk", millions, thousands ), 0755 );
|
|
|
|
|
|
|
|
|
|
snprintf( filename,size, "data/accounts/%03dm/%03dk/%03d", millions, thousands, ones );
|
|
|
|
|
//printf( "Checking %s\n", filename );
|
|
|
|
|
if( 0 == stat(filename,&s) ) {
|
|
|
|
|
//printf( "Using path %s for account %d\n", filename, id );
|
|
|
|
|
return filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf( "No account path found for %d\n", id );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
const char* account_get_path( unsigned int id, char* buffer, int size )
|
|
|
|
|
{
|
|
|
|
|
return get_account_path( id, buffer, size );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct account* account_load_from_id( int id, int recurse_limit )
|
|
|
|
|
{
|
|
|
|
|
if( recurse_limit <= 0 ) {
|
|
|
|
@ -180,22 +214,24 @@ static struct account* account_load_from_id( int id, int recurse_limit )
|
|
|
|
|
return new_system_account();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char filename[512];
|
|
|
|
|
// Get account path
|
|
|
|
|
char path[512];
|
|
|
|
|
if( !get_account_path( id, path, sizeof(path) ) ) {
|
|
|
|
|
printf( "No account path for %d\n", id );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct account* a = account_new();
|
|
|
|
|
a->id = id;
|
|
|
|
|
|
|
|
|
|
snprintf( filename, 512, "data/accounts/%d/data.json", id );
|
|
|
|
|
char filename[512];
|
|
|
|
|
snprintf( filename, 512, "%s/data.json", path );
|
|
|
|
|
if( !json_read_object_layout_from_file( filename, account_layout, a ) ) {
|
|
|
|
|
printf( "Could not load data file from %s\n", filename );
|
|
|
|
|
account_free(a);
|
|
|
|
|
a = account_new();
|
|
|
|
|
a->id = id;
|
|
|
|
|
|
|
|
|
|
snprintf( filename, 512, "data/accounts/%d.json", id );
|
|
|
|
|
if( !json_read_object_layout_from_file( filename, account_layout, a ) ) {
|
|
|
|
|
account_free(a);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( a->replaced_by && a->replaced_by != a->id ) {
|
|
|
|
@ -309,13 +345,15 @@ struct account* account_from_uri( const char* uri )
|
|
|
|
|
struct account* a = account_from_id( account_id );
|
|
|
|
|
|
|
|
|
|
// Only return non-stub accounts here. This will force a refetch attempt
|
|
|
|
|
if( a && a->stub ) {
|
|
|
|
|
printf( "This account is a stub\n" );
|
|
|
|
|
account_free(a);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if( a ) {
|
|
|
|
|
if( a->stub ) {
|
|
|
|
|
printf( "This account is a stub\n" );
|
|
|
|
|
account_free(a);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
index_uri_to_account_id( a->account_url, a->id );
|
|
|
|
|
index_uri_to_account_id( a->account_url, a->id );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
@ -357,8 +395,11 @@ struct account* account_from_webfinger( const char* handle, const char* default_
|
|
|
|
|
|
|
|
|
|
struct crypto_keys* account_get_public_key( struct account* a, const char* key_name )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
get_account_path( a->id, path,sizeof(path) );
|
|
|
|
|
|
|
|
|
|
char filename[512];
|
|
|
|
|
FILE* f = fopen( format( filename, sizeof(filename), "data/accounts/%d/%s.pem", a->id, key_name ), "r" );
|
|
|
|
|
FILE* f = fopen( format( filename, sizeof(filename), "%s/%s.pem", path, key_name ), "r" );
|
|
|
|
|
if( !f ) {
|
|
|
|
|
//printf( "Failed to open file %s\n", filename );
|
|
|
|
|
return NULL;
|
|
|
|
@ -381,14 +422,28 @@ struct crypto_keys* account_get_private_key( struct account* a )
|
|
|
|
|
|
|
|
|
|
static void create_account_skeleton( int account_id )
|
|
|
|
|
{
|
|
|
|
|
char filename[512];
|
|
|
|
|
char b[512];
|
|
|
|
|
|
|
|
|
|
//*
|
|
|
|
|
// Check new-style path
|
|
|
|
|
int millions = account_id / 1000000;
|
|
|
|
|
int thousands = ( account_id % 1000000 ) / 1000;
|
|
|
|
|
int ones = account_id % 1000;
|
|
|
|
|
|
|
|
|
|
mkdir( format(filename,512, "data/accounts/%03dm", millions ), 0755 );
|
|
|
|
|
mkdir( format(filename,512, "data/accounts/%03dm/%03dk", millions, thousands ), 0755 );
|
|
|
|
|
mkdir( format(filename,512, "data/accounts/%03dm/%03dk/%03d", millions, thousands, ones ), 0755 );
|
|
|
|
|
/*/
|
|
|
|
|
// Use old-style path
|
|
|
|
|
snprintf( filename,512, "data/accounts/%d", account_id );
|
|
|
|
|
//*/
|
|
|
|
|
|
|
|
|
|
// Make sure the account directory exists
|
|
|
|
|
mkdir( format( b, 512, "data/accounts/%d", account_id ), 0755 );
|
|
|
|
|
mkdir( format( b, 512, "data/accounts/%d/timeline", account_id ), 0755 );
|
|
|
|
|
mkdir( format( b, 512, "data/accounts/%d/timeline/pinned", account_id ), 0755 );
|
|
|
|
|
mkdir( format( b, 512, "%s/timeline", filename), 0755 );
|
|
|
|
|
mkdir( format( b, 512, "%s/timeline/pinned", filename ), 0755 );
|
|
|
|
|
|
|
|
|
|
fs_list_set( format( b, 512, "data/accounts/%d/timeline/HEAD", account_id ), 0 );
|
|
|
|
|
//fs_list_set( format( b, 512, format( b, 512, "%s//timeline/HEAD", account_id ), 0 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct account* account_fetch_from_uri( const char* uri )
|
|
|
|
@ -400,10 +455,9 @@ struct account* account_fetch_from_uri( const char* uri )
|
|
|
|
|
account_id = fs_list_inc( "data/accounts/HEAD" );
|
|
|
|
|
|
|
|
|
|
index_uri_to_account_id( uri, account_id );
|
|
|
|
|
create_account_skeleton(account_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
create_account_skeleton(account_id);
|
|
|
|
|
|
|
|
|
|
struct account* a = NULL;
|
|
|
|
|
a = account_from_id(account_id);
|
|
|
|
|
if( a ) {
|
|
|
|
@ -425,8 +479,10 @@ struct account* account_fetch_from_uri( const char* uri )
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fetch the ActivityPub actor data if we don't already have it
|
|
|
|
|
char path[512];
|
|
|
|
|
get_account_path(account_id,path,512);
|
|
|
|
|
char filename[512];
|
|
|
|
|
snprintf( filename,sizeof(filename), "data/accounts/%d/ap.json", account_id );
|
|
|
|
|
snprintf( filename,sizeof(filename), "%s/ap.json", path );
|
|
|
|
|
pull_remote_file_if_older( filename, uri, 60*60*24*3 );
|
|
|
|
|
|
|
|
|
|
FILE* f = fopen( filename, "r" );
|
|
|
|
@ -490,8 +546,11 @@ void account_free( struct account* a )
|
|
|
|
|
|
|
|
|
|
void account_save( struct account* a )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
get_account_path( a->id, path, 512 );
|
|
|
|
|
|
|
|
|
|
char filename[512];
|
|
|
|
|
snprintf( filename, 512, "data/accounts/%d/data.json", a->id );
|
|
|
|
|
snprintf( filename, 512, "%s/data.json", path );
|
|
|
|
|
|
|
|
|
|
printf( "Saving to filename %s\n", filename );
|
|
|
|
|
json_write_object_layout_to_file( filename, "\t", account_layout, a );
|
|
|
|
@ -576,19 +635,23 @@ reindex:
|
|
|
|
|
|
|
|
|
|
void account_add_follower( struct account* a, struct account* follower )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
|
|
|
|
|
// Insert an entry for this follower (only does something if not already set)
|
|
|
|
|
char filename[512];
|
|
|
|
|
char key[32];
|
|
|
|
|
get_account_path(a->id,path,512);
|
|
|
|
|
ffdb_trie_set(
|
|
|
|
|
format( filename, sizeof(filename), "data/accounts/%d/followers", a->id ),
|
|
|
|
|
format( filename, sizeof(filename), "%s/followers", path ),
|
|
|
|
|
format(key,sizeof(key),"%d", follower->id),
|
|
|
|
|
"T"
|
|
|
|
|
);
|
|
|
|
|
a->followers_count = ffdb_trie_count(filename);
|
|
|
|
|
account_save(a);
|
|
|
|
|
|
|
|
|
|
get_account_path(follower->id,path,512);
|
|
|
|
|
ffdb_trie_set(
|
|
|
|
|
format( filename, sizeof(filename), "data/accounts/%d/following", follower->id ),
|
|
|
|
|
format( filename, sizeof(filename), "%s/following", path ),
|
|
|
|
|
format(key,sizeof(key),"%d", a->id),
|
|
|
|
|
"T"
|
|
|
|
|
);
|
|
|
|
@ -608,10 +671,12 @@ void account_add_follower( struct account* a, struct account* follower )
|
|
|
|
|
}
|
|
|
|
|
bool account_is_follower( struct account* a, struct account* possible_follower )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
get_account_path(a->id,path,512);
|
|
|
|
|
char filename[512];
|
|
|
|
|
char key[32];
|
|
|
|
|
char* value = ffdb_trie_get(
|
|
|
|
|
format( filename, sizeof(filename), "data/accounts/%d/followers", a->id ),
|
|
|
|
|
format( filename, sizeof(filename), "%s/followers", path ),
|
|
|
|
|
format( key, sizeof(key), "%d", possible_follower->id )
|
|
|
|
|
);
|
|
|
|
|
if( !value ) {
|
|
|
|
@ -621,6 +686,22 @@ bool account_is_follower( struct account* a, struct account* possible_follower )
|
|
|
|
|
free(value);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
struct timeline* account_get_timeline( struct account* a )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
if( !get_account_path( a->id, path, sizeof(path) ) )
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char timeline_path[512];
|
|
|
|
|
snprintf( timeline_path,512, "%s/timeline", path );
|
|
|
|
|
printf( "Using timeline path %s\n", timeline_path );
|
|
|
|
|
|
|
|
|
|
struct timeline* tl = timeline_from_path(timeline_path);
|
|
|
|
|
if( !tl ) { printf( "! Failed to create timeline object!\n" ); }
|
|
|
|
|
return tl;
|
|
|
|
|
}
|
|
|
|
|
void account_remove_follower( struct account* a, struct account* follower )
|
|
|
|
|
{
|
|
|
|
|
if( !account_is_follower( a, follower ) ) {
|
|
|
|
@ -628,16 +709,19 @@ void account_remove_follower( struct account* a, struct account* follower )
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove the follow
|
|
|
|
|
char path[512];
|
|
|
|
|
char filename[512];
|
|
|
|
|
char key[32];
|
|
|
|
|
get_account_path(a->id,path,512);
|
|
|
|
|
ffdb_trie_remove(
|
|
|
|
|
format( filename, sizeof(filename), "data/accounts/%d/followers", a->id ),
|
|
|
|
|
format( filename, sizeof(filename), "%s/followers", path ),
|
|
|
|
|
format( key, sizeof(key), "%d", follower->id )
|
|
|
|
|
);
|
|
|
|
|
a->followers_count = ffdb_trie_count(filename);
|
|
|
|
|
|
|
|
|
|
get_account_path(follower->id,path,512);
|
|
|
|
|
ffdb_trie_remove(
|
|
|
|
|
format( filename, sizeof(filename), "data/accounts/%d/following", follower->id ),
|
|
|
|
|
format( filename, sizeof(filename), "%s/following", path ),
|
|
|
|
|
format( key, sizeof(key), "%d", a->id )
|
|
|
|
|
);
|
|
|
|
|
follower->following_count = ffdb_trie_count(filename);
|
|
|
|
@ -680,13 +764,19 @@ static void account_list( const char* filename, int offset, int limit, struct in
|
|
|
|
|
|
|
|
|
|
void account_list_followers( struct account* a, int offset, int limit, void* id_array )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
get_account_path(a->id,path,512);
|
|
|
|
|
|
|
|
|
|
char filename[512];
|
|
|
|
|
account_list( format( filename, sizeof(filename), "data/accounts/%d/followers", a->id ), offset, limit, id_array );
|
|
|
|
|
account_list( format( filename, sizeof(filename), "%s/followers", path ), offset, limit, id_array );
|
|
|
|
|
}
|
|
|
|
|
void account_list_following( struct account* a, int offset, int limit, void* id_array )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
get_account_path(a->id,path,512);
|
|
|
|
|
|
|
|
|
|
char filename[512];
|
|
|
|
|
account_list( format( filename, sizeof(filename), "data/accounts/%d/following", a->id ), offset, limit, id_array );
|
|
|
|
|
account_list( format( filename, sizeof(filename), "%s/following", path ), offset, limit, id_array );
|
|
|
|
|
}
|
|
|
|
|
void account_move( struct account* old_account, const char* new_uri )
|
|
|
|
|
{
|
|
|
|
@ -738,9 +828,12 @@ bool account_does_follow( struct account* a, int account_id )
|
|
|
|
|
char index[512];
|
|
|
|
|
char key[32];
|
|
|
|
|
|
|
|
|
|
char path[512];
|
|
|
|
|
account_get_path( a->id, path, 512 );
|
|
|
|
|
|
|
|
|
|
// Make sure the account to unfollow has previously been followed
|
|
|
|
|
char* value = ffdb_trie_get(
|
|
|
|
|
format(index,512,"data/accounts/%d/following", a->id),
|
|
|
|
|
format(index,512,"%s/following", path),
|
|
|
|
|
format(key,32,"%d", account_id)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
@ -753,8 +846,11 @@ bool account_does_follow( struct account* a, int account_id )
|
|
|
|
|
|
|
|
|
|
void account_pin_status( struct account* a, struct status* s )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
account_get_path( a->id, path, 512 );
|
|
|
|
|
|
|
|
|
|
char buffer[512];
|
|
|
|
|
snprintf( buffer,sizeof(buffer), "data/accounts/%d/timeline/pinned", a->id );
|
|
|
|
|
snprintf( buffer,sizeof(buffer), "%s/timeline/pinned", path );
|
|
|
|
|
s->pinned = true;
|
|
|
|
|
|
|
|
|
|
struct timeline* pinned = timeline_from_path( buffer );
|
|
|
|
@ -780,8 +876,11 @@ void account_pin_status( struct account* a, struct status* s )
|
|
|
|
|
}
|
|
|
|
|
void account_unpin_status( struct account* a, struct status* s )
|
|
|
|
|
{
|
|
|
|
|
char path[512];
|
|
|
|
|
account_get_path( a->id, path, 512 );
|
|
|
|
|
|
|
|
|
|
char buffer[512];
|
|
|
|
|
snprintf( buffer,sizeof(buffer), "data/accounts/%d/timeline/pinned", a->id );
|
|
|
|
|
snprintf( buffer,sizeof(buffer), "%s/timeline/pinned", path );
|
|
|
|
|
s->pinned = false;
|
|
|
|
|
|
|
|
|
|
struct timeline* pinned = timeline_from_path( buffer );
|
|
|
|
|