Basic follow support
rodzic
195be1afea
commit
b69c08e5bb
121
index.php
121
index.php
|
@ -106,6 +106,10 @@
|
|||
send(); // API for posting content to the Fediverse
|
||||
case "outbox":
|
||||
outbox(); // Optional. Dynamic.
|
||||
case "follow":
|
||||
follow(); // User interface for following an external user
|
||||
case "follow_user":
|
||||
follow_user(); // API for following a user
|
||||
case "/":
|
||||
home(); // Optional. Can be dynamic
|
||||
default:
|
||||
|
@ -265,10 +269,10 @@
|
|||
// Close cURL session
|
||||
curl_close($ch);
|
||||
|
||||
// Save the actor's data in `/data/followers/`
|
||||
if( ! is_dir( "data/followers" ) ) { mkdir( "data"); mkdir( "data/followers"); }
|
||||
$follower_filename = urlencode( $follower_actor );
|
||||
file_put_contents( "data/followers/{$follower_filename}.json", $inbox_actor_json );
|
||||
// Save the actor's data in `/data/followers/`
|
||||
if( ! is_dir( "data/followers" ) ) { mkdir( "data"); mkdir( "data/followers"); }
|
||||
$follower_filename = urlencode( $follower_actor );
|
||||
file_put_contents( "data/followers/{$follower_filename}.json", $inbox_actor_json );
|
||||
|
||||
} else {
|
||||
die();
|
||||
|
@ -713,7 +717,7 @@ HTML;
|
|||
// This goes in the generic "tag" property
|
||||
// TODO: Add this to the CC field
|
||||
foreach ( $usernames as $username ) {
|
||||
list( $null, $user, $domain ) = explode( "@", $username );
|
||||
list( , $user, $domain ) = explode( "@", $username );
|
||||
$tags[] = array(
|
||||
"type" => "Mention",
|
||||
"href" => "https://{$domain}/@{$user}",
|
||||
|
@ -773,6 +777,113 @@ HTML;
|
|||
die();
|
||||
}
|
||||
|
||||
// This creates a UI for the user to follow another user
|
||||
function follow() {
|
||||
echo <<< HTML
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-GB">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Follow</title>
|
||||
<style>
|
||||
*{font-family:sans-serif;font-size:1.1em;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form action="/follow_user" method="post" enctype="multipart/form-data">
|
||||
<label for="user">User to follow</label>
|
||||
<input name="user" id="user" type="text" size="32" placeholder="@user@example.com" /><br>
|
||||
<label for="password">Password</label><br>
|
||||
<input name="password" id="password" type="password" size="32"><br>
|
||||
<input type="submit" value="Post Message">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
die();
|
||||
}
|
||||
|
||||
// This receives a request to follow an external user
|
||||
// It looks up the external user's details
|
||||
// Then it sends a follow request
|
||||
// If the request is accepted, it saves the details in `data/following/` as a JSON file
|
||||
function follow_user() {
|
||||
global $password, $server, $username, $key_private;
|
||||
|
||||
// Does the posted password match the stored password?
|
||||
if( $password != $_POST["password"] ) { echo "Wrong Password!"; die(); }
|
||||
|
||||
// Get the posted content
|
||||
$user = $_POST["user"];
|
||||
|
||||
// Split the user into username and server
|
||||
list( , $follow_name, $follow_server ) = explode( "@", $user );
|
||||
|
||||
// Get the Webfinger
|
||||
$webfingerURl = "https://{$follow_server}/.well-known/webfinger?resource=acct:{$follow_name}@{$follow_server}";
|
||||
$webfingerJSON = file_get_contents( $webfingerURl );
|
||||
$webfinger = json_decode( $webfingerJSON, true );
|
||||
|
||||
foreach( $webfinger["links"] as $link ) {
|
||||
if ( "self" == $link["rel"] ) {
|
||||
$profileURl = $link["href"];
|
||||
}
|
||||
}
|
||||
if ( !isset( $profileURl ) ) { echo "No profile"; die(); }
|
||||
|
||||
// Get the user's details
|
||||
// This request does not need to be signed. But it does need to specify that it wants a JSON response
|
||||
$context = stream_context_create(
|
||||
[ "http" => [ "header" => "Accept: application/activity+json" ] ]
|
||||
);
|
||||
$profileJSON = file_get_contents( $profileURl, false, $context );
|
||||
$profileData = json_decode( $profileJSON, true );
|
||||
|
||||
// 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 = [
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "https://{$server}/{$guid}",
|
||||
"type" => "Follow",
|
||||
"actor" => "https://{$server}/{$username}",
|
||||
"object" => $profileURl
|
||||
];
|
||||
|
||||
// Sign a request 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 ) ) {
|
||||
file_put_contents( "error.txt", curl_error( $ch ) );
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
// Save the user's details
|
||||
// Save headers and request data to the timestamped file in the logs directory
|
||||
if( ! is_dir( "data/following" ) ) { mkdir( "data"); mkdir( "data/following"); }
|
||||
$following_filename = urlencode( $profileURl );
|
||||
file_put_contents( "data/following/{$following_filename}.json", $profileJSON );
|
||||
|
||||
// Render the JSON so the user can see the POST has worked
|
||||
header( "Location: https://{$server}/data/following/" . urlencode( $following_filename ) . ".json" );
|
||||
die();
|
||||
}
|
||||
|
||||
// Verify the signature sent with the message.
|
||||
// This is optional
|
||||
// It is very confusing
|
||||
|
|
Ładowanie…
Reference in New Issue