Rework account path handling, sort outbox envelopes by number of accounts at each url, change account timeline handling

master
teknomunk 9 months ago
parent 81af6d65cd
commit d258d1fc70

@ -1 +1 @@
Subproject commit 44a552b831da2c1d9556fe909b6c77614f99a59e
Subproject commit 7ffee8875d10e1d3a52c5f8817112d235bec53bf

@ -123,9 +123,12 @@ done:
return true;
}
bool handle_timeline( struct http_request* req, int timeline_id )
bool handle_timeline( struct http_request* req, int account_id )
{
struct timeline* tl = timeline_from_id( timeline_id );
//struct timeline* tl = timeline_from_id( timeline_id );
struct account* a = account_from_id( account_id );
struct timeline* tl = account_get_timeline( a );
account_free(a);
bool res = handle_timeline_internal(req,tl);
timeline_free(tl);

@ -409,7 +409,7 @@ static bool route_create( struct ap_object* act )
bool account_followed = account_actor_is_followed(obj);
if( !mentions_me && !account_followed ) {
// Discard without action
//printf( "Discarding create. account_followed=%c, mentions_me=%c\n", account_followed ? 'T' : 'F', mentions_me ? 'T' : 'F' );
printf( "Discarding create. account_followed=%c, mentions_me=%c\n", account_followed ? 'T' : 'F', mentions_me ? 'T' : 'F' );
goto discard;
}
@ -452,13 +452,16 @@ static bool route_create( struct ap_object* act )
//status_add_to_timeline( s, federated_timeline_id );
status_add_to_timeline( s, s->account_id );
//printf( "route_create.complete\n" );
discard:
//printf( "route_create.discard\n" );
result = true;
goto cleanup;
cleanup:
status_free(s);
return result;
failed:
//printf( "route_create.failed\n" );
if( !g_server->develop ) { goto discard; }
result = false;
goto cleanup;

@ -1 +1 @@
Subproject commit 87d5d2462561348fd8efaa096aba620e93552a8d
Subproject commit 45f1653cd4243ca46a36105f3528b95968a087d5

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

@ -10,6 +10,7 @@ struct status;
struct ap_object;
struct outbox_envelope;
struct outbox_envelope_list;
struct timeline;
enum {
system_account_id = INT_MAX,
@ -120,6 +121,8 @@ struct crypto_keys* account_get_private_key( struct account* a );
void account_list_followers( struct account* a, int offset, int limit, void* id_array );
void account_list_following( struct account* a, int offset, int limit, void* id_array );
bool account_is_follower( struct account* a, struct account* possible_follower );
struct timeline* account_get_timeline( struct account* a );
const char* account_get_path( unsigned int id, char* buffer, int size );
// Activity pub data
struct ap_object* account_ap_actor( struct account* a );

@ -21,7 +21,7 @@ struct ap_object* account_activity_pub( struct account* a )
{
// Pass thru remote activity pub actor data if present
char filename[512];
struct ap_object* obj = ap_object_from_file( format( filename, 512, "data/accounts/%d/ap.json", a->id ) );
//struct ap_object* obj = ap_object_from_file( format( filename, 512, "data/accounts/%d/ap.json", a->id ) );
if( obj ) { return obj; }
return account_ap_actor(a);
@ -108,16 +108,23 @@ struct ap_object* account_ap_outbox( struct account* a )
outbox->id = aformat( "https://%s/outbox", g_server->domain );
outbox->first.tag = apaot_ref;
outbox->first.ref = aformat( "https://%s/outbox/page-0", g_server->domain );
char path[512];
account_get_path( a->id, path, 512 );
char buffer[512];
outbox->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/timeline", a->id ) );
outbox->total_items = ffdb_trie_count( format( buffer, 512, "%s/timeline", path ) );
return outbox;
}
struct ap_object* account_ap_outbox_page( struct account* a, int page )
{
enum { items_per_page = 10 };
char path[512];
account_get_path( a->id, path, 512 );
char buffer[512];
int total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/timeline", a->id ) );
int total_items = ffdb_trie_count( format( buffer, 512, "%s/timeline", path ) );
int page_count = ( total_items + items_per_page - 1 ) / items_per_page;
if( page >= page_count ) { return NULL; }
@ -251,8 +258,12 @@ struct ap_object* account_ap_followers( struct account* a )
o->id = aformat( "https://%s/owner/followers", g_server->domain );
o->first.tag = apaot_object;
o->first.ptr = account_ap_followers_page( a, 0 );
char path[512];
account_get_path( a->id, path, 512 );
char buffer[512];
o->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/followers", a->id ) );
o->total_items = ffdb_trie_count( format( buffer, 512, "%sd/followers", path ) );
return o;
}
@ -265,8 +276,11 @@ struct ap_object* account_ap_followers_page( struct account* a, int page )
char page_format[512];
snprintf( page_format, 512, "https://%s/owner/followers/page-%%d", g_server->domain );
char path[512];
account_get_path( a->id, path, 512 );
char trie_filename[512];
snprintf( trie_filename, 512, "data/accounts/%d/followers", a->id );
snprintf( trie_filename, 512, "%s/followers", path );
return account_list_page( page, part_of, page_format, trie_filename );
}
@ -282,8 +296,12 @@ struct ap_object* account_ap_following( struct account* a )
o->id = aformat( "https://%s/owner/following", g_server->domain );
o->first.tag = apaot_object;
o->first.ptr = account_ap_following_page( a, 0 );
char path[512];
account_get_path( a->id, path, 512 );
char buffer[512];
o->total_items = ffdb_trie_count( format( buffer, 512, "data/accounts/%d/following", a->id ) );
o->total_items = ffdb_trie_count( format( buffer, 512, "%s/following", path ) );
return o;
}
@ -296,8 +314,11 @@ struct ap_object* account_ap_following_page( struct account* a, int page )
char page_format[512];
snprintf( page_format, 512, "https://%s/owner/following/page-%%d", g_server->domain );
char path[512];
account_get_path( a->id, path, 512 );
char trie_filename[512];
snprintf( trie_filename, 512, "data/accounts/%d/following", a->id );
snprintf( trie_filename, 512, "%s/following", path );
return account_list_page( page, part_of, page_format, trie_filename );
}
@ -308,9 +329,12 @@ struct ap_object* account_ap_featured( struct account* a )
o->type = ap_OrderedCollection;
o->ap_context.language = clang_undefined;
//o->published = time(NULL);
char path[512];
account_get_path( a->id, path, 512 );
char buffer[512];
o->id = aformat( "https://%s/owner/collections/featured", g_server->domain );
o->total_items = ffdb_trie_count( format( buffer,512, "data/accounts/%d/timeline/pinned", a->id ) );
o->total_items = ffdb_trie_count( format( buffer,512, "%s/timeline/pinned", path ) );
o->ordered_items.items = malloc(1);
if( o->total_items > 0 ) {
@ -321,12 +345,15 @@ struct ap_object* account_ap_featured( struct account* a )
}
struct ap_object* account_ap_featured_page( struct account* a, int page )
{
char path[512];
account_get_path( a->id, path, 512 );
char buffer[512];
snprintf( buffer,512, "data/accounts/%d/timeline/pinned", a->id );
snprintf( buffer,512, "%s/timeline/pinned", path );
struct timeline* tl = timeline_from_path( buffer );
if( !tl ) { return NULL; }
int total_items = ffdb_trie_count( format( buffer,512, "data/accounts/%d/timeline/pinned", a->id ) );
int total_items = ffdb_trie_count( format( buffer,512, "%s/timeline/pinned", path ) );
enum { items_per_page = 20 };
const int total_pages = ( total_items + items_per_page - 1 ) / items_per_page;

@ -19,8 +19,11 @@ struct ap_object* account_get_activity_pub_data( struct account* a )
{
if( !a ) { return NULL; }
char path[512];
account_get_path( a->id, path, 512 );
char filename[512];
snprintf( filename, 512, "data/accounts/%d/ap.json", a->id );
snprintf( filename, 512, "%s/ap.json", path );
printf( "ap_object_from_file( %s )\n", filename );
if( a->account_id ) {
@ -177,7 +180,9 @@ bool account_sync_from_activity( struct account* a, struct ap_object* obj )
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" );
char path[512];
account_get_path( a->id, path, 512 );
FILE* key_pem = fopen( format(filename,sizeof(filename),"%s/%s.pem", path, key_id), "w" );
if( !key_pem ) {
printf( "Unable to save public key to %s\n", filename );
} else {

@ -31,6 +31,7 @@ void account_deliver_activity( struct account* a, struct ap_object* act, struct
if( e->shared_inbox && a->shared_inbox && 0 == strcmp(e->shared_inbox,a->shared_inbox) ) {
//printf( "\tUsing shared inbox %s already in delivery list\n", a->shared_inbox );
// This account will get the message delivered thru the shared inbox
e->count += 1;
return;
}
}
@ -38,6 +39,7 @@ void account_deliver_activity( struct account* a, struct ap_object* act, struct
struct outbox_envelope* env = outbox_envelope_new();
env->activity_id = act->local_id;
env->count = 1;
if( a->shared_inbox ) {
env->shared_inbox = strdup(a->shared_inbox);
printf( "Delivering activity %s to account %s via shared inbox %s\n", act->id, a->account_url, a->shared_inbox );
@ -57,8 +59,11 @@ void account_deliver_activity_to_followers( struct account* a, struct ap_object*
} keys;
memset( &keys, 0, sizeof(keys) );
char path[512];
account_get_path( a->id, path, 512 );
char filename[512];
snprintf( filename, sizeof(filename), "data/accounts/%d/followers", a->id );
snprintf( filename, sizeof(filename), "%s/followers", path );
// TODO: handle shared inbox delivery
int pages = (a->followers_count+31) / 32;
@ -171,7 +176,10 @@ void account_follow( struct account* a, struct account* to_follow )
account_add_follower( to_follow, a );
// Record the activity so it an Undo can be issued in the future
mkdir( format(filename,sizeof(filename),"data/accounts/%d/follow-activities", a->id), 0755 );
char path[512];
account_get_path( a->id, path, 512 );
mkdir( format(filename,sizeof(filename),"%s/follow-activities", path), 0755 );
ffdb_trie_set(filename, format(key,32,"%d", to_follow->id ), format(value,32,"%d", act_id ) );
// Save account data
@ -184,9 +192,12 @@ void account_unfollow( struct account* a, struct account* to_unfollow )
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* res = ffdb_trie_get(
format(index,512,"data/accounts/%d/follow-activities", a->id),
format(index,512,"%s/follow-activities", path),
format(key,32,"%d", to_unfollow->id)
);
if( !res ) {

@ -5,6 +5,7 @@
#include "util/format.h"
#include "ffdb/fs_list.h"
#include "collections/array.h"
#include "collections/sort/bubble.h"
#include "ap/object.h"
// Model
@ -390,6 +391,20 @@ static void deliver_to( struct outbox_envelope_list* oel, struct ap_object* act,
}
}
static int compare_envelope_counts( struct collection it, void* a, void* b )
{
struct outbox_envelope* ea = a;
struct outbox_envelope* eb = b;
if( ea->count < eb->count ) {
return -1;
} else if( ea->count > eb->count ) {
return 1;
} else {
return 0;
}
}
void activity_deliver( struct ap_object* act )
{
printf( "activity_deliver( act = %s )\n", act->id );
@ -407,6 +422,17 @@ void activity_deliver( struct ap_object* act )
deliver_to( &oel, act, act->bcc.items[i] );
}
// Sort so that oel[0] has the lowest count and oel[size-1] has the highest
struct collection c;
static struct item_type_vtable itable = {
.size = sizeof(oel.items[0]),
.compare = compare_envelope_counts,
};
c.ptr = &oel;
c.vtable = &array_vtable;
c.itable = &itable;
collection_sort_bubble( c, false );
printf( "Delivering to %d inboxes\n", oel.count );
outbox_envelope_list_save(&oel);
outbox_envelope_list_free_composite(&oel);

@ -269,9 +269,14 @@ struct hsv_result http_signature_validate( struct ap_envelope* env, const char*
// Get the public key
keys = account_get_public_key( actor, key_name );
if( !keys ) {
printf( "! Failed to find public key for actor=%s (id=%d), key_name=%s\n", actor->account_url, actor->id, key_name );
result.error = HSV_NO_PUBKEY;
goto failed;
account_sync_from_activity_pub( actor->id, true );
keys = account_get_public_key( actor, key_name );
if( !keys ) {
printf( "! Failed to find public key for actor=%s (id=%d), key_name=%s\n", actor->account_url, actor->id, key_name );
result.error = HSV_NO_PUBKEY;
goto failed;
}
}
}

@ -12,6 +12,7 @@ struct outbox_envelope
bool sent;
time_t retry_after;
int retries;
int count;
};
struct outbox_envelope* outbox_envelope_new();

@ -741,11 +741,21 @@ void status_delete( struct status* s )
};
for( int i = 0; i < sizeof(ids)/sizeof(ids[0]); ++i ) {
struct timeline* tl = timeline_from_id( ids[i] );
struct account* a = account_from_id( ids[i] );
if( !a ) {
printf( "No account for id %d\n", ids[i] );
continue;
}
struct timeline* tl = account_get_timeline(a);
if( tl ) {
timeline_remove_post( tl, s );
timeline_free(tl);
} else {
printf( "No timeline for account %d\n", ids[i] );
}
account_free(a);
}
char filename[512];
@ -756,11 +766,17 @@ void status_delete( struct status* s )
status_free(s);
}
void status_add_to_timeline( struct status* s, int timeline_id )
void status_add_to_timeline( struct status* s, int account_id )
{
struct timeline* tl = timeline_from_id(timeline_id);
//printf( "status_add_to_timeline( s, account_id=%d )\n", account_id );
struct account* a = account_from_id( account_id );
//printf( "Adding status %d to timeline for %s (%d/%d)\n", s->id, a->account_id, account_id, a->id );
struct timeline* tl = account_get_timeline(a);
timeline_add_post( tl, s );
timeline_free(tl);
account_free(a);
}
void status_make_reply_to( struct status* s, int in_reply_to_id )

@ -23,15 +23,6 @@ struct timeline* timeline_new()
memset(tl,0,sizeof(*tl));
return tl;
}
struct timeline* timeline_from_id( int id )
{
struct timeline* tl = timeline_new();
tl->id = id;
tl->path = aformat( "data/accounts/%d/timeline", tl->id );
return tl;
}
struct timeline* timeline_from_path( const char* path )
{
struct timeline* tl = timeline_new();
@ -84,6 +75,7 @@ int timeline_load_statuses( struct timeline* tl, int offset_from_head, int count
}
void timeline_add_post( struct timeline* tl, struct status* s )
{
//printf( "- timeline_add_post( tl=%s, s=%d )\n", tl->path, s->id );
char key[512];
rfc3339_time_string( s->published, key, sizeof(key) );

@ -7,7 +7,6 @@ struct timeline
char* path;
};
struct timeline* timeline_from_id( int id );
struct timeline* timeline_from_path( const char* path );
struct timeline* timeline_new();
void timeline_free( struct timeline* tl );

Loading…
Cancel
Save