forked from GNUsocial/gnu-social
[TESTS] Added unit tests
This commit is contained in:
parent
d53fef09a8
commit
95f95d2dd8
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -42,27 +44,29 @@ class apActorFollowersAction extends ManagedAction
|
||||
/**
|
||||
* Handle the Followers Collection request
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
try {
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$profile_id = $profile->getID();
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||
}
|
||||
|
||||
if (!$profile->isLocal()) {
|
||||
ActivityPubReturn::error("This is not a local user.", 403);
|
||||
ActivityPubReturn::error('This is not a local user.', 403);
|
||||
}
|
||||
|
||||
if (!isset($_GET["page"])) {
|
||||
if (!isset($_GET['page'])) {
|
||||
$page = 0;
|
||||
} else {
|
||||
$page = intval($this->trimmed('page'));
|
||||
$page = (int) ($this->trimmed('page'));
|
||||
}
|
||||
|
||||
if ($page < 0) {
|
||||
@ -72,32 +76,32 @@ class apActorFollowersAction extends ManagedAction
|
||||
$since = ($page - 1) * PROFILES_PER_MINILIST;
|
||||
$limit = PROFILES_PER_MINILIST;
|
||||
|
||||
/* Calculate total items */
|
||||
// Calculate total items
|
||||
$total_subs = Activitypub_profile::subscriberCount($profile);
|
||||
$total_pages = ceil($total_subs / PROFILES_PER_MINILIST);
|
||||
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
'@context' => [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1',
|
||||
],
|
||||
'id' => common_local_url('apActorFollowers', ['id' => $profile_id]).(($page != 0) ? '?page='.$page : ''),
|
||||
'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'),
|
||||
'totalItems' => $total_subs
|
||||
'id' => common_local_url('apActorFollowers', ['id' => $profile_id]) . (($page != 0) ? '?page=' . $page : ''),
|
||||
'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'),
|
||||
'totalItems' => $total_subs,
|
||||
];
|
||||
|
||||
if ($page == 0) {
|
||||
$res['first'] = common_local_url('apActorFollowers', ['id' => $profile_id]).'?page=1';
|
||||
$res['first'] = common_local_url('apActorFollowers', ['id' => $profile_id]) . '?page=1';
|
||||
} else {
|
||||
$res['orderedItems'] = $this->generate_followers($profile, $since, $limit);
|
||||
$res['partOf'] = common_local_url('apActorFollowers', ['id' => $profile_id]);
|
||||
$res['partOf'] = common_local_url('apActorFollowers', ['id' => $profile_id]);
|
||||
|
||||
if ($page+1 < $total_pages) {
|
||||
$res['next'] = common_local_url('apActorFollowers', ['id' => $profile_id]).'page='.($page+1 == 1 ? 2 : $page+1);
|
||||
if ($page + 1 < $total_pages) {
|
||||
$res['next'] = common_local_url('apActorFollowers', ['id' => $profile_id]) . 'page=' . ($page + 1 == 1 ? 2 : $page + 1);
|
||||
}
|
||||
|
||||
if ($page > 1) {
|
||||
$res['prev'] = common_local_url('apActorFollowers', ['id' => $profile_id]).'?page='.($page-1 <= 0 ? 1 : $page-1);
|
||||
$res['prev'] = common_local_url('apActorFollowers', ['id' => $profile_id]) . '?page=' . ($page - 1 <= 0 ? 1 : $page - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,10 +112,13 @@ class apActorFollowersAction extends ManagedAction
|
||||
* Generates a list of stalkers for a given profile.
|
||||
*
|
||||
* @param Profile $profile
|
||||
* @param int $since
|
||||
* @param int $limit
|
||||
* @return array of URIs
|
||||
* @param int $since
|
||||
* @param int $limit
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return array of URIs
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function generate_followers($profile, $since, $limit)
|
||||
@ -120,7 +127,7 @@ class apActorFollowersAction extends ManagedAction
|
||||
try {
|
||||
$sub = Activitypub_profile::getSubscribers($profile, $since, $limit);
|
||||
|
||||
/* Get followers' URLs */
|
||||
// Get followers' URLs
|
||||
foreach ($sub as $s) {
|
||||
$subs[] = $s->getUri();
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -42,27 +44,29 @@ class apActorFollowingAction extends ManagedAction
|
||||
/**
|
||||
* Handle the Following Collection request
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
try {
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$profile_id = $profile->getID();
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||
}
|
||||
|
||||
if (!$profile->isLocal()) {
|
||||
ActivityPubReturn::error("This is not a local user.", 403);
|
||||
ActivityPubReturn::error('This is not a local user.', 403);
|
||||
}
|
||||
|
||||
if (!isset($_GET["page"])) {
|
||||
if (!isset($_GET['page'])) {
|
||||
$page = 0;
|
||||
} else {
|
||||
$page = intval($this->trimmed('page'));
|
||||
$page = (int) ($this->trimmed('page'));
|
||||
}
|
||||
|
||||
if ($page < 0) {
|
||||
@ -72,32 +76,32 @@ class apActorFollowingAction extends ManagedAction
|
||||
$since = ($page - 1) * PROFILES_PER_MINILIST;
|
||||
$limit = PROFILES_PER_MINILIST;
|
||||
|
||||
/* Calculate total items */
|
||||
// Calculate total items
|
||||
$total_subs = Activitypub_profile::subscriptionCount($profile);
|
||||
$total_pages = ceil($total_subs / PROFILES_PER_MINILIST);
|
||||
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
'@context' => [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1',
|
||||
],
|
||||
'id' => common_local_url('apActorFollowing', ['id' => $profile_id]).(($page != 0) ? '?page='.$page : ''),
|
||||
'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'),
|
||||
'totalItems' => $total_subs
|
||||
'id' => common_local_url('apActorFollowing', ['id' => $profile_id]) . (($page != 0) ? '?page=' . $page : ''),
|
||||
'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'),
|
||||
'totalItems' => $total_subs,
|
||||
];
|
||||
|
||||
if ($page == 0) {
|
||||
$res['first'] = common_local_url('apActorFollowing', ['id' => $profile_id]).'?page=1';
|
||||
$res['first'] = common_local_url('apActorFollowing', ['id' => $profile_id]) . '?page=1';
|
||||
} else {
|
||||
$res['orderedItems'] = $this->generate_following($profile, $since, $limit);
|
||||
$res['partOf'] = common_local_url('apActorFollowing', ['id' => $profile_id]);
|
||||
$res['partOf'] = common_local_url('apActorFollowing', ['id' => $profile_id]);
|
||||
|
||||
if ($page+1 < $total_pages) {
|
||||
$res['next'] = common_local_url('apActorFollowing', ['id' => $profile_id]).'page='.($page+1 == 1 ? 2 : $page+1);
|
||||
if ($page + 1 < $total_pages) {
|
||||
$res['next'] = common_local_url('apActorFollowing', ['id' => $profile_id]) . 'page=' . ($page + 1 == 1 ? 2 : $page + 1);
|
||||
}
|
||||
|
||||
if ($page > 1) {
|
||||
$res['prev'] = common_local_url('apActorFollowing', ['id' => $profile_id]).'?page='.($page-1 <= 0 ? 1 : $page-1);
|
||||
$res['prev'] = common_local_url('apActorFollowing', ['id' => $profile_id]) . '?page=' . ($page - 1 <= 0 ? 1 : $page - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,10 +112,13 @@ class apActorFollowingAction extends ManagedAction
|
||||
* Generates the list of those a given profile is stalking.
|
||||
*
|
||||
* @param Profile $profile
|
||||
* @param int $since
|
||||
* @param int $limit
|
||||
* @return array of URIs
|
||||
* @param int $since
|
||||
* @param int $limit
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return array of URIs
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function generate_following($profile, $since, $limit)
|
||||
@ -120,7 +127,7 @@ class apActorFollowingAction extends ManagedAction
|
||||
try {
|
||||
$sub = Activitypub_profile::getSubscribed($profile, $since, $limit);
|
||||
|
||||
/* Get followed' URLs */
|
||||
// Get followed' URLs
|
||||
foreach ($sub as $s) {
|
||||
$subs[] = $s->getUri();
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -42,27 +44,29 @@ class apActorLikedAction extends ManagedAction
|
||||
/**
|
||||
* Handle the Liked Collection request
|
||||
*
|
||||
* @return void
|
||||
* @throws EmptyPkeyValueException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
try {
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$profile_id = $profile->getID();
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||
}
|
||||
|
||||
if (!$profile->isLocal()) {
|
||||
ActivityPubReturn::error("This is not a local user.", 403);
|
||||
ActivityPubReturn::error('This is not a local user.', 403);
|
||||
}
|
||||
|
||||
$limit = intval($this->trimmed('limit'));
|
||||
$since_id = intval($this->trimmed('since_id'));
|
||||
$max_id = intval($this->trimmed('max_id'));
|
||||
$limit = (int) ($this->trimmed('limit'));
|
||||
$since_id = (int) ($this->trimmed('since_id'));
|
||||
$max_id = (int) ($this->trimmed('max_id'));
|
||||
|
||||
$limit = empty($limit) ? 40 : $limit; // Default is 40
|
||||
$since_id = empty($since_id) ? null : $since_id;
|
||||
@ -75,20 +79,20 @@ class apActorLikedAction extends ManagedAction
|
||||
|
||||
$fave = $this->fetch_faves($profile_id, $limit, $since_id, $max_id);
|
||||
|
||||
$faves = array();
|
||||
$faves = [];
|
||||
while ($fave->fetch()) {
|
||||
$faves[] = $this->pretty_fave(clone ($fave));
|
||||
$faves[] = $this->pretty_fave(clone $fave);
|
||||
}
|
||||
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
'@context' => [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1',
|
||||
],
|
||||
'id' => common_local_url('apActorLiked', ['id' => $profile_id]),
|
||||
'type' => 'OrderedCollection',
|
||||
'totalItems' => Fave::countByProfile($profile),
|
||||
'orderedItems' => $faves
|
||||
'orderedItems' => $faves,
|
||||
];
|
||||
|
||||
ActivityPubReturn::answer($res);
|
||||
@ -99,16 +103,19 @@ class apActorLikedAction extends ManagedAction
|
||||
* as a plugin answer
|
||||
*
|
||||
* @param Fave $fave_object
|
||||
* @return array pretty array representating a Fave
|
||||
*
|
||||
* @throws EmptyPkeyValueException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return array pretty array representating a Fave
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function pretty_fave($fave_object)
|
||||
{
|
||||
$res = [
|
||||
'created' => $fave_object->created,
|
||||
'object' => Activitypub_notice::notice_to_array(Notice::getByID($fave_object->notice_id))
|
||||
'object' => Activitypub_notice::notice_to_array(Notice::getByID($fave_object->notice_id)),
|
||||
];
|
||||
|
||||
return $res;
|
||||
@ -118,10 +125,12 @@ class apActorLikedAction extends ManagedAction
|
||||
* Fetch faves
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param int $limit
|
||||
* @param int $since_id
|
||||
* @param int $max_id
|
||||
*
|
||||
* @return Fave fetchable fave collection
|
||||
*/
|
||||
private static function fetch_faves(
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -47,55 +49,55 @@ class apActorOutboxAction extends ManagedAction
|
||||
protected function handle()
|
||||
{
|
||||
try {
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$profile_id = $profile->getID();
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||
}
|
||||
|
||||
if (!$profile->isLocal()) {
|
||||
ActivityPubReturn::error("This is not a local user.", 403);
|
||||
ActivityPubReturn::error('This is not a local user.', 403);
|
||||
}
|
||||
|
||||
if (!isset($_GET["page"])) {
|
||||
if (!isset($_GET['page'])) {
|
||||
$page = 0;
|
||||
} else {
|
||||
$page = intval($this->trimmed('page'));
|
||||
$page = (int) ($this->trimmed('page'));
|
||||
}
|
||||
|
||||
if ($page < 0) {
|
||||
ActivityPubReturn::error('Invalid page number.');
|
||||
}
|
||||
|
||||
$since = ($page - 1) * PROFILES_PER_MINILIST;
|
||||
$since = ($page - 1) * PROFILES_PER_MINILIST;
|
||||
$limit = (($page - 1) == 0 ? 1 : $page) * PROFILES_PER_MINILIST;
|
||||
|
||||
/* Calculate total items */
|
||||
// Calculate total items
|
||||
$total_notes = $profile->noticeCount();
|
||||
$total_pages = ceil($total_notes / PROFILES_PER_MINILIST);
|
||||
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
'@context' => [
|
||||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1',
|
||||
],
|
||||
'id' => common_local_url('apActorOutbox', ['id' => $profile_id]).(($page != 0) ? '?page='.$page : ''),
|
||||
'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'),
|
||||
'totalItems' => $total_notes
|
||||
'id' => common_local_url('apActorOutbox', ['id' => $profile_id]) . (($page != 0) ? '?page=' . $page : ''),
|
||||
'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'),
|
||||
'totalItems' => $total_notes,
|
||||
];
|
||||
|
||||
if ($page == 0) {
|
||||
$res['first'] = common_local_url('apActorOutbox', ['id' => $profile_id]).'?page=1';
|
||||
$res['first'] = common_local_url('apActorOutbox', ['id' => $profile_id]) . '?page=1';
|
||||
} else {
|
||||
$res['orderedItems'] = $this->generate_outbox($profile);
|
||||
$res['partOf'] = common_local_url('apActorOutbox', ['id' => $profile_id]);
|
||||
$res['partOf'] = common_local_url('apActorOutbox', ['id' => $profile_id]);
|
||||
|
||||
if ($page+1 < $total_pages) {
|
||||
$res['next'] = common_local_url('apActorOutbox', ['id' => $profile_id]).'page='.($page+1 == 1 ? 2 : $page+1);
|
||||
if ($page + 1 < $total_pages) {
|
||||
$res['next'] = common_local_url('apActorOutbox', ['id' => $profile_id]) . 'page=' . ($page + 1 == 1 ? 2 : $page + 1);
|
||||
}
|
||||
|
||||
if ($page > 1) {
|
||||
$res['prev'] = common_local_url('apActorOutbox', ['id' => $profile_id]).'?page='.($page-1 <= 0 ? 1 : $page-1);
|
||||
$res['prev'] = common_local_url('apActorOutbox', ['id' => $profile_id]) . '?page=' . ($page - 1 <= 0 ? 1 : $page - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,17 +108,20 @@ class apActorOutboxAction extends ManagedAction
|
||||
* Generates a list of people following given profile.
|
||||
*
|
||||
* @param Profile $profile
|
||||
* @return array of Notices
|
||||
*
|
||||
* @throws EmptyPkeyValueException
|
||||
* @throws InvalidUrlException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return array of Notices
|
||||
*
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
*/
|
||||
public function generate_outbox($profile)
|
||||
{
|
||||
/* Fetch Notices */
|
||||
// Fetch Notices
|
||||
$notices = [];
|
||||
$notice = $profile->getNotices();
|
||||
$notice = $profile->getNotices();
|
||||
while ($notice->fetch()) {
|
||||
$note = $notice;
|
||||
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -42,9 +44,11 @@ class apActorProfileAction extends ManagedAction
|
||||
/**
|
||||
* Handle the Actor Profile request
|
||||
*
|
||||
* @return void
|
||||
* @throws InvalidUrlException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function handle()
|
||||
@ -65,7 +69,7 @@ class apActorProfileAction extends ManagedAction
|
||||
}
|
||||
|
||||
if (!$profile->isLocal()) {
|
||||
ActivityPubReturn::error("This is not a local user.", 403);
|
||||
ActivityPubReturn::error('This is not a local user.', 403);
|
||||
}
|
||||
|
||||
$res = Activitypub_profile::profile_to_array($profile);
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,19 +32,22 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
class apInboxAction extends ManagedAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
protected $canPost = true;
|
||||
|
||||
/**
|
||||
* Handle the Inbox request
|
||||
*
|
||||
* @return void
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function handle()
|
||||
@ -120,8 +124,8 @@ class apInboxAction extends ManagedAction
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Failed to updated remote actor information.');
|
||||
}
|
||||
$actor_public_key = new Activitypub_rsa();
|
||||
$actor_public_key = $actor_public_key->ensure_public_key($actor);
|
||||
$actor_public_key = new Activitypub_rsa();
|
||||
$actor_public_key = $actor_public_key->ensure_public_key($actor);
|
||||
list($verified, /*$headers*/) = HTTPSignature::verify($actor_public_key, $signatureData, $headers, $path, $body);
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -132,10 +134,12 @@ class apNoticeAction extends ManagedAction
|
||||
/**
|
||||
* Handle the Notice request
|
||||
*
|
||||
* @return void
|
||||
* @throws EmptyPkeyValueException
|
||||
* @throws InvalidUrlException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function handle(): void
|
||||
@ -143,9 +147,9 @@ class apNoticeAction extends ManagedAction
|
||||
if (is_null($this->notice)) {
|
||||
ActivityPubReturn::error('Invalid Activity URI.', 404);
|
||||
}
|
||||
if (!$this->notice->isLocal()) {
|
||||
// We have no authority on the requested activity.
|
||||
ActivityPubReturn::error("This is not a local activity.", 403);
|
||||
|
||||
if (!$notice->isLocal()) {
|
||||
ActivityPubReturn::error('This is not a local notice.', 403);
|
||||
}
|
||||
|
||||
$res = Activitypub_notice::notice_to_array($this->notice);
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,13 +32,13 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
class Activitypub_activityverb2 extends Managed_DataObject
|
||||
{
|
||||
const FULL_LIST =
|
||||
[
|
||||
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',
|
||||
@ -65,25 +66,26 @@ class Activitypub_activityverb2 extends Managed_DataObject
|
||||
'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'
|
||||
'Question' => 'https://www.w3.org/ns/activitystreams#Question',
|
||||
];
|
||||
|
||||
const KNOWN =
|
||||
[
|
||||
const KNOWN = [
|
||||
'Accept',
|
||||
'Create',
|
||||
'Delete',
|
||||
'Follow',
|
||||
'Like',
|
||||
'Undo',
|
||||
'Announce'
|
||||
'Announce',
|
||||
];
|
||||
|
||||
/**
|
||||
* Converts canonical into verb.
|
||||
*
|
||||
* @author GNU social
|
||||
*
|
||||
* @param string $verb
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function canonical($verb)
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -33,6 +34,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -44,17 +46,20 @@ class Activitypub_explorer
|
||||
* Shortcut function to get a single profile from its URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
* @return Profile
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
*
|
||||
* @throws HTTP_Request2_Exception Network issues
|
||||
* @throws NoProfileException This won't happen
|
||||
* @throws Exception Invalid request
|
||||
* @throws ServerException Error storing remote actor
|
||||
* @throws NoProfileException This won't happen
|
||||
* @throws Exception Invalid request
|
||||
* @throws ServerException Error storing remote actor
|
||||
*
|
||||
* @return Profile
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function get_profile_from_url(string $url, bool $grab_online = true): Profile
|
||||
{
|
||||
$discovery = new Activitypub_explorer();
|
||||
$discovery = new self();
|
||||
// Get valid Actor object
|
||||
$actor_profile = $discovery->lookup($url, $grab_online);
|
||||
if (!empty($actor_profile)) {
|
||||
@ -68,13 +73,16 @@ class Activitypub_explorer
|
||||
* This function cleans the $this->discovered_actor_profiles array
|
||||
* so that there is no erroneous data
|
||||
*
|
||||
* @param string $url User's url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
* @return array of Profile objects
|
||||
* @param string $url User's url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws NoProfileException
|
||||
* @throws Exception
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return array of Profile objects
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function lookup(string $url, bool $grab_online = true)
|
||||
@ -94,13 +102,16 @@ class Activitypub_explorer
|
||||
* This is a recursive function that will accumulate the results on
|
||||
* $discovered_actor_profiles array
|
||||
*
|
||||
* @param string $url User's url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
* @return array of Profile objects
|
||||
* @param string $url User's url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return array of Profile objects
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function _lookup(string $url, bool $grab_online = true): array
|
||||
@ -120,11 +131,14 @@ class Activitypub_explorer
|
||||
* Get a local user profile from its URL and joins it on
|
||||
* $this->discovered_actor_profiles
|
||||
*
|
||||
* @param string $uri Actor's uri
|
||||
* @param bool $online
|
||||
* @return bool success state
|
||||
* @param string $uri Actor's uri
|
||||
* @param bool $online
|
||||
*
|
||||
* @throws NoProfileException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool success state
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function grab_local_user(string $uri, bool $online = false): bool
|
||||
@ -153,9 +167,9 @@ class Activitypub_explorer
|
||||
if ($online) {
|
||||
common_debug('ActivityPub Explorer: Double-checking ' . $alias . ' to confirm it as a legitimate alias');
|
||||
|
||||
$disco = new Discovery();
|
||||
$xrd = $disco->lookup($aprofile->getUri());
|
||||
$doublecheck_aliases = array_merge(array($xrd->subject), $xrd->aliases);
|
||||
$disco = new Discovery();
|
||||
$xrd = $disco->lookup($aprofile->getUri());
|
||||
$doublecheck_aliases = array_merge([$xrd->subject], $xrd->aliases);
|
||||
|
||||
if (in_array($uri, $doublecheck_aliases)) {
|
||||
// the original URI is present, we're sure now!
|
||||
@ -177,11 +191,11 @@ class Activitypub_explorer
|
||||
common_debug('ActivityPub Explorer: Unable to find a local Aprofile for ' . $alias . ' - looking for a Profile instead.');
|
||||
// Well, maybe it is a pure blood?
|
||||
// Iff, we are in the same instance:
|
||||
$ACTIVITYPUB_BASE_ACTOR_URI = common_local_url('userbyid', ['id' => null], null, null, false, true); // @FIXME: Could this be too hardcoded?
|
||||
$ACTIVITYPUB_BASE_ACTOR_URI = common_local_url('userbyid', ['id' => null], null, null, false, true); // @FIXME: Could this be too hardcoded?
|
||||
$ACTIVITYPUB_BASE_ACTOR_URI_length = strlen($ACTIVITYPUB_BASE_ACTOR_URI);
|
||||
if (substr($alias, 0, $ACTIVITYPUB_BASE_ACTOR_URI_length) === $ACTIVITYPUB_BASE_ACTOR_URI) {
|
||||
try {
|
||||
$profile = Profile::getByID((int)substr($alias, $ACTIVITYPUB_BASE_ACTOR_URI_length));
|
||||
$profile = Profile::getByID((int) substr($alias, $ACTIVITYPUB_BASE_ACTOR_URI_length));
|
||||
common_debug('ActivityPub Explorer: Found a Profile for ' . $alias);
|
||||
// We found something!
|
||||
$this->discovered_actor_profiles[] = $profile;
|
||||
@ -208,19 +222,22 @@ class Activitypub_explorer
|
||||
* $this->discovered_actor_profiles
|
||||
*
|
||||
* @param string $url User's url
|
||||
* @return bool success state
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool success state
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function grab_remote_user(string $url): bool
|
||||
{
|
||||
common_debug('ActivityPub Explorer: Trying to grab a remote actor for ' . $url);
|
||||
$client = new HTTPClient();
|
||||
$client = new HTTPClient();
|
||||
$response = $client->get($url, ACTIVITYPUB_HTTP_CLIENT_HEADERS);
|
||||
$res = json_decode($response->getBody(), true);
|
||||
$res = json_decode($response->getBody(), true);
|
||||
if ($response->getStatus() == 410) { // If it was deleted
|
||||
return true; // Nothing to add.
|
||||
} elseif (!$response->isOk()) { // If it is unavailable
|
||||
@ -251,29 +268,32 @@ class Activitypub_explorer
|
||||
* Save remote user profile in local instance
|
||||
*
|
||||
* @param array $res remote response
|
||||
* @return Profile remote Profile object
|
||||
*
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return Profile remote Profile object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function store_profile(array $res): Profile
|
||||
{
|
||||
// ActivityPub Profile
|
||||
$aprofile = new Activitypub_profile;
|
||||
$aprofile->uri = $res['id'];
|
||||
$aprofile->nickname = $res['preferredUsername'];
|
||||
$aprofile->fullname = $res['name'] ?? null;
|
||||
$aprofile->bio = isset($res['summary']) ? substr(strip_tags($res['summary']), 0, 1000) : null;
|
||||
$aprofile->inboxuri = $res['inbox'];
|
||||
$aprofile = new Activitypub_profile;
|
||||
$aprofile->uri = $res['id'];
|
||||
$aprofile->nickname = $res['preferredUsername'];
|
||||
$aprofile->fullname = $res['name'] ?? null;
|
||||
$aprofile->bio = isset($res['summary']) ? substr(strip_tags($res['summary']), 0, 1000) : null;
|
||||
$aprofile->inboxuri = $res['inbox'];
|
||||
$aprofile->sharedInboxuri = $res['endpoints']['sharedInbox'] ?? $res['inbox'];
|
||||
$aprofile->profileurl = $res['url'] ?? $aprofile->uri;
|
||||
$aprofile->profileurl = $res['url'] ?? $aprofile->uri;
|
||||
|
||||
$aprofile->do_insert();
|
||||
$profile = $aprofile->local_profile();
|
||||
|
||||
// Public Key
|
||||
$apRSA = new Activitypub_rsa();
|
||||
$apRSA = new Activitypub_rsa();
|
||||
$apRSA->profile_id = $profile->getID();
|
||||
$apRSA->public_key = $res['publicKey']['publicKeyPem'];
|
||||
$apRSA->store_keys();
|
||||
@ -296,7 +316,9 @@ class Activitypub_explorer
|
||||
* response is a valid profile or not
|
||||
*
|
||||
* @param array $res remote response
|
||||
*
|
||||
* @return bool success state
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function validate_remote_response(array $res): bool
|
||||
@ -315,15 +337,17 @@ class Activitypub_explorer
|
||||
* this hacky workaround (at least for now)
|
||||
*
|
||||
* @param string $v URL
|
||||
* @return bool|Activitypub_profile false if fails | Aprofile object if successful
|
||||
*
|
||||
* @return Activitypub_profile|bool false if fails | Aprofile object if successful
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function get_aprofile_by_url(string $v)
|
||||
{
|
||||
$i = Managed_DataObject::getcached("Activitypub_profile", "uri", $v);
|
||||
$i = Managed_DataObject::getcached('Activitypub_profile', 'uri', $v);
|
||||
if (empty($i)) { // false = cache miss
|
||||
$i = new Activitypub_profile;
|
||||
$result = $i->get("uri", $v);
|
||||
$i = new Activitypub_profile;
|
||||
$result = $i->get('uri', $v);
|
||||
if ($result) {
|
||||
// Hit!
|
||||
$i->encache();
|
||||
@ -338,14 +362,17 @@ class Activitypub_explorer
|
||||
* Given a valid actor profile url returns its inboxes
|
||||
*
|
||||
* @param string $url of Actor profile
|
||||
* @return bool|array false if fails to validate the answer | array with inbox and shared inbox if successful
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws Exception If an irregular error happens (status code, body format or GONE)
|
||||
* @throws Exception If an irregular error happens (status code, body format or GONE)
|
||||
*
|
||||
* @return array|bool false if fails to validate the answer | array with inbox and shared inbox if successful
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function get_actor_inboxes_uri(string $url)
|
||||
{
|
||||
$client = new HTTPClient();
|
||||
$client = new HTTPClient();
|
||||
$response = $client->get($url, ACTIVITYPUB_HTTP_CLIENT_HEADERS);
|
||||
if ($response->getStatus() == 410) { // If it was deleted
|
||||
throw new Exception('This actor is GONE.');
|
||||
@ -359,8 +386,8 @@ class Activitypub_explorer
|
||||
}
|
||||
if (self::validate_remote_response($res)) {
|
||||
return [
|
||||
'inbox' => $res['inbox'],
|
||||
'sharedInbox' => isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : $res['inbox']
|
||||
'inbox' => $res['inbox'],
|
||||
'sharedInbox' => isset($res['endpoints']['sharedInbox']) ? $res['endpoints']['sharedInbox'] : $res['inbox'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -373,9 +400,12 @@ class Activitypub_explorer
|
||||
* TODO: Should be in AProfile instead?
|
||||
*
|
||||
* @param Profile $profile
|
||||
* @param string $url
|
||||
* @return Avatar The Avatar we have on disk. (seldom used)
|
||||
* @param string $url
|
||||
*
|
||||
* @throws Exception in various failure cases
|
||||
*
|
||||
* @return Avatar The Avatar we have on disk. (seldom used)
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function update_avatar(Profile $profile, string $url): Avatar
|
||||
@ -386,7 +416,7 @@ class Activitypub_explorer
|
||||
|
||||
$id = $profile->getID();
|
||||
|
||||
$type = $imagefile->preferredType();
|
||||
$type = $imagefile->preferredType();
|
||||
$filename = Avatar::filename(
|
||||
$id,
|
||||
image_type_to_extension($type),
|
||||
@ -412,31 +442,34 @@ class Activitypub_explorer
|
||||
* Allows the Explorer to transverse a collection of persons.
|
||||
*
|
||||
* @param string $url
|
||||
* @return bool
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function travel_collection(string $url): bool
|
||||
{
|
||||
$client = new HTTPClient();
|
||||
$client = new HTTPClient();
|
||||
$response = $client->get($url, ACTIVITYPUB_HTTP_CLIENT_HEADERS);
|
||||
$res = json_decode($response->getBody(), true);
|
||||
$res = json_decode($response->getBody(), true);
|
||||
|
||||
if (!isset($res['orderedItems'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($res["orderedItems"] as $profile) {
|
||||
foreach ($res['orderedItems'] as $profile) {
|
||||
if ($this->_lookup($profile) == false) {
|
||||
common_debug('ActivityPub Explorer: Found an invalid actor for ' . $profile);
|
||||
// TODO: Invalid actor found, fallback to OStatus
|
||||
}
|
||||
}
|
||||
// Go through entire collection
|
||||
if (!is_null($res["next"])) {
|
||||
$this->travel_collection($res["next"]);
|
||||
if (!is_null($res['next'])) {
|
||||
$this->travel_collection($res['next']);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -447,13 +480,16 @@ class Activitypub_explorer
|
||||
* profile updating and shall not be used for anything else)
|
||||
*
|
||||
* @param string $url User's url
|
||||
* @return array|false If it is able to fetch, false if it's gone
|
||||
*
|
||||
* @throws Exception Either network issues or unsupported Activity format
|
||||
*
|
||||
* @return array|false If it is able to fetch, false if it's gone
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function get_remote_user_activity(string $url)
|
||||
{
|
||||
$client = new HTTPClient();
|
||||
$client = new HTTPClient();
|
||||
$response = $client->get($url, ACTIVITYPUB_HTTP_CLIENT_HEADERS);
|
||||
// If it was deleted
|
||||
if ($response->getStatus() == 410) {
|
||||
@ -466,7 +502,7 @@ class Activitypub_explorer
|
||||
common_debug('ActivityPub Explorer: Invalid JSON returned from given Actor URL: ' . $response->getBody());
|
||||
throw new Exception('Given Actor URL didn\'t return a valid JSON.');
|
||||
}
|
||||
if (Activitypub_explorer::validate_remote_response($res)) {
|
||||
if (self::validate_remote_response($res)) {
|
||||
common_debug('ActivityPub Explorer: Found a valid remote actor for ' . $url);
|
||||
return $res;
|
||||
}
|
||||
|
@ -14,22 +14,25 @@
|
||||
*
|
||||
* @category Network
|
||||
* @package Nautilus
|
||||
*
|
||||
* @author Aaron Parecki <aaron@parecki.com>
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://github.com/aaronpk/Nautilus/blob/master/app/ActivityPub/HTTPSignature.php
|
||||
*
|
||||
* @see https://github.com/aaronpk/Nautilus/blob/master/app/ActivityPub/HTTPSignature.php
|
||||
*/
|
||||
|
||||
class HttpSignature
|
||||
class httpsignature
|
||||
{
|
||||
/**
|
||||
* Sign a message with an Actor
|
||||
*
|
||||
* @param Profile $user Actor signing
|
||||
* @param string $url Inbox url
|
||||
* @param string|bool $body Data to sign (optional)
|
||||
* @param array $addlHeaders Additional headers (optional)
|
||||
* @return array Headers to be used in curl
|
||||
* @param Profile $user Actor signing
|
||||
* @param string $url Inbox url
|
||||
* @param bool|string $body Data to sign (optional)
|
||||
* @param array $addlHeaders Additional headers (optional)
|
||||
*
|
||||
* @throws Exception Attempted to sign something that belongs to an Actor we don't own
|
||||
*
|
||||
* @return array Headers to be used in curl
|
||||
*/
|
||||
public static function sign(Profile $user, string $url, $body = false, array $addlHeaders = []): array
|
||||
{
|
||||
@ -37,16 +40,16 @@ class HttpSignature
|
||||
if ($body) {
|
||||
$digest = self::_digest($body);
|
||||
}
|
||||
$headers = self::_headersToSign($url, $digest);
|
||||
$headers = array_merge($headers, $addlHeaders);
|
||||
$stringToSign = self::_headersToSigningString($headers);
|
||||
$signedHeaders = implode(' ', array_map('strtolower', array_keys($headers)));
|
||||
$headers = self::_headersToSign($url, $digest);
|
||||
$headers = array_merge($headers, $addlHeaders);
|
||||
$stringToSign = self::_headersToSigningString($headers);
|
||||
$signedHeaders = implode(' ', array_map('strtolower', array_keys($headers)));
|
||||
$actor_private_key = new Activitypub_rsa();
|
||||
// Intentionally unhandled exception, we want this to explode if that happens as it would be a bug
|
||||
$actor_private_key = $actor_private_key->get_private_key($user);
|
||||
$key = openssl_pkey_get_private($actor_private_key);
|
||||
$key = openssl_pkey_get_private($actor_private_key);
|
||||
openssl_sign($stringToSign, $signature, $key, OPENSSL_ALGO_SHA256);
|
||||
$signature = base64_encode($signature);
|
||||
$signature = base64_encode($signature);
|
||||
$signatureHeader = 'keyId="' . $user->getUri() . '#public-key' . '",headers="' . $signedHeaders . '",algorithm="rsa-sha256",signature="' . $signature . '"';
|
||||
unset($headers['(request-target)']);
|
||||
$headers['Signature'] = $signatureHeader;
|
||||
@ -56,6 +59,7 @@ class HttpSignature
|
||||
|
||||
/**
|
||||
* @param mixed $body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function _digest($body): string
|
||||
@ -68,9 +72,11 @@ class HttpSignature
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param mixed $digest
|
||||
* @return array
|
||||
* @param mixed $digest
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function _headersToSign(string $url, $digest = false): array
|
||||
{
|
||||
@ -78,11 +84,11 @@ class HttpSignature
|
||||
|
||||
$headers = [
|
||||
'(request-target)' => 'post ' . parse_url($url, PHP_URL_PATH),
|
||||
'Date' => $date->format('D, d M Y H:i:s \G\M\T'),
|
||||
'Host' => parse_url($url, PHP_URL_HOST),
|
||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json, application/json',
|
||||
'User-Agent' => 'GNU social ActivityPub Plugin - '.GNUSOCIAL_ENGINE_URL,
|
||||
'Content-Type' => 'application/activity+json'
|
||||
'Date' => $date->format('D, d M Y H:i:s \G\M\T'),
|
||||
'Host' => parse_url($url, PHP_URL_HOST),
|
||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json, application/json',
|
||||
'User-Agent' => 'GNU social ActivityPub Plugin - ' . GNUSOCIAL_ENGINE_URL,
|
||||
'Content-Type' => 'application/activity+json',
|
||||
];
|
||||
|
||||
if ($digest) {
|
||||
@ -94,6 +100,7 @@ class HttpSignature
|
||||
|
||||
/**
|
||||
* @param array $headers
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function _headersToSigningString(array $headers): string
|
||||
@ -105,22 +112,24 @@ class HttpSignature
|
||||
|
||||
/**
|
||||
* @param array $headers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function _headersToCurlArray(array $headers): array
|
||||
{
|
||||
return array_map(function ($k, $v) {
|
||||
return "$k: $v";
|
||||
return "{$k}: {$v}";
|
||||
}, array_keys($headers), $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $signature
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function parseSignatureHeader(string $signature): array
|
||||
{
|
||||
$parts = explode(',', $signature);
|
||||
$parts = explode(',', $signature);
|
||||
$signatureData = [];
|
||||
|
||||
foreach ($parts as $part) {
|
||||
@ -131,19 +140,19 @@ class HttpSignature
|
||||
|
||||
if (!isset($signatureData['keyId'])) {
|
||||
return [
|
||||
'error' => 'No keyId was found in the signature header. Found: ' . implode(', ', array_keys($signatureData))
|
||||
'error' => 'No keyId was found in the signature header. Found: ' . implode(', ', array_keys($signatureData)),
|
||||
];
|
||||
}
|
||||
|
||||
if (!filter_var($signatureData['keyId'], FILTER_VALIDATE_URL)) {
|
||||
return [
|
||||
'error' => 'keyId is not a URL: ' . $signatureData['keyId']
|
||||
'error' => 'keyId is not a URL: ' . $signatureData['keyId'],
|
||||
];
|
||||
}
|
||||
|
||||
if (!isset($signatureData['headers']) || !isset($signatureData['signature'])) {
|
||||
return [
|
||||
'error' => 'Signature is missing headers or signature parts'
|
||||
'error' => 'Signature is missing headers or signature parts',
|
||||
];
|
||||
}
|
||||
|
||||
@ -156,14 +165,15 @@ class HttpSignature
|
||||
* @param $inputHeaders
|
||||
* @param $path
|
||||
* @param $body
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function verify($publicKey, $signatureData, $inputHeaders, $path, $body): array
|
||||
{
|
||||
// We need this because the used Request headers fields specified by Signature are in lower case.
|
||||
$headersContent = array_change_key_case($inputHeaders, CASE_LOWER);
|
||||
$digest = 'SHA-256=' . base64_encode(hash('sha256', $body, true));
|
||||
$headersToSign = [];
|
||||
$digest = 'SHA-256=' . base64_encode(hash('sha256', $body, true));
|
||||
$headersToSign = [];
|
||||
foreach (explode(' ', $signatureData['headers']) as $h) {
|
||||
if ($h == '(request-target)') {
|
||||
$headersToSign[$h] = 'post ' . $path;
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -43,15 +45,17 @@ class Activitypub_inbox_handler
|
||||
/**
|
||||
* Create a Inbox Handler to receive something from someone.
|
||||
*
|
||||
* @param array $activity Activity we are receiving
|
||||
* @param array $activity Activity we are receiving
|
||||
* @param Profile $actor_profile Actor originating the activity
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function __construct($activity, $actor_profile = null)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
$this->object = $activity['object'];
|
||||
$this->object = $activity['object'];
|
||||
|
||||
// Validate Activity
|
||||
if (!$this->validate_activity()) {
|
||||
@ -73,7 +77,9 @@ class Activitypub_inbox_handler
|
||||
* Validates if a given Activity is valid. Throws exception if not.
|
||||
*
|
||||
* @throws Exception if invalid
|
||||
*
|
||||
* @return bool true if valid and acceptable, false if unsupported
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function validate_activity(): bool
|
||||
@ -127,6 +133,7 @@ class Activitypub_inbox_handler
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function process()
|
||||
@ -162,6 +169,7 @@ class Activitypub_inbox_handler
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_accept()
|
||||
@ -180,6 +188,7 @@ class Activitypub_inbox_handler
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_accept_follow()
|
||||
@ -200,6 +209,7 @@ class Activitypub_inbox_handler
|
||||
* Handles a Create Activity received by our inbox.
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_create()
|
||||
@ -215,6 +225,7 @@ class Activitypub_inbox_handler
|
||||
* Handle a Create Note Activity received by our inbox.
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
*/
|
||||
private function handle_create_note()
|
||||
@ -229,6 +240,9 @@ class Activitypub_inbox_handler
|
||||
/**
|
||||
* Handles a Delete Activity received by our inbox.
|
||||
*
|
||||
* @throws NoProfileException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
@ -236,10 +250,10 @@ class Activitypub_inbox_handler
|
||||
{
|
||||
$object = $this->object;
|
||||
if (is_string($object)) {
|
||||
$client = new HTTPClient();
|
||||
$client = new HTTPClient();
|
||||
$response = $client->get($object, ACTIVITYPUB_HTTP_CLIENT_HEADERS);
|
||||
$not_gone = $response->isOk();
|
||||
if ($not_gone) { // It's not gone, we're updating it.
|
||||
$gone = !$response->isOk();
|
||||
if (!$gone) { // It's not gone, we're updating it.
|
||||
$object = json_decode($response->getBody(), true);
|
||||
switch ($object['type']) {
|
||||
case 'Person':
|
||||
@ -269,16 +283,37 @@ class Activitypub_inbox_handler
|
||||
common_log(LOG_INFO, "Ignoring Delete activity, we do not understand for {$object['type']}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We don't know the type of the deleted object :(
|
||||
// Nor if it's gone or not.
|
||||
try {
|
||||
if (is_array($object)) {
|
||||
$object = $object['id'];
|
||||
}
|
||||
$aprofile = Activitypub_profile::fromUri($object, false);
|
||||
$res = Activitypub_explorer::get_remote_user_activity($object);
|
||||
Activitypub_profile::update_profile($aprofile, $res);
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
// Means this wasn't a profile
|
||||
}
|
||||
|
||||
// IFF we reached this point, it either is gone or it's an array
|
||||
// If it's gone, we don't know the type of the deleted object, we only have a Tombstone
|
||||
// If we were given an array, we don't know if it's Gone or not via status code...
|
||||
// In both cases, we will want to fetch the ID and act on that as it is easier than updating the fields
|
||||
$object = $object['id'] ?? null;
|
||||
if (is_null($object)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$client = new HTTPClient();
|
||||
$response = $client->get($object, ACTIVITYPUB_HTTP_CLIENT_HEADERS);
|
||||
// If it was deleted
|
||||
if ($response->getStatus() == 410) {
|
||||
$notice = ActivityPubPlugin::grab_notice_from_url($object, false);
|
||||
if ($notice instanceof Notice) {
|
||||
$notice->delete();
|
||||
}
|
||||
} else {
|
||||
// We can't update a note's contents so, we'll ignore it for now...
|
||||
}
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
// Means we didn't have this note already
|
||||
}
|
||||
|
||||
// Was it a profile?
|
||||
try {
|
||||
@ -316,6 +351,7 @@ class Activitypub_inbox_handler
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_follow()
|
||||
@ -327,6 +363,7 @@ class Activitypub_inbox_handler
|
||||
* Handles a Like Activity received by our inbox.
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_like()
|
||||
@ -342,6 +379,7 @@ class Activitypub_inbox_handler
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_undo()
|
||||
@ -364,6 +402,7 @@ class Activitypub_inbox_handler
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_undo_follow()
|
||||
@ -387,6 +426,7 @@ class Activitypub_inbox_handler
|
||||
* @throws AlreadyFulfilledException
|
||||
* @throws ServerException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_undo_like()
|
||||
@ -399,6 +439,7 @@ class Activitypub_inbox_handler
|
||||
* Handles a Announce Activity received by our inbox.
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function handle_announce()
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,17 +42,19 @@ class Activitypub_accept
|
||||
* Generates an ActivityPub representation of a Accept
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*
|
||||
* @param array $object
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*/
|
||||
public static function accept_to_array($object)
|
||||
{
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => common_root_url().'accept_follow_from_'.urlencode($object['actor']).'_to_'.urlencode($object['object']),
|
||||
'type' => 'Accept',
|
||||
'actor' => $object['object'],
|
||||
'object' => $object
|
||||
'id' => common_root_url() . 'accept_follow_from_' . urlencode($object['actor']) . '_to_' . urlencode($object['object']),
|
||||
'type' => 'Accept',
|
||||
'actor' => $object['object'],
|
||||
'object' => $object,
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
@ -59,8 +63,11 @@ class Activitypub_accept
|
||||
* Verifies if a given object is acceptable for an Accept Activity.
|
||||
*
|
||||
* @param array $object
|
||||
* @return bool
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function validate_object($object)
|
||||
@ -75,7 +82,7 @@ class Activitypub_accept
|
||||
case 'Follow':
|
||||
// Validate data
|
||||
if (!filter_var($object['object'], FILTER_VALIDATE_URL)) {
|
||||
throw new Exception("Object is not a valid Object URI for Activity.");
|
||||
throw new Exception('Object is not a valid Object URI for Activity.');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,31 +42,30 @@ class Activitypub_announce
|
||||
* Generates an ActivityPub representation of a Announce
|
||||
*
|
||||
* @param Profile $actor
|
||||
* @param Notice $notice
|
||||
* @param Notice $repeat_of
|
||||
* @param Notice $notice
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function announce_to_array(
|
||||
Profile $actor,
|
||||
Notice $notice,
|
||||
Notice $repeat_of
|
||||
): array {
|
||||
$actor_uri = $actor->getUri();
|
||||
public static function announce_to_array(Profile $actor, Notice $notice): array
|
||||
{
|
||||
$actor_uri = $actor->getUri();
|
||||
$notice_url = Activitypub_notice::getUrl($notice);
|
||||
|
||||
$to = [common_local_url('apActorFollowers', ['id' => $actor->getID()])];
|
||||
foreach ($notice->getAttentionProfiles() as $to_profile) {
|
||||
$to[] = $to_profile->getUri();
|
||||
}
|
||||
|
||||
$cc[]= 'https://www.w3.org/ns/activitystreams#Public';
|
||||
$cc[] = 'https://www.w3.org/ns/activitystreams#Public';
|
||||
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => Activitypub_notice::getUri($notice),
|
||||
'id' => common_root_url() . 'share_from_' . urlencode($actor_uri) . '_to_' . urlencode($notice_url),
|
||||
'type' => 'Announce',
|
||||
'actor' => $actor_uri,
|
||||
'object' => Activitypub_notice::getUri($repeat_of),
|
||||
'object' => $notice_url,
|
||||
'to' => $to,
|
||||
'cc' => $cc,
|
||||
];
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,13 +42,15 @@ class Activitypub_attachment
|
||||
* Generates a pretty array from an Attachment object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*
|
||||
* @param Attachment $attachment
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*/
|
||||
public static function attachment_to_array($attachment)
|
||||
{
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'type' => 'Document',
|
||||
'mediaType' => $attachment->mimetype,
|
||||
'url' => $attachment->getUrl(),
|
||||
@ -55,10 +59,10 @@ class Activitypub_attachment
|
||||
];
|
||||
|
||||
// Image
|
||||
if (substr($res["mediaType"], 0, 5) == "image") {
|
||||
$res["meta"]= [
|
||||
if (substr($res['mediaType'], 0, 5) == 'image') {
|
||||
$res['meta'] = [
|
||||
'width' => $attachment->width,
|
||||
'height' => $attachment->height
|
||||
'height' => $attachment->height,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,23 +42,24 @@ class Activitypub_create
|
||||
* Generates an ActivityPub representation of a Create
|
||||
*
|
||||
* @param string $actor
|
||||
* @param string $uri
|
||||
* @param mixed $object
|
||||
* @param bool $directMessage whether it is a private Create activity or not
|
||||
* @param array $object
|
||||
* @param bool $directMessage whether it is a private Create activity or not
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function create_to_array(string $actor, string $uri, $object, bool $directMessage = false): array
|
||||
{
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => $uri,
|
||||
'type' => 'Create',
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => $object['id'] . '/create',
|
||||
'type' => 'Create',
|
||||
'directMessage' => $directMessage,
|
||||
'to' => $object['to'],
|
||||
'cc' => $object['cc'],
|
||||
'actor' => $actor,
|
||||
'object' => $object
|
||||
'to' => $object['to'],
|
||||
'cc' => $object['cc'],
|
||||
'actor' => $actor,
|
||||
'object' => $object,
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
@ -65,8 +68,11 @@ class Activitypub_create
|
||||
* Verifies if a given object is acceptable for a Create Activity.
|
||||
*
|
||||
* @param array $object
|
||||
* @return bool True if acceptable, false if valid but unsupported
|
||||
*
|
||||
* @throws Exception if invalid
|
||||
*
|
||||
* @return bool True if acceptable, false if valid but unsupported
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function validate_object($object): bool
|
||||
@ -100,7 +106,9 @@ class Activitypub_create
|
||||
* https://github.com/w3c/activitypub/issues/196#issuecomment-304958984
|
||||
*
|
||||
* @param array $activity received Create-Note activity
|
||||
*
|
||||
* @return bool true if note is private, false otherwise
|
||||
*
|
||||
* @author Bruno casteleiro <brunoccast@fc.up.pt>
|
||||
*/
|
||||
public static function isPrivateNote(array $activity): bool
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -39,35 +41,35 @@ class Activitypub_delete
|
||||
/**
|
||||
* Generates an ActivityStreams 2.0 representation of a Delete
|
||||
*
|
||||
* @param Notice $object
|
||||
* @param string $actor actor URI
|
||||
* @param string $object object URI
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function delete_to_array($object): array
|
||||
{
|
||||
if ($object instanceof Notice) {
|
||||
return Activitypub_notice::notice_to_array($object);
|
||||
} else if ($object instanceof Profile) {
|
||||
$actor_uri = $object->getUri();
|
||||
return [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => $actor_uri . '#delete',
|
||||
'type' => 'Delete',
|
||||
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
'actor' => $actor_uri,
|
||||
'object' => $object
|
||||
];
|
||||
} else {
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => $object . '/delete',
|
||||
'type' => 'Delete',
|
||||
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
'actor' => $actor,
|
||||
'object' => $object,
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if a given object is acceptable for a Delete Activity.
|
||||
*
|
||||
* @param array|string $object
|
||||
* @return bool
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
*/
|
||||
public static function validate_object($object): bool
|
||||
@ -80,7 +82,7 @@ class Activitypub_delete
|
||||
if (!isset($object['type'])) {
|
||||
throw new Exception('Object type was not specified for Delete Activity.');
|
||||
}
|
||||
if ($object['type'] !== "Tombstone" && $object['type'] !== "Person") {
|
||||
if ($object['type'] !== 'Tombstone' && $object['type'] !== 'Person') {
|
||||
throw new Exception('Invalid Object type for Delete Activity.');
|
||||
}
|
||||
if (!isset($object['id'])) {
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,13 +42,15 @@ class Activitypub_error
|
||||
* Generates a pretty error from a string
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*
|
||||
* @param string $m
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*/
|
||||
public static function error_message_to_array(string $m): array
|
||||
{
|
||||
return [
|
||||
'error'=> $m
|
||||
$res = [
|
||||
'error' => $m,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,24 +42,26 @@ class Activitypub_follow
|
||||
* Generates an ActivityPub representation of a subscription
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $actor
|
||||
* @param string $object
|
||||
* @param string|null $id Activity id, to be used when generating for an Accept Activity
|
||||
*
|
||||
* @param string $actor
|
||||
* @param string $object
|
||||
* @param null|string $id Activity id, to be used when generating for an Accept Activity
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*/
|
||||
public static function follow_to_array(string $actor, string $object, ?string $id = null): array
|
||||
{
|
||||
if ($id === null) {
|
||||
$id = common_root_url().'follow_from_'.urlencode($actor).'_to_'.urlencode($object);
|
||||
$id = common_root_url() . 'follow_from_' . urlencode($actor) . '_to_' . urlencode($object);
|
||||
}
|
||||
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => $id,
|
||||
'type' => 'Follow',
|
||||
'actor' => $actor,
|
||||
'object' => $object
|
||||
];
|
||||
'id' => $id,
|
||||
'type' => 'Follow',
|
||||
'actor' => $actor,
|
||||
'object' => $object,
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
|
||||
@ -65,12 +69,14 @@ class Activitypub_follow
|
||||
* Handles a Follow Activity received by our inbox.
|
||||
*
|
||||
* @param Profile $actor_profile Remote Actor
|
||||
* @param string $object Local Actor
|
||||
* @param string $id Activity id
|
||||
* @param string $object Local Actor
|
||||
* @param string $id Activity id
|
||||
*
|
||||
* @throws AlreadyFulfilledException
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws NoProfileException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function follow(Profile $actor_profile, string $object, string $id)
|
||||
@ -85,13 +91,13 @@ class Activitypub_follow
|
||||
if (!Subscription::exists($actor_profile, $object_profile)) {
|
||||
Subscription::start($actor_profile, $object_profile);
|
||||
Activitypub_profile::subscribeCacheUpdate($actor_profile, $object_profile);
|
||||
common_debug('ActivityPubPlugin: Accepted Follow request from '.$actor_profile->getUri().' to '.$object);
|
||||
common_debug('ActivityPubPlugin: Accepted Follow request from ' . $actor_profile->getUri() . ' to ' . $object);
|
||||
} else {
|
||||
common_debug('ActivityPubPlugin: Received a repeated Follow request from '.$actor_profile->getUri().' to '.$object);
|
||||
common_debug('ActivityPubPlugin: Received a repeated Follow request from ' . $actor_profile->getUri() . ' 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 '.$actor_profile->getUri().' to '.$object);
|
||||
common_debug('ActivityPubPlugin: Notifying remote instance that we have accepted their Follow request request from ' . $actor_profile->getUri() . ' to ' . $object);
|
||||
$postman = new Activitypub_postman($object_profile, [$actor_aprofile]);
|
||||
$postman->accept_follow($id);
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -39,8 +41,11 @@ class Activitypub_like
|
||||
/**
|
||||
* Generates an ActivityPub representation of a Like
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*
|
||||
* @param string $actor Actor URI
|
||||
* @param Notice $notice Notice URI
|
||||
* @param string $object Notice URI
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
@ -48,10 +53,10 @@ class Activitypub_like
|
||||
{
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => Activitypub_notice::getUri($notice),
|
||||
'id' => common_root_url() . 'like_from_' . urlencode($actor) . '_to_' . urlencode($object),
|
||||
'type' => 'Like',
|
||||
'actor' => $actor,
|
||||
'object' => Activitypub_notice::getUri($notice->getParent()),
|
||||
'object' => $object,
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,18 +42,20 @@ class Activitypub_mention_tag
|
||||
* Generates an ActivityPub representation of a Mention Tag
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*
|
||||
* @param string $href Actor Uri
|
||||
* @param string $name Mention name
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*/
|
||||
public static function mention_tag_to_array_from_values(string $href, string $name): array
|
||||
{
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
"type" => "Mention",
|
||||
"href" => $href,
|
||||
"name" => $name
|
||||
];
|
||||
'type' => 'Mention',
|
||||
'href' => $href,
|
||||
'name' => $name,
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,11 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -38,7 +38,9 @@ class Activitypub_message
|
||||
* Generates a pretty message from a Notice object
|
||||
*
|
||||
* @param Notice $message
|
||||
*
|
||||
* @return array array to be used in a response
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
*/
|
||||
public static function message_to_array(Notice $message): array
|
||||
@ -54,21 +56,21 @@ class Activitypub_message
|
||||
|
||||
$to = [];
|
||||
foreach ($message->getAttentionProfiles() as $to_profile) {
|
||||
$to[] = $href = $to_profile->getUri();
|
||||
$tags[] = Activitypub_mention_tag::mention_tag_to_array_from_values($href, $to_profile->getNickname().'@'.parse_url($href, PHP_URL_HOST));
|
||||
$to[] = $href = $to_profile->getUri();
|
||||
$tags[] = Activitypub_mention_tag::mention_tag_to_array_from_values($href, $to_profile->getNickname() . '@' . parse_url($href, PHP_URL_HOST));
|
||||
}
|
||||
|
||||
$item = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => common_local_url('showmessage', ['message' => $message->getID()]),
|
||||
'type' => 'Note',
|
||||
'published' => str_replace(' ', 'T', $message->created).'Z',
|
||||
'attributedTo' => $from->getUri(),
|
||||
'to' => $to,
|
||||
'cc' => [],
|
||||
'content' => $message->getRendered(),
|
||||
'attachment' => [],
|
||||
'tag' => $tags
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => common_local_url('showmessage', ['message' => $message->getID()]),
|
||||
'type' => 'Note',
|
||||
'published' => str_replace(' ', 'T', $message->created) . 'Z',
|
||||
'attributedTo' => $from->getUri(),
|
||||
'to' => $to,
|
||||
'cc' => [],
|
||||
'content' => $message->getRendered(),
|
||||
'attachment' => [],
|
||||
'tag' => $tags,
|
||||
];
|
||||
|
||||
return $item;
|
||||
@ -79,10 +81,13 @@ class Activitypub_message
|
||||
* Returns created Notice.
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
* @param array $object
|
||||
*
|
||||
* @param array $object
|
||||
* @param Profile $actor_profile
|
||||
* @return Notice
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return Notice
|
||||
*/
|
||||
public static function create_message(array $object, Profile $actor_profile = null): Notice
|
||||
{
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,16 +42,19 @@ class Activitypub_notice
|
||||
* Generates a pretty notice from a Notice object
|
||||
*
|
||||
* @param Notice $notice
|
||||
* @return array array to be used in a response
|
||||
*
|
||||
* @throws EmptyPkeyValueException
|
||||
* @throws InvalidUrlException
|
||||
* @throws ServerException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return array array to be used in a response
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function notice_to_array(Notice $notice): array
|
||||
{
|
||||
$profile = $notice->getProfile();
|
||||
$profile = $notice->getProfile();
|
||||
$attachments = [];
|
||||
foreach ($notice->attachments() as $attachment) {
|
||||
$attachments[] = Activitypub_attachment::attachment_to_array($attachment);
|
||||
@ -57,7 +62,7 @@ class Activitypub_notice
|
||||
|
||||
$tags = [];
|
||||
foreach ($notice->getTags() as $tag) {
|
||||
if ($tag != "") { // Hacky workaround to avoid stupid outputs
|
||||
if ($tag != '') { // Hacky workaround to avoid stupid outputs
|
||||
$tags[] = Activitypub_tag::tag_to_array($tag);
|
||||
}
|
||||
}
|
||||
@ -75,46 +80,25 @@ class Activitypub_notice
|
||||
}
|
||||
|
||||
foreach ($notice->getAttentionProfiles() as $to_profile) {
|
||||
$to[] = $href = $to_profile->getUri();
|
||||
$to[] = $href = $to_profile->getUri();
|
||||
$tags[] = Activitypub_mention_tag::mention_tag_to_array_from_values($href, $to_profile->getNickname() . '@' . parse_url($href, PHP_URL_HOST));
|
||||
}
|
||||
|
||||
if (ActivityUtils::compareVerbs($notice->getVerb(), ActivityVerb::DELETE)) {
|
||||
$item = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => self::getUri($notice),
|
||||
'type' => 'Delete',
|
||||
// XXX: A bit of ugly code here
|
||||
'object' => array_merge(Activitypub_tombstone::tombstone_to_array((int)substr(explode(':', $notice->getUri())[2], 9)), ['deleted' => str_replace(' ', 'T', $notice->getCreated()) . 'Z']),
|
||||
'url' => $notice->getUrl(),
|
||||
'actor' => $profile->getUri(),
|
||||
'to' => $to,
|
||||
'cc' => $cc,
|
||||
'conversationId' => $notice->getConversationUrl(false),
|
||||
'conversationUrl' => $notice->getConversationUrl(),
|
||||
'content' => $notice->getRendered(),
|
||||
'isLocal' => $notice->isLocal(),
|
||||
'attachment' => $attachments,
|
||||
'tag' => $tags
|
||||
];
|
||||
} else { // Note
|
||||
$item = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => self::note_uri($notice->getID()),
|
||||
'type' => 'Note',
|
||||
'published' => str_replace(' ', 'T', $notice->getCreated()) . 'Z',
|
||||
'url' => $notice->getUrl(),
|
||||
'attributedTo' => $profile->getUri(),
|
||||
'to' => $to,
|
||||
'cc' => $cc,
|
||||
'conversationId' => $notice->getConversationUrl(false),
|
||||
'conversationUrl' => $notice->getConversationUrl(),
|
||||
'content' => $notice->getRendered(),
|
||||
'isLocal' => $notice->isLocal(),
|
||||
'attachment' => $attachments,
|
||||
'tag' => $tags
|
||||
];
|
||||
}
|
||||
$item = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => self::getUrl($notice),
|
||||
'type' => 'Note',
|
||||
'published' => str_replace(' ', 'T', $notice->getCreated()) . 'Z',
|
||||
'url' => self::getUrl($notice),
|
||||
'attributedTo' => $profile->getUri(),
|
||||
'to' => $to,
|
||||
'cc' => $cc,
|
||||
'conversation' => $notice->getConversationUrl(),
|
||||
'content' => $notice->getRendered(),
|
||||
'isLocal' => $notice->isLocal(),
|
||||
'attachment' => $attachments,
|
||||
'tag' => $tags,
|
||||
];
|
||||
|
||||
// Is this a reply?
|
||||
if (!empty($notice->reply_to)) {
|
||||
@ -123,8 +107,8 @@ class Activitypub_notice
|
||||
|
||||
// Do we have a location for this notice?
|
||||
try {
|
||||
$location = Notice_location::locFromStored($notice);
|
||||
$item['latitude'] = $location->lat;
|
||||
$location = Notice_location::locFromStored($notice);
|
||||
$item['latitude'] = $location->lat;
|
||||
$item['longitude'] = $location->lon;
|
||||
} catch (Exception $e) {
|
||||
// Apparently no.
|
||||
@ -137,17 +121,20 @@ class Activitypub_notice
|
||||
* Create a Notice via ActivityPub Note Object.
|
||||
* Returns created Notice.
|
||||
*
|
||||
* @param array $object
|
||||
* @param array $object
|
||||
* @param Profile $actor_profile
|
||||
* @param bool $directMessage
|
||||
* @return Notice
|
||||
* @param bool $directMessage
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return Notice
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function create_notice(array $object, Profile $actor_profile, bool $directMessage = false): Notice
|
||||
{
|
||||
$id = $object['id']; // int
|
||||
$url = isset($object['url']) ? $object['url'] : $id; // string
|
||||
$id = $object['id']; // int
|
||||
$url = isset($object['url']) ? $object['url'] : $id; // string
|
||||
$content = $object['content']; // string
|
||||
|
||||
// possible keys: ['inReplyTo', 'latitude', 'longitude']
|
||||
@ -162,15 +149,15 @@ class Activitypub_notice
|
||||
$settings['longitude'] = $object['longitude'];
|
||||
}
|
||||
|
||||
$act = new Activity();
|
||||
$act->verb = ActivityVerb::POST;
|
||||
$act->time = time();
|
||||
$act->actor = $actor_profile->asActivityObject();
|
||||
$act = new Activity();
|
||||
$act->verb = ActivityVerb::POST;
|
||||
$act->time = time();
|
||||
$act->actor = $actor_profile->asActivityObject();
|
||||
$act->context = new ActivityContext();
|
||||
$options = ['source' => 'ActivityPub',
|
||||
'uri' => $id,
|
||||
'url' => $url,
|
||||
'is_local' => self::getNotePolicyType($object, $actor_profile)];
|
||||
$options = ['source' => 'ActivityPub',
|
||||
'uri' => $id,
|
||||
'url' => $url,
|
||||
'is_local' => self::getNotePolicyType($object, $actor_profile), ];
|
||||
|
||||
if ($directMessage) {
|
||||
$options['scope'] = Notice::MESSAGE_SCOPE;
|
||||
@ -179,8 +166,8 @@ class Activitypub_notice
|
||||
// Is this a reply?
|
||||
if (isset($settings['inReplyTo'])) {
|
||||
try {
|
||||
$inReplyTo = ActivityPubPlugin::grab_notice_from_url($settings['inReplyTo']);
|
||||
$act->context->replyToID = $inReplyTo->getUri();
|
||||
$inReplyTo = ActivityPubPlugin::grab_notice_from_url($settings['inReplyTo']);
|
||||
$act->context->replyToID = $inReplyTo->getUri();
|
||||
$act->context->replyToUrl = $inReplyTo->getUrl();
|
||||
} catch (Exception $e) {
|
||||
// It failed to grab, maybe we got this note from another source
|
||||
@ -203,7 +190,7 @@ class Activitypub_notice
|
||||
}
|
||||
}
|
||||
$mentions_profiles = [];
|
||||
$discovery = new Activitypub_explorer;
|
||||
$discovery = new Activitypub_explorer;
|
||||
foreach ($mentions as $mention) {
|
||||
try {
|
||||
$mentioned_profile = $discovery->lookup($mention);
|
||||
@ -240,32 +227,10 @@ class Activitypub_notice
|
||||
&& $attachment['type'] === 'Document'
|
||||
&& array_key_exists('url', $attachment)) {
|
||||
try {
|
||||
$file = new File();
|
||||
$file->url = $attachment['url'];
|
||||
$file->title = array_key_exists('type', $attachment) ? $attachment['name'] : null;
|
||||
if (array_key_exists('type', $attachment)) {
|
||||
$file->mimetype = $attachment['mediaType'];
|
||||
} else {
|
||||
$http = new HTTPClient();
|
||||
common_debug(
|
||||
'Performing HEAD request for incoming activity '
|
||||
. 'to avoid unnecessarily downloading too '
|
||||
. 'large files. URL: ' . $file->url
|
||||
);
|
||||
$head = $http->head($file->url);
|
||||
$headers = $head->getHeader();
|
||||
$headers = array_change_key_case($headers, CASE_LOWER);
|
||||
if (array_key_exists('content-type', $headers)) {
|
||||
$file->mimetype = $headers['content-type'];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists('content-length', $headers)) {
|
||||
$file->size = $headers['content-length'];
|
||||
}
|
||||
}
|
||||
$file->saveFile();
|
||||
$attachments[] = $file;
|
||||
// throws exception on failure
|
||||
$attachment = MediaFile::fromUrl($attachment['url'], $actor_profile, $attachment['name']);
|
||||
$act->enclosures[] = $attachment->getEnclosure();
|
||||
$attachments[] = $attachment;
|
||||
} catch (Exception $e) {
|
||||
// Whatever.
|
||||
continue;
|
||||
@ -274,9 +239,9 @@ class Activitypub_notice
|
||||
}
|
||||
}
|
||||
|
||||
$actobj = new ActivityObject();
|
||||
$actobj->type = ActivityObject::NOTE;
|
||||
$actobj->content = strip_tags($content, '<p><b><i><u><a><ul><ol><li><br>');
|
||||
$actobj = new ActivityObject();
|
||||
$actobj->type = ActivityObject::NOTE;
|
||||
$actobj->content = strip_tags($content, '<p><b><i><u><a><ul><ol><li>');
|
||||
|
||||
// Finally add the activity object to our activity
|
||||
$act->objects[] = $actobj;
|
||||
@ -284,8 +249,8 @@ class Activitypub_notice
|
||||
$note = Notice::saveActivity($act, $actor_profile, $options);
|
||||
|
||||
// Attachments (last part)
|
||||
foreach ($attachments as $file) {
|
||||
File_to_post::processNew($file, $note);
|
||||
foreach ($attachments as $attachment) {
|
||||
$attachment->attachToNotice($note);
|
||||
}
|
||||
|
||||
return $note;
|
||||
@ -295,8 +260,11 @@ class Activitypub_notice
|
||||
* Validates a note.
|
||||
*
|
||||
* @param array $object
|
||||
* @return bool false if unacceptable for GS but valid ActivityPub object
|
||||
*
|
||||
* @throws Exception if invalid ActivityPub object
|
||||
*
|
||||
* @return bool false if unacceptable for GS but valid ActivityPub object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function validate_note(array $object): bool
|
||||
@ -316,7 +284,7 @@ class Activitypub_notice
|
||||
common_debug('ActivityPub Notice Validator: Rejected because Object URL is invalid.');
|
||||
throw new Exception('Invalid Object URL.');
|
||||
}
|
||||
if (!(isset($object['to']) && isset($object['cc']))) {
|
||||
if (!(isset($object['to'], $object['cc']))) {
|
||||
common_debug('ActivityPub Notice Validator: Rejected because either Object CC or TO wasn\'t specified.');
|
||||
throw new Exception('Either Object CC or TO wasn\'t specified.');
|
||||
}
|
||||
@ -331,9 +299,12 @@ class Activitypub_notice
|
||||
* Get the original representation URL of a given notice.
|
||||
*
|
||||
* @param Notice $notice notice from which to retrieve the URL
|
||||
* @return string URL
|
||||
*
|
||||
* @throws InvalidUrlException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string URL
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
* @see note_uri when it's not a generic activity but a object type note
|
||||
*/
|
||||
@ -362,9 +333,11 @@ class Activitypub_notice
|
||||
/**
|
||||
* Extract note policy type from note targets.
|
||||
*
|
||||
* @param array $note received Note
|
||||
* @param array $note received Note
|
||||
* @param Profile $actor_profile Note author
|
||||
*
|
||||
* @return int Notice policy type
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
*/
|
||||
public static function getNotePolicyType(array $note, Profile $actor_profile): int
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,15 +42,17 @@ class Activitypub_reject
|
||||
* Generates an ActivityPub representation of a Reject
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*
|
||||
* @param array $object
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*/
|
||||
public static function reject_to_array(array $object): array
|
||||
{
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'type' => 'Reject',
|
||||
'object' => $object
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'type' => 'Reject',
|
||||
'object' => $object,
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,15 +42,17 @@ class Activitypub_tag
|
||||
* Generates a pretty tag from a Tag object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $tag
|
||||
*
|
||||
* @param array Tag $tag
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*/
|
||||
public static function tag_to_array(string $tag): array
|
||||
{
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'name' => $tag,
|
||||
'url' => common_local_url('tag', ['tag' => $tag]),
|
||||
'name' => $tag,
|
||||
'url' => common_local_url('tag', ['tag' => $tag]),
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -31,6 +32,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -40,17 +42,19 @@ class Activitypub_undo
|
||||
* Generates an ActivityPub representation of a Undo
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*
|
||||
* @param array $object
|
||||
*
|
||||
* @return array pretty array to be used in a response
|
||||
*/
|
||||
public static function undo_to_array(array $object): array
|
||||
{
|
||||
$res = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => $object['id'] . '#undo',
|
||||
'type' => 'Undo',
|
||||
'actor' => $object['actor'],
|
||||
'object' => $object
|
||||
'id' => $object['id'] . '/undo',
|
||||
'type' => 'Undo',
|
||||
'actor' => $object['actor'],
|
||||
'object' => $object,
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
@ -59,8 +63,11 @@ class Activitypub_undo
|
||||
* Verifies if a given object is acceptable for a Undo Activity.
|
||||
*
|
||||
* @param array $object
|
||||
* @return bool
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public static function validate_object(array $object): bool
|
||||
|
@ -18,12 +18,13 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
/**
|
||||
@ -34,6 +35,7 @@ defined('GNUSOCIAL') || die();
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -50,14 +52,16 @@ class Activitypub_postman
|
||||
* Create a postman to deliver something to someone
|
||||
*
|
||||
* @param Profile $from sender Profile
|
||||
* @param array $to receiver AProfiles
|
||||
* @param array $to receiver Profiles
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function __construct(Profile $from, array $to = [])
|
||||
{
|
||||
$this->actor = $from;
|
||||
$this->to = $to;
|
||||
$this->to = $to;
|
||||
|
||||
$this->actor_uri = $this->actor->getUri();
|
||||
|
||||
@ -82,21 +86,24 @@ class Activitypub_postman
|
||||
/**
|
||||
* Send something to remote instance
|
||||
*
|
||||
* @param string $data request body
|
||||
* @param string $inbox url of remote inbox
|
||||
* @param string $data request body
|
||||
* @param string $inbox url of remote inbox
|
||||
* @param string $method request method
|
||||
* @return GNUsocial_HTTPResponse
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws HTTP_Request2_LogicException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return GNUsocial_HTTPResponse
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function send($data, $inbox, $method = 'POST')
|
||||
{
|
||||
common_debug('ActivityPub Postman: Delivering '.$data.' to '.$inbox);
|
||||
common_debug('ActivityPub Postman: Delivering ' . $data . ' to ' . $inbox);
|
||||
|
||||
$headers = HttpSignature::sign($this->actor, $inbox, $data);
|
||||
|
||||
common_debug('ActivityPub Postman: Delivery headers were: '.print_r($headers, true));
|
||||
common_debug('ActivityPub Postman: Delivery headers were: ' . print_r($headers, true));
|
||||
|
||||
$this->client->setBody($data);
|
||||
|
||||
@ -108,25 +115,27 @@ class Activitypub_postman
|
||||
$response = $this->client->get($inbox, $headers);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported request method for postman.");
|
||||
throw new Exception('Unsupported request method for postman.');
|
||||
}
|
||||
|
||||
common_debug('ActivityPub Postman: Delivery result with status code '.$response->getStatus().': '.$response->getBody());
|
||||
common_debug('ActivityPub Postman: Delivery result with status code ' . $response->getStatus() . ': ' . $response->getBody());
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a follow notification to remote instance
|
||||
*
|
||||
* @return bool
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function follow()
|
||||
{
|
||||
$data = Activitypub_follow::follow_to_array($this->actor_uri, $this->to[0]->getUrl());
|
||||
$res = $this->send(json_encode($data, JSON_UNESCAPED_SLASHES), $this->to[0]->get_inbox());
|
||||
$data = Activitypub_follow::follow_to_array($this->actor_uri, $this->to[0]->getUrl());
|
||||
$res = $this->send(json_encode($data, JSON_UNESCAPED_SLASHES), $this->to[0]->get_inbox());
|
||||
$res_body = json_decode($res->getBody(), true);
|
||||
|
||||
if ($res->getStatus() == 200 || $res->getStatus() == 202 || $res->getStatus() == 409) {
|
||||
@ -137,18 +146,20 @@ class Activitypub_postman
|
||||
throw new Exception($res_body['error']);
|
||||
}
|
||||
|
||||
throw new Exception("An unknown error occurred.");
|
||||
throw new Exception('An unknown error occurred.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Undo Follow notification to remote instance
|
||||
*
|
||||
* @return bool
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function undo_follow()
|
||||
@ -157,9 +168,9 @@ class Activitypub_postman
|
||||
Activitypub_follow::follow_to_array(
|
||||
$this->actor_uri,
|
||||
$this->to[0]->getUrl()
|
||||
)
|
||||
);
|
||||
$res = $this->send(json_encode($data, JSON_UNESCAPED_SLASHES), $this->to[0]->get_inbox());
|
||||
)
|
||||
);
|
||||
$res = $this->send(json_encode($data, JSON_UNESCAPED_SLASHES), $this->to[0]->get_inbox());
|
||||
$res_body = json_decode($res->getBody(), true);
|
||||
|
||||
if ($res->getStatus() == 200 || $res->getStatus() == 202 || $res->getStatus() == 409) {
|
||||
@ -171,16 +182,19 @@ class Activitypub_postman
|
||||
if (isset($res_body['error'])) {
|
||||
throw new Exception($res_body['error']);
|
||||
}
|
||||
throw new Exception("An unknown error occurred.");
|
||||
throw new Exception('An unknown error occurred.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Accept Follow notification to remote instance
|
||||
*
|
||||
* @param string $id Follow activity id
|
||||
* @return bool
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws Exception Description of HTTP Response error or generic error message.
|
||||
* @throws Exception Description of HTTP Response error or generic error message.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function accept_follow(string $id): bool
|
||||
@ -190,9 +204,9 @@ class Activitypub_postman
|
||||
$this->to[0]->getUrl(),
|
||||
$this->actor_uri,
|
||||
$id
|
||||
)
|
||||
);
|
||||
$res = $this->send(json_encode($data, JSON_UNESCAPED_SLASHES), $this->to[0]->get_inbox());
|
||||
)
|
||||
);
|
||||
$res = $this->send(json_encode($data, JSON_UNESCAPED_SLASHES), $this->to[0]->get_inbox());
|
||||
$res_body = json_decode($res->getBody(), true);
|
||||
|
||||
if ($res->getStatus() == 200 || $res->getStatus() == 202 || $res->getStatus() == 409) {
|
||||
@ -203,16 +217,18 @@ class Activitypub_postman
|
||||
if (isset($res_body['error'])) {
|
||||
throw new Exception($res_body['error']);
|
||||
}
|
||||
throw new Exception("An unknown error occurred.");
|
||||
throw new Exception('An unknown error occurred.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Like notification to remote instances holding the notice
|
||||
*
|
||||
* @param Notice $notice
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws InvalidUrlException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function like(Notice $notice): void
|
||||
@ -239,7 +255,7 @@ class Activitypub_postman
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
common_log(LOG_ERR, sizeof($errors) . " instance/s failed to handle the like activity!");
|
||||
common_log(LOG_ERR, sizeof($errors) . ' instance/s failed to handle the like activity!');
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,9 +263,11 @@ class Activitypub_postman
|
||||
* Send a Undo Like notification to remote instances holding the notice
|
||||
*
|
||||
* @param Notice $notice
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws InvalidUrlException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function undo_like($notice)
|
||||
@ -279,7 +297,7 @@ class Activitypub_postman
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
common_log(LOG_ERR, sizeof($errors) . " instance/s failed to handle the undo-like activity!");
|
||||
common_log(LOG_ERR, sizeof($errors) . ' instance/s failed to handle the undo-like activity!');
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,10 +305,12 @@ class Activitypub_postman
|
||||
* Send a Create notification to remote instances
|
||||
*
|
||||
* @param Notice $notice
|
||||
*
|
||||
* @throws EmptyPkeyValueException
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws InvalidUrlException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function create_note($notice)
|
||||
@ -319,7 +339,7 @@ class Activitypub_postman
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
common_log(LOG_ERR, sizeof($errors) . " instance/s failed to handle the create-note activity!");
|
||||
common_log(LOG_ERR, sizeof($errors) . ' instance/s failed to handle the create-note activity!');
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,6 +347,7 @@ class Activitypub_postman
|
||||
* Send a Create direct-notification to remote instances
|
||||
*
|
||||
* @param Notice $message
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
*/
|
||||
public function create_direct_note(Notice $message)
|
||||
@ -346,13 +367,12 @@ class Activitypub_postman
|
||||
if (!($res->getStatus() == 200 || $res->getStatus() == 202 || $res->getStatus() == 409)) {
|
||||
$res_body = json_decode($res->getBody(), true);
|
||||
$errors[] = isset($res_body['error']) ?
|
||||
$res_body['error'] : "An unknown error occurred.";
|
||||
$to_failed[$inbox] = $message;
|
||||
$res_body['error'] : 'An unknown error occurred.';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
common_log(LOG_ERR, sizeof($errors) . " instance/s failed to handle the create-note activity!");
|
||||
common_log(LOG_ERR, sizeof($errors) . ' instance/s failed to handle the create-note activity!');
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,8 +380,10 @@ class Activitypub_postman
|
||||
* Send a Announce notification to remote instances
|
||||
*
|
||||
* @param Notice $notice
|
||||
* @param Notice $repeat_of
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function announce(Notice $notice, Notice $repeat_of): void
|
||||
@ -388,7 +410,7 @@ class Activitypub_postman
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
common_log(LOG_ERR, sizeof($errors) . " instance/s failed to handle the announce activity!");
|
||||
common_log(LOG_ERR, sizeof($errors) . ' instance/s failed to handle the announce activity!');
|
||||
}
|
||||
}
|
||||
|
||||
@ -396,16 +418,18 @@ class Activitypub_postman
|
||||
* Send a Delete notification to remote instances holding the notice
|
||||
*
|
||||
* @param Notice $notice
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws InvalidUrlException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function delete_note($notice)
|
||||
{
|
||||
$data = Activitypub_delete::delete_to_array($notice);
|
||||
$errors = [];
|
||||
$data = json_encode($data, JSON_UNESCAPED_SLASHES);
|
||||
$data = json_encode($data, JSON_UNESCAPED_SLASHES);
|
||||
foreach ($this->to_inbox() as $inbox) {
|
||||
try {
|
||||
$res = $this->send($data, $inbox);
|
||||
@ -427,8 +451,12 @@ class Activitypub_postman
|
||||
/**
|
||||
* Send a Delete notification to remote followers of some deleted profile
|
||||
*
|
||||
* @param Profile $deleted_profile
|
||||
* @param Notice $notice
|
||||
*
|
||||
* @throws HTTP_Request2_Exception
|
||||
* @throws InvalidUrlException
|
||||
* @throws Exception
|
||||
*
|
||||
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
|
||||
*/
|
||||
public function delete_profile(Profile $deleted_profile)
|
||||
@ -453,7 +481,7 @@ class Activitypub_postman
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
common_log(LOG_ERR, sizeof($errors) . " instance/s failed to handle the delete_profile activity!");
|
||||
common_log(LOG_ERR, sizeof($errors) . ' instance/s failed to handle the delete_profile activity!');
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,8 +489,11 @@ class Activitypub_postman
|
||||
* Clean list of inboxes to deliver messages
|
||||
*
|
||||
* @param bool $actorFollowers whether to include the actor's follower collection
|
||||
* @return array To Inbox URLs
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return array To Inbox URLs
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
private function to_inbox(bool $actorFollowers = true): array
|
||||
@ -472,7 +503,7 @@ class Activitypub_postman
|
||||
$followers = apActorFollowersAction::generate_followers($this->actor, 0, null);
|
||||
foreach ($followers as $sub) {
|
||||
try {
|
||||
$this->to[]= Activitypub_profile::from_profile($discovery->lookup($sub)[0]);
|
||||
$this->to[] = Activitypub_profile::from_profile($discovery->lookup($sub)[0]);
|
||||
} catch (Exception $e) {
|
||||
// Not an ActivityPub Remote Follower, let it go
|
||||
}
|
||||
|
@ -19,17 +19,18 @@
|
||||
* ActivityPub implementation for GNU social
|
||||
*
|
||||
* @package GNUsocial
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*
|
||||
* @see http://www.gnu.org/software/social/
|
||||
*/
|
||||
|
||||
define('INSTALLDIR', dirname(__DIR__, 3));
|
||||
define('PUBLICDIR', INSTALLDIR . DIRECTORY_SEPARATOR . 'public');
|
||||
|
||||
$shortoptions = 'u:af';
|
||||
$longoptions = ['uri=', 'all', 'force'];
|
||||
$longoptions = ['uri=', 'all', 'force'];
|
||||
|
||||
$helptext = <<<END_OF_HELP
|
||||
update_activitypub_profiles.php [options]
|
||||
@ -42,7 +43,7 @@ you have no backup.
|
||||
|
||||
END_OF_HELP;
|
||||
|
||||
require_once INSTALLDIR.'/scripts/commandline.inc';
|
||||
require_once INSTALLDIR . '/scripts/commandline.inc';
|
||||
|
||||
if (!have_option('q', 'quiet')) {
|
||||
echo "ActivityPub Profiles updater will now start!\n";
|
||||
@ -50,15 +51,15 @@ if (!have_option('q', 'quiet')) {
|
||||
}
|
||||
|
||||
if (have_option('u', 'uri')) {
|
||||
$uri = get_option_value('u', 'uri');
|
||||
$uri = get_option_value('u', 'uri');
|
||||
$user = Activitypub_profile::from_profile(Activitypub_explorer::get_profile_from_url($uri));
|
||||
try {
|
||||
$res = Activitypub_explorer::get_remote_user_activity($uri);
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage()."\n";
|
||||
echo $e->getMessage() . "\n";
|
||||
exit(1);
|
||||
}
|
||||
printfnq('Updated '.Activitypub_profile::update_profile($user, $res)->getBestName()."\n");
|
||||
printfnq('Updated ' . Activitypub_profile::update_profile($user, $res)->getBestName() . "\n");
|
||||
exit(0);
|
||||
} elseif (!have_option('a', 'all')) {
|
||||
show_help();
|
||||
@ -66,7 +67,7 @@ if (have_option('u', 'uri')) {
|
||||
}
|
||||
|
||||
$user = new Activitypub_profile();
|
||||
$cnt = $user->find();
|
||||
$cnt = $user->find();
|
||||
if (!empty($cnt)) {
|
||||
printfnq("Found {$cnt} ActivityPub profiles:\n");
|
||||
} else {
|
||||
@ -79,11 +80,11 @@ if (!empty($cnt)) {
|
||||
}
|
||||
while ($user->fetch()) {
|
||||
try {
|
||||
$res = Activitypub_explorer::get_remote_user_activity($user->uri);
|
||||
$res = Activitypub_explorer::get_remote_user_activity($user->uri);
|
||||
$updated_profile = Activitypub_profile::update_profile($user, $res);
|
||||
printfnq('Updated '.$updated_profile->getBestName()."\n");
|
||||
printfnq('Updated ' . $updated_profile->getBestName() . "\n");
|
||||
} catch (NoProfileException $e) {
|
||||
printfnq('Deleted '.$user->uri."\n");
|
||||
printfnq('Deleted ' . $user->uri . "\n");
|
||||
} catch (Exception $e) {
|
||||
// let it go
|
||||
}
|
||||
|
@ -44,7 +44,7 @@
|
||||
<a href="{{ path("favourites", {'nickname' : user_nickname}) }}" class='hover-effect {{ active("favourites") }}'>Favourites</a>
|
||||
<a href='#'>Reverse Favs</a>
|
||||
<a href="{{ path('settings_personal_info') }}" class='hover-effect {{ active('settings_') }}'>Settings</a>
|
||||
<a href='#'>Logout</a>
|
||||
<a href='{{ path('logout') }}'>Logout</a>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<a href="{{ path('doc_faq') }}" class='hover-effect {{ active('doc_faq') }}'>FAQ</a>
|
||||
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
// GNU social 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.
|
||||
//
|
||||
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
|
||||
// }}}
|
||||
|
||||
namespace App\Tests\Util\Form\ActorArrayTransformer;
|
||||
|
||||
use App\Entity\GSActor;
|
||||
use App\Util\Form\ActorArrayTransformer;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
class ActorArrayTransformerTest extends WebTestCase
|
||||
{
|
||||
public function testTransform()
|
||||
{
|
||||
static::assertSame('', (new ActorArrayTransformer)->transform([]));
|
||||
|
||||
$user1 = GSActor::create(['nickname' => 'user1']);
|
||||
$user2 = GSActor::create(['nickname' => 'user2']);
|
||||
$user3 = GSActor::create(['nickname' => 'user3']);
|
||||
|
||||
$testArr = [$user1, $user2, $user3];
|
||||
|
||||
static::assertSame('user1 user2 user3', (new ActorArrayTransformer)->transform($testArr));
|
||||
}
|
||||
public function testReverseTransform()
|
||||
{
|
||||
$testString = '';
|
||||
static::assertSame([], (new ActorArrayTransformer)->reverseTransform($testString));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user