|
|
|
#include "outbox.h"
|
|
|
|
|
|
|
|
// Submodules
|
|
|
|
#include "ffdb/fs_list.h"
|
|
|
|
#include "collections/array.h"
|
|
|
|
#include "http/client/client.h"
|
|
|
|
#include "ap/object.h"
|
|
|
|
#include "ap/object/rsa_signature_2017.h"
|
|
|
|
|
|
|
|
// Model
|
|
|
|
#include "model/server.h"
|
|
|
|
#include "model/crypto/keys.h"
|
|
|
|
#include "model/crypto/http_sign.h"
|
|
|
|
#include "model/account.h"
|
|
|
|
#include "model/activity.h"
|
|
|
|
#include "model/outbox_envelope.h"
|
|
|
|
|
|
|
|
// Stdlib
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
static bool process_envelope( struct outbox_envelope* env )
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
char* postdata = NULL;
|
|
|
|
struct crypto_keys* keys = NULL;
|
|
|
|
FILE* f = NULL;
|
|
|
|
struct ap_object* act = NULL;
|
|
|
|
|
|
|
|
if( env->sent ) { return false; }
|
|
|
|
if( env->retry_after > time(NULL) ) { return false; }
|
|
|
|
if( env->account_id == 0 ) { goto discard; }
|
|
|
|
|
|
|
|
printf( "Processing outbox/%d.json\n", env->id );
|
|
|
|
printf( "account_id=%d\n", env->account_id );
|
|
|
|
printf( "activity_id=%d\n", env->activity_id );
|
|
|
|
|
|
|
|
// Get outbox URL
|
|
|
|
struct account* to_account = account_from_id( env->account_id );
|
|
|
|
|
|
|
|
// Load crypto keys
|
|
|
|
keys = crypto_keys_new();
|
|
|
|
if( !crypto_keys_load_private( keys, "data/owner/private.pem" ) ) {
|
|
|
|
printf( "Failed to load private key\n" );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load target account
|
|
|
|
act = activity_from_local_id( env->activity_id );
|
|
|
|
if( !act ) {
|
|
|
|
printf( "! No activity\n" );
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create signature
|
|
|
|
ap_activity_create_rsa_signature_2017( act, keys );
|
|
|
|
|
|
|
|
// Create post data
|
|
|
|
size_t size;
|
|
|
|
{
|
|
|
|
FILE* f2 = open_memstream( &postdata, &size );
|
|
|
|
ap_object_write_to_FILE( act, f2 );
|
|
|
|
fclose(f2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Force null termination
|
|
|
|
postdata = realloc( postdata, size + 1 );
|
|
|
|
postdata[size] = '\0';
|
|
|
|
printf( "post: %s\n", postdata );
|
|
|
|
|
|
|
|
const char* inbox = to_account->inbox;
|
|
|
|
if( env->shared_inbox ) {
|
|
|
|
inbox = env->shared_inbox;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct http_signature hs;
|
|
|
|
if( !http_signature_make( inbox, keys, &hs, postdata ) ) {
|
|
|
|
printf( "! Failed to make HTTP signature\n" );
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
char date_header[512];
|
|
|
|
snprintf( date_header, sizeof(date_header), "Date: %s", hs.date );
|
|
|
|
|
|
|
|
char sign_header[512];
|
|
|
|
snprintf( sign_header, sizeof(sign_header), "Signature: keyId=\"https://%s/owner/actor#mainKey\",headers=\"(request-target) host date content-length digest\",signature=\"%s\"",
|
|
|
|
g_server->domain,
|
|
|
|
hs.signature
|
|
|
|
);
|
|
|
|
|
|
|
|
char user_agent[512];
|
|
|
|
snprintf( user_agent, sizeof(user_agent), "User-Agent: curl (Apogee/0.1; +https://%s/)", g_server->domain );
|
|
|
|
|
|
|
|
char digest_header[512];
|
|
|
|
snprintf( digest_header, sizeof(digest_header), "Digest: %s", hs.digest );
|
|
|
|
|
|
|
|
char content_length_header[512];
|
|
|
|
snprintf( content_length_header, sizeof(content_length_header), "Content-Length: %d", hs.content_length );
|
|
|
|
|
|
|
|
printf( "Performing post to %s\n", inbox );
|
|
|
|
long status_code = -1;
|
|
|
|
const void* request[] = {
|
|
|
|
HTTP_REQ_URL, inbox,
|
|
|
|
HTTP_REQ_HEADER, user_agent,
|
|
|
|
HTTP_REQ_HEADER, date_header,
|
|
|
|
HTTP_REQ_HEADER, sign_header,
|
|
|
|
HTTP_REQ_HEADER, content_length_header,
|
|
|
|
HTTP_REQ_HEADER, digest_header,
|
|
|
|
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" );
|
|
|
|
fflush(stdout);
|
|
|
|
http_client_do(request);
|
|
|
|
printf( "\n\n" );
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
http_signature_free( &hs );
|
|
|
|
|
|
|
|
if( status_code == 200 || status_code == 202 ) {
|
|
|
|
printf( "Submitted successfully\n" );
|
|
|
|
goto discard;
|
|
|
|
} else {
|
|
|
|
printf( "\nServer returned status code %d\n", status_code );
|
|
|
|
if( env->retry_after ) {
|
|
|
|
env->retries += 1;
|
|
|
|
}
|
|
|
|
if( env->retries > 10 ) {
|
|
|
|
// Force discard after 10 delivery attempts
|
|
|
|
// TODO: change this to a configuration option
|
|
|
|
goto discard;
|
|
|
|
}
|
|
|
|
env->retry_after = time(NULL) + 60 * ( env->retries + 1 ) * ( env->retries + 1 );
|
|
|
|
outbox_envelope_save(env);
|
|
|
|
}
|
|
|
|
|
|
|
|
goto failed;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
ap_object_free(act);
|
|
|
|
account_free(to_account);
|
|
|
|
free(postdata);
|
|
|
|
|
|
|
|
crypto_keys_free(keys);
|
|
|
|
if( f ) { fclose(f); }
|
|
|
|
|
|
|
|
return result;
|
|
|
|
failed:
|
|
|
|
result = false;
|
|
|
|
goto cleanup;
|
|
|
|
discard:
|
|
|
|
env->sent = true;
|
|
|
|
outbox_envelope_save(env);
|
|
|
|
result = true;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool process_pending()
|
|
|
|
{
|
|
|
|
int head = fs_list_get("data/outbox/HEAD");
|
|
|
|
int tail = fs_list_get("data/outbox/TAIL");
|
|
|
|
bool result = false;
|
|
|
|
for( int i = tail + 1; i <= head; ++i ) {
|
|
|
|
struct outbox_envelope* env = outbox_envelope_from_id( i );
|
|
|
|
if( !env ) {
|
|
|
|
// Envelope file doesn't exist, advance tail
|
|
|
|
fs_list_set( "data/outbox/TAIL", i );
|
|
|
|
tail += 1;
|
|
|
|
} else if( env ) {
|
|
|
|
if( env->sent && i == tail+1 ) {
|
|
|
|
// Envelope already sent, advance tail
|
|
|
|
fs_list_set( "data/outbox/TAIL", i );
|
|
|
|
tail += 1;
|
|
|
|
} else if( process_envelope(env) ) {
|
|
|
|
printf( "Done with outbox/%d.json\n", i );
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
outbox_envelope_free(env);
|
|
|
|
}
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
bool cleanup_box( const char* box );
|
|
|
|
|
|
|
|
extern bool terminate;
|
|
|
|
void process_outbox()
|
|
|
|
{
|
|
|
|
while( !terminate ) {
|
|
|
|
bool activity = false;
|
|
|
|
activity |= process_pending();
|
|
|
|
activity |= cleanup_box("data/outbox");
|
|
|
|
|
|
|
|
if( !activity ) {
|
|
|
|
fflush(stdout);
|
|
|
|
sleep(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|