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.
234 lines
5.5 KiB
C
234 lines
5.5 KiB
C
#include "status.h"
|
|
|
|
#include "json/json.h"
|
|
#include "json/layout.h"
|
|
#include "collections/array.h"
|
|
|
|
#include "http/server/request.h"
|
|
#include "model/status.h"
|
|
#include "model/status/react.h"
|
|
#include "model/account.h"
|
|
#include "model/ap/activity.h"
|
|
#include "model/ap/outbox_envelope.h"
|
|
|
|
#include "view/api/Account.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
|
|
void write_json_escaped( FILE* f, const char* str );
|
|
|
|
void api_status_write_as_json( struct status* s, FILE* f )
|
|
{
|
|
if( s->stub ) {
|
|
struct status* system_status = status_new_system_stub( s );
|
|
s = system_status;
|
|
}
|
|
|
|
struct account* account = account_from_id( s->account_id );
|
|
|
|
struct status* in_reply_to = NULL;
|
|
struct account* in_reply_to_account = NULL;
|
|
if( s->in_reply_to ) {
|
|
in_reply_to = status_from_id( s->in_reply_to );
|
|
in_reply_to_account = account_from_id( in_reply_to->account_id );
|
|
}
|
|
|
|
struct status* repost = NULL;
|
|
if( s->repost_id ) {
|
|
repost = status_from_id( s->repost_id );
|
|
}
|
|
|
|
#include "src/view/api/status.json.inc"
|
|
|
|
cleanup:
|
|
account_free(account);
|
|
account_free(in_reply_to_account);
|
|
status_free(in_reply_to);
|
|
}
|
|
|
|
void show_status( struct http_request* req, struct status* s )
|
|
{
|
|
http_request_send_headers( req, 200, "application/json", true );
|
|
FILE* f = http_request_get_response_body( req );
|
|
|
|
api_status_write_as_json(s,f);
|
|
}
|
|
|
|
// Route: /api/v1/statuses/%d{s->id}/context
|
|
void show_status_context( struct http_request* req, struct status* s )
|
|
{
|
|
http_request_send_headers( req, 200, "application/json", true );
|
|
FILE* f = http_request_get_response_body( req );
|
|
|
|
struct {
|
|
struct status** items;
|
|
int count;
|
|
} ancestors, replies;
|
|
status_get_context( s, &ancestors, &replies );
|
|
|
|
fprintf( f, "{\"ancestors\":[" );
|
|
for( int i = 0; i < ancestors.count; ++i ) {
|
|
struct status* s2 = ancestors.items[i];
|
|
api_status_write_as_json(s2,f);
|
|
status_free(s2);
|
|
|
|
if( i != ancestors.count - 1 ) {
|
|
fprintf( f, "," );
|
|
}
|
|
}
|
|
free(ancestors.items);
|
|
|
|
fprintf( f, "],\"descendants\":[" );
|
|
for( int i = 0; i < replies.count; ++i ) {
|
|
struct status* s2 = replies.items[i];
|
|
api_status_write_as_json(s2,f);
|
|
status_free(s2);
|
|
|
|
if( i != replies.count - 1 ) {
|
|
fprintf( f, "," );
|
|
}
|
|
}
|
|
free(replies.items);
|
|
fprintf( f, "]}" );
|
|
}
|
|
|
|
void show_statuses( struct http_request* req, struct status** ss, int count )
|
|
{
|
|
http_request_send_headers( req, 200, "application/json", true );
|
|
FILE* f = http_request_get_response_body( req );
|
|
|
|
fprintf( f, "[" );
|
|
for( int i = 0; i < count; ++i ) {
|
|
if( i > 0 ) {
|
|
fprintf( f, "," );
|
|
}
|
|
api_status_write_as_json(ss[i],f);
|
|
}
|
|
fprintf( f, "]" );
|
|
}
|
|
|
|
// Route: POST /api/v1/statuses
|
|
bool handle_post( struct http_request* req, struct account* a )
|
|
{
|
|
bool result = false;
|
|
struct status* s = NULL;
|
|
|
|
struct params_t
|
|
{
|
|
struct {
|
|
int* items;
|
|
int count;
|
|
} media_ids;
|
|
|
|
bool sensitive;
|
|
|
|
char* status;
|
|
char* visibility;
|
|
char* in_reply_to_id;
|
|
char* spoiler_text;
|
|
} params;
|
|
memset(¶ms,0,sizeof(params));
|
|
|
|
#define OBJ_TYPE struct params_t
|
|
static struct json_object_field layout[] = {
|
|
JSON_FIELD_ARRAY_OF_INTS( media_ids, false ),
|
|
JSON_FIELD_BOOL( sensitive, false ),
|
|
JSON_FIELD_STRING( status, true ),
|
|
JSON_FIELD_STRING( visibility, true ),
|
|
JSON_FIELD_STRING( in_reply_to_id, false ),
|
|
JSON_FIELD_STRING( spoiler_text, false ),
|
|
JSON_FIELD_END,
|
|
};
|
|
#undef OBJ_TYPE
|
|
|
|
FILE* data = http_request_get_request_data( req );
|
|
if( !json_read_object_layout_from_FILE( data, layout, ¶ms ) ) {
|
|
return false;
|
|
}
|
|
|
|
s = malloc(sizeof(struct status));
|
|
memset(s,0,sizeof(*s));
|
|
|
|
s->published = time(NULL);
|
|
s->source = strdup( params.status );
|
|
status_save_new(s);
|
|
|
|
if( params.in_reply_to_id ) {
|
|
status_make_reply_to( s, atoi( params.in_reply_to_id ) );
|
|
}
|
|
status_save(s);
|
|
|
|
// Add to owner timeline, public timeline and home timelines
|
|
status_add_to_timeline( s, a->id );
|
|
status_add_to_timeline( s, public_timeline_id );
|
|
status_add_to_timeline( s, home_timeline_id );
|
|
//status_add_post_to_timeline( s, federated_timeline_id );
|
|
|
|
// Federate
|
|
struct ap_activity* note = ap_activity_create_note(s);
|
|
struct ap_activity* create = ap_activity_create_Create(note);
|
|
ap_activity_save(create);
|
|
|
|
struct outbox_envelope_list oel;
|
|
memset(&oel,0,sizeof(oel));
|
|
account_deliver_activity_to_followers( a, create, &oel );
|
|
printf( "Delivering to %d inboxes\n", oel.count );
|
|
outbox_envelope_list_save(&oel);
|
|
outbox_envelope_list_free_composite(&oel);
|
|
|
|
ap_activity_free(create);
|
|
ap_activity_free(note);
|
|
|
|
http_request_send_headers( req, 200, "application/json", true );
|
|
FILE* f = http_request_get_response_body(req);
|
|
api_status_write_as_json(s,f);
|
|
result = true;
|
|
cleanup:
|
|
free(params.status);
|
|
free(params.visibility);
|
|
free(params.in_reply_to_id);
|
|
free(params.spoiler_text);
|
|
status_free(s);
|
|
return result;
|
|
failed:
|
|
result = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Route: POST /api/v1/statuses/%d{id}/reblog
|
|
bool handle_repost( struct http_request* req, struct status* s )
|
|
{
|
|
bool result = false;
|
|
struct account* owner = account_from_id(owner_account_id);
|
|
struct status* repost = status_new_repost( s, owner );
|
|
|
|
// Flag the status as reposted
|
|
s->reposted_status_id = repost->id;
|
|
status_save(s);
|
|
|
|
// Add repost to timelines
|
|
status_add_to_timeline( repost, owner->id );
|
|
status_add_to_timeline( repost, home_timeline_id );
|
|
status_add_to_timeline( repost, public_timeline_id );
|
|
|
|
// Federate
|
|
account_announce( owner, s );
|
|
|
|
// Show the new status as the response
|
|
show_status( req, repost );
|
|
success:
|
|
result = true;
|
|
cleanup:
|
|
account_free(owner);
|
|
status_free(repost);
|
|
|
|
return result;
|
|
failed:
|
|
result = false;
|
|
goto cleanup;
|
|
}
|
|
|