[TESTS] Added unit tests

This commit is contained in:
Daniel 2020-10-16 01:07:01 +01:00 committed by Hugo Sales
parent d53fef09a8
commit 95f95d2dd8
Signed by untrusted user: someonewithpc
GPG Key ID: 7D0C7EAFC9D835A0
29 changed files with 716 additions and 477 deletions

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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(

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -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()

View File

@ -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:

View File

@ -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,
];

View File

@ -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,
];
}

View File

@ -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

View File

@ -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'])) {

View File

@ -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,
];
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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
{

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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>

View File

@ -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));
}
}