From 70404d1b12731e202232d5d904c88d29ce07d8fb Mon Sep 17 00:00:00 2001 From: Terence Eden Date: Thu, 29 Feb 2024 17:26:10 +0000 Subject: [PATCH] Better logging --- README.md | 12 +++++--- index.php | 82 +++++++++++++++++++++++++++++++++---------------------- 2 files changed, 57 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 7ac7363..8154f91 100644 --- a/README.md +++ b/README.md @@ -14,18 +14,22 @@ There are no tests, no checks, no security features, no formal verifications, no 1. Visit `https://test.example.com/.well-known/webfinger` and check that it shows a JSON file with your user's details. 1. Go to Mastodon or other Fediverse site and search for your user: `@username@test.example.com` 1. Follow your user. -1. Check your `/logs/` directory to see if the follow request was received correctly. +1. Check your `/data/logs/` directory to see if the follow request was received correctly. 1. To post a message, visit `https://test.example.com/write` type in your message and password. Press the "Post Message" button. 1. Check social media to see if the message appears. +1. To follow other users, visit `https://test.example.com/follow` type in the name of the user you want to follow and your password. Press the "Send Follow Request" button. +1. Check social media to see if the follow request came through. ## How this works * The `.htaccess` file transforms requests from `example.com/whatever` to `example.com/index.php?path=whatever`. * The `index.php` file performs a specific action depending on the path requested. -* Log files are saved as .txt in the `/logs` directory. +* Log files are saved as .txt in the `/data/logs` directory. * Post files are saved as .json in the `/posts` directory. -* Followers' details are saved as .json in the `/data/followers` directory. -* This has sloppy support for linking #hashtags, https:// URls, and @ mentions. +* Details of accounts who follow you are saved as .json in the `/data/followers` directory. +* Details of accounts who you follow are saved as .json in the `/data/following` directory. +* Messages sent to your inbox are saved as .json in the `/data/inbox` directory. +* This has sloppy support for sending posts with linked #hashtags, https:// URls, and @ mentions. * HTTP Message Signatures are verified. ## Requirements diff --git a/index.php b/index.php index 9cce61d..1a0cede 100644 --- a/index.php +++ b/index.php @@ -41,8 +41,21 @@ // Internal data $server = $_SERVER["SERVER_NAME"]; // Do not change this! + // Set up where logs and messages go + $data = "data"; + $directories = array( + "inbox" => "{$data}/inbox", + "followers" => "{$data}/followers", + "following" => "{$data}/following", + "logs" => "{$data}/logs", + "posts" => "posts", + ); + foreach ( $directories as $directory ) { + if( !is_dir( $directory ) ) { mkdir( $data ); mkdir( $directory ); } + } + // Logging: - // ActivityPub is a "chatty" protocol. This takes all the requests your server receives and saves them in `/logs/` as a datestamped text file. + // ActivityPub is a "chatty" protocol. This takes all the requests your server receives and saves them as a datestamped text file. // Get all headers and requests sent to this server $headers = print_r( getallheaders(), true ); @@ -59,10 +72,10 @@ // Get the type of request - used in the log filename if ( isset( $body["type"] ) ) { // Sanitise before using it in a filename - $type = " " . urlencode( $body["type"] ); + $type = "." . urlencode( $body["type"] ); } else { // Sanitise the path requested - $type = " " . urlencode( $path ); + $type = "." . urlencode( $path ); } // Create a timestamp for the filename @@ -74,9 +87,7 @@ $filename = "{$timestamp}{$type}.txt"; // Save headers and request data to the timestamped file in the logs directory - if( ! is_dir( "logs" ) ) { mkdir( "logs"); } - - file_put_contents( "logs/{$filename}", + file_put_contents( $directories["logs"] . "/{$filename}", "Headers: \n$headers \n\n" . "Body Data: \n$bodyData \n\n" . "POST Data: \n$postData \n\n" . @@ -222,7 +233,7 @@ // The details of the remote user's server is saved to a file so that future messages can be delivered to the follower. // An accept request is cryptographically signed and POST'd back to the remote server. function inbox() { - global $body, $server, $username, $key_private; + global $body, $server, $username, $key_private, $directories; // Validate HTTP Message Signature // This logs whether the signature was validated or not @@ -232,6 +243,19 @@ $inbox_message = $body; $inbox_type = $inbox_message["type"]; + // Save any Follow, Create, Announce, Like messages + // This ignores Delete, Undo, and anything else + if ( match( $inbox_type ) { + "Follow", "Create", "Announce", "Like" => true, + default => false, + } ) { + // Save the message in `/data/inbox/` + $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); + $inbox_filename = $timestamp . "." . urlencode( $inbox_type ) . ".json"; + file_put_contents( $directories["inbox"] . "/{$inbox_filename}", json_encode( $inbox_message ) ); + } + + // This inbox only responds to follow requests if ( "Follow" != $inbox_type ) { die(); } @@ -270,9 +294,8 @@ curl_close($ch); // Save the actor's data in `/data/followers/` - if( ! is_dir( "data/followers" ) ) { mkdir( "data"); mkdir( "data/followers"); } $follower_filename = urlencode( $follower_actor ); - file_put_contents( "data/followers/{$follower_filename}.json", $inbox_actor_json ); + file_put_contents( $directories["followers"] . "/{$follower_filename}.json", $inbox_actor_json ); } else { die(); @@ -316,9 +339,10 @@ // Check for errors if( curl_errno( $ch ) ) { - file_put_contents( "error.txt", curl_error( $ch ) ); + $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); + file_put_contents( $directories["logs"] . "/{$timestamp}.Error.txt", curl_error( $ch ) ); } - curl_close($ch); + curl_close( $ch ); die(); } @@ -507,7 +531,7 @@ HTML; // It constructs a list of shared inboxes and unique inboxes // It sends the message to every server that is following this account. function send() { - global $password, $server, $username, $key_private; + global $password, $server, $username, $key_private, $directories; // Does the posted password match the stored password? if( $password != $_POST["password"] ) { die(); } @@ -599,8 +623,7 @@ HTML; // Save the permalink $note_json = json_encode( $note ); // Check for posts/ directory and create it - if( ! is_dir( "posts" ) ) { mkdir( "posts"); } - file_put_contents( "posts/{$guid}.json", print_r( $note_json, true ) ); + file_put_contents( $directories["posts"] . "/{$guid}.json", print_r( $note_json, true ) ); // Read existing followers $followers = glob( "data/followers/*.json" ); @@ -795,7 +818,7 @@ HTML;


- + @@ -808,7 +831,7 @@ HTML; // Then it sends a follow request // If the request is accepted, it saves the details in `data/following/` as a JSON file function follow_user() { - global $password, $server, $username, $key_private; + global $password, $server, $username, $key_private, $directories; // Does the posted password match the stored password? if( $password != $_POST["password"] ) { echo "Wrong Password!"; die(); } @@ -869,15 +892,14 @@ HTML; // Check for errors if( curl_errno( $ch ) ) { - file_put_contents( "error.txt", curl_error( $ch ) ); + $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); + file_put_contents( $directories["logs"] . "/{$timestamp}.Error.txt", curl_error( $ch ) ); } curl_close($ch); // Save the user's details - // Save headers and request data to the timestamped file in the logs directory - if( ! is_dir( "data/following" ) ) { mkdir( "data"); mkdir( "data/following"); } $following_filename = urlencode( $profileURl ); - file_put_contents( "data/following/{$following_filename}.json", $profileJSON ); + file_put_contents( $directories["following"] . "/{$following_filename}.json", $profileJSON ); // Render the JSON so the user can see the POST has worked header( "Location: https://{$server}/data/following/" . urlencode( $following_filename ) . ".json" ); @@ -888,7 +910,7 @@ HTML; // This is optional // It is very confusing function verifyHTTPSignature() { - global $input, $body, $server; + global $input, $body, $server, $directories; // Get the headers send with the request $headers = getallheaders(); @@ -907,12 +929,10 @@ HTML; // Write a log detailing the error $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); // Filename for the log - $filename = "{$timestamp} Time Failure.txt"; + $filename = "{$timestamp}.Signature.Time_Failure.txt"; // Save headers and request data to the timestamped file in the logs directory - if( !is_dir( "logs" ) ) { mkdir( "logs"); } - - file_put_contents( "logs/{$filename}", + file_put_contents( $directories["logs"] . "/{$filename}", "Original Date:\n" . print_r( $dateHeader, true ) . "\n" . "Local Date:\n" . print_r( $currentDatetime->format('D, d M Y H:i:s T'), true ) . "\n" ); @@ -944,12 +964,10 @@ HTML; // Write a log detailing the error $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); // Filename for the log - $filename = "{$timestamp} Digest Failure.txt"; + $filename = "{$timestamp}.Signature.Digest_Failure.txt"; // Save headers and request data to the timestamped file in the logs directory - if( ! is_dir( "logs" ) ) { mkdir( "logs"); } - - file_put_contents( "logs/{$filename}", + file_put_contents( $directories["logs"] . "/{$filename}", "Original Input:\n" . print_r( $input, true ) . "\n" . "Original Digest:\n" . print_r( $digestString, true ) . "\n" . "Calculated Digest:\n" . print_r( $digestCalculated, true ) . "\n" @@ -1028,12 +1046,10 @@ HTML; // Write a log detailing the signature verification process $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); // Filename for the log - $filename = "{$timestamp} Signature ". json_encode( $verified ) . ".txt"; + $filename = "{$timestamp}.Signature.". json_encode( $verified ) . ".txt"; // Save headers and request data to the timestamped file in the logs directory - if( ! is_dir( "logs" ) ) { mkdir( "logs"); } - - file_put_contents( "logs/{$filename}", + file_put_contents( $directories["logs"] . "/{$filename}", "Original Body:\n" . print_r( $body, true ) . "\n\n" . "Original Headers:\n" . print_r( $headers, true ) . "\n\n" . "Signature Headers:\n" . print_r( $signatureHeaders, true ) . "\n\n" .