Proper AP Notices (fixed #46)

Explorer now works both for local and remote URIs
Fixed various serious errors (most of them in Explorer
and some in AP Profiles)
This commit is contained in:
Diogo Cordeiro
2018-07-29 02:35:04 +01:00
parent 5c351efb06
commit edb3633bcd
16 changed files with 278 additions and 129 deletions

View File

@@ -34,6 +34,9 @@ require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "di
require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "explorer.php";
require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "postman.php";
// So that this isn't hardcoded everywhere
define('ACTIVITYPUB_BASE_INSTANCE_URI', common_root_url()."index.php/user/");
/**
* @category Plugin
* @package GNUsocial
@@ -54,7 +57,7 @@ class ActivityPubPlugin extends Plugin
public static function actor_uri($profile)
{
if ($profile->isLocal()) {
return common_root_url()."index.php/user/".$profile->getID();
return ACTIVITYPUB_BASE_INSTANCE_URI.$profile->getID();
} else {
return $profile->getUri();
}
@@ -74,46 +77,30 @@ class ActivityPubPlugin extends Plugin
}
/**
* Get remote user's ActivityPub_profile via a identifier
* Returns a notice from its URL since GNU Social doesn't provide
* this functionality
*
* @author GNU Social
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param string $arg A remote user identifier
* @return Activitypub_profile|null Valid profile in success | null otherwise
* @param string $url Notice's URL
* @return Notice The Notice object
* @throws Exception This function or provides a Notice or fails with exception
*/
public static function pull_remote_profile($arg)
public static function get_local_notice_from_url($url)
{
if (preg_match('!^((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)$!', $arg)) {
// webfinger lookup
try {
return Activitypub_profile::ensure_web_finger($arg);
return Notice::getByUri($data->object->inReplyTo);
} catch (Exception $e) {
common_log(LOG_ERR, 'Webfinger lookup failed for ' .
$arg . ': ' . $e->getMessage());
}
}
// Look for profile URLs, with or without scheme:
$urls = array();
if (preg_match('!^https?://((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)$!', $arg)) {
$urls[] = $arg;
}
if (preg_match('!^((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)$!', $arg)) {
$schemes = array('http', 'https');
foreach ($schemes as $scheme) {
$urls[] = "$scheme://$arg";
}
}
foreach ($urls as $url) {
try {
return Activitypub_profile::get_from_uri($url);
$candidate = Notice::getByID(intval(substr($url, strlen(common_local_url('shownotice', ['notice' => ''])))));
if ($candidate->getUrl() == $url) {
return $candidate;
} else {
throw new Exception("Notice not found.");
}
} catch (Exception $e) {
common_log(LOG_ERR, 'Profile lookup failed for ' .
$arg . ': ' . $e->getMessage());
throw new Exception("Notice not found.");
}
}
return null;
}
/**
@@ -234,6 +221,49 @@ class ActivityPubPlugin extends Plugin
* WebFinger Events *
********************************************************/
/**
* Get remote user's ActivityPub_profile via a identifier
*
* @author GNU Social
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param string $arg A remote user identifier
* @return Activitypub_profile|null Valid profile in success | null otherwise
*/
public static function pull_remote_profile($arg)
{
if (preg_match('!^((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)$!', $arg)) {
// webfinger lookup
try {
return Activitypub_profile::ensure_web_finger($arg);
} catch (Exception $e) {
common_log(LOG_ERR, 'Webfinger lookup failed for ' .
$arg . ': ' . $e->getMessage());
}
}
// Look for profile URLs, with or without scheme:
$urls = array();
if (preg_match('!^https?://((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)$!', $arg)) {
$urls[] = $arg;
}
if (preg_match('!^((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)$!', $arg)) {
$schemes = array('http', 'https');
foreach ($schemes as $scheme) {
$urls[] = "$scheme://$arg";
}
}
foreach ($urls as $url) {
try {
return Activitypub_profile::fromUri($url);
} catch (Exception $e) {
common_log(LOG_ERR, 'Profile lookup failed for ' .
$arg . ': ' . $e->getMessage());
}
}
return null;
}
/**
* Webfinger matches: @user@example.com or even @user--one.george_orwell@1984.biz
*
@@ -357,7 +387,7 @@ class ActivityPubPlugin extends Plugin
$url = "$scheme://$target";
$this->log(LOG_INFO, "Checking profile address '$url'");
try {
$aprofile = Activitypub_profile::get_from_uri($url);
$aprofile = Activitypub_profile::fromUri($url);
$profile = $aprofile->local_profile();
$displayName = !empty($profile->nickname) && mb_strlen($profile->nickname) < mb_strlen($target) ?
$profile->nickname : $target;
@@ -432,7 +462,7 @@ class ActivityPubPlugin extends Plugin
{
$aprofile = Activitypub_profile::getKV('profile_id', $profile->id);
if ($aprofile instanceof Activitypub_profile) {
$uri = $aprofile->get_uri();
$uri = $aprofile->getUri();
return false;
}
return true;
@@ -449,27 +479,9 @@ class ActivityPubPlugin extends Plugin
*/
public function onStartGetProfileFromURI($uri, &$profile)
{
// Don't want to do Web-based discovery on our own server,
// so we check locally first. This duplicates the functionality
// in the Profile class, since the plugin always runs before
// that local lookup, but since we return false it won't run double.
$user = User::getKV('uri', $uri);
if ($user instanceof User) {
$profile = $user->getProfile();
return false;
} else {
$group = User_group::getKV('uri', $uri);
if ($group instanceof User_group) {
$profile = $group->getProfile();
return false;
}
}
// Now, check remotely
try {
$aprofile = Activitypub_profile::get_from_uri($uri);
$profile = $aprofile->local_profile();
$explorer = new Activitypub_explorer();
$profile = $explorer->lookup($uri)[0];
return false;
} catch (Exception $e) {
return true; // It's not an ActivityPub profile as far as we know, continue event handling

View File

@@ -58,6 +58,10 @@ class apActorFollowersAction extends ManagedAction
ActivityPubReturn::error('Invalid Actor URI.', 404);
}
if (!$profile->isLocal()) {
ActivityPubReturn::error("This is not a local user.");
}
if (!isset($_GET["page"])) {
$page = 1;
} else {
@@ -93,7 +97,7 @@ class apActorFollowersAction extends ManagedAction
/* Get followers' URLs */
$subs = array();
while ($sub->fetch()) {
$subs[] = $sub->profileurl;
$subs[] = ActivityPubPlugin::actor_uri($sub);
}
$res = [

View File

@@ -58,6 +58,10 @@ class apActorFollowingAction extends ManagedAction
ActivityPubReturn::error('Invalid Actor URI.', 404);
}
if (!$profile->isLocal()) {
ActivityPubReturn::error("This is not a local user.");
}
if (!isset($_GET["page"])) {
$page = 1;
} else {
@@ -93,7 +97,7 @@ class apActorFollowingAction extends ManagedAction
/* Get followed' URLs */
$subs = array();
while ($sub->fetch()) {
$subs[] = $sub->profileurl;
$subs[] = ActivityPubPlugin::actor_uri($sub);
}
$res = [

View File

@@ -57,6 +57,10 @@ class apActorInboxAction extends ManagedAction
ActivityPubReturn::error('Invalid Actor URI.', 404);
}
if (!$profile->isLocal()) {
ActivityPubReturn::error("This is not a local user.");
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
ActivityPubReturn::error("C2S not implemented just yet.");
}

View File

@@ -58,6 +58,10 @@ class apActorLikedAction extends ManagedAction
ActivityPubReturn::error('Invalid Actor URI.', 404);
}
if (!$profile->isLocal()) {
ActivityPubReturn::error("This is not a local user.");
}
$limit = intval($this->trimmed('limit'));
$since_id = intval($this->trimmed('since_id'));
$max_id = intval($this->trimmed('max_id'));

View File

@@ -30,7 +30,7 @@ if (!defined('GNUSOCIAL')) {
}
try {
Notice::getByUri($data->object->id)->repeat($actor_profile, "ActivityPub");
ActivityPubPlugin::get_local_notice_from_url($data->object->id)->repeat($actor_profile, "ActivityPub");
ActivityPubReturn::answer("Notice repeated successfully.");
} catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 403);

View File

@@ -32,8 +32,10 @@ if (!defined('GNUSOCIAL')) {
$valid_object_types = array("Note");
// Validate data
if (!isset($data->id)) {
ActivityPubReturn::error("Id not specified.");
if (!isset($data->object->id)) {
ActivityPubReturn::error("Object ID not specified.");
} elseif (!filter_var($data->object->id, FILTER_VALIDATE_URL)) {
ActivityPubReturn::error("Invalid Object ID.");
}
if (!(isset($data->object->type) && in_array($data->object->type, $valid_object_types))) {
ActivityPubReturn::error("Invalid Object type.");
@@ -60,19 +62,19 @@ $act->actor = $actor_profile->asActivityObject();
$act->context = new ActivityContext();
// Is this a reply?
if (isset($data->object->reply_to)) {
if (isset($data->object->inReplyTo)) {
try {
$reply_to = Notice::getByUri($data->object->reply_to);
$inReplyTo = ActivityPubPlugin::get_local_notice_from_url($data->object->inReplyTo);
} catch (Exception $e) {
ActivityPubReturn::error("Invalid Object reply_to value.");
ActivityPubReturn::error("Invalid Object inReplyTo value.");
}
$act->context->replyToID = $reply_to->getUri();
$act->context->replyToUrl = $reply_to->getUrl();
$act->context->replyToID = $inReplyTo->getUri();
$act->context->replyToUrl = $inReplyTo->getUrl();
} else {
$reply_to = null;
$inReplyTo = null;
}
$act->context->attention = common_get_attentions($content, $actor_profile, $reply_to);
$act->context->attention = common_get_attentions($content, $actor_profile, $inReplyTo);
$discovery = new Activitypub_explorer;
if ($to_profiles == "https://www.w3.org/ns/activitystreams#Public") {
@@ -86,22 +88,43 @@ if (is_array($data->object->to)) {
try {
$to_profiles = array_merge($to_profiles, $discovery->lookup($to_url));
} catch (Exception $e) {
// XXX: Invalid actor found, not sure how we handle those
// Invalid actor found, just let it go.
}
}
} elseif (empty($data->object->to) || in_array($data->object->to, $public_to)) {
// No need to do anything else at this point, let's just break out the if
} else {
try {
$to_profiles[]= $discovery->lookup($data->object->to);
$to_profiles = array_merge($to_profiles, $discovery->lookup($data->object->to));
} catch (Exception $e) {
ActivityPubReturn::error("Invalid Actor.", 404);
// Invalid actor found, just let it go.
}
}
// Generate Cc objects
if (isset($data->object->cc) && is_array($data->object->cc)) {
// Remove duplicates from Cc actors set
array_unique($data->object->to);
foreach ($data->object->cc as $cc_url) {
try {
$to_profiles = array_merge($to_profiles, $discovery->lookup($cc_url));
} catch (Exception $e) {
// Invalid actor found, just let it go.
}
}
} elseif (empty($data->object->cc) || in_array($data->object->cc, $public_to)) {
// No need to do anything else at this point, let's just break out the if
} else {
try {
$to_profiles = array_merge($to_profiles, $discovery->lookup($data->object->cc));
} catch (Exception $e) {
// Invalid actor found, just let it go.
}
}
unset($discovery);
foreach ($to_profiles as $to) {
$act->context->attention[ActivityPubPlugin::actor_uri($to)] = "http://activitystrea.ms/schema/1.0/person";
foreach ($to_profiles as $tp) {
$act->context->attention[ActivityPubPlugin::actor_uri($tp)] = "http://activitystrea.ms/schema/1.0/person";
}
// Reject notice if it is too long (without the HTML)
@@ -116,7 +139,7 @@ ToSelector::fillActivity($this, $act, $options);
$actobj = new ActivityObject();
$actobj->type = ActivityObject::NOTE;
$actobj->content = common_render_content($content, $actor_profile, $reply_to);
$actobj->content = common_render_content($content, $actor_profile, $inReplyTo);
// Finally add the activity object to our activity
$act->objects[] = $actobj;

View File

@@ -30,7 +30,7 @@ if (!defined('GNUSOCIAL')) {
}
try {
$notice = Notice::getByUri($data->object->id);
$notice = ActivityPubPlugin::get_local_notice_from_url($data->object->id);
$notice_to_array = Activitypub_notice::notice_to_array($notice);
$notice->deleteAs($actor_profile);
ActivityPubReturn::answer(Activitypub_delete::delete_to_array($notice_to_array));

View File

@@ -34,8 +34,13 @@ if (!isset($data->object->id)) {
}
try {
Fave::addNew($actor_profile, Notice::getByUri($data->object->id));
ActivityPubReturn::answer(Activitypub_like::like_to_array(Activitypub_notice::notice_to_array($data->actor, json_decode($data->object))));
try {
$object_notice = ActivityPubPlugin::get_local_notice_from_url($data->object->id);
} catch (Exception $e) {
ActivityPubReturn::error("Invalid Object ID value.");
}
Fave::addNew($actor_profile, $object_notice);
ActivityPubReturn::answer(Activitypub_like::like_to_array($data->actor, Activitypub_notice::notice_to_array($object_notice)));
} catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 403);
}

View File

@@ -41,7 +41,7 @@ case "Like":
if (!isset($data->object->object->id)) {
ActivityPubReturn::error("Notice ID was not specified.");
}
Fave::removeEntry($actor_profile, Notice::getByUri($data->object->object->id));
Fave::removeEntry($actor_profile, ActivityPubPlugin::get_local_notice_from_url($data->object->object->id));
// Notice disfavorited successfully.
ActivityPubReturn::answer(
Activitypub_undo::undo_to_array(

View File

@@ -34,7 +34,6 @@ if (!defined('GNUSOCIAL')) {
*
* @category Plugin
* @package GNUsocial
* @author Daniel Supernault <danielsupernault@gmail.com>
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
@@ -44,13 +43,13 @@ class Activitypub_notice extends Managed_DataObject
/**
* Generates a pretty notice from a Notice object
*
* @author Daniel Supernault <danielsupernault@gmail.com>
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param Notice $notice
* @return pretty array to be used in a response
*/
public static function notice_to_array($notice)
{
$profile = $notice->getProfile();
$attachments = array();
foreach ($notice->attachments() as $attachment) {
$attachments[] = Activitypub_attachment::attachment_to_array($attachment);
@@ -63,7 +62,7 @@ class Activitypub_notice extends Managed_DataObject
}
}
$to = array();
$to = [];
foreach ($notice->getAttentionProfiles() as $to_profile) {
$to[] = $to_profile->getUri();
}
@@ -72,16 +71,18 @@ class Activitypub_notice extends Managed_DataObject
}
$item = [
'id' => $notice->getUri(),
'id' => $notice->getUrl(),
'type' => 'Note',
'actor' => $notice->getProfile()->getUrl(),
'inReplyTo' => empty($notice->reply_to) ? null : Notice::getById($notice->reply_to)->getUrl(),
'published' => $notice->getCreated(),
'to' => $to,
'content' => $notice->getContent(),
'url' => $notice->getUrl(),
'reply_to' => empty($notice->reply_to) ? null : Notice::getById($notice->reply_to)->getUri(),
'is_local' => $notice->isLocal(),
'atributedTo' => ActivityPubPlugin::actor_uri($profile),
'to' => $to,
'atomUri' => $notice->getUrl(),
'inReplyToAtomUri' => empty($notice->reply_to) ? null : Notice::getById($notice->reply_to)->getUrl(),
'conversation' => $notice->getConversationUrl(),
'content' => $notice->getContent(),
'is_local' => $notice->isLocal(),
'attachment' => $attachments,
'tag' => $tags
];

View File

@@ -41,6 +41,18 @@ if (!defined('GNUSOCIAL')) {
class Activitypub_profile extends Managed_DataObject
{
public $__table = 'Activitypub_profile';
public $uri; // text() not_null
public $profile_id; // int(4) primary_key not_null
public $inboxuri; // text() not_null
public $sharedInboxuri; // text()
public $nickname; // varchar(64) multiple_key not_null
public $fullname; // text()
public $profileurl; // text()
public $homepage; // text()
public $bio; // text() multiple_key
public $location; // text()
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
/**
* Return table definition for Schema setup and DB_DataObject usage.
@@ -108,7 +120,7 @@ class Activitypub_profile extends Managed_DataObject
'liked' => common_local_url("apActorLiked", array("id" => $id)),
'inbox' => common_local_url("apActorInbox", array("id" => $id)),
'preferredUsername' => $profile->getNickname(),
'name' => $profile->getFullname(),
'name' => $profile->getBestName(),
'summary' => ($desc = $profile->getDescription()) == null ? "" : $desc,
'url' => $profile->getUrl(),
'manuallyApprovesFollowers' => false,
@@ -280,11 +292,33 @@ class Activitypub_profile extends Managed_DataObject
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @return string URI
*/
public function get_uri()
public function getUri()
{
return $this->uri;
}
/**
* Getter for url property
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @return string URL
*/
public function getUrl()
{
return $this->getUri();
}
/**
* Getter for id property
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @return int32
*/
public function getID()
{
return $this->profile_id;
}
/**
* Ensures a valid Activitypub_profile when provided with a valid URI.
*
@@ -293,7 +327,7 @@ class Activitypub_profile extends Managed_DataObject
* @return Activitypub_profile
* @throws Exception if it isn't possible to return an Activitypub_profile
*/
public static function get_from_uri($url)
public static function fromUri($url)
{
$explorer = new Activitypub_explorer();
$profiles_found = $explorer->lookup($url);
@@ -331,7 +365,7 @@ class Activitypub_profile extends Managed_DataObject
throw new Exception(_m('Not a valid webfinger address (via cache).'));
}
try {
return self::get_from_uri($uri);
return self::fromUri($uri);
} catch (Exception $e) {
common_log(LOG_ERR, sprintf(__METHOD__ . ': Webfinger address cache inconsistent with database, did not find Activitypub_profile uri==%s', $uri));
self::cacheSet(sprintf('activitypub_profile:webfinger:%s', $addr), false);
@@ -372,8 +406,8 @@ class Activitypub_profile extends Managed_DataObject
$profileUrl = $hints['profileurl'];
try {
common_log(LOG_INFO, "Discovery on acct:$addr with profile URL $profileUrl");
$aprofile = self::get_from_uri($hints['profileurl']);
self::cacheSet(sprintf('activitypub_profile:webfinger:%s', $addr), $aprofile->get_uri());
$aprofile = self::fromUri($hints['profileurl']);
self::cacheSet(sprintf('activitypub_profile:webfinger:%s', $addr), $aprofile->getUri());
return $aprofile;
} catch (Exception $e) {
common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage());

View File

@@ -49,11 +49,17 @@ class ProfileObjectTest extends TestCase
/* Test get_inbox() */
$this->assertTrue($aprofile->sharedInboxuri == $aprofile->get_inbox());
/* Test get_uri() */
$this->assertTrue($aprofile->uri == $aprofile->get_uri());
/* Test getUri() */
$this->assertTrue($aprofile->uri == $aprofile->getUri());
/* Test get_from_uri() */
$this->assertTrue($this->compare_aprofiles($aprofile, \Activitypub_profile::get_from_uri($aprofile->uri)));
/* Test getUrl() */
$this->assertTrue($profile->getUrl() == $aprofile->getUrl());
/* Test getID() */
$this->assertTrue($profile->getID() == $aprofile->getID());
/* Test fromUri() */
$this->assertTrue($this->compare_aprofiles($aprofile, \Activitypub_profile::fromUri($aprofile->uri)));
/* Remove Remote User Test 1 */
$old_id = $profile->getID();
@@ -124,20 +130,28 @@ class ProfileObjectTest extends TestCase
private function compare_aprofiles(\Activitypub_profile $a, \Activitypub_profile $b)
{
if (($av = $a->get_uri()) != ($bv = $b->get_uri())) {
if (($av = $a->getUri()) != ($bv = $b->getUri())) {
throw new Exception('Compare AProfiles 1 Fail: $a: '.$av.' is different from $b: '.$bv);
}
if (($av = $a->profile_id) != ($bv = $b->profile_id)) {
if (($av = $a->getUrl()) != ($bv = $b->getUrl())) {
throw new Exception('Compare AProfiles 2 Fail: $a: '.$av.' is different from $b: '.$bv);
}
if (($av = $a->inboxuri) != ($bv = $b->inboxuri)) {
if (($av = $a->getID()) != ($bv = $b->getID())) {
throw new Exception('Compare AProfiles 3 Fail: $a: '.$av.' is different from $b: '.$bv);
}
if (($av = $a->profile_id) != ($bv = $b->profile_id)) {
throw new Exception('Compare AProfiles 4 Fail: $a: '.$av.' is different from $b: '.$bv);
}
if (($av = $a->inboxuri) != ($bv = $b->inboxuri)) {
throw new Exception('Compare AProfiles 5 Fail: $a: '.$av.' is different from $b: '.$bv);
}
if (($av = $a->sharedInboxuri) != ($bv = $b->sharedInboxuri)) {
throw new Exception('Compare AProfiles 1 Fail: $a: '.$av.' is different from $b: '.$bv);
throw new Exception('Compare AProfiles 6 Fail: $a: '.$av.' is different from $b: '.$bv);
}
return true;

View File

@@ -32,7 +32,7 @@ if (!defined('GNUSOCIAL')) {
/**
* ActivityPub's own Explorer
*
* Allows to discovery new (or the same) ActivityPub profiles
* Allows to discovery new (or the same) Profiles (both local or remote)
*
* @category Plugin
* @package GNUsocial
@@ -55,6 +55,7 @@ class Activitypub_explorer
*/
public function lookup($url)
{
common_debug("Explorer started now looking for ".$url);
$this->discovered_actor_profiles = array();
return $this->_lookup($url);
@@ -95,9 +96,6 @@ class Activitypub_explorer
$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);
if (!$response->isOk()) {
throw new Exception("Invalid Actor URL.");
}
$res = json_decode($response->getBody(), JSON_UNESCAPED_SLASHES);
if (self::validate_remote_response($res)) {
$this->temp_res = $res;
@@ -108,7 +106,7 @@ class Activitypub_explorer
}
/**
* Get a local user profiles from its URL and joins it on
* Get a local user profile from its URL and joins it on
* $this->discovered_actor_profiles
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
@@ -117,6 +115,11 @@ class Activitypub_explorer
*/
private function grab_local_user($uri, $online = false)
{
if ($online) {
common_debug("Explorer is searching locally for ".$uri. " online.");
} else {
common_debug("Explorer is searching locally for ".$uri. " offline.");
}
// Ensure proper remote URI
// If an exception occurs here it's better to just leave everything
// break than to continue processing
@@ -126,14 +129,31 @@ class Activitypub_explorer
// Try standard ActivityPub route
// Is this a known filthy little mudblood?
$aprofile = Activitypub_profile::getKV("uri", $uri);
$aprofile = self::get_aprofile_by_url($uri);
if ($aprofile instanceof Activitypub_profile) {
$profile = $aprofile->local_profile();
common_debug("Explorer found a local Aprofile for ".$uri);
// We found something!
$this->discovered_actor_profiles[]= $profile;
unset($this->temp_res); // IMPORTANT to avoid _dangerous_ noise in the Explorer system
return true;
} else {
common_debug("Explorer didn't find a local Aprofile for ".$uri);
// Well, maybe it is a pure blood?
// Iff, we are in the same instance:
$ACTIVITYPUB_BASE_INSTANCE_URI_length = strlen(ACTIVITYPUB_BASE_INSTANCE_URI);
if (substr($uri, 0, $ACTIVITYPUB_BASE_INSTANCE_URI_length) == ACTIVITYPUB_BASE_INSTANCE_URI) {
try {
$profile = Profile::getByID(intval(substr($uri, $ACTIVITYPUB_BASE_INSTANCE_URI_length)));
// We found something!
$this->discovered_actor_profiles[]= $profile;
unset($this->temp_res); // IMPORTANT to avoid _dangerous_ noise in the Explorer system
return true;
} catch (Exception $e) {
// Let the exception go on its merry way.
}
}
}
// If offline grabbing failed, attempt again with online resources
@@ -154,15 +174,13 @@ class Activitypub_explorer
*/
private function grab_remote_user($url)
{
common_debug("Explorer is grabbing a remote profile for ".$url);
if (!isset($this->temp_res)) {
$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);
if (!$response->isOk()) {
throw new Exception("Invalid Actor URL.");
}
$res = json_decode($response->getBody(), JSON_UNESCAPED_SLASHES);
} else {
$res = $this->temp_res;
@@ -234,6 +252,32 @@ class Activitypub_explorer
return true;
}
/**
* Get a ActivityPub Profile from it's uri
* Unfortunately GNU Social cache is not truly reliable when handling
* potential ActivityPub remote profiles, as so it is important to use
* this hacky workaround (at least for now)
*
* @author Diogo Cordeiro <diogo@fc.up.pt>
* @param string $v URL
* @return boolean|Activitypub_profile false if fails | Aprofile object if successful
*/
public static function get_aprofile_by_url($v)
{
$i = Managed_DataObject::getcached("Activitypub_profile", "uri", $v);
if (empty($i)) { // false = cache miss
$i = new Activitypub_profile;
$result = $i->get("uri", $v);
if ($result) {
// Hit!
$i->encache();
} else {
return false;
}
}
return $i;
}
/**
* Given a valid actor profile url returns its inboxes
*

View File

@@ -168,12 +168,12 @@ class Activitypub_postman
public function create($notice)
{
$data = Activitypub_create::create_to_array(
$notice->getUri(),
$notice->getUrl(),
ActivityPubPlugin::actor_uri($this->actor),
Activitypub_notice::notice_to_array($notice)
array_merge(Activitypub_notice::notice_to_array($notice), ['cc' => common_local_url('apActorFollowers', ['id' => $this->actor->getID()]),])
);
if (isset($notice->reply_to)) {
$data["object"]["reply_to"] = $notice->getParent()->getUri();
$data["object"]["reply_to"] = $notice->getParent()->getUrl();
}
$this->client->setBody(json_encode($data));
foreach ($this->to_inbox() as $inbox) {