Inbox Reinvented

This commit is contained in:
Diogo Cordeiro 2018-08-04 04:11:17 +01:00
parent de432cda88
commit 43ebdfa727
29 changed files with 700 additions and 765 deletions

View File

@ -38,6 +38,7 @@ require_once __DIR__ . DIRECTORY_SEPARATOR . 'utils' . DIRECTORY_SEPARATOR . 'd
require_once __DIR__ . DIRECTORY_SEPARATOR . 'utils' . DIRECTORY_SEPARATOR . 'AcceptHeader.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'utils' . DIRECTORY_SEPARATOR . 'explorer.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'utils' . DIRECTORY_SEPARATOR . 'postman.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'utils' . DIRECTORY_SEPARATOR . 'inbox_handler.php';
// So that this isn't hardcoded everywhere
define('ACTIVITYPUB_BASE_ACTOR_URI', common_root_url().'index.php/user/');
@ -122,40 +123,9 @@ class ActivityPubPlugin extends Plugin
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
$response = $client->get($url, $headers);
$res = json_decode($response->getBody(), true);
$settings = [];
try {
Activitypub_notice::validate_remote_notice($res);
} catch (Exception $e) {
common_debug('ActivityPubPlugin Notice Grabber: Invalid potential remote notice while processing id: '.$url. '. He returned the following: '.json_encode($res, JSON_UNESCAPED_SLASHES));
throw $e;
}
if (isset($res->inReplyTo)) {
$settings['inReplyTo'] = $res->inReplyTo;
}
if (isset($res->latitude)) {
$settings['latitude'] = $res->latitude;
}
if (isset($res->longitude)) {
$settings['longitude'] = $res->longitude;
}
try {
return Activitypub_notice::create_notice(
ActivityPub_explorer::get_profile_from_url($res['attributedTo']),
$res['id'],
$res['url'],
$res['content'],
$res['cc'],
$settings
);
} catch (Exception $e) {
common_debug('ActivityPubPlugin Notice Grabber: failed to find: '.$url.' online.');
throw $e;
}
// When all the above failed in its quest of grabbing the Notice
throw new Exception('Notice not found.');
$object = json_decode($response->getBody(), true);
Activitypub_notice::validate_note($object);
return Activitypub_notice::create_notice($object);
}
/**
@ -210,13 +180,13 @@ class ActivityPubPlugin extends Plugin
$m->connect(
'user/:id/inbox.json',
['action' => 'apActorInbox'],
['action' => 'apInbox'],
['id' => '[0-9]+']
);
$m->connect(
'inbox.json',
['action' => 'apSharedInbox']
['action' => 'apInbox']
);
}
@ -941,7 +911,7 @@ class ActivityPubReturn
* @param int32 $code Status Code
* @return void
*/
public static function error($m, $code = 500)
public static function error($m, $code = 400)
{
http_response_code($code);
header('Content-Type: application/activity+json');

View File

@ -1,130 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
/**
* Actor's Inbox
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class apActorInboxAction extends ManagedAction
{
protected $needLogin = false;
protected $canPost = true;
/**
* Handle the Actor Inbox request
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @return void
*/
protected function handle()
{
try {
$profile = Profile::getByID($this->trimmed('id'));
} catch (Exception $e) {
ActivityPubReturn::error('Invalid Actor URI.', 404);
}
if (!$profile->isLocal()) {
ActivityPubReturn::error('This is not a local user.');
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
ActivityPubReturn::error('C2S not implemented just yet.');
}
common_debug('ActivityPub Inbox: Received a POST request.');
$data = file_get_contents('php://input');
common_debug('ActivityPub Inbox: Request contents: '.$data);
$data = json_decode(file_get_contents('php://input'), true);
// Validate data
if (!(isset($data['type']))) {
ActivityPubReturn::error('Type was not specified.');
}
if (!isset($data['actor'])) {
ActivityPubReturn::error('Actor was not specified.');
}
if (!isset($data['object'])) {
ActivityPubReturn::error('Object was not specified.');
}
// Get valid Actor object
try {
$actor_profile = ActivityPub_explorer::get_profile_from_url($data['actor']);
} catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 404);
}
$cc = [$profile];
// Process request
define('INBOX_HANDLERS', __DIR__ . DIRECTORY_SEPARATOR . 'inbox' . DIRECTORY_SEPARATOR);
switch ($data['type']) {
// Data available:
// Profile $actor_profile Actor performing the action
// string|object $data->object Object to be handled
// Array|String $cc Destinataries
// Profile $profile Local user to whom this action is directed
case 'Create':
$cc = array_merge ([$profile], $data['object']['cc']);
require_once INBOX_HANDLERS . 'Create.php';
break;
case 'Follow':
require_once INBOX_HANDLERS . 'Follow.php';
break;
case 'Like':
require_once INBOX_HANDLERS . 'Like.php';
break;
case 'Announce':
require_once INBOX_HANDLERS . 'Announce.php';
break;
case 'Undo':
require_once INBOX_HANDLERS . 'Undo.php';
break;
case 'Delete':
require_once INBOX_HANDLERS . 'Delete.php';
break;
case 'Accept':
require_once INBOX_HANDLERS . 'Accept.php';
break;
case 'Reject':
require_once INBOX_HANDLERS . 'Reject.php';
break;
default:
ActivityPubReturn::error('Invalid type value.');
}
}
}

View File

@ -29,30 +29,42 @@ if (!defined('GNUSOCIAL')) {
exit(1);
}
// Validate data
if (!isset($data['object']['type'])) {
ActivityPubReturn::error("Type was not specified.");
}
/**
* Inbox Request Handler
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class apInboxAction extends ManagedAction
{
protected $needLogin = false;
protected $canPost = true;
switch ($data['object']['type']) {
case "Follow":
// Validate data
if (!isset($data['object']['object'])) {
ActivityPubReturn::error("Object Actor URL was not specified.");
/**
* Handle the Inbox request
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @return void
*/
protected function handle()
{
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
ActivityPubReturn::error('Only POST requests allowed.');
}
// Get valid Object profile
common_debug('ActivityPub Shared Inbox: Received a POST request.');
$data = file_get_contents('php://input');
common_debug('ActivityPub Shared Inbox: Request contents: '.$data);
$data = json_decode(file_get_contents('php://input'), true);
try {
$object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($data['object']['object'])[0];
new Activitypub_inbox_handler($data);
ActivityPubReturn::answer();
} catch (Exception $e) {
ActivityPubReturn::error("Invalid Object Actor URL.", 404);
ActivityPubReturn::error($e->getMessage());
}
$pending_list = new Activitypub_pending_follow_requests($actor_profile->getID(), $object_profile->getID());
$pending_list->remove();
ActivityPubReturn::answer(); // You are now being followed by this person.
break;
default:
ActivityPubReturn::error("Invalid object type.");
break;
}
}

View File

@ -1,121 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
/**
* Shared Inbox Handler
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class apSharedInboxAction extends ManagedAction
{
protected $needLogin = false;
protected $canPost = true;
/**
* Handle the Shared Inbox request
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @return void
*/
protected function handle()
{
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
ActivityPubReturn::error('Only POST requests allowed.');
}
common_debug('ActivityPub Shared Inbox: Received a POST request.');
$data = file_get_contents('php://input');
common_debug('ActivityPub Shared Inbox: Request contents: '.$data);
$data = json_decode(file_get_contents('php://input'), true);
// Validate data
if (!isset($data['type'])) {
ActivityPubReturn::error('Type was not specified.');
}
if (!isset($data['actor'])) {
ActivityPubReturn::error('Actor was not specified.');
}
if (!isset($data['object'])) {
ActivityPubReturn::error('Object was not specified.');
}
// Get valid Actor object
try {
$actor_profile = ActivityPub_explorer::get_profile_from_url($data['actor']);
} catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 404);
}
$cc = [];
// Process request
define('INBOX_HANDLERS', __DIR__ . DIRECTORY_SEPARATOR . 'inbox' . DIRECTORY_SEPARATOR);
switch ($data['type']) {
// Data available:
// Profile $actor_profile Actor performing the action
// string|object $data->object Object to be handled
// Array|String $cc Destinataries
// string|object $data->object
case 'Create':
$cc = $data['object']['cc'];
$res = $data['object'];
require_once INBOX_HANDLERS . 'Create.php';
break;
case 'Follow':
require_once INBOX_HANDLERS . 'Follow.php';
break;
case 'Like':
require_once INBOX_HANDLERS . 'Like.php';
break;
case 'Announce':
require_once INBOX_HANDLERS . 'Announce.php';
break;
case 'Undo':
require_once INBOX_HANDLERS . 'Undo.php';
break;
case 'Delete':
require_once INBOX_HANDLERS . 'Delete.php';
break;
case 'Accept':
require_once INBOX_HANDLERS . 'Accept.php';
break;
case 'Reject':
require_once INBOX_HANDLERS . 'Reject.php';
break;
default:
ActivityPubReturn::error('Invalid type value.');
}
}
}

View File

@ -1,42 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
try {
try {
$object_notice = ActivityPubPlugin::grab_notice_from_url($data['object']);
} catch (Exception $e) {
ActivityPubReturn::error('Invalid Object specified.');
}
$object_notice->repeat($actor_profile, 'ActivityPub');
ActivityPubReturn::answer();
} catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 403);
}

View File

@ -1,73 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
$valid_object_types = ['Note'];
try {
Activitypub_notice::validate_remote_notice($res);
} catch (Exception $e) {
common_debug('ActivityPub Inbox Create Note: Invalid note: '.$e->getMessage());
ActivityPubReturn::error($e->getMessage());
}
$settings = [];
if (isset($res['inReplyTo'])) {
$settings['inReplyTo'] = $res['inReplyTo'];
}
if (isset($res['latitude'])) {
$settings['latitude'] = $res['latitude'];
}
if (isset($res['longitude'])) {
$settings['longitude'] = $res['longitude'];
}
if (isset($res['attachment'])) {
$settings['attachment'] = $res['attachment'];
}
try {
Activitypub_notice::create_notice(
$actor_profile,
$res['id'],
$res['url'],
$res['content'],
$cc,
$settings
);
ActivityPubReturn::answer();
} catch (AlreadyFulfilledException $e) {
// Notice URI already exists
common_debug('ActivityPub Inbox Create Note: Note already exists: '.$e->getMessage());
ActivityPubReturn::error('Note already exists.', 202);
} catch (Exception $e) {
common_debug('ActivityPub Inbox Create Note: Failed Create Note: '.$e->getMessage());
ActivityPubReturn::error($e->getMessage());
}

View File

@ -1,38 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
try {
$notice = ActivityPubPlugin::grab_notice_from_url($data['object']);
$notice->deleteAs($actor_profile);
ActivityPubReturn::answer();
} catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 403);
}

View File

@ -1,65 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
// Validate Object
if (!filter_var($data['object'], FILTER_VALIDATE_URL)) {
ActivityPubReturn::error('Invalid Object Actor URL.');
}
// Ensure valid Object profile
try {
if (!isset($profile)) {
$object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($data['object'])[0];
} else {
$object_profile = $profile;
unset($profile);
}
} catch (Exception $e) {
ActivityPubReturn::error('Invalid Object Actor URL.', 404);
}
// Get Actor's Aprofile
$actor_aprofile = Activitypub_profile::from_profile($actor_profile);
if (!Subscription::exists($actor_profile, $object_profile)) {
Subscription::start($actor_profile, $object_profile);
common_debug('ActivityPubPlugin: Accepted Follow request from '.$data['actor'].' to '.$data['object']);
// Notify remote instance that we have accepted their request
common_debug('ActivityPubPlugin: Notifying remote instance that we have accepted their Follow request request from '.$data['actor'].' to '.$data['object']);
$postman = new Activitypub_postman($actor_profile, [$actor_aprofile]);
$postman->follow();
ActivityPubReturn::answer();
} else {
common_debug('ActivityPubPlugin: Received a repeated Follow request from '.$data['actor'].' to '.$data['object']);
ActivityPubReturn::error('Already following.', 202);
}

View File

@ -1,42 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
try {
try {
$object_notice = ActivityPubPlugin::grab_notice_from_url($data['object']);
} catch (Exception $e) {
ActivityPubReturn::error('Invalid Object specified.');
}
Fave::addNew($actor_profile, $object_notice);
ActivityPubReturn::answer();
} catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 403);
}

View File

@ -1,32 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
// This is a dummy file as there is nothing to do if we fall in this case

View File

@ -1,80 +0,0 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
// Validate data
if (!isset($data['type'])) {
ActivityPubReturn::error('Type was not specified.');
}
switch ($data['object']['type']) {
case 'Like':
try {
// Validate data
if (!isset($data['object']['object'])) {
ActivityPubReturn::error('Notice URI was not specified.');
}
Fave::removeEntry($actor_profile, ActivityPubPlugin::grab_notice_from_url($data['object']['object']));
// Notice disfavorited successfully.
ActivityPubReturn::answer();
} catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 403);
}
break;
case 'Follow':
// Validate data
if (!isset($data['object']['object'])) {
ActivityPubReturn::error('Object Actor URL was not specified.');
}
// Get valid Object profile
try {
$object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($data['object']['object'])[0];
} catch (Exception $e) {
ActivityPubReturn::error('Invalid Object Actor URL.', 404);
}
if (Subscription::exists($actor_profile, $object_profile)) {
Subscription::cancel($actor_profile, $object_profile);
// You are no longer following this person.
ActivityPubReturn::answer();
} else {
// 409: You are not following this person already.
ActivityPubReturn::answer();
}
break;
case 'Announce':
// This is a dummy entry point as GNU Social doesn't allow Undo Announce
ActivityPubReturn::answer();
// no break
default:
ActivityPubReturn::error('Invalid object type.');
break;
}

View File

@ -61,4 +61,32 @@ class Activitypub_accept extends Managed_DataObject
];
return $res;
}
/**
* Verifies if a given object is acceptable for an Accept Activity.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Array $object
* @throws Exception
*/
public static function validate_object($object)
{
if (!is_array($object)) {
throw new Exception('Invalid Object Format for Accept Activity.');
}
if (!isset($object['type'])) {
throw new Exception('Object type was not specified for Accept Activity.');
}
switch ($object['type']) {
case 'Follow':
// Validate data
if (!filter_var($object['object'], FILTER_VALIDATE_URL)) {
throw new Exception("Object is not a valid Object URI for Activity.");
}
break;
default:
throw new Exception('This is not a supported Object Type for Accept Activity.');
}
return true;
}
}

View File

@ -49,11 +49,15 @@ class Activitypub_announce extends Managed_DataObject
*/
public static function announce_to_array($actor, $object)
{
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
"type" => "Announce",
"actor" => $actor,
"object" => $object
);
$res = [
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
"type" => "Announce",
"actor" => $actor,
"object" => $object
];
return $res;
}
}

View File

@ -50,18 +50,22 @@ class Activitypub_attachment extends Managed_DataObject
public static function attachment_to_array($attachment)
{
$res = [
'type' => 'Document',
'mediaType' => $attachment->mimetype,
'url' => $attachment->getUrl(),
'size' => intval($attachment->size), // $attachment->getSize ()
'name' => $attachment->getTitle(),
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'type' => 'Document',
'mediaType' => $attachment->mimetype,
'url' => $attachment->getUrl(),
'size' => intval($attachment->size), // $attachment->getSize ()
'name' => $attachment->getTitle(),
];
// Image
if (substr($res["mediaType"], 0, 5) == "image") {
$res["meta"]= [
'width' => $attachment->width,
'height' => $attachment->height
'width' => $attachment->width,
'height' => $attachment->height
];
}

View File

@ -51,7 +51,10 @@ class Activitypub_create extends Managed_DataObject
public static function create_to_array($id, $actor, $object)
{
$res = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'id' => $id.'/create',
'type' => 'Create',
'to' => $object['to'],
@ -61,4 +64,29 @@ class Activitypub_create extends Managed_DataObject
];
return $res;
}
/**
* Verifies if a given object is acceptable for a Create Activity.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Array $object
* @throws Exception
*/
public static function validate_object($object)
{
if (!is_array($object)) {
throw new Exception('Invalid Object Format for Create Activity.');
}
if (!isset($object['type'])) {
throw new Exception('Object type was not specified for Create Activity.');
}
switch ($object['type']) {
case 'Note':
// Validate data
Activitypub_notice::validate_note($object);
break;
default:
throw new Exception('This is not a supported Object Type for Create Activity.');
}
}
}

View File

@ -50,7 +50,10 @@ class Activitypub_delete extends Managed_DataObject
public static function delete_to_array($actor, $object)
{
$res = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'id' => $object.'/delete',
'type' => 'Delete',
'actor' => $actor,

View File

@ -50,8 +50,8 @@ class Activitypub_error extends Managed_DataObject
public static function error_message_to_array($m)
{
$res = [
'error'=> $m
];
'error'=> $m
];
return $res;
}
}

View File

@ -62,4 +62,34 @@ class Activitypub_follow extends Managed_DataObject
];
return $res;
}
/**
* Handles a Follow Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Profile $actor_profile Remote Actor
* @param string $object Local Actor
* @throws Exception
*/
public static function follow($actor_profile, $object)
{
// Get Actor's Aprofile
$actor_aprofile = Activitypub_profile::from_profile($actor_profile);
// Get Object profile
$object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($object)[0];
if (!Subscription::exists($actor_profile, $object_profile)) {
Subscription::start($actor_profile, $object_profile);
common_debug('ActivityPubPlugin: Accepted Follow request from '.ActivityPubPlugin::actor_uri($actor_profile).' to '.$object);
} else {
common_debug('ActivityPubPlugin: Received a repeated Follow request from '.ActivityPubPlugin::actor_uri($actor_profile).' to '.$object);
}
// Notify remote instance that we have accepted their request
common_debug('ActivityPubPlugin: Notifying remote instance that we have accepted their Follow request request from '.ActivityPubPlugin::actor_uri($actor_profile).' to '.$object);
$postman = new Activitypub_postman($actor_profile, [$actor_aprofile]);
$postman->accept_follow();
}
}

View File

@ -51,6 +51,10 @@ class Activitypub_mention_tag extends Managed_DataObject
public static function mention_tag_to_array_from_values($href, $name)
{
$res = [
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
"type" => "Mention",
"href" => $href,
"name" => $name

View File

@ -72,12 +72,15 @@ class Activitypub_notice extends Managed_DataObject
$to[]= 'https://www.w3.org/ns/activitystreams#Public';
$item = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'id' => $notice->getUrl(),
'type' => 'Note',
'published' => str_replace(' ', 'T', $notice->getCreated()).'Z',
'url' => $notice->getUrl(),
'atributtedTo' => ActivityPubPlugin::actor_uri($profile),
'attributedTo' => ActivityPubPlugin::actor_uri($profile),
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'cc' => $cc,
'atomUri' => $notice->getUrl(),
@ -107,21 +110,42 @@ class Activitypub_notice extends Managed_DataObject
}
/**
* Create a Notice via ActivityPub data.
* Create a Notice via ActivityPub Note Object.
* Returns created Notice.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Profile $actor_profile
* @param int32 $id
* @param string $url
* @param string $content
* @param array|string $cc
* @param array $settings possible keys: ['inReplyTo', 'latitude', 'longitude', 'attachment']
* @param Array $object
* @param Profile|null $actor_profile
* @return Notice
* @throws Exception
*/
public static function create_notice($actor_profile, $id, $url, $content, $cc, $settings)
public static function create_notice($object, $actor_profile = null)
{
$id = $object['id']; // int32
$url = $object['url']; // string
$content = $object['content']; // string
$cc = $object['cc']; // array|string
// possible keys: ['inReplyTo', 'latitude', 'longitude', 'attachment']
$settings = [];
if (isset($object['inReplyTo'])) {
$settings['inReplyTo'] = $object['inReplyTo'];
}
if (isset($object['latitude'])) {
$settings['latitude'] = $object['latitude'];
}
if (isset($object['longitude'])) {
$settings['longitude'] = $object['longitude'];
}
if (isset($object['attachment'])) {
$settings['attachment'] = $object['attachment'];
}
// Ensure Actor Profile
if (is_null($actor_profile)) {
$actor_profile = ActivityPub_explorer::get_profile_from_url($object['actor']);
}
$act = new Activity();
$act->verb = ActivityVerb::POST;
$act->time = time();
@ -150,6 +174,7 @@ class Activitypub_notice extends Managed_DataObject
$imagefile = new ImageFile(null, $temp_filename);
$filename = hash(File::FILEHASH_ALG, $imgData).image_type_to_extension($imagefile->type);
unset($imgData); // No need to carry this in memory.
rename($temp_filename, File::path($filename));
common_debug('ActivityPub Create Notice: Moved image from: '.$temp_filename.' to '.$filename);
@ -158,11 +183,9 @@ class Activitypub_notice extends Managed_DataObject
} catch (Exception $e) {
common_debug('ActivityPub Create Notice: Something went wrong while processing the image from: '.$attach_url.' details: '.$e->getMessage());
unlink($temp_filename);
$content .= ($content==='' ? '' : ' ') . $attach_url;
}
} else {
$content .= ($content==='' ? '' : ' ') . $attach_url;
}
$content .= ($content==='' ? '' : ' ') . '<br><a href="'.$attach_url.'">Remote Attachment Source</a>';
}
// Is this a reply?
@ -181,12 +204,13 @@ class Activitypub_notice extends Managed_DataObject
$discovery = new Activitypub_explorer;
// Generate Cc objects
$cc_profiles = [];
if (is_array($cc)) {
// Remove duplicates from Cc actors set
array_unique($cc);
foreach ($cc as $cc_url) {
try {
$cc = array_merge($cc, $discovery->lookup($cc_url));
$cc_profiles = array_merge($cc_profiles, $discovery->lookup($cc_url));
} catch (Exception $e) {
// Invalid actor found, just let it go. // TODO: Fallback to OStatus
}
@ -195,7 +219,7 @@ class Activitypub_notice extends Managed_DataObject
// No need to do anything else at this point, let's just break out the if
} else {
try {
$cc = array_merge($cc, $discovery->lookup($cc));
$cc_profiles = $discovery->lookup($cc);
} catch (Exception $e) {
// Invalid actor found, just let it go. // TODO: Fallback to OStatus
}
@ -203,8 +227,8 @@ class Activitypub_notice extends Managed_DataObject
unset($discovery);
foreach ($cc as $tp) {
$act->context->attention[ActivityPubPlugin::actor_uri($tp)] = 'http://activitystrea.ms/schema/1.0/person';
foreach ($cc_profiles as $cp) {
$act->context->attention[ActivityPubPlugin::actor_uri($cp)] = 'http://activitystrea.ms/schema/1.0/person';
}
// Add location if that is set
@ -224,56 +248,51 @@ class Activitypub_notice extends Managed_DataObject
// Finally add the activity object to our activity
$act->objects[] = $actobj;
try {
$note = Notice::saveActivity($act, $actor_profile, $options);
if (ActivityPubPlugin::$store_images_from_remote_notes_attachments && isset($mediaFile)) {
$mediaFile->attachToNotice($note);
}
return $note;
} catch (Exception $e) {
throw $e;
$note = Notice::saveActivity($act, $actor_profile, $options);
if (ActivityPubPlugin::$store_images_from_remote_notes_attachments && isset($mediaFile)) {
$mediaFile->attachToNotice($note);
}
return $note;
}
/**
* Validates a remote notice.
* Validates a note.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Array $data
* @return boolean true in case of success
* @param Array $object
* @throws Exception
*/
public static function validate_remote_notice($data)
public static function validate_note($object)
{
/*if (!isset($data['attributedTo'])) {
if (!isset($object['attributedTo'])) {
common_debug('ActivityPub Notice Validator: Rejected because attributedTo was not specified.');
throw new Exception('No attributedTo specified.');
}
if (!isset($data['id'])) {
if (!isset($object['id'])) {
common_debug('ActivityPub Notice Validator: Rejected because Object ID was not specified.');
throw new Exception('Object ID not specified.');
} elseif (!filter_var($data['id'], FILTER_VALIDATE_URL)) {
} elseif (!filter_var($object['id'], FILTER_VALIDATE_URL)) {
common_debug('ActivityPub Notice Validator: Rejected because Object ID is invalid.');
throw new Exception('Invalid Object ID.');
}
if (!isset($data['type']) || $data['type'] !== 'Note') {
if (!isset($object['type']) || $object['type'] !== 'Note') {
common_debug('ActivityPub Notice Validator: Rejected because of Type.');
throw new Exception('Invalid Object type.');
}
if (!isset($data['content'])) {
if (!isset($object['content'])) {
common_debug('ActivityPub Notice Validator: Rejected because Content was not specified.');
throw new Exception('Object content was not specified.');
}
if (!isset($data['url'])) {
if (!isset($object['url'])) {
throw new Exception('Object URL was not specified.');
} elseif (!filter_var($data['url'], FILTER_VALIDATE_URL)) {
} elseif (!filter_var($object['url'], FILTER_VALIDATE_URL)) {
common_debug('ActivityPub Notice Validator: Rejected because Object URL is invalid.');
throw new Exception('Invalid Object URL.');
}
if (!isset($data['cc'])) {
if (!isset($object['cc'])) {
common_debug('ActivityPub Notice Validator: Rejected because Object CC was not specified.');
throw new Exception('Object CC was not specified.');
}*/
}
return true;
}
}

View File

@ -105,7 +105,7 @@ class Activitypub_profile extends Managed_DataObject
'following' => common_local_url("apActorFollowing", array("id" => $id)),
'followers' => common_local_url("apActorFollowers", array("id" => $id)),
'liked' => common_local_url("apActorLiked", array("id" => $id)),
'inbox' => common_local_url("apActorInbox", array("id" => $id)),
'inbox' => common_local_url("apInbox", array("id" => $id)),
'preferredUsername' => $profile->getNickname(),
'name' => $profile->getBestName(),
'summary' => ($desc = $profile->getDescription()) == null ? "" : $desc,
@ -128,7 +128,7 @@ class Activitypub_profile extends Managed_DataObject
];
if ($profile->isLocal()) {
$res['endpoints']['sharedInbox'] = common_local_url('apSharedInbox');
$res['endpoints']['sharedInbox'] = common_local_url('apInbox');
} else {
$aprofile = new Activitypub_profile();
$aprofile = $aprofile->from_profile($profile);

View File

@ -49,10 +49,14 @@ class Activitypub_reject extends Managed_DataObject
*/
public static function reject_to_array($object)
{
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
"type" => "Reject",
"object" => $object
);
$res = [
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
"type" => "Reject",
"object" => $object
];
return $res;
}
}

View File

@ -50,16 +50,13 @@ class Activitypub_tag extends Managed_DataObject
public static function tag_to_array($tag)
{
$res = [
'@context' => [
"https://www.w3.org/ns/activitystreams",
[
"@language" => "en"
]
],
'name' => $tag,
'url' => common_local_url('tag', array('tag' => $tag))
];
'@context' => [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
'name' => $tag,
'url' => common_local_url('tag', ['tag' => $tag])
];
return $res;
}
}

View File

@ -61,4 +61,33 @@ class Activitypub_undo extends Managed_DataObject
];
return $res;
}
/**
* Verifies if a given object is acceptable for a Undo Activity.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Array $object
* @throws Exception
*/
public static function validate_object($object)
{
if (!is_array($object)) {
throw new Exception('Invalid Object Format for Undo Activity.');
}
if (!isset($object['type'])) {
throw new Exception('Object type was not specified for Undo Activity.');
}
switch ($object['type']) {
case 'Follow':
case 'Like':
// Validate data
if (!filter_var($object['object'], FILTER_VALIDATE_URL)) {
throw new Exception('Object is not a valid Object URI for Activity.');
}
break;
default:
throw new Exception('This is not a supported Object Type for Undo Activity.');
}
return true;
}
}

View File

@ -34,7 +34,7 @@ class HTTPSignatureTest extends TestCase
$this->assertTrue(class_exists('\HttpSignatures\Context'));
$this->assertTrue(class_exists('\HttpSignatures\GuzzleHttpSignatures'));
}
public function setUp()
{
$this->context = new Context([

View File

@ -0,0 +1,102 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
/**
* Utility class to hold a bunch of constant defining default verb types
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class Activitypub_activityverb2 extends Managed_DataObject
{
const FULL_LIST =
[
'Accept' => 'https://www.w3.org/ns/activitystreams#Accept',
'TentativeAccept' => 'https://www.w3.org/ns/activitystreams#TentativeAccept',
'Add' => 'https://www.w3.org/ns/activitystreams#Add',
'Arrive' => 'https://www.w3.org/ns/activitystreams#Arrive',
'Create' => 'https://www.w3.org/ns/activitystreams#Create',
'Delete' => 'https://www.w3.org/ns/activitystreams#Delete',
'Follow' => 'https://www.w3.org/ns/activitystreams#Follow',
'Ignore' => 'https://www.w3.org/ns/activitystreams#Ignore',
'Join' => 'https://www.w3.org/ns/activitystreams#Join',
'Leave' => 'https://www.w3.org/ns/activitystreams#Leave',
'Like' => 'https://www.w3.org/ns/activitystreams#Like',
'Offer' => 'https://www.w3.org/ns/activitystreams#Offer',
'Invite' => 'https://www.w3.org/ns/activitystreams#Invite',
'Reject' => 'https://www.w3.org/ns/activitystreams#Reject',
'TentativeReject' => 'https://www.w3.org/ns/activitystreams#TentativeReject',
'Remove' => 'https://www.w3.org/ns/activitystreams#Remove',
'Undo' => 'https://www.w3.org/ns/activitystreams#Undo',
'Update' => 'https://www.w3.org/ns/activitystreams#Update',
'View' => 'https://www.w3.org/ns/activitystreams#View',
'Listen' => 'https://www.w3.org/ns/activitystreams#Listen',
'Read' => 'https://www.w3.org/ns/activitystreams#Read',
'Move' => 'https://www.w3.org/ns/activitystreams#Move',
'Travel' => 'https://www.w3.org/ns/activitystreams#Travel',
'Announce' => 'https://www.w3.org/ns/activitystreams#Announce',
'Block' => 'https://www.w3.org/ns/activitystreams#Block',
'Flag' => 'https://www.w3.org/ns/activitystreams#Flag',
'Dislike' => 'https://www.w3.org/ns/activitystreams#Dislike',
'Question' => 'https://www.w3.org/ns/activitystreams#Question'
];
const KNOWN =
[
'Accept',
'Create',
'Delete',
'Follow',
'Like',
'Undo',
'Announce'
];
/**
* Converts canonical into verb.
*
* @author GNU Social
* @param string $verb
* @return string
*/
public static function canonical($verb)
{
$ns = 'https://www.w3.org/ns/activitystreams#';
if (substr($verb, 0, mb_strlen($ns)) == $ns) {
return substr($verb, mb_strlen($ns));
} else {
return $verb;
}
}
}

View File

@ -32,9 +32,9 @@ class DiscoveryHints
{
public static function fromXRD(XML_XRD $xrd)
{
$hints = array();
$hints = [];
if (Event::handle('StartDiscoveryHintsFromXRD', array($xrd, &$hints))) {
if (Event::handle('StartDiscoveryHintsFromXRD', [$xrd, &$hints])) {
foreach ($xrd->links as $link) {
switch ($link->rel) {
case WebFingerResource_Profile::PROFILEPAGE:
@ -55,7 +55,7 @@ class DiscoveryHints
break;
}
}
Event::handle('EndDiscoveryHintsFromXRD', array($xrd, &$hints));
Event::handle('EndDiscoveryHintsFromXRD', [$xrd, &$hints]);
}
return $hints;
@ -88,10 +88,10 @@ class DiscoveryHints
$hcard = self::_hcard($body, $url);
if (empty($hcard)) {
return array();
return [];
}
$hints = array();
$hints = [];
// XXX: don't copy stuff into an array and then copy it again
@ -131,7 +131,7 @@ class DiscoveryHints
return null;
}
$hcards = array();
$hcards = [];
foreach ($mf2['items'] as $item) {
if (!in_array('h-card', $item['type'])) {

296
utils/inbox_handler.php Normal file
View File

@ -0,0 +1,296 @@
<?php
/**
* GNU social - a federating social network
*
* ActivityPubPlugin implementation for GNU Social
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @author Daniel Supernault <danielsupernault@gmail.com>
* @copyright 2018 Free Software Foundation http://fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link https://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
/**
* ActivityPub Inbox Handler
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class Activitypub_inbox_handler
{
private $activity;
private $actor;
private $object;
/**
* Create a Inbox Handler to receive something from someone.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Array $activity Activity we are receiving
*/
public function __construct($activity)
{
$this->activity = $activity;
$this->object = $activity['object'];
// Validate Activity
$this->validate_activity();
// Get Actor's Profile
$this->actor = ActivityPub_explorer::get_profile_from_url($this->activity['actor']);
// Handle the Activity
$this->process();
}
/**
* Validates if a given Activity is valid. Throws exception if not.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @throws Exception
*/
private function validate_activity()
{
// Activity validation
// Validate data
if (!(isset($this->activity['type']))) {
throw new Exception('Activity Validation Failed: Type was not specified.');
}
if (!isset($this->activity['actor'])) {
throw new Exception('Activity Validation Failed: Actor was not specified.');
}
if (!isset($this->activity['object'])) {
throw new Exception('Activity Validation Failed: Object was not specified.');
}
// Object validation
switch ($this->activity['type']) {
case 'Accept':
Activitypub_accept::validate_object($this->object);
break;
case 'Create':
Activitypub_create::validate_object($this->object);
break;
case 'Delete':
case 'Follow':
case 'Like':
case 'Announce':
if (!filter_var($this->object, FILTER_VALIDATE_URL)) {
throw new Exception("Object is not a valid Object URI for Activity.");
}
break;
case 'Undo':
Activitypub_undo::validate_object($this->object);
break;
default:
throw new Exception('Unknown Activity Type.');
}
}
/**
* Sends the Activity to proper handler in order to be processed.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
*/
private function process()
{
switch ($this->activity['type']) {
case 'Accept':
$this->handle_accept($this->actor, $this->object);
break;
case 'Create':
$this->handle_create($this->actor, $this->object);
break;
case 'Delete':
$this->handle_delete($this->actor, $this->object);
break;
case 'Follow':
$this->handle_follow($this->actor, $this->object);
break;
case 'Like':
$this->handle_like($this->actor, $this->object);
break;
case 'Undo':
$this->handle_undo($this->actor, $this->object);
break;
case 'Announce':
$this->handle_announce($this->actor, $this->object);
break;
}
}
/**
* Handles an Accept Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_accept($actor, $object)
{
switch ($object['type']) {
case 'Follow':
$this->handle_accept_follow($actor, $object);
break;
}
}
/**
* Handles an Accept Follow Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_accept_follow($actor, $object)
{
// Get valid Object profile
$object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($object['object'])[0];
$pending_list = new Activitypub_pending_follow_requests($actor->getID(), $object_profile->getID());
$pending_list->remove();
}
/**
* Handles a Create Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_create($actor, $object)
{
switch ($object['type']) {
case 'Note':
Activitypub_notice::create_notice($object, $actor);
break;
}
}
/**
* Handles a Delete Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_delete($actor, $object)
{
$notice = ActivityPubPlugin::grab_notice_from_url($object['object']);
$notice->deleteAs($actor);
}
/**
* Handles a Follow Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_follow($actor, $object)
{
Activitypub_follow::follow($actor, $object);
}
/**
* Handles a Like Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_like($actor, $object)
{
$notice = ActivityPubPlugin::grab_notice_from_url($object);
Fave::addNew($actor, $notice);
}
/**
* Handles a Undo Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_undo($actor, $object)
{
switch ($object['type']) {
case 'Follow':
$this->handle_undo_follow($actor, $object['object']);
break;
case 'Like':
$this->handle_undo_like($actor, $object['object']);
break;
}
}
/**
* Handles a Undo Like Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_undo_like($actor, $object)
{
$notice = ActivityPubPlugin::grab_notice_from_url($object);
Fave::removeEntry($actor, $notice);
}
/**
* Handles a Undo Follow Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_undo_follow($actor, $object)
{
// Get Object profile
$object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($object)[0];
if (Subscription::exists($actor, $object_profile)) {
Subscription::cancel($actor, $object_profile);
// You are no longer following this person.
} else {
// 409: You are not following this person already.
}
}
/**
* Handles a Announce Activity received by our inbox.
*
* @author Diogo Cordeiro <diogo@fc.upt.pt>
* @param Profile $actor Actor
* @param Array $object Activity
*/
private function handle_announce($actor, $object)
{
$object_notice = ActivityPubPlugin::grab_notice_from_url($object);
$object_notice->repeat($actor, 'ActivityPub');
}
}

View File

@ -103,7 +103,7 @@ class Activitypub_postman
common_debug('ActivityPub Postman: Delivery result with status code '.$response->getStatusCode().': '.$response->getBody()->getContents());
return $response;
}
/**
* Send a follow notification to remote instance
*
@ -135,11 +135,39 @@ class Activitypub_postman
public function undo_follow()
{
$data = Activitypub_undo::undo_to_array(
Activitypub_follow::follow_to_array(
ActivityPubPlugin::actor_uri($this->actor),
$this->to[0]->getUrl()
)
);
Activitypub_follow::follow_to_array(
ActivityPubPlugin::actor_uri($this->actor),
$this->to[0]->getUrl()
)
);
$res = $this->send(json_encode($data, JSON_UNESCAPED_SLASHES), $this->to[0]->get_inbox());
$res_body = json_decode($res->getBody()->getContents());
if ($res->getStatusCode() == 200 || $res->getStatusCode() == 202 || $res->getStatusCode() == 409) {
$pending_list = new Activitypub_pending_follow_requests($this->actor->getID(), $this->to[0]->getID());
$pending_list->remove();
return true;
}
if (isset($res_body[0]->error)) {
throw new Exception($res_body[0]->error);
}
throw new Exception("An unknown error occurred.");
}
/**
* Send a Accept Follow notification to remote instance
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
*/
public function accept_follow()
{
$data = Activitypub_accept::accept_to_array(
Activitypub_follow::follow_to_array(
$this->to[0]->getUrl(),
ActivityPubPlugin::actor_uri($this->actor)
)
);
$res = $this->send(json_encode($data, JSON_UNESCAPED_SLASHES), $this->to[0]->get_inbox());
$res_body = json_decode($res->getBody()->getContents());
@ -278,7 +306,7 @@ class Activitypub_postman
foreach ($this->to as $to_profile) {
$i = $to_profile->get_inbox();
// Prevent delivering to self
if ($i == [common_local_url('apSharedInbox')]) {
if ($i == [common_local_url('apInbox')]) {
continue;
}
$to_inboxes[] = $i;