General tidy up
rodzic
cd145915be
commit
195be1afea
96
index.php
96
index.php
|
@ -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;
|
||||
|
|
Ładowanie…
Reference in New Issue