General tidy up

merge-requests/5/head
Terence Eden 2024-02-26 19:56:55 +00:00
rodzic cd145915be
commit 195be1afea
1 zmienionych plików z 51 dodań i 45 usunięć

Wyświetl plik

@ -18,7 +18,9 @@
*/
// Preamble: Set your details here
// This is where you set up your account's name and bio. You also need to provide a public/private keypair. The posting page is protected with a password that also needs to be set here.
// This is where you set up your account's name and bio.
// You also need to provide a public/private keypair.
// The posting page is protected with a password that also needs to be set here.
// Set up the Actor's information
// Edit these:
@ -48,17 +50,19 @@
$getData = print_r( $_GET, true );
$filesData = print_r( $_FILES, true );
$input = file_get_contents( "php://input" );
$body = json_decode( $input, true );
$body = json_decode( $input,true );
$bodyData = print_r( $body, true );
$requestData = print_r( $_REQUEST, true );
$serverData = print_r( $_SERVER, true );
!empty( $_GET["path"] ) ? $path = $_GET["path"] : $path = "/";
// Get the type of request - used in the log filename
if ( isset( $body["type"] ) ) {
// Sanitise type to only include letter
$type = " " . preg_replace( '/[^a-zA-Z]/', '', $body["type"] );
// Sanitise before using it in a filename
$type = " " . urlencode( $body["type"] );
} else {
$type = "";
// Sanitise the path requested
$type = " " . urlencode( $path );
}
// Create a timestamp for the filename
@ -85,7 +89,6 @@
// Routing:
// The .htaccess changes /whatever to /?path=whatever
// This runs the function of the path requested.
!empty( $_GET["path"] ) ? $path = $_GET["path"] : home();
switch ($path) {
case ".well-known/webfinger":
webfinger(); // Mandatory. Static.
@ -94,20 +97,22 @@
case "following":
following(); // Mandatory. Static
case "followers":
followers(); // Mandatory. Could be dynamic
followers(); // Mandatory. Can be dynamic
case "inbox":
inbox(); // Mandatory. Only accepts follow requests.
case "write":
write(); // User interface for writing posts
case "send": // API for posting content to the Fediverse
send();
case "outbox": // Optional. Dynamic.
outbox();
case "send":
send(); // API for posting content to the Fediverse
case "outbox":
outbox(); // Optional. Dynamic.
case "/":
home(); // Optional. Can be dynamic
default:
die();
}
// The [WebFinger Protocol](https://docs.joinmastodon.org/spec/webfinger/) is used to identify accounts.
// The WebFinger Protocol is used to identify accounts.
// It is requested with `example.com/.well-known/webfinger?resource=acct:username@example.com`
// This server only has one user, so it ignores the query string and always returns the same details.
function webfinger() {
@ -150,7 +155,7 @@
"url" => "https://{$server}/{$username}",
"manuallyApprovesFollowers" => true,
"discoverable" => true,
"published" => "2024-02-12T11:51:00Z",
"published" => "2024-02-29T12:34:00Z",
"icon" => [
"type" => "Image",
"mediaType" => "image/png",
@ -210,8 +215,7 @@
// The `/inbox` is the main server. It receives all requests.
// This server only responds to "Follow" requests.
// A remote server sends a follow request which is a JSON file saying who they are.
// This code does not cryptographically validate the headers of the received message.
// The name of the remote user's server is saved to a file so that future messages can be delivered to it.
// 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;
@ -228,8 +232,8 @@
if ( "Follow" != $inbox_type ) { die(); }
// Get the parameters
$follower_id = $inbox_message["id"];
$follower_actor = $inbox_message["actor"];
$follower_id = $inbox_message["id"]; // E.g. https://mastodon.social/(unique id)
$follower_actor = $inbox_message["actor"]; // E.g. https://mastodon.social/users/Edent
$follower_host = parse_url( $follower_actor, PHP_URL_HOST );
$follower_path = parse_url( $follower_actor, PHP_URL_PATH );
@ -242,7 +246,7 @@
// Request the JSON representation of the the user
$ch = curl_init( $follower_actor );
// Get the signed headers
// Generate signed headers for this request
$headers = generate_signed_headers( null, $follower_host, $follower_path, "GET" );
// Set cURL options
@ -302,7 +306,7 @@
$ch = curl_init( $follower_inbox );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode($message) );
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $message ) );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
curl_exec( $ch );
@ -330,7 +334,8 @@
// Headers:
// Every message that your server sends needs to be cryptographically signed with your Private Key.
// This is a complicated process. Please read https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/ for more information.
// This is a complicated process.
// Please read https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/ for more information.
function generate_signed_headers( $message, $host, $path, $method ) {
global $server, $username, $key_private;
@ -344,7 +349,6 @@
$date = date( "D, d M Y H:i:s \G\M\T" );
// There are subtly different signing requirements for POST and GET
// GET does not require a digest
if ( "POST" == $method ) {
// Encode the message object to JSON
$message_json = json_encode( $message );
@ -369,14 +373,14 @@
// Full signature header
$signature_header = 'keyId="' . $keyId . '",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="' . $signature_b64 . '"';
// Header for POST reply
// Header for POST request
$headers = array(
"Host: {$host}",
"Date: {$date}",
"Digest: SHA-256={$digest}",
"Signature: {$signature_header}",
"Host: {$host}",
"Date: {$date}",
"Digest: SHA-256={$digest}",
"Signature: {$signature_header}",
"Content-Type: application/activity+json",
"Accept: application/activity+json",
"Accept: application/activity+json",
);
} else if ( "GET" == $method ) {
// Sign the path, host, date - NO DIGEST
@ -396,7 +400,7 @@
// Full signature header
$signature_header = 'keyId="' . $keyId . '",algorithm="rsa-sha256",headers="(request-target) host date",signature="' . $signature_b64 . '"';
// Header for POST reply
// Header for GET request
$headers = array(
"Host: {$host}",
"Date: {$date}",
@ -435,8 +439,10 @@ echo <<< HTML
<p>This site is a basic <a href="https://www.w3.org/TR/activitypub/">ActivityPub</a> server designed to be <a href="https://shkspr.mobi/blog/2024/02/activitypub-server-in-a-single-file/">a lightweight educational tool</a>.</p>
<ul>
HTML;
// Get all posts
// Get all posts, most recent first
$posts = array_reverse( glob("posts/*.json") );
// Loop through the posts
foreach ($posts as $post) {
$postJSON = file_get_contents($post);
$postData = json_decode($postJSON, true);
@ -449,6 +455,7 @@ HTML;
} else {
$postImg = "";
}
// Display the post
echo "<li><a href='/{$post}'><time datetime='{$postTime}'>$postTime</time></a><br>{$postHTML}<br>{$postImg}</li>";
}
echo <<< HTML
@ -523,10 +530,8 @@ HTML;
// Move media to the correct location
// Create a directory if it doesn't exist
if( ! is_dir( $image_path ) ) {
mkdir( $image_path );
}
move_uploaded_file($image, $image_full_path );
if( ! is_dir( $image_path ) ) { mkdir( $image_path ); }
move_uploaded_file( $image, $image_full_path );
// Get the alt text
if ( isset( $_POST["alt"] ) ) {
@ -601,7 +606,8 @@ HTML;
foreach ( $followers as $follower ) {
$follower_info = json_decode( file_get_contents( $follower ), true );
// Shared inboxes
// Some servers have "Shared inboxes"
// If you have lots of followers on a single server, you only need to send the message once
if( isset( $follower_info["endpoints"]["sharedInbox"] ) ) {
$sharedInbox = $follower_info["endpoints"]["sharedInbox"];
if ( !in_array( $sharedInbox, $inboxes ) ) {
@ -617,6 +623,7 @@ HTML;
}
// Prepare to use the multiple cURL handle
// This makes it more efficient to send many simultaneous messages
$mh = curl_multi_init();
// Loop through all the inboxes of the followers
@ -716,7 +723,6 @@ HTML;
// Add HTML links to usernames
$username_link = "<a href=\"https://{$domain}/@{$user}\">$username</a>";
$content = str_replace( $username, $username_link, $content );
}
// Construct HTML breaks from carriage returns and line breaks
@ -738,7 +744,7 @@ HTML;
global $server, $username;
// Get all posts
$posts = array_reverse( glob("posts/" . "*.json") );
$posts = array_reverse( glob("posts/*.json") );
// Number of posts
$totalItems = count( $posts );
// Create an ordered list
@ -793,7 +799,7 @@ HTML;
$filename = "{$timestamp} Time Failure.txt";
// Save headers and request data to the timestamped file in the logs directory
if( ! is_dir( "logs" ) ) { mkdir( "logs"); }
if( !is_dir( "logs" ) ) { mkdir( "logs"); }
file_put_contents( "logs/{$filename}",
"Original Date:\n" . print_r( $dateHeader, true ) . "\n" .
@ -846,7 +852,7 @@ HTML;
// Extract key information from the Signature header
$signatureParts = [];
// Converts 'a=b,c=d e f' into ["a"=>"b", "c"=>"d e f"]
// word="text"
// word="text"
preg_match_all('/(\w+)="([^"]+)"/', $signatureHeader, $matches);
foreach ($matches[1] as $index => $key) {
$signatureParts[$key] = $matches[2][$index];
@ -872,7 +878,7 @@ HTML;
$signatureString = trim( $signatureString );
// Get the Public Key
// The link to the key may be sent with the body, but is always sent in the Signature header.
// The link to the key might be sent with the body, but is always sent in the Signature header.
$publicKeyURL = $signatureParts["keyId"];
// This is usually in the form `https://example.com/user/username#main-key`
@ -917,13 +923,13 @@ HTML;
if( ! is_dir( "logs" ) ) { mkdir( "logs"); }
file_put_contents( "logs/{$filename}",
"Original Body:\n" . print_r( $body, true ) . "\n\n" .
"Original Headers:\n" . print_r( $headers, true ) . "\n\n" .
"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" .
"Calculated signatureString:\n" . print_r( $signatureString, true ) . "\n\n" .
"Calculated algorithm:\n" . print_r( $algorithm, true ) . "\n\n" .
"publicKeyURL:\n" . print_r( $publicKeyURL, true ) . "\n\n" .
"publicKey:\n" . print_r( $publicKey, true ) . "\n"
"Calculated signatureString:\n" . print_r( $signatureString, true ) . "\n\n" .
"Calculated algorithm:\n" . print_r( $algorithm, true ) . "\n\n" .
"publicKeyURL:\n" . print_r( $publicKeyURL, true ) . "\n\n" .
"publicKey:\n" . print_r( $publicKey, true ) . "\n"
);
return $verified;