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.

184 lines
4.0 KiB
C

#include "outbox.h"
// Submodules
#include "ffdb/fs_list.h"
#include "collections/array.h"
#include "http_client/client.h"
// Model
#include "model/server.h"
#include "model/crypto/keys.h"
#include "model/crypto/http_sign.h"
#include "model/account.h"
#include "model/ap/activity.h"
#include "model/ap/activity/rsa_signature_2017.h"
// Stdlib
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
static bool process_one( int id )
{
bool result = false;
char* postdata = NULL;
struct crypto_keys* keys = crypto_keys_new();
FILE* f = NULL;
struct ap_activity* act = NULL;
ARRAY_OF(char*) inboxes;
memset( &inboxes, 0, sizeof(inboxes) );
if( !crypto_keys_load_private( keys, "data/owner/private.pem" ) ) {
printf( "Failed to load private key\n" );
return false;
}
char buffer[512];
snprintf( buffer, 512, "data/outbox/%d.json", id );
f = fopen( buffer, "r" );
if( !f ) {
printf( "Unable to open file %s\n", buffer );
goto discard;
}
// TODO: REWORK to no longer have multiple target inboxes
char* toline = NULL;
size_t n;
if( -1 == getline( &toline, &n, f ) ) {
printf( "no to line" );
free(toline);
goto failed;
}
if( strlen(toline) < 5 ) {
printf( "too short.\n" );
goto failed;
}
act = ap_activity_from_FILE(f);
if( !act ) {
printf( "No activity\n" );
goto failed;
}
f = NULL;
char* remainder = NULL;
char* iter = strtok_r( &toline[4],",",&remainder);
do
{
int id;
if( sscanf( iter, "%d", &id ) ) {
int compare( void* a, void* b ) { return strcmp( (char*)a, (char*)b ); }
struct account* to_account = account_from_id( id );
if( to_account ) {
if( to_account->inbox ) {
char* item_to_add = strdup(to_account->inbox);
array_append_unique( &inboxes, sizeof(item_to_add), &item_to_add, compare );
}
account_free(to_account);
}
}
iter = strtok_r( NULL,",",&remainder);
} while( iter );
free(toline);
if( inboxes.count > 1 ) {
goto failed;
}
ap_activity_create_rsa_signature_2017( act, keys );
size_t size;
{
FILE* f2 = open_memstream( &postdata, &size );
ap_activity_write_to_FILE( act, f2 );
fclose(f2);
}
// Force null termination
postdata = realloc( postdata, size + 1 );
postdata[size] = '\0';
printf( "post: %s\n", postdata );
int i = 0;
const char* inbox = inboxes.items[i];
printf( "item[%d] = %s\n", i, inbox );
struct http_signature hs;
if( !http_signature_make( inboxes.items[i], keys, &hs ) ) {
goto failed;
}
char date_line[512];
snprintf( date_line, sizeof(date_line), "Date: %s", hs.date );
char sign_line[512];
snprintf( sign_line, sizeof(sign_line), "Signature: keyId=\"https://%s/owner/actor\",headers=\"(request-target) host date\",signature=\"%s\"",
g_server_name,
hs.signature
);
char user_agent[512];
snprintf( user_agent, sizeof(user_agent), "User-Agent: curl (Apogee/0.1; +https://%s/)", g_server_name );
long status_code = -1;
const void* request[] = {
HTTP_REQ_URL, inbox,
HTTP_REQ_HEADER, user_agent,
HTTP_REQ_HEADER, date_line,
HTTP_REQ_HEADER, sign_line,
HTTP_REQ_HEADER, "Content-Type: application/activity+json",
HTTP_REQ_POSTDATA, postdata,
HTTP_REQ_RESULT_STATUS, &status_code,
NULL
};
// POST to inbox
printf( "\n\nRequest result:\n" );
http_client_do(request);
printf( "\n\n" );
http_signature_free( &hs );
if( status_code == 200 ) {
printf( "Submitted successfully\n" );
goto discard;
}
goto failed;
cleanup:
void release( void* item ) { free( *(char**)item ); }
array_free( &inboxes, sizeof(char*), release );
ap_activity_free(act);
free(postdata);
crypto_keys_free(keys);
if( f ) { fclose(f); }
return result;
failed:
result = false;
goto cleanup;
discard:
result = true;
goto cleanup;
}
void process_outbox()
{
int head = fs_list_get("data/outbox/HEAD");
int tail = fs_list_get("data/outbox/TAIL");
if( tail < head ) {
printf( "Processing outbox/%d.json\n", tail+1 );
if( process_one(tail+1) ) {
printf( "Done with outbox/%d.json\n", tail );
fs_list_set( "data/outbox/TAIL", tail + 1 );
}
}
exit(0);
}