#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/outbox_envelope.h" #include "model/ap/activity/rsa_signature_2017.h" // Stdlib #include #include #include 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; // Get next outbox item struct outbox_envelope* env = outbox_envelope_load_next(); if( !env ) { printf( "? No envelope\n" ); goto failed; } 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 if( !crypto_keys_load_private( keys, "data/owner/private.pem" ) ) { printf( "Failed to load private key\n" ); return false; } // Load target account act = ap_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_activity_write_to_FILE( act, f2 ); fclose(f2); } // Force null termination postdata = realloc( postdata, size + 1 ); postdata[size] = '\0'; printf( "post: %s\n", postdata ); struct http_signature hs; if( !http_signature_make( to_account->inbox, keys, &hs ) ) { 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\",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, to_account->inbox, HTTP_REQ_HEADER, user_agent, HTTP_REQ_HEADER, date_header, HTTP_REQ_HEADER, sign_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" ); http_client_do(request); printf( "\n\n" ); http_signature_free( &hs ); if( status_code == 200 ) { printf( "Submitted successfully\n" ); goto discard; } goto failed; cleanup: ap_activity_free(act); account_free(to_account); outbox_envelope_free(env); 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 ); } } else { printf( "? No processing done\n" ); } exit(0); }