|
|
|
#define _GNU_SOURCE
|
|
|
|
#include "status.h"
|
|
|
|
#include "status/react.h"
|
|
|
|
|
|
|
|
#include "model/account.h"
|
|
|
|
#include "model/timeline.h"
|
|
|
|
#include "model/ap/activity.h"
|
|
|
|
#include "model/notification.h"
|
|
|
|
|
|
|
|
// Submodules
|
|
|
|
#include "json/json.h"
|
|
|
|
#include "json/layout.h"
|
|
|
|
#include "ffdb/hash_index.h"
|
|
|
|
#include "sha256/sha256.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
static struct json_object_field status_layout[] = {
|
|
|
|
{ "account_id", offsetof( struct status, account_id ), true, &json_field_integer },
|
|
|
|
|
|
|
|
{ "url", offsetof( struct status, url ), false, &json_field_string },
|
|
|
|
{ "content", offsetof( struct status, source ), false, &json_field_string },
|
|
|
|
{ "source", offsetof( struct status, source ), false, &json_field_string },
|
|
|
|
{ "sensitive", offsetof( struct status, sensitive ), true, &json_field_bool },
|
|
|
|
|
|
|
|
|
|
|
|
{ "media", offsetof( struct status, media ), false, &json_field_array_of, &json_field_string },
|
|
|
|
{ "reacts", offsetof( struct status, reacts ), false, &json_field_array_of, &status_react_type },
|
|
|
|
|
|
|
|
{ "likes", offsetof( struct status, likes ), false, &json_field_array_of, &json_field_integer },
|
|
|
|
{ "reposts", offsetof( struct status, reposts ), false, &json_field_array_of, &json_field_integer },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
void* allocate( size_t s )
|
|
|
|
{
|
|
|
|
void* ptr = malloc(s);
|
|
|
|
memset( ptr, 0, s );
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct status* status_from_id( unsigned int id )
|
|
|
|
{
|
|
|
|
struct status* s = NULL;
|
|
|
|
|
|
|
|
char filename[512];
|
|
|
|
snprintf( filename, 512, "data/statuses/%d.json", id );
|
|
|
|
|
|
|
|
s = allocate(sizeof(struct status));
|
|
|
|
s->id = id;
|
|
|
|
|
|
|
|
if( !json_read_object_layout_from_file( filename, status_layout, s ) ) {
|
|
|
|
printf( "Failed to load status %d\n", id );
|
|
|
|
status_free(s);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !s->source ) {
|
|
|
|
s->source = strdup("");
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
struct status* status_from_uri( const char* uri )
|
|
|
|
{
|
|
|
|
int id = -1;
|
|
|
|
if( !hash_index_get( "data/statuses/uri", uri, &id ) ) { return NULL; }
|
|
|
|
|
|
|
|
return status_from_id(id);
|
|
|
|
}
|
|
|
|
struct status* status_new_system_unfollow( int account_id )
|
|
|
|
{
|
|
|
|
struct account* a = account_from_id(account_id);
|
|
|
|
if( !a ) { return NULL; }
|
|
|
|
|
|
|
|
struct status* s;
|
|
|
|
s = malloc(sizeof(*s));
|
|
|
|
memset(s,0,sizeof(*s));
|
|
|
|
|
|
|
|
s->id = -1;
|
|
|
|
s->account_id = -1;
|
|
|
|
asprintf( &s->content, "%s unfollowed you\n", a->display_name );
|
|
|
|
s->sensitive = true;
|
|
|
|
|
|
|
|
account_free(a);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
struct status* status_new_system_block( int account_id )
|
|
|
|
{
|
|
|
|
struct account* a = account_from_id(account_id);
|
|
|
|
if( !a ) { return NULL; }
|
|
|
|
|
|
|
|
struct status* s;
|
|
|
|
s = malloc(sizeof(*s));
|
|
|
|
memset(s,0,sizeof(*s));
|
|
|
|
|
|
|
|
s->id = -1;
|
|
|
|
s->account_id = -1;
|
|
|
|
asprintf( &s->content, "%s blocked you\n", a->display_name );
|
|
|
|
s->sensitive = true;
|
|
|
|
|
|
|
|
account_free(a);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct status* status_from_activity( struct ap_activity* act )
|
|
|
|
{
|
|
|
|
struct status* s;
|
|
|
|
s = malloc(sizeof(*s));
|
|
|
|
memset(s,0,sizeof(*s));
|
|
|
|
|
|
|
|
struct account* a = account_from_uri(act->actor);
|
|
|
|
s->account_id = a->id;
|
|
|
|
|
|
|
|
// Add status to account timeline
|
|
|
|
struct timeline* tl = timeline_from_id( a->id );
|
|
|
|
printf( "tl->id=%d\n", tl->id );
|
|
|
|
timeline_add_post(tl,s);
|
|
|
|
timeline_free(tl);
|
|
|
|
account_free(a);
|
|
|
|
|
|
|
|
s->source = strdup(act->source);
|
|
|
|
//s->content = status_render_source(s);
|
|
|
|
s->url = strdup( act->id );
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool status_save_new( struct status* s )
|
|
|
|
{
|
|
|
|
int head = -1;
|
|
|
|
FILE* f = fopen("data/statuses/HEAD","r");
|
|
|
|
fscanf(f,"%d",&head);
|
|
|
|
if( head == -1 ) { return false; }
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
s->id = head + 1;
|
|
|
|
|
|
|
|
f = fopen("data/statuses/HEAD.tmp","w");
|
|
|
|
fprintf( f, "%d", s->id );
|
|
|
|
fclose(f);
|
|
|
|
rename( "data/statuses/HEAD.tmp", "data/statuses/HEAD" );
|
|
|
|
|
|
|
|
status_save( s );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void status_save( struct status* s )
|
|
|
|
{
|
|
|
|
char filename[512];
|
|
|
|
snprintf( filename, 512, "data/statuses/%d.json", s->id );
|
|
|
|
|
|
|
|
json_write_object_layout_to_file( filename, "\t", status_layout, s );
|
|
|
|
|
|
|
|
// Index the status
|
|
|
|
if( s->url ) {
|
|
|
|
hash_index_set( "data/statuses/uri", s->url, s->id );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void status_free( struct status* s )
|
|
|
|
{
|
|
|
|
if( !s ) { return; }
|
|
|
|
|
|
|
|
for( int i = 0; i < s->media.count; ++i ) {
|
|
|
|
free(s->media.items[i]);
|
|
|
|
}
|
|
|
|
free(s->media.items);
|
|
|
|
|
|
|
|
for( int i = 0; i < s->reacts.count; ++i ) {
|
|
|
|
free(s->reacts.items[i]);
|
|
|
|
}
|
|
|
|
free(s->reacts.items);
|
|
|
|
|
|
|
|
free(s->reposts.items);
|
|
|
|
|
|
|
|
free(s->content);
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void status_add_react( struct status* s, const char* react )
|
|
|
|
{
|
|
|
|
printf( "TODO: add reaction '%s' to status #%d\n", react, s->id );
|
|
|
|
|
|
|
|
if( s->url ) {
|
|
|
|
// TODO generate outbox element
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void status_add_like( struct status* s, struct account* a )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < s->likes.count; ++i ) {
|
|
|
|
if( s->likes.items[i] == a->id ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( s->account_id == 0 ) {
|
|
|
|
// Create notification for liking the owner's post
|
|
|
|
struct notification* note = notification_new();
|
|
|
|
note->type = nt_like;
|
|
|
|
note->account_id = a->id;
|
|
|
|
note->created_at = time(NULL);
|
|
|
|
notification_save( note );
|
|
|
|
notification_free( note );
|
|
|
|
}
|
|
|
|
|
|
|
|
status_save(s);
|
|
|
|
}
|
|
|
|
|