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. 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

Wyświetl plik

@ -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;
<input name="user" id="user" type="text" size="32" placeholder="@user@example.com" /><br>
<label for="password">Password</label><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>
</body>
</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" .