merge-requests/5/head
Terence Eden 2024-02-29 17:26:10 +00:00
rodzic b69c08e5bb
commit 70404d1b12
2 zmienionych plików z 57 dodań i 37 usunięć

Wyświetl plik

@ -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. 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. Go to Mastodon or other Fediverse site and search for your user: `@username@test.example.com`
1. Follow your user. 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. 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. 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 ## How this works
* The `.htaccess` file transforms requests from `example.com/whatever` to `example.com/index.php?path=whatever`. * 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. * 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. * Post files are saved as .json in the `/posts` directory.
* Followers' details are saved as .json in the `/data/followers` directory. * Details of accounts who follow you are saved as .json in the `/data/followers` directory.
* This has sloppy support for linking #hashtags, https:// URls, and @ mentions. * 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. * HTTP Message Signatures are verified.
## Requirements ## Requirements

Wyświetl plik

@ -41,8 +41,21 @@
// Internal data // Internal data
$server = $_SERVER["SERVER_NAME"]; // Do not change this! $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: // 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 // Get all headers and requests sent to this server
$headers = print_r( getallheaders(), true ); $headers = print_r( getallheaders(), true );
@ -59,10 +72,10 @@
// Get the type of request - used in the log filename // Get the type of request - used in the log filename
if ( isset( $body["type"] ) ) { if ( isset( $body["type"] ) ) {
// Sanitise before using it in a filename // Sanitise before using it in a filename
$type = " " . urlencode( $body["type"] ); $type = "." . urlencode( $body["type"] );
} else { } else {
// Sanitise the path requested // Sanitise the path requested
$type = " " . urlencode( $path ); $type = "." . urlencode( $path );
} }
// Create a timestamp for the filename // Create a timestamp for the filename
@ -74,9 +87,7 @@
$filename = "{$timestamp}{$type}.txt"; $filename = "{$timestamp}{$type}.txt";
// Save headers and request data to the timestamped file in the logs directory // Save headers and request data to the timestamped file in the logs directory
if( ! is_dir( "logs" ) ) { mkdir( "logs"); } file_put_contents( $directories["logs"] . "/{$filename}",
file_put_contents( "logs/{$filename}",
"Headers: \n$headers \n\n" . "Headers: \n$headers \n\n" .
"Body Data: \n$bodyData \n\n" . "Body Data: \n$bodyData \n\n" .
"POST Data: \n$postData \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. // 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. // An accept request is cryptographically signed and POST'd back to the remote server.
function inbox() { function inbox() {
global $body, $server, $username, $key_private; global $body, $server, $username, $key_private, $directories;
// Validate HTTP Message Signature // Validate HTTP Message Signature
// This logs whether the signature was validated or not // This logs whether the signature was validated or not
@ -232,6 +243,19 @@
$inbox_message = $body; $inbox_message = $body;
$inbox_type = $inbox_message["type"]; $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 // This inbox only responds to follow requests
if ( "Follow" != $inbox_type ) { die(); } if ( "Follow" != $inbox_type ) { die(); }
@ -270,9 +294,8 @@
curl_close($ch); curl_close($ch);
// Save the actor's data in `/data/followers/` // Save the actor's data in `/data/followers/`
if( ! is_dir( "data/followers" ) ) { mkdir( "data"); mkdir( "data/followers"); }
$follower_filename = urlencode( $follower_actor ); $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 { } else {
die(); die();
@ -316,9 +339,10 @@
// Check for errors // Check for errors
if( curl_errno( $ch ) ) { 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(); die();
} }
@ -507,7 +531,7 @@ HTML;
// It constructs a list of shared inboxes and unique inboxes // It constructs a list of shared inboxes and unique inboxes
// It sends the message to every server that is following this account. // It sends the message to every server that is following this account.
function send() { function send() {
global $password, $server, $username, $key_private; global $password, $server, $username, $key_private, $directories;
// Does the posted password match the stored password? // Does the posted password match the stored password?
if( $password != $_POST["password"] ) { die(); } if( $password != $_POST["password"] ) { die(); }
@ -599,8 +623,7 @@ HTML;
// Save the permalink // Save the permalink
$note_json = json_encode( $note ); $note_json = json_encode( $note );
// Check for posts/ directory and create it // Check for posts/ directory and create it
if( ! is_dir( "posts" ) ) { mkdir( "posts"); } file_put_contents( $directories["posts"] . "/{$guid}.json", print_r( $note_json, true ) );
file_put_contents( "posts/{$guid}.json", print_r( $note_json, true ) );
// Read existing followers // Read existing followers
$followers = glob( "data/followers/*.json" ); $followers = glob( "data/followers/*.json" );
@ -795,7 +818,7 @@ HTML;
<input name="user" id="user" type="text" size="32" placeholder="@user@example.com" /><br> <input name="user" id="user" type="text" size="32" placeholder="@user@example.com" /><br>
<label for="password">Password</label><br> <label for="password">Password</label><br>
<input name="password" id="password" type="password" size="32"><br> <input name="password" id="password" type="password" size="32"><br>
<input type="submit" value="Post Message"> <input type="submit" value="Send Follow Request">
</form> </form>
</body> </body>
</html> </html>
@ -808,7 +831,7 @@ HTML;
// Then it sends a follow request // Then it sends a follow request
// If the request is accepted, it saves the details in `data/following/` as a JSON file // If the request is accepted, it saves the details in `data/following/` as a JSON file
function follow_user() { function follow_user() {
global $password, $server, $username, $key_private; global $password, $server, $username, $key_private, $directories;
// Does the posted password match the stored password? // Does the posted password match the stored password?
if( $password != $_POST["password"] ) { echo "Wrong Password!"; die(); } if( $password != $_POST["password"] ) { echo "Wrong Password!"; die(); }
@ -869,15 +892,14 @@ HTML;
// Check for errors // Check for errors
if( curl_errno( $ch ) ) { 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);
// Save the user's details // 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 ); $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 // Render the JSON so the user can see the POST has worked
header( "Location: https://{$server}/data/following/" . urlencode( $following_filename ) . ".json" ); header( "Location: https://{$server}/data/following/" . urlencode( $following_filename ) . ".json" );
@ -888,7 +910,7 @@ HTML;
// This is optional // This is optional
// It is very confusing // It is very confusing
function verifyHTTPSignature() { function verifyHTTPSignature() {
global $input, $body, $server; global $input, $body, $server, $directories;
// Get the headers send with the request // Get the headers send with the request
$headers = getallheaders(); $headers = getallheaders();
@ -907,12 +929,10 @@ HTML;
// Write a log detailing the error // Write a log detailing the error
$timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED );
// Filename for the log // 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 // Save headers and request data to the timestamped file in the logs directory
if( !is_dir( "logs" ) ) { mkdir( "logs"); } file_put_contents( $directories["logs"] . "/{$filename}",
file_put_contents( "logs/{$filename}",
"Original Date:\n" . print_r( $dateHeader, true ) . "\n" . "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" "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 // Write a log detailing the error
$timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED );
// Filename for the log // 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 // Save headers and request data to the timestamped file in the logs directory
if( ! is_dir( "logs" ) ) { mkdir( "logs"); } file_put_contents( $directories["logs"] . "/{$filename}",
file_put_contents( "logs/{$filename}",
"Original Input:\n" . print_r( $input, true ) . "\n" . "Original Input:\n" . print_r( $input, true ) . "\n" .
"Original Digest:\n" . print_r( $digestString, true ) . "\n" . "Original Digest:\n" . print_r( $digestString, true ) . "\n" .
"Calculated Digest:\n" . print_r( $digestCalculated, true ) . "\n" "Calculated Digest:\n" . print_r( $digestCalculated, true ) . "\n"
@ -1028,12 +1046,10 @@ HTML;
// Write a log detailing the signature verification process // Write a log detailing the signature verification process
$timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED ); $timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED );
// Filename for the log // 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 // Save headers and request data to the timestamped file in the logs directory
if( ! is_dir( "logs" ) ) { mkdir( "logs"); } file_put_contents( $directories["logs"] . "/{$filename}",
file_put_contents( "logs/{$filename}",
"Original Body:\n" . print_r( $body, true ) . "\n\n" . "Original Body:\n" . print_r( $body, true ) . "\n\n" .
"Original Headers:\n" . print_r( $headers, true ) . "\n\n" . "Original Headers:\n" . print_r( $headers, true ) . "\n\n" .
"Signature Headers:\n" . print_r( $signatureHeaders, true ) . "\n\n" . "Signature Headers:\n" . print_r( $signatureHeaders, true ) . "\n\n" .