From 911ba8422054afb710db34b3abca33edd8c76013 Mon Sep 17 00:00:00 2001 From: Terence Eden Date: Mon, 12 Feb 2024 21:21:05 +0000 Subject: [PATCH] Initial upload --- .htaccess | 12 ++ CRAPL-LICENSE.txt | 115 ++++++++++++ README.md | 95 ++-------- icon.png | Bin 0 -> 45368 bytes index.php | 435 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 574 insertions(+), 83 deletions(-) create mode 100644 .htaccess create mode 100644 CRAPL-LICENSE.txt create mode 100644 icon.png create mode 100644 index.php diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..1bd3d7a --- /dev/null +++ b/.htaccess @@ -0,0 +1,12 @@ +Options -Indexes + + + RewriteEngine On + RewriteBase / + + # Redirect all paths to queries + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.*)$ index.php?path=$1 [QSA,L] + + diff --git a/CRAPL-LICENSE.txt b/CRAPL-LICENSE.txt new file mode 100644 index 0000000..57ce56f --- /dev/null +++ b/CRAPL-LICENSE.txt @@ -0,0 +1,115 @@ + THE CRAPL v0 BETA 1 + + +0. Information about the CRAPL + +If you have questions or concerns about the CRAPL, or you need more +information about this license, please contact: + + Matthew Might + http://matt.might.net/ + + +I. Preamble + +Science thrives on openness. + +In modern science, it is often infeasible to replicate claims without +access to the software underlying those claims. + +Let's all be honest: when scientists write code, aesthetics and +software engineering principles take a back seat to having running, +working code before a deadline. + +So, let's release the ugly. And, let's be proud of that. + + +II. Definitions + +1. "This License" refers to version 0 beta 1 of the Community + Research and Academic Programming License (the CRAPL). + +2. "The Program" refers to the medley of source code, shell scripts, + executables, objects, libraries and build files supplied to You, + or these files as modified by You. + + [Any appearance of design in the Program is purely coincidental and + should not in any way be mistaken for evidence of thoughtful + software construction.] + +3. "You" refers to the person or persons brave and daft enough to use + the Program. + +4. "The Documentation" refers to the Program. + +5. "The Author" probably refers to the caffeine-addled graduate + student that got the Program to work moments before a submission + deadline. + + +III. Terms + +1. By reading this sentence, You have agreed to the terms and + conditions of this License. + +2. If the Program shows any evidence of having been properly tested + or verified, You will disregard this evidence. + +3. You agree to hold the Author free from shame, embarrassment or + ridicule for any hacks, kludges or leaps of faith found within the + Program. + +4. You recognize that any request for support for the Program will be + discarded with extreme prejudice. + +5. The Author reserves all rights to the Program, except for any + rights granted under any additional licenses attached to the + Program. + + +IV. Permissions + +1. You are permitted to use the Program to validate published + scientific claims. + +2. You are permitted to use the Program to validate scientific claims + submitted for peer review, under the condition that You keep + modifications to the Program confidential until those claims have + been published. + +3. You are permitted to use and/or modify the Program for the + validation of novel scientific claims if You make a good-faith + attempt to notify the Author of Your work and Your claims prior to + submission for publication. + +4. If You publicly release any claims or data that were supported or + generated by the Program or a modification thereof, in whole or in + part, You will release any inputs supplied to the Program and any + modifications You made to the Progam. This License will be in + effect for the modified program. + + +V. Disclaimer of Warranty + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + + +VI. Limitation of Liability + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/README.md b/README.md index 25a3c33..18c1c30 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,22 @@ -# ActivityPub-Single-PHP-File - +# ActivityPub Server in a Single PHP File +This is a single PHP file - and an `.htaccess file` - which acts as an extremely basic ActivityPub server. ## Getting started -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +This is designed to be a lightweight educational tool to show you the basics of how ActivityPub works. -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! +There are no tests, no checks, no security features, no header verifications, no containers, no gods, no masters. -## Add your files +Edit the `.php` file to add a username, password, and keypair. -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +Upload the `.php` and `.htaccess` file to the *root* directory of your domain. For example `test.example.com/`. It will not work in a subdirectory. -``` -cd existing_repo -git remote add origin https://gitlab.com/edent/activitypub-single-php-file.git -git branch -M main -git push -uf origin main -``` +Optionally, upload an `icon.png` as well to make the account look nice. -## Integrate with your tools +## How this works -- [ ] [Set up project integrations](https://gitlab.com/edent/activitypub-single-php-file/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README - -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +* 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 root directory. +* Post files are saved as .json in the `/posts` directory. diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ffee42fcb39d6a3e5776b81a469ac4443c03a60d GIT binary patch literal 45368 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 "acct:{$username}@{$server}", + "links" => array( + array( + "rel" => "self", + "type" => "application/activity+json", + "href" => "https://{$server}/{$username}" + ) + ) + ); + header("Content-Type: application/json"); + echo json_encode( $webfinger ); + die(); + } + + function username() { + // Display the username JSON + global $username, $realName, $server, $key_public; + + $user = array( + "@context" => [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1" + ], + "id" => "https://{$server}/{$username}", + "type" => "Person", + "following" => "https://{$server}/following", + "followers" => "https://{$server}/followers", + "inbox" => "https://{$server}/inbox", + "preferredUsername" => rawurldecode($username), + "name" => "{$realName}", + "summary" => "A single file ActivityPub server.", + "url" => "https://{$server}", + "manuallyApprovesFollowers" => true, + "discoverable" => true, + "published" => "2024-02-12T11:51:00Z", + "icon" => [ + "type" => "Image", + "mediaType" => "image/png", + "url" => "https://{$server}/icon.png" + ], + "publicKey" => [ + "id" => "https://{$server}/{$username}#main-key", + "owner" => "https://{$server}/{$username}", + "publicKeyPem" => $key_public + ] + ); + header("Content-Type: application/activity+json"); + echo json_encode( $user ); + die(); + } + + function following() { + // Display the following JSON + global $server; + + $following = array( + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "https://{$server}/following", + "type" => "Collection", + "totalItems" => 0, + "items" => [] + ); + header("Content-Type: application/activity+json"); + echo json_encode( $following ); + die(); + } + + function followers() { + // Display the followers JSON + global $server; + $followers = array( + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "https://{$server}/followers", + "type" => "Collection", + "totalItems" => 0, + "items" => [] + ); + header("Content-Type: application/activity+json"); + echo json_encode( $followers ); + die(); + } + + function inbox() { + // Respond to InBox requests + global $body, $server, $username, $key_private; + + // Get the message and type + $inbox_message = $body; + $inbox_type = $inbox_message["type"]; + + // This inbox only responds to follow requests + if ( "Follow" != $inbox_type ) { die(); } + + // Get the parameters + $inbox_id = $inbox_message["id"]; + $inbox_actor = $inbox_message["actor"]; + $inbox_url = parse_url($inbox_actor, PHP_URL_SCHEME) . "://" . parse_url($inbox_actor, PHP_URL_HOST); + $inbox_host = parse_url($inbox_actor, PHP_URL_HOST); + + // Does this account have any followers? + if( file_exists( "followers.json" ) ) { + $followers_file = file_get_contents( "followers.json" ); + $followers_json = json_decode( $followers_file, true ); + } else { + $followers_json = array(); + } + + // Add user to list. Don't care about duplicate users, server is what's important + $followers_json[$inbox_host]["users"][] = $inbox_actor; + + // Save the new followers file + file_put_contents( "followers.json", print_r( json_encode( $followers_json ), true ) ); + + // Response Message ID + // This isn't used for anything important so could just be a random number + $guid = uuid(); + + // Create the Accept message + $message = [ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "https://{$server}/{$guid}", + "type" => "Accept", + "actor" => "https://{$server}/{$username}", + "object" => [ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => $inbox_id, + "type" => $inbox_type, + "actor" => $inbox_actor, + "object" => "https://{$server}/{$username}", + ] + ]; + $message_json = json_encode( $message ); + + // The Accept is sent to the server of the user who requested the follow + // TODO: The path doesn't *always* end with/inbox + $host = $inbox_host; + $path = parse_url($inbox_actor, PHP_URL_PATH) . "/inbox"; + + // Set up signing + $keyId = "https://{$server}/{$username}#main-key"; + + // Generate signing variables + $hash = hash('sha256', $message_json, true); + $digest = base64_encode($hash); + $date = date('D, d M Y H:i:s \G\M\T'); + + $signer = openssl_get_privatekey($key_private); + $stringToSign = "(request-target): post $path\nhost: $host\ndate: $date\ndigest: SHA-256=$digest"; + openssl_sign($stringToSign, $signature, $signer, OPENSSL_ALGO_SHA256); + $signature_b64 = base64_encode($signature); + + $header = 'keyId="' . $keyId . '",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="' . $signature_b64 . '"'; + + // Header for POST reply + $headers = array( + "Host: {$host}", + "Date: {$date}", + "Digest: SHA-256={$digest}", + "Signature: {$header}", + "Content-Type: application/activity+json", + "Accept: application/activity+json", + ); + + // Specify the URL of the remote server's inbox + // TODO: The path doesn't *always* end with /inbox + $remoteServerUrl = $inbox_actor . "/inbox"; + + // POST the message and header to the requester's inbox + $ch = curl_init( $remoteServerUrl ); + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); + curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" ); + curl_setopt( $ch, CURLOPT_POSTFIELDS, $message_json ); + curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers ); + $response = curl_exec( $ch ); + + // Check for errors + if( curl_errno( $ch ) ) { + file_put_contents( "error.txt", curl_error( $ch ) ); + } + curl_close($ch); + die(); + } + + function uuid() { + // Date sortable UUID + return sprintf('%08x-%04x-%04x-%04x-%012x', + time(), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0x3fff) | 0x8000, + mt_rand(0, 0xffffffffffff) + ); + } + + function write() { + // Display an HTML form for the user to enter a message. +echo <<< HTML + + + + + Send Message + + + +
+
+
+
+
+ +
+ + +HTML; + die(); + } + + function send() { + global $password, $server, $username, $key_private; + + // Does the posted password match the stored password? + if( $password != $_POST["password"] ) { die(); } + + // Get the posted content + $content = $_POST["content"]; + + // Current time + $timestamp = date("c"); + + // Outgoing Message ID + $guid = uuid(); + + // Construct the Note + $note = [ + "@context" => array( + "https://www.w3.org/ns/activitystreams" + ), + "id" => "https://{$server}/posts/{$guid}.json", + "type" => "Note", + "published" => $timestamp, + "attributedTo" => "https://{$server}/{$username}", + "content" => $content, + "contentMap" => ["en" => $content], + "to" => ["https://www.w3.org/ns/activitystreams#Public"] + ]; + + // Construct the Message + $message = [ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "https://{$server}/posts/{$guid}.json", + "type" => "Create", + "actor" => "https://{$server}/{$username}", + "to" => [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc" => [ + "https://{$server}/followers" + ], + "object" => $note + ]; + $message_json = json_encode($message); + + // Create the context for the permalink + $note = [ "@context" => "https://www.w3.org/ns/activitystreams", ...$note]; + + // Save the permalink + $note_json = json_encode( $note ); + file_put_contents( "posts/{$guid}.json", print_r( $note_json, true ) ); + + // Read existing users and get their hosts + $followers_file = file_get_contents( "followers.json" ); + $followers_json = json_decode( $followers_file, true ); + $hosts = array_keys( $followers_json ); + + // Prepare to use the multiple cURL handle + $mh = curl_multi_init(); + + // Loop through all the severs of the followers + // Each server needs its own cURL handle + // Each POST to an inbox needs to be signed separately + foreach ( $hosts as $host ) { + $path = '/inbox'; + + // Set up signing + $privateKey = $key_private; + $keyId = "https://{$server}/{$username}#main-key"; + + $hash = hash( "sha256", $message_json, true ); + $digest = base64_encode( $hash ); + $date = date('D, d M Y H:i:s \G\M\T'); + + $signer = openssl_get_privatekey( $key_private ); + $stringToSign = "(request-target): post $path\nhost: $host\ndate: $date\ndigest: SHA-256=$digest"; + openssl_sign( $stringToSign, $signature, $signer, OPENSSL_ALGO_SHA256 ); + $signature_b64 = base64_encode($signature); + + $header = 'keyId="' . $keyId . '",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="' . $signature_b64 . '"'; + + // Header for POST reply + $headers = array( + "Host: {$host}", + "Date: {$date}", + "Digest: SHA-256={$digest}", + "Signature: {$header}", + "Content-Type: application/activity+json", + "Accept: application/activity+json", + ); + + // Specify the URL of the remote server + $remoteServerUrl = "https://{$host}{$path}"; + + // POST the message and header to the requester's inbox + $ch = curl_init( $remoteServerUrl ); + + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); + curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" ); + curl_setopt( $ch, CURLOPT_POSTFIELDS, $message_json ); + curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers ); + + // Add the handle to the multi-handle + curl_multi_add_handle( $mh, $ch ); + } + + // Execute the multi-handle + do { + $status = curl_multi_exec( $mh, $active ); + if ( $active ) { + curl_multi_select( $mh ); + } + } while ( $active && $status == CURLM_OK ); + + // Close the multi-handle + curl_multi_close( $mh ); + + // Render the JSON so the user can see the POST has worked + header( "Location: https://{$server}/posts/{$guid}.json" ); + die(); + } + + die(); + die(); + die();