Implement federated media posts

master
teknomunk 1 year ago
parent 6d3b608925
commit d80b5e1e62

@ -1,13 +1,16 @@
#include "status.h"
#include "http/server/request.h"
#include "json/json.h"
#include "json/layout.h"
#include "collections/array.h"
#include "format.h"
#include "http/server/request.h"
#include "model/server.h"
#include "model/status.h"
#include "model/status/react.h"
#include "model/account.h"
#include "model/media.h"
#include "model/ap/activity.h"
#include "model/ap/outbox_envelope.h"
@ -91,7 +94,7 @@ bool handle_post( struct http_request* req, struct account* a )
struct params_t
{
struct {
int* items;
char** items;
int count;
} media_ids;
@ -106,7 +109,7 @@ bool handle_post( struct http_request* req, struct account* a )
#define OBJ_TYPE struct params_t
static struct json_object_field layout[] = {
JSON_FIELD_ARRAY_OF_INTS( media_ids, false ),
JSON_FIELD_ARRAY_OF_STRINGS( media_ids, false ),
JSON_FIELD_BOOL( sensitive, false ),
JSON_FIELD_STRING( status, true ),
JSON_FIELD_STRING( visibility, true ),
@ -128,6 +131,14 @@ bool handle_post( struct http_request* req, struct account* a )
s->source = strdup( params.status );
status_save_new(s);
for( int i = 0; i < params.media_ids.count; ++i ) {
struct media* m = media_from_id( atoi(params.media_ids.items[i]) );
if( !m ) { continue; }
char* url = aformat( "https://%s/media/%d/blob", g_server_name, m->id );
array_append( &s->media, sizeof(url), &url );
}
if( params.in_reply_to_id ) {
status_make_reply_to( s, atoi( params.in_reply_to_id ) );
}
@ -163,6 +174,9 @@ cleanup:
free(params.visibility);
free(params.in_reply_to_id);
free(params.spoiler_text);
for( int i = 0; i < params.media_ids.count; ++i ) {
free( params.media_ids.items[i] );
}
status_free(s);
return result;
failed:

@ -1,4 +1,7 @@
#include "http/server/request.h"
#include "format.h"
#include "model/media.h"
#include "controller/mastodon_api.h"
#include "controller/pleroma_api.h"
@ -118,6 +121,35 @@ cleanup:
return result;
}
// Route: /media/
bool route_media( struct http_request* req )
{
char* id_str = http_request_route_get_dir_or_file(req);
if( !id_str || !*id_str ) { return false; }
// Route: /media/%d{id}/
int id = -1;
if( 1 != sscanf( id_str, "%d", &id ) ) { return false; }
struct media* m = media_from_id( id );
if( !m ) { return false; }
bool subroute( struct http_request* req, struct media* m ) {
char filename[512];
if( http_request_route_term( req, "blob" ) ) {
return http_request_send_file( req, format(filename,sizeof(filename),"data/media/%d.blob",m->id), m->content_type );
} else if( http_request_route_term( req, "preview" ) ) {
return http_request_send_file( req, format(filename,sizeof(filename),"data/media/%d.blob.preview",m->id), m->content_type );
}
return false;
}
bool res = subroute( req, m );
media_free(m);
return res;
}
bool route_request( struct http_request* req )
{
bool inner( struct http_request* req ) {
@ -133,6 +165,8 @@ bool route_request( struct http_request* req )
return route_inbox( req );
} else if( http_request_route( req, "/note/" ) ) {
return route_ap_note( req );
} else if( http_request_route( req, "/media/" ) ) {
return route_media( req );
} else if( http_request_route( req, "/.well-known" ) ) {
if( http_request_route( req, "/webfinger?" ) ) {
return route_wellknown_webfinger( req );

@ -12,6 +12,7 @@
#include "model/account.h"
#include "model/notification.h"
#include "model/server.h"
#include "model/media.h"
#include "model/ap/activity.h"
#include "controller/pleroma_api.h"
@ -21,6 +22,8 @@
#include "controller/api/notice.h"
#include "controller/api/accounts.h"
#include "view/api/Attachement.h"
#include <stddef.h>
#include <limits.h>
#include <stdio.h>
@ -83,7 +86,7 @@ success:
ssize_t getline_stripped( char** restrict lineptr, size_t* restrict n, FILE* restrict stream);
bool http_request_write_multipart_to_FILE( struct http_request* req, FILE* f )
bool http_request_write_multipart_to_FILE( struct http_request* req, FILE* f, char** content_type )
{
char* line = NULL;
char* boundary = NULL;
@ -145,11 +148,12 @@ bool http_request_write_multipart_to_FILE( struct http_request* req, FILE* f )
bool filled = false;
// Eat all header data
char* content_type = NULL;
while( getline_stripped( &line, &n, data ) > 0 ) {
if( 0 == strncasecmp( line, "Content-Type: ", 14 ) ) {
content_type = strdup( &line[14] );
printf( "content_type=%s\n", content_type );
if( content_type ) {
*content_type = strdup( &line[14] );
}
printf( "content_type=%s\n", *content_type );
} else {
printf( "Header: %s\n", line );
}
@ -180,7 +184,6 @@ bool http_request_write_multipart_to_FILE( struct http_request* req, FILE* f )
//printf( "%d bytes uploaded\n", filesize );
free(content_type);
free(boundary);
free(buffer.data);
return true;
@ -188,17 +191,46 @@ bool http_request_write_multipart_to_FILE( struct http_request* req, FILE* f )
bool handle_media( struct http_request* req )
{
bool result = false;
char filename[512];
FILE* f = NULL;
struct media* m = NULL;
int id = fs_list_get( "data/media/HEAD" ) + 1;
fs_list_set( "data/media/HEAD", id );
m = malloc(sizeof(*m));
memset(m,0,sizeof(*m));
m->id = id;
f = fopen(format(filename,sizeof(filename), "data/media/%d.blob",id), "w" );
if( !http_request_write_multipart_to_FILE( req, f, &m->content_type ) ) { goto failed; }
fclose(f); f = NULL;
if( 0 == strncmp("image/",m->content_type,6) ) {
printf( "This is an image, create preview\n" );
char buffer[512];
system( "set -x; pwd" );
int res = system( format( buffer,sizeof(buffer),"set -x; /usr/bin/convert '%s' -thumbnail '250x80>' '%s.preview'", filename, filename ) );
printf( "$ %s -> %d\n", buffer, res );
}
int id = fs_list_get( "data/media/HEAD" );
media_save(m);
FILE* f = fopen(format(filename,sizeof(filename), "data/media/%d.blob",id), "w" );
bool result = http_request_write_multipart_to_FILE( req, f );
fclose(f);
http_request_send_headers( req, 200, "application/json", true );
FILE* body = http_request_get_response_body( req );
api_Attachement_write( m, body, 0 );
if( !result ) { return false; }
result = true;
return false;
cleanup:
if( f ) { fclose(f); }
media_free(m);
return result;
failed:
result = false;
goto cleanup;
}
bool route_mastodon_api( struct http_request* req )

@ -9,6 +9,7 @@
#include "model/server.h"
#include "model/account.h"
#include "model/status.h"
#include "model/media.h"
#include "model/ap/outbox_envelope.h"
#include "model/ap/activity/rsa_signature_2017.h"
@ -44,6 +45,33 @@ struct ap_activity* ap_activity_dup( struct ap_activity* act )
new_act->content.content = safe_strdup(act->content.content);
new_act->content.mime_type = safe_strdup(act->content.mime_type);
for( int i = 0; i < act->tags.count; ++i ) {
struct ap_activity_tag* old_tag = act->tags.items[i];
struct ap_activity_tag* new_tag;
new_tag = malloc(sizeof(*new_tag));
memset(new_tag,0,sizeof(*new_tag));
new_tag->type = old_tag->type;
new_tag->href = safe_strdup(old_tag->href);
new_tag->name = safe_strdup(old_tag->name);
array_append( &new_act->tags, sizeof(new_tag), &new_tag );
}
for( int i = 0; i < act->attachments.count; ++i ) {
struct ap_attachement* old_att = act->attachments.items[i];
struct ap_attachement* new_att;
new_att = malloc(sizeof(*new_att));
memset(new_att,0,sizeof(*new_att));
new_att->type = old_att->type;
new_att->mediaType = safe_strdup(old_att->mediaType);
new_att->name = safe_strdup(old_att->mediaType);
new_att->url = safe_strdup(old_att->url);
new_att->value = safe_strdup(old_att->value);
array_append( &new_act->attachments, sizeof(new_att), &new_att );
}
array_dup( &new_act->to, sizeof(char*), &act->to );
for( int i = 0; i < new_act->to.count; ++i ) {
new_act->to.items[i] = strdup(new_act->to.items[i]);
@ -349,6 +377,22 @@ struct ap_activity* ap_activity_create_note( struct status* s )
char* str = strdup("https://www.w3.org/ns/activitystreams#Public");
array_append( &act->to, sizeof(str), &str );
for( int i = 0; i < s->media.count; ++i ) {
struct media* m = media_from_local_uri( s->media.items[i] );
if( m ) {
struct ap_attachement* att;
att = malloc(sizeof(*att));
memset(att,0,sizeof(*att));
att->type = apot_document;
att->url = strdup(s->media.items[i]);
att->mediaType = strdup(m->content_type);
att->name = strdup("");
array_append( &act->attachments, sizeof(att), &att );
}
}
str = aformat( "https://%s/owner/followers", g_server_name );
array_append( &act->cc, sizeof(str), &str );

@ -3,12 +3,15 @@
#include <stdlib.h>
#include <string.h>
extern struct json_enum ap_object_type_enum[];
#define OBJ_TYPE struct ap_attachement
struct json_object_field ap_attachement_layout[] = {
JSON_FIELD_STRING( mediaType, false ),
JSON_FIELD_STRING( name, false ),
JSON_FIELD_STRING( url, false ),
JSON_FIELD_STRING( value, false ),
JSON_FIELD_STRING( url, true ),
JSON_FIELD_ENUM( type, ap_object_type_enum, true ),
JSON_FIELD_END
};
#undef OBJ_TYPE

@ -44,6 +44,7 @@ struct json_enum ap_object_type_enum[] = {
{ "Note", apat_note },
{ "Person", apat_person },
{ "Service", apot_service },
{ "Document", apot_document },
{ NULL, 0 },
};

@ -0,0 +1,70 @@
#include "media.h"
#include "json/layout.h"
#include "format.h"
#include "model/server.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#define OBJ_TYPE struct media
static struct json_object_field media_layout[] = {
JSON_FIELD_STRING( content_type, true ),
JSON_FIELD_END,
};
#undef OBJ_TYPE
struct media* media_from_id( int id )
{
if( id == 0 ) { return NULL; }
char filename[512];
FILE* f = fopen( format( filename, sizeof(filename), "data/media/%d.json", id ), "r" );
if( !f ) { return NULL; }
struct media* m;
m = malloc(sizeof(*m));
memset(m,0,sizeof(*m));
m->id = id;
if( !json_read_object_layout_from_FILE( f, media_layout, m ) ) {
media_free(m);
return NULL;
}
return m;
}
struct media* media_from_local_uri( const char* uri )
{
if( 0 != strncmp( "https://", uri, 8 ) ) { return NULL; }
uri += 8;
int server_name_length = strlen(g_server_name);
if( 0 != strncmp( g_server_name, uri, server_name_length ) ) { return NULL; }
uri += server_name_length;
if( 0 != strncmp( "/media/", uri, 7 ) ) { return NULL; }
uri += 7;
// Note: zero is never a valid status id
int id = atoi(uri);
if( id == 0 ) { return NULL; }
return media_from_id(id);
}
void media_free( struct media* m )
{
if( !m ) { return; }
free(m->content_type);
free(m);
}
void media_save( struct media* m )
{
assert( m );
assert( m->id );
char filename[512];
json_write_object_layout_to_file( format(filename,sizeof(filename),"data/media/%d.json", m->id ), "\t", media_layout, m );
}

@ -0,0 +1,13 @@
#pragma once
struct media
{
int id;
char* content_type;
};
struct media* media_from_id( int id );
struct media* media_from_local_uri( const char* uri );
void media_free( struct media* m );
void media_save( struct media* m );

@ -0,0 +1,65 @@
#include "Attachement.h"
#include "json/layout.h"
#include "format.h"
#include "model/server.h"
#include "model/media.h"
bool int_to_string_callback( void* field_data, bool is_read, char** res );
bool preview_url_string_callback( void* field_data, bool is_read, char** res )
{
struct media* m = field_data;
if( !is_read ) {
*res = aformat( "https://%s/media/%d/preview", g_server_name, m->id );
return true;
}
return false;
}
bool url_string_callback( void* field_data, bool is_read, char** res )
{
struct media* m = field_data;
if( !is_read ) {
*res = aformat( "https://%s/media/%d/blob", g_server_name, m->id );
return true;
}
return false;
}
#define OBJ_TYPE struct media
static struct json_object_field api_Attachment_layout[] = {
{
.key = "id",
.offset = offsetof( OBJ_TYPE, id ),
.type = &json_field_callback_string,
.string_callback = int_to_string_callback,
},
JSON_FIELD_FIXED_STRING( type, "image", true ),
{
.key = "preview_url",
.offset = 0,
.required = true,
.type = &json_field_callback_string,
.string_callback = preview_url_string_callback,
},
{
.key = "url",
.offset = 0,
.required = true,
.type = &json_field_callback_string,
.string_callback = url_string_callback,
},
JSON_FIELD_END,
};
#undef OBJ_TYPE
void api_Attachement_write( struct media* m, FILE* f, int indent )
{
struct json_writer jw = {
.f = f,
.indentation = "\t",
.indent = indent,
};
json_write_pretty_object_layout( &jw, api_Attachment_layout, m );
}

@ -0,0 +1,7 @@
#pragma once
#include <stdio.h>
struct media;
void api_Attachement_write( struct media* m, FILE* f, int indent );

@ -72,6 +72,7 @@ void api_Notification_write( struct notification* note, FILE* f, int indent )
.indentation = "\t",
.indent = indent,
};
/*
switch( n->type ) {
case nt_unfollow:

Loading…
Cancel
Save