diff --git a/README.md b/README.md index c6b1275..90644f2 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,38 @@ I actively do *not* want you to use this code in production. It is not suitable Please take note of [CRAPL v0](https://matt.might.net/articles/crapl/): -> Any appearance of design in the Program is purely coincidental and should not in any way be mistaken for evidence of thoughtful software construction. \ No newline at end of file +> Any appearance of design in the Program is purely coincidental and should not in any way be mistaken for evidence of thoughtful software construction. + +## Features + +### Working + +* ✅ User can be followed +* ✅ User can post messages to followers +* ✅ User can post a message to a specific external account +* ✅ User can post an image & alt text +* ✅ User can follow, unfollow, block, and unblock external accounts +* ✅ Server validates the HTTP Message Signatures of all incoming messages +* ✅ Incoming messages are saved +* ✅ User can read incoming messages (Create, Update, Announce, Like) +* ✅ User can read the results of polls +* ✅ User can see attached images, videos, audio +* ✅ User can hide "Content Warning" text +* ✅ User can receive Updates to messages +* ✅ User can Announce / boost / repost an external post +* ✅ User can see if a post is a reply to them + +### Not Working + +* ❌ See posts from Lemmy communities +* ❌ See contents of Announced posts +* ❌ Delete own post +* ❌ Update own post +* ❌ Receive Undo messages (Unfollow, delete post, remove Like, remove Announce) +* ❌ Create Polls +* ❌ Attach multiple images +* ❌ Set focus point for images +* ❌ Set sensitivity for images / blur +* ❌ Set "Content Warning" +* ❌ See external users' avatars, names, or other details +* ❌ ...? diff --git a/index.php b/index.php index 991599a..e46ce3c 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ * The Actor can send messages to followers. * The message can have linkable URls, hashtags, and mentions. * An image and alt text can be attached to the message. - * The Actor can follow remote accounts. + * The Actor can follow, unfollow, block, and unblock remote accounts. * The Server saves logs about requests it receives and sends. * This code is NOT suitable for production use. * SPDX-License-Identifier: AGPL-3.0-or-later @@ -43,9 +43,9 @@ $server = $_SERVER["SERVER_NAME"]; // Do not change this! // Some requests require a User-Agent string. - define("USERAGENT", "activitypub-single-php-file/0.0"); + define( "USERAGENT", "activitypub-single-php-file/0.0" ); - // Set up where logs and messages go. + // Set up where to save logs, posts, and images. // You can change these directories to something more suitable if you like. $data = "data"; $directories = array( @@ -75,7 +75,11 @@ switch ( $path ) { case ".well-known/webfinger": webfinger(); // Mandatory. Static. - case rawurldecode( $username ): + case ".well-known/nodeinfo": + wk_nodeinfo(); // Optional. Static. + case "nodeinfo/2.1": + nodeinfo(); // Optional. Static. + case rawurldecode( $username ): case "@" . rawurldecode( $username ): // Some software assumes usernames start with an `@` username(); // Mandatory. Static case "following": @@ -93,15 +97,11 @@ case "users": users(); // User interface for (un)following & (un)blocking an external user. case "action/users": - action_users(); // API for following a user. + action_users();// API for following a user. case "read": view( "read" );// User interface for reading posts. - case ".well-known/nodeinfo": - wk_nodeinfo(); // Optional. Static. - case "nodeinfo/2.1": - nodeinfo(); // Optional. Static. case "/": - view( "home" );// Optional. Can be dynamic + view( "home" );// User interface for seeing what the user has posted. default: die(); } @@ -178,7 +178,7 @@ global $server, $directories; // Get all the files - $following_files = glob( $directories["following"] . "/*.json"); + $following_files = glob( $directories["following"] . "/*.json" ); // Number of users $totalItems = count( $following_files ); @@ -206,14 +206,14 @@ // You can set this to any number you like. // Get all the files - $follower_files = glob( $directories["followers"] . "/*.json"); + $follower_files = glob( $directories["followers"] . "/*.json" ); // Number of users $totalItems = count( $follower_files ); // Create a list of everyone being followed $items = array(); foreach ( $follower_files as $follower_file ) { - $following = json_decode( file_get_contents( $follower_file ),true ); + $following = json_decode( file_get_contents( $follower_file ), true ); $items[] = $following["id"]; } @@ -268,28 +268,20 @@ } if ( !$reply && !$from_following ) { + // Don't bother processing it at all. die(); } - - // Save any Follow, Create, Update, Announce, Like messages - // This ignores Delete, Undo, and anything else - if ( match( $inbox_type ) { - "Follow", "Create", "Update", "Announce", "Like" => true, - default => false, - } ) { + + // Validate HTTP Message Signature + if ( !verifyHTTPSignature() ) { die(); } - // Validate HTTP Message Signature - // This logs whether the signature was validated or not - if ( !verifyHTTPSignature() ) { die(); } + // If the message is valid, save the message in `/data/inbox/` + $uuid = uuid(); + $inbox_filename = $uuid . "." . urlencode( $inbox_type ) . ".json"; + file_put_contents( $directories["inbox"] . "/{$inbox_filename}", json_encode( $inbox_message ) ); - // If the message is valid, save the message in `/data/inbox/` - $uuid = uuid(); - $inbox_filename = $uuid . "." . urlencode( $inbox_type ) . ".json"; - file_put_contents( $directories["inbox"] . "/{$inbox_filename}", json_encode( $inbox_message ) ); - } - - // This inbox only responds to follow requests. - // A remote server sends the inbox follow request which is a JSON file saying who they are. + // This inbox only sends responses to follow requests. + // A remote server sends the inbox a follow request which is a JSON file saying who they are. // 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. if ( "Follow" != $inbox_type ) { die(); } @@ -328,7 +320,7 @@ ]; // The Accept is POSTed to the inbox on the server of the user who requested the follow - sentMessageToSingle( $follower_inbox, $message ); + sendMessageToSingle( $follower_inbox, $message ); die(); } @@ -426,7 +418,6 @@ return $headers; } - // User Interface for Homepage. // This creates a basic HTML page. This content appears when someone visits the root of your site. function view( $style ) { @@ -721,8 +712,12 @@ HTML; $object = $message["object"]; $objectHTML = "{$object}"; $messageHTML = "{$timeHTML} {$actorHTML} boosted {$objectHTML}"; + } else { + // Not a message we're interested in displaying. Probably a delete or undo. + continue; } + // Display the message echo "
  • {$messageHTML}
    {$interactHTML}
  • "; } echo <<< HTML @@ -963,7 +958,7 @@ HTML; // Is this message going to one user? (Usually a Like) if ( isset( $inbox_single ) ) { - $messageSent = sentMessageToSingle( $inbox_single, $message ); + $messageSent = sendMessageToSingle( $inbox_single, $message ); } else { // Send to all the user's followers $messageSent = sendMessageToFollowers( $message ); } @@ -979,7 +974,7 @@ HTML; } // POST a signed message to a single inbox - function sentMessageToSingle( $inbox, $message ) { + function sendMessageToSingle( $inbox, $message ) { global $directories; $inbox_host = parse_url( $inbox, PHP_URL_HOST ); @@ -1386,7 +1381,7 @@ HTML; } // Sign & send the request - sentMessageToSingle( $profileInbox, $message ); + sendMessageToSingle( $profileInbox, $message ); if ( "Follow" == $action ) { // Save the user's details