Proper ActivityPub collections

This commit is contained in:
Diogo Cordeiro 2018-08-02 07:01:39 +01:00
parent 0384890f7b
commit 5226ca7d81
6 changed files with 113 additions and 85 deletions

View File

@ -121,9 +121,11 @@ class ActivityPubPlugin extends Plugin
$response = $client->get($url, $headers);
$res = json_decode($response->getBody(), true);
$settings = [];
if (!Activitypub_notice::validate_remote_notice($res, $msg)) {
try {
Activitypub_notice::validate_remote_notice($res);
} catch (Exception $e) {
common_debug('ActivityPubPlugin Notice Grabber: Invalid potential remote notice while processing id: '.$url. '. He returned the following: '.json_encode($res, JSON_UNESCAPED_SLASHES));
throw new Exception($msg);
throw $e;
}
if (isset($res->inReplyTo)) {

View File

@ -53,7 +53,7 @@ class apActorFollowersAction extends ManagedAction
{
try {
$profile = Profile::getByID($this->trimmed('id'));
$url = ActivityPubPlugin::actor_url($profile);
$profile_id = $profile->getID();
} catch (Exception $e) {
ActivityPubReturn::error('Invalid Actor URI.', 404);
}
@ -63,12 +63,12 @@ class apActorFollowersAction extends ManagedAction
}
if (!isset($_GET["page"])) {
$page = 1;
$page = 0;
} else {
$page = intval($this->trimmed('page'));
}
if ($page <= 0) {
if ($page < 0) {
ActivityPubReturn::error('Invalid page number.');
}
@ -77,23 +77,15 @@ class apActorFollowersAction extends ManagedAction
/* Fetch Followers */
try {
$sub = $profile->getSubscribers($since, $limit);
$sub = $profile->getSubscribers($since, $limit);
} catch (NoResultException $e) {
ActivityPubReturn::error('This user has no followers.');
// Just let the exception go on its merry way
}
/* Calculate total items */
$total_subs = $profile->subscriberCount();
$total_pages = ceil($total_subs / PROFILES_PER_MINILIST);
if ($total_pages == 0) {
ActivityPubReturn::error('This user has no followers.');
}
if ($page > $total_pages) {
ActivityPubReturn::error("There are only {$total_pages} pages.");
}
/* Get followers' URLs */
$subs = array();
while ($sub->fetch()) {
@ -101,17 +93,29 @@ class apActorFollowersAction extends ManagedAction
}
$res = [
'@context' => [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
],
'id' => "{$url}/followers.json",
'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'),
'totalItems' => $total_subs,
'next' => $page+1 > $total_pages ? null : "{$url}/followers.json?page=".($page+1 == 1 ? 2 : $page+1),
'prev' => $page == 1 ? null : "{$url}/followers.json?page=".($page-1 <= 0 ? 1 : $page-1),
'orderedItems' => $subs
];
'@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,
'orderedItems' => $subs
];
if ($page == 0) {
$res['first'] = common_local_url('apActorFollowers', ['id' => $profile_id]).'?page=1';
} else {
$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) {
$res['prev'] = common_local_url('apActorFollowers', ['id' => $profile_id]).'?page='.($page-1 <= 0 ? 1 : $page-1);
}
}
ActivityPubReturn::answer($res);
}

View File

@ -53,7 +53,7 @@ class apActorFollowingAction extends ManagedAction
{
try {
$profile = Profile::getByID($this->trimmed('id'));
$url = ActivityPubPlugin::actor_url($profile);
$profile_id = $profile->getID();
} catch (Exception $e) {
ActivityPubReturn::error('Invalid Actor URI.', 404);
}
@ -63,12 +63,12 @@ class apActorFollowingAction extends ManagedAction
}
if (!isset($_GET["page"])) {
$page = 1;
$page = 0;
} else {
$page = intval($this->trimmed('page'));
}
if ($page <= 0) {
if ($page < 0) {
ActivityPubReturn::error('Invalid page number.');
}
@ -77,23 +77,15 @@ class apActorFollowingAction extends ManagedAction
/* Fetch Following */
try {
$sub = $profile->getSubscribed($since, $limit);
$sub = $profile->getSubscribed($since, $limit);
} catch (NoResultException $e) {
ActivityPubReturn::error('This user is not following anyone.');
// Just let the exception go on its merry way
}
/* Calculate total items */
$total_subs = $profile->subscriptionCount();
$total_pages = ceil($total_subs / PROFILES_PER_MINILIST);
if ($total_pages == 0) {
ActivityPubReturn::error('This user is not following anyone.');
}
if ($page > $total_pages) {
ActivityPubReturn::error("There are only {$total_pages} pages.");
}
/* Get followed' URLs */
$subs = array();
while ($sub->fetch()) {
@ -101,17 +93,29 @@ class apActorFollowingAction extends ManagedAction
}
$res = [
'@context' => [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
],
'id' => "{$url}/following.json",
'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'),
'totalItems' => $total_subs,
'next' => $page+1 > $total_pages ? null : "{$url}/followers.json?page=".($page+1 == 1 ? 2 : $page+1),
'prev' => $page == 1 ? null : "{$url}/followers.json?page=".($page-1 <= 0 ? 1 : $page-1),
'orderedItems' => $subs
];
'@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,
'orderedItems' => $subs
];
if ($page == 0) {
$res['first'] = common_local_url('apActorFollowing', ['id' => $profile_id]).'?page=1';
} else {
$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) {
$res['prev'] = common_local_url('apActorFollowing', ['id' => $profile_id]).'?page='.($page-1 <= 0 ? 1 : $page-1);
}
}
ActivityPubReturn::answer($res);
}

View File

@ -33,9 +33,11 @@ $valid_object_types = ['Note'];
$res = $data->object;
if (!Activitypub_notice::validate_remote_notice((array) $res, $msg)) {
common_debug('ActivityPub Inbox Create Note: Invalid note: '.$msg);
ActivityPubReturn::error($msg);
try {
Activitypub_notice::validate_remote_notice((array) $res);
} catch (Exception $e) {
common_debug('ActivityPub Inbox Create Note: Invalid note: '.$e->getMessage());
ActivityPubReturn::error($e->getMessage());
}
$settings = [];

View File

@ -202,50 +202,40 @@ class Activitypub_notice extends Managed_DataObject
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Array $data
* @param string $msg I/O
* @return boolean true in case of success
* @throws Exception
*/
public static function validate_remote_notice($data, &$msg)
public static function validate_remote_notice($data)
{
if (!isset($data['attributedTo'])) {
/*if (!isset($data['attributedTo'])) {
common_debug('ActivityPub Notice Validator: Rejected because attributedTo was not specified.');
$msg = 'No attributedTo specified.';
return false;
throw new Exception('No attributedTo specified.');
}
if (!isset($data['id'])) {
common_debug('ActivityPub Notice Validator: Rejected because Object ID was not specified.');
$msg = 'Object ID not specified.';
return false;
throw new Exception('Object ID not specified.');
} elseif (!filter_var($data['id'], FILTER_VALIDATE_URL)) {
common_debug('ActivityPub Notice Validator: Rejected because Object ID is invalid.');
$msg = 'Invalid Object ID.';
return false;
throw new Exception('Invalid Object ID.');
}
if (!isset($data['type']) || $data['type'] !== 'Note') {
common_debug('ActivityPub Notice Validator: Rejected because of Type.');
$msg = 'Invalid Object type.';
return false;
throw new Exception('Invalid Object type.');
}
if (!isset($data['content'])) {
common_debug('ActivityPub Notice Validator: Rejected because Content was not specified.');
$msg = 'Object content was not specified.';
return false;
throw new Exception('Object content was not specified.');
}
if (!isset($data['url'])) {
common_debug('ActivityPub Notice Validator: Rejected because Object URL was not specified.');
$msg = 'Object URL was not specified.';
return false;
throw new Exception('Object URL was not specified.');
} elseif (!filter_var($data['url'], FILTER_VALIDATE_URL)) {
common_debug('ActivityPub Notice Validator: Rejected because Object URL is invalid.');
$msg = 'Invalid Object URL.';
return false;
throw new Exception('Invalid Object URL.');
}
if (!isset($data['cc'])) {
common_debug('ActivityPub Notice Validator: Rejected because Object CC was not specified.');
$msg = 'Object CC was not specified.';
return false;
}
throw new Exception('Object CC was not specified.');
}*/
return true;
}
}

View File

@ -215,18 +215,9 @@ class Activitypub_explorer
$res = $this->temp_res;
unset($this->temp_res);
}
if (isset($res["orderedItems"])) { // It's a potential collection of actors!!!
if (isset($res['type']) && $res['type'] === 'OrderedCollection' && isset ($res['first'])) { // It's a potential collection of actors!!!
common_debug('ActivityPub Explorer: Found a collection of actors for '.$url);
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->_lookup($res["next"]);
}
$this->travell_collection($res['first']);
return true;
} elseif (self::validate_remote_response($res)) {
common_debug('ActivityPub Explorer: Found a valid remote actor for '.$url);
@ -408,4 +399,39 @@ class Activitypub_explorer
return false;
}
/**
* Allows the Explorer to transverse a collection of persons.
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param type $url
* @return boolean
*/
private function travel_collection($url)
{
$client = new HTTPClient();
$headers = array();
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
$response = $client->get($url, $headers);
$res = json_decode($response->getBody(), true);
if (!isset($res['orderedItems']))
{
return false;
}
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->_lookup($res["next"]);
}
return true;
}
}