Move GET and POST into their own functions
rodzic
098e1a23e5
commit
5fdfd84e6f
417
index.php
417
index.php
|
@ -43,7 +43,7 @@
|
|||
$server = $_SERVER["SERVER_NAME"]; // Do not change this!
|
||||
|
||||
// Set up where logs and messages go.
|
||||
// If you want to, you can change these directories to something more suitable for you.
|
||||
// You can change these directories to something more suitable if you like.
|
||||
$data = "data";
|
||||
$directories = array(
|
||||
"inbox" => "{$data}/inbox",
|
||||
|
@ -53,6 +53,7 @@
|
|||
"posts" => "posts",
|
||||
"images" => "images",
|
||||
);
|
||||
// Create the directories if they don't already exist.
|
||||
foreach ( $directories as $directory ) {
|
||||
if( !is_dir( $directory ) ) { mkdir( $data ); mkdir( $directory ); }
|
||||
}
|
||||
|
@ -71,7 +72,8 @@
|
|||
$bodyData = print_r( $body, true );
|
||||
$requestData = print_r( $_REQUEST, true );
|
||||
$serverData = print_r( $_SERVER, true );
|
||||
!empty( $_GET["path"] ) ? $path = $_GET["path"] : $path = "/";
|
||||
// If the root has been requested, manually set the path to `/`
|
||||
!empty( $_GET["path"] ) ? $path = $_GET["path"] : $path = "/";
|
||||
|
||||
// Get the type of request - used in the log filename
|
||||
if ( isset( $body["type"] ) ) {
|
||||
|
@ -123,21 +125,21 @@
|
|||
case "outbox":
|
||||
outbox(); // Optional. Dynamic.
|
||||
case "write":
|
||||
write(); // User interface for writing posts
|
||||
write(); // User interface for writing posts.
|
||||
case "send":
|
||||
send(); // API for posting content to the Fediverse
|
||||
send(); // API for posting content to the Fediverse.
|
||||
case "follow":
|
||||
follow(); // User interface for following an external user
|
||||
follow(); // User interface for following an external user.
|
||||
case "follow_user":
|
||||
follow_user(); // API for following a user
|
||||
follow_user(); // API for following a user.
|
||||
case "read":
|
||||
view( "read" ); // User interface for reading posts
|
||||
view( "read" );// User interface for reading posts.
|
||||
case ".well-known/nodeinfo":
|
||||
wk_nodeinfo(); // Optional. Static.
|
||||
wk_nodeinfo(); // Optional. Static.
|
||||
case "nodeinfo/2.1":
|
||||
nodeinfo(); // Optional. Static.
|
||||
nodeinfo(); // Optional. Static.
|
||||
case "/":
|
||||
view( "home" ); // Optional. Can be dynamic
|
||||
view( "home" );// Optional. Can be dynamic
|
||||
default:
|
||||
die();
|
||||
}
|
||||
|
@ -221,7 +223,7 @@
|
|||
// Create a list of all the followers
|
||||
$items = array();
|
||||
foreach ( $following_files as $following_file ) {
|
||||
$following = json_decode( file_get_contents( $following_file ),true );
|
||||
$following = json_decode( file_get_contents( $following_file ), true );
|
||||
$items[] = $following["id"];
|
||||
}
|
||||
|
||||
|
@ -238,8 +240,8 @@
|
|||
}
|
||||
function followers() {
|
||||
global $server, $directories;
|
||||
// The number of followers is self-reported
|
||||
// You can set this to any number you like
|
||||
// The number of followers is self-reported.
|
||||
// You can set this to any number you like.
|
||||
|
||||
// Get all the files
|
||||
$follower_files = glob( $directories["followers"] . "/*.json");
|
||||
|
@ -322,13 +324,13 @@
|
|||
$inbox_actor_json = curl_exec( $ch );
|
||||
|
||||
// Check for errors
|
||||
if (curl_errno($ch)) {
|
||||
if ( curl_errno( $ch ) ) {
|
||||
// TODO: Handle cURL error
|
||||
die();
|
||||
}
|
||||
|
||||
// Close cURL session
|
||||
curl_close($ch);
|
||||
curl_close( $ch );
|
||||
|
||||
// Save the actor's data in `/data/followers/`
|
||||
$follower_filename = urlencode( $follower_actor );
|
||||
|
@ -362,24 +364,7 @@
|
|||
];
|
||||
|
||||
// The Accept is POSTed to the inbox on the server of the user who requested the follow
|
||||
$follower_inbox_path = parse_url( $follower_inbox, PHP_URL_PATH );
|
||||
// Generate the signed headers
|
||||
$headers = generate_signed_headers( $message, $follower_host, $follower_inbox_path, "POST" );
|
||||
|
||||
// POST the message and header to the requester's inbox
|
||||
$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_HTTPHEADER, $headers );
|
||||
curl_exec( $ch );
|
||||
|
||||
// Check for errors
|
||||
if( curl_errno( $ch ) ) {
|
||||
$timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED );
|
||||
file_put_contents( $directories["logs"] . "/{$timestamp}.Error.txt", curl_error( $ch ) );
|
||||
}
|
||||
curl_close( $ch );
|
||||
sentMessageToSingle( $follower_inbox, $message );
|
||||
die();
|
||||
}
|
||||
|
||||
|
@ -405,7 +390,7 @@
|
|||
global $server, $username, $key_private;
|
||||
|
||||
// Location of the Public Key
|
||||
$keyId = "https://{$server}/{$username}#main-key";
|
||||
$keyId = "https://{$server}/{$username}#main-key";
|
||||
|
||||
// Get the Private Key
|
||||
$signer = openssl_get_privatekey( $key_private );
|
||||
|
@ -413,7 +398,7 @@
|
|||
// Timestamp this message was sent
|
||||
$date = date( "D, d M Y H:i:s \G\M\T" );
|
||||
|
||||
// There are subtly different signing requirements for POST and GET
|
||||
// There are subtly different signing requirements for POST and GET.
|
||||
if ( "POST" == $method ) {
|
||||
// Encode the message object to JSON
|
||||
$message_json = json_encode( $message );
|
||||
|
@ -497,64 +482,64 @@
|
|||
}
|
||||
|
||||
// Counters for followers, following, and posts
|
||||
$follower_files = glob( $directories["followers"] . "/*.json");
|
||||
$follower_files = glob( $directories["followers"] . "/*.json" );
|
||||
$totalFollowers = count( $follower_files );
|
||||
$following_files = glob( $directories["following"] . "/*.json");
|
||||
$following_files = glob( $directories["following"] . "/*.json" );
|
||||
$totalFollowing = count( $following_files );
|
||||
|
||||
// Show the HTML page
|
||||
echo <<< HTML
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-GB">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:url" content="https://{$server}">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="{$realName}">
|
||||
<meta property="og:description" content="{$summary}">
|
||||
<meta property="og:image" content="https://{$server}/banner.png">
|
||||
<title>{$h1} {$realName}</title>
|
||||
<style>
|
||||
body { margin:0; padding: 0; font-family:sans-serif; }
|
||||
@media screen and (max-width: 800px) { body { width: 100%; }}
|
||||
@media screen and (min-width: 799px) { body { width: 800px; margin: 0 auto; }}
|
||||
address { font-style: normal; }
|
||||
img { max-width: 50%; }
|
||||
.h-feed { margin:auto; width: 100%; }
|
||||
.h-feed > header { text-align: center; margin: 0 auto; }
|
||||
.h-feed .banner { text-align: center; margin:0 auto; max-width: 650px; }
|
||||
.h-feed > h1, .h-feed > h2 { margin-top: 10px; margin-bottom: 0; }
|
||||
.h-feed > header > h1:has(span.p-author), h2:has(a.p-nickname) { word-wrap: break-word; max-width: 90%; padding-left:20px; }
|
||||
.h-feed .u-feature:first-child { margin-top: 10px; margin-bottom: -150px; max-width: 100%;}
|
||||
.h-feed .u-photo { max-height: 8vw; max-width:100%; min-height: 120px; }
|
||||
.h-feed .about { font-size: smaller; background-color: #F5F5F5; padding: 10px; border-top: dotted 1px #808080; border-bottom: dotted 1px #808080; }
|
||||
.h-feed > ul { padding-left: 0; list-style-type: none; }
|
||||
.h-feed > ul > li { padding: 10px; border-bottom: dotted 1px #808080; }
|
||||
.h-entry { padding-right: 10px; }
|
||||
.h-entry time { font-weight: bold; }
|
||||
.h-entry .e-content a { word-wrap: break-word; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="h-feed">
|
||||
<header>
|
||||
<div class="banner">
|
||||
<img src="banner.png" alt="" class="u-feature"><br>
|
||||
<img src="icon.png" alt="icon" class="u-photo">
|
||||
</div>
|
||||
<address>
|
||||
<h1 class="p-name p-author">{$realName}</h1>
|
||||
<h2><a class="p-nickname u-url" rel="author" href="https://{$server}/{$username}">@{$rawUsername}@{$server}</a></h2>
|
||||
</address>
|
||||
<p class="p-summary">{$summary}</p>
|
||||
<p>Following: {$totalFollowing} | Followers: {$totalFollowers}</p>
|
||||
<div class="about">
|
||||
<p><a href="https://gitlab.com/edent/activitypub-single-php-file/">This software is licenced under AGPL 3.0</a>.</p>
|
||||
<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>
|
||||
</div>
|
||||
</header>
|
||||
<ul>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-GB">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:url" content="https://{$server}">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="{$realName}">
|
||||
<meta property="og:description" content="{$summary}">
|
||||
<meta property="og:image" content="https://{$server}/banner.png">
|
||||
<title>{$h1} {$realName}</title>
|
||||
<style>
|
||||
body { margin:0; padding: 0; font-family:sans-serif; }
|
||||
@media screen and (max-width: 800px) { body { width: 100%; }}
|
||||
@media screen and (min-width: 799px) { body { width: 800px; margin: 0 auto; }}
|
||||
address { font-style: normal; }
|
||||
img { max-width: 50%; }
|
||||
.h-feed { margin:auto; width: 100%; }
|
||||
.h-feed > header { text-align: center; margin: 0 auto; }
|
||||
.h-feed .banner { text-align: center; margin:0 auto; max-width: 650px; }
|
||||
.h-feed > h1, .h-feed > h2 { margin-top: 10px; margin-bottom: 0; }
|
||||
.h-feed > header > h1:has(span.p-author), h2:has(a.p-nickname) { word-wrap: break-word; max-width: 90%; padding-left:20px; }
|
||||
.h-feed .u-feature:first-child { margin-top: 10px; margin-bottom: -150px; max-width: 100%;}
|
||||
.h-feed .u-photo { max-height: 8vw; max-width:100%; min-height: 120px; }
|
||||
.h-feed .about { font-size: smaller; background-color: #F5F5F5; padding: 10px; border-top: dotted 1px #808080; border-bottom: dotted 1px #808080; }
|
||||
.h-feed > ul { padding-left: 0; list-style-type: none; }
|
||||
.h-feed > ul > li { padding: 10px; border-bottom: dotted 1px #808080; }
|
||||
.h-entry { padding-right: 10px; }
|
||||
.h-entry time { font-weight: bold; }
|
||||
.h-entry .e-content a { word-wrap: break-word; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="h-feed">
|
||||
<header>
|
||||
<div class="banner">
|
||||
<img src="banner.png" alt="" class="u-feature"><br>
|
||||
<img src="icon.png" alt="icon" class="u-photo">
|
||||
</div>
|
||||
<address>
|
||||
<h1 class="p-name p-author">{$realName}</h1>
|
||||
<h2><a class="p-nickname u-url" rel="author" href="https://{$server}/{$username}">@{$rawUsername}@{$server}</a></h2>
|
||||
</address>
|
||||
<p class="p-summary">{$summary}</p>
|
||||
<p>Following: {$totalFollowing} | Followers: {$totalFollowers}</p>
|
||||
<div class="about">
|
||||
<p><a href="https://gitlab.com/edent/activitypub-single-php-file/">This software is licenced under AGPL 3.0</a>.</p>
|
||||
<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>
|
||||
</div>
|
||||
</header>
|
||||
<ul>
|
||||
HTML;
|
||||
// Get all the files in the directory
|
||||
$message_files = array_reverse( glob( $directories[$directory] . "/*.json") );
|
||||
|
@ -612,16 +597,21 @@ HTML;
|
|||
|
||||
// Set up the common components
|
||||
$object = $message["object"];
|
||||
|
||||
// Get the message's ID.
|
||||
// Set up the HTML representation
|
||||
if ( isset( $message["object"]["id"] ) ) {
|
||||
$id = $message["object"]["id"];
|
||||
$timeHTML = "<time datetime=\"{$published}\" class=\"u-url\" rel=\"bookmark\"><a href=\"{$id}\">{$published}</a></time>";
|
||||
$publishedHTML = "<a href=\"{$id}\">{$published}</a>";
|
||||
} else if ( isset( $message["id"] ) ) {
|
||||
$id = $message["id"];
|
||||
$timeHTML = "<time datetime=\"{$published}\" class=\"u-url\" rel=\"bookmark\"><a href=\"{$id}\">{$published}</a></time>";
|
||||
$publishedHTML = "<a href=\"{$id}\">{$published}</a>";
|
||||
} else {
|
||||
$id = "";
|
||||
$timeHTML = "<time datetime=\"{$published}\" class=\"u-url\" rel=\"bookmark\">{$published}</time>";
|
||||
$publishedHTML = $published;
|
||||
}
|
||||
// For displaying the post's information
|
||||
$timeHTML = "<time datetime=\"{$published}\" class=\"u-url\" rel=\"bookmark\">{$publishedHTML}</time>";
|
||||
|
||||
// Get the actor who authored the message
|
||||
if ( isset( $message["actor"] ) ) {
|
||||
|
@ -656,10 +646,11 @@ HTML;
|
|||
|
||||
// Render the message according to type
|
||||
if ( "Create" == $type || "Update" == $type || "Note" == $type ) {
|
||||
// Get the HTML content and sanitise it.
|
||||
// Get the HTML content
|
||||
// There is a slight difference between the formatting of sent and received messages
|
||||
"Note" == $type ? $content = $message["content"] : $content = $object["content"];
|
||||
$content = strip_tags( $content, $allowed_elements );
|
||||
// Sanitise the HTML
|
||||
$content = strip_tags( $content, $allowed_elements );
|
||||
|
||||
// Is this a reply to something?
|
||||
if ( isset( $object["inReplyTo"] ) ) {
|
||||
|
@ -680,7 +671,7 @@ HTML;
|
|||
if ( isset( $object["summary"] ) ) {
|
||||
$summary = $object["summary"];
|
||||
$summary = strip_tags( $summary, $allowed_elements );
|
||||
|
||||
// Hide the content until the user interacts with it.
|
||||
$content = "<details><summary>{$summary}</summary>{$content}</details>";
|
||||
}
|
||||
|
||||
|
@ -712,16 +703,20 @@ HTML;
|
|||
|
||||
// Only use things which have a MIME Type set
|
||||
if ( isset( $attachment["mediaType"] ) ) {
|
||||
$mediaType = explode( "/", $attachment["mediaType"])[0];
|
||||
$mediaURl = $attachment["url"];
|
||||
$mime = $attachment["mediaType"];
|
||||
// Use the first half of the MIME Type.
|
||||
// For example `image/png` or `video/mp4`
|
||||
$mediaType = explode( "/", $mime )[0];
|
||||
|
||||
if ( "image" == $mediaType ) {
|
||||
// Get the alt text
|
||||
isset( $attachment["name"] ) ? $alt = $attachment["name"] : $alt = "";
|
||||
$content .= "<img src='" . $attachment["url"] . "' alt='{$alt}'>";
|
||||
$content .= "<img src='{$mediaURl}' alt='{$alt}'>";
|
||||
} else if ( "video" == $mediaType ) {
|
||||
$content .= "<video controls><source src='" . $attachment["url"] . "' type='" . $attachment["mediaType"] . "' /></video>";
|
||||
$content .= "<video controls><source src='{$mediaURl}' type='{$mime}'></video>";
|
||||
}else if ( "audio" == $mediaType ) {
|
||||
$content .= "<audio controls src='" . $attachment["url"] . "' type='" . $attachment["mediaType"] . "'></audio>";
|
||||
$content .= "<audio controls src='{$mediaURl}' type='{$mime}'></audio>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -767,7 +762,6 @@ HTML;
|
|||
}
|
||||
echo <<< HTML
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -866,7 +860,7 @@ HTML;
|
|||
$type = $_POST["type"];
|
||||
|
||||
// Likes and Announces have an identical message structure
|
||||
if ( $type == "Like" || $type == "Announce" ) {
|
||||
if ( "Like" == $type || "Announce" == $type ) {
|
||||
// Was a URl sent?
|
||||
if ( isset( $_POST["postURl"] ) && filter_var( $_POST["postURl"], FILTER_VALIDATE_URL ) ) {
|
||||
$postURl = $_POST["postURl"];
|
||||
|
@ -875,6 +869,11 @@ HTML;
|
|||
die();
|
||||
}
|
||||
|
||||
if ( "Like" == $type ) {
|
||||
// The message will need to be sent to the inbox of the author of the message
|
||||
$inbox_single = getInboxFromMessageURl( $postURl );
|
||||
}
|
||||
|
||||
// Outgoing Message ID
|
||||
$guid = uuid();
|
||||
|
||||
|
@ -888,16 +887,14 @@ HTML;
|
|||
"object" => $postURl
|
||||
];
|
||||
|
||||
// Annouces are sent to an audience
|
||||
// Announces are sent to an audience
|
||||
// The audience is public and it is sent to all followers
|
||||
if ( $type == "Announce") {
|
||||
// TODO: Let the original poster know we boosted them
|
||||
if ( $type == "Announce" ) {
|
||||
$message = array_merge( $message,
|
||||
array("to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc" => [
|
||||
"https://{$server}/followers"
|
||||
])
|
||||
array(
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => ["https://{$server}/followers"])
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -920,7 +917,7 @@ HTML;
|
|||
list( "HTML" => $content, "TagArray" => $tags ) = process_content( $content );
|
||||
|
||||
// Is there an image attached?
|
||||
if ( isset( $_FILES['image']['tmp_name'] ) && ("" != $_FILES['image']['tmp_name'] ) ) {
|
||||
if ( isset( $_FILES['image']['tmp_name'] ) && ( "" != $_FILES['image']['tmp_name'] ) ) {
|
||||
// Get information about the image
|
||||
$image = $_FILES['image']['tmp_name'];
|
||||
$image_info = getimagesize( $image );
|
||||
|
@ -948,8 +945,7 @@ HTML;
|
|||
"mediaType" => "{$image_mime}",
|
||||
"url" => "https://{$server}/{$image_full_path}",
|
||||
"name" => $alt
|
||||
];
|
||||
|
||||
];
|
||||
} else {
|
||||
$attachment = [];
|
||||
}
|
||||
|
@ -1000,6 +996,54 @@ HTML;
|
|||
$note_json = json_encode( $note );
|
||||
file_put_contents( $directories["posts"] . "/{$guid}.json", print_r( $note_json, true ) );
|
||||
|
||||
// Is this message going to one user? (Usually a Like)
|
||||
if ( isset( $inbox_single ) ) {
|
||||
$messageSent = sentMessageToSingle( $inbox_single, $message );
|
||||
} else { // Send to all the user's followers
|
||||
$messageSent = sendMessageToFollowers( $message );
|
||||
}
|
||||
|
||||
// Render the JSON so the user can see the POST has worked
|
||||
if ( $messageSent ) {
|
||||
header( "Location: https://{$server}/posts/{$guid}.json" );
|
||||
die();
|
||||
} else {
|
||||
echo "ERROR!";
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
// POST a signed message to a single inbox
|
||||
function sentMessageToSingle( $inbox, $message ) {
|
||||
global $directories;
|
||||
|
||||
$inbox_host = parse_url( $inbox, PHP_URL_HOST );
|
||||
$inbox_path = parse_url( $inbox, PHP_URL_PATH );
|
||||
|
||||
// Generate the signed headers
|
||||
$headers = generate_signed_headers( $message, $inbox_host, $inbox_path, "POST" );
|
||||
|
||||
// POST the message and header to the requester's inbox
|
||||
$ch = curl_init( $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_HTTPHEADER, $headers );
|
||||
curl_exec( $ch );
|
||||
|
||||
// Check for errors
|
||||
if( curl_errno( $ch ) ) {
|
||||
$timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED );
|
||||
file_put_contents( $directories["logs"] . "/{$timestamp}.Error.txt", curl_error( $ch ) );
|
||||
return false;
|
||||
}
|
||||
curl_close($ch);
|
||||
return true;
|
||||
}
|
||||
|
||||
// POST a signed message to the inboxes of all followers
|
||||
function sendMessageToFollowers( $message ) {
|
||||
global $directories;
|
||||
// Read existing followers
|
||||
$followers = glob( $directories["followers"] . "/*.json" );
|
||||
|
||||
|
@ -1062,9 +1106,7 @@ HTML;
|
|||
// 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();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Content can be plain text. But to add clickable links and hashtags, it needs to be turned into HTML.
|
||||
|
@ -1115,7 +1157,7 @@ HTML;
|
|||
|
||||
// Construct the mentions value for the note object
|
||||
// This goes in the generic "tag" property
|
||||
// TODO: Add this to the CC field
|
||||
// TODO: Add this to the CC field & appropriate inbox
|
||||
foreach ( $usernames as $username ) {
|
||||
list( , $user, $domain ) = explode( "@", $username );
|
||||
$tags[] = array(
|
||||
|
@ -1137,11 +1179,69 @@ HTML;
|
|||
$content = "<p>{$content}</p>";
|
||||
|
||||
return [
|
||||
"HTML" => $content,
|
||||
"HTML" => $content,
|
||||
"TagArray" => $tags
|
||||
];
|
||||
}
|
||||
|
||||
// When given the URl of a post, this looks up the post, finds the user, then returns their inbox or shared inbox
|
||||
function getInboxFromMessageURl( $url ) {
|
||||
|
||||
// Get details about the message
|
||||
$messageData = getDataFromURl( $url );
|
||||
|
||||
// The author is the user who the message is attributed to
|
||||
if ( isset ( $messageData["attributedTo"] ) && filter_var( $messageData["attributedTo"], FILTER_VALIDATE_URL) ) {
|
||||
$profileData = getDataFromURl( $messageData["attributedTo"] );
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the shared inbox or personal inbox
|
||||
if( isset( $profileData["endpoints"]["sharedInbox"] ) ) {
|
||||
$inbox = $profileData["endpoints"]["sharedInbox"];
|
||||
} else {
|
||||
// If not, use the individual inbox
|
||||
$inbox = $profileData["inbox"];
|
||||
}
|
||||
|
||||
// Return the destination inbox if it is valid
|
||||
if ( filter_var( $inbox, FILTER_VALIDATE_URL) ) {
|
||||
return $inbox;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// GET a request to a URl and returns structured data
|
||||
function getDataFromURl ( $url ) {
|
||||
// Split the URL
|
||||
$url_host = parse_url( $url, PHP_URL_HOST );
|
||||
$url_path = parse_url( $url, PHP_URL_PATH );
|
||||
|
||||
// Generate signed headers for this request
|
||||
$headers = generate_signed_headers( null, $url_host, $url_path, "GET" );
|
||||
|
||||
// Set cURL options
|
||||
$ch = curl_init( $url );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
|
||||
|
||||
// Execute the cURL session
|
||||
$urlJSON = curl_exec( $ch );
|
||||
|
||||
// Check for errors
|
||||
if (curl_errno( $ch )) {
|
||||
// Handle cURL error
|
||||
die();
|
||||
}
|
||||
|
||||
// Close cURL session
|
||||
curl_close( $ch );
|
||||
|
||||
return json_decode( $urlJSON, true );
|
||||
}
|
||||
|
||||
// The Outbox contains a date-ordered list (newest first) of all the user's posts
|
||||
// This is optional.
|
||||
function outbox() {
|
||||
|
@ -1239,42 +1339,11 @@ HTML;
|
|||
if ( !isset( $profileURl ) ) { echo "No profile"; die(); }
|
||||
|
||||
// Get the user's details
|
||||
// This request does not need to be signed normally.
|
||||
// Some servers will only respond to signed requests.
|
||||
// It need to specify that it wants a JSON response
|
||||
|
||||
$profileURl_host = parse_url( $profileURl, PHP_URL_HOST );
|
||||
$profileURl_path = parse_url( $profileURl, PHP_URL_PATH );
|
||||
|
||||
// Request the JSON representation of the the user
|
||||
$ch = curl_init( $profileURl );
|
||||
|
||||
// Generate signed headers for this request
|
||||
$headers = generate_signed_headers( null, $profileURl_host, $profileURl_path, "GET" );
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
|
||||
|
||||
// Execute the cURL session
|
||||
$profileJSON = curl_exec( $ch );
|
||||
|
||||
// Check for errors
|
||||
if (curl_errno($ch)) {
|
||||
// Handle cURL error
|
||||
die();
|
||||
}
|
||||
|
||||
// Close cURL session
|
||||
curl_close($ch);
|
||||
|
||||
$profileData = json_decode( $profileJSON, true );
|
||||
$profileData = getDataFromURl( $profileURl );
|
||||
|
||||
// Get the user's inbox
|
||||
$profileInbox = $profileData["inbox"];
|
||||
$inbox_host = parse_url( $profileInbox, PHP_URL_HOST );
|
||||
$inbox_path = parse_url( $profileInbox, PHP_URL_PATH );
|
||||
|
||||
|
||||
// Create a follow request
|
||||
$guid = uuid();
|
||||
$message = [
|
||||
|
@ -1287,27 +1356,11 @@ HTML;
|
|||
|
||||
// Sign a request to follow
|
||||
// The Accept is POSTed to the inbox on the server of the user who requested the follow
|
||||
// Get the signed headers
|
||||
$headers = generate_signed_headers( $message, $inbox_host, $inbox_path, "POST" );
|
||||
|
||||
// POST the message and header to the requester's inbox
|
||||
$ch = curl_init( $profileInbox );
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
|
||||
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $message ) );
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
|
||||
curl_exec( $ch );
|
||||
|
||||
// Check for errors
|
||||
if( curl_errno( $ch ) ) {
|
||||
$timestamp = ( new DateTime() )->format( DATE_RFC3339_EXTENDED );
|
||||
file_put_contents( $directories["logs"] . "/{$timestamp}.Error.txt", curl_error( $ch ) );
|
||||
}
|
||||
curl_close($ch);
|
||||
sentMessageToSingle( $profileInbox, $message );
|
||||
|
||||
// Save the user's details
|
||||
$following_filename = urlencode( $profileURl );
|
||||
file_put_contents( $directories["following"] . "/{$following_filename}.json", $profileJSON );
|
||||
file_put_contents( $directories["following"] . "/{$following_filename}.json", json_encode( $profileData ) );
|
||||
|
||||
// Render the JSON so the user can see the POST has worked
|
||||
header( "Location: https://{$server}/data/following/" . urlencode( $following_filename ) . ".json" );
|
||||
|
@ -1422,36 +1475,8 @@ HTML;
|
|||
// This is usually in the form `https://example.com/user/username#main-key`
|
||||
// This is to differentiate if the user has multiple keys
|
||||
// TODO: Check the actual key
|
||||
// This request does not need to be signed normally.
|
||||
// Some servers will only respond to signed requests.
|
||||
// It need to specify that it wants a JSON response
|
||||
|
||||
$publicKeyURL_host = parse_url( $publicKeyURL, PHP_URL_HOST );
|
||||
$publicKeyURL_path = parse_url( $publicKeyURL, PHP_URL_PATH );
|
||||
|
||||
// Request the JSON representation of the the user
|
||||
$ch = curl_init( $publicKeyURL );
|
||||
|
||||
// Generate signed headers for this request
|
||||
$headers = generate_signed_headers( null, $publicKeyURL_host, $publicKeyURL_path, "GET" );
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
|
||||
|
||||
// Execute the cURL session
|
||||
$userJSON = curl_exec( $ch );
|
||||
|
||||
// Check for errors
|
||||
if (curl_errno($ch)) {
|
||||
// Handle cURL error
|
||||
die();
|
||||
}
|
||||
|
||||
// Close cURL session
|
||||
curl_close($ch);
|
||||
|
||||
$userData = json_decode( $userJSON, true );
|
||||
$userData = getDataFromURl( $publicKeyURL );
|
||||
$publicKey = $userData["publicKey"]["publicKeyPem"];
|
||||
|
||||
// Get the remaining parts
|
||||
|
|
Ładowanie…
Reference in New Issue