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:
@@ -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
|
||||
|
@@ -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 = [
|
||||
|
@@ -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 = [
|
||||
|
@@ -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.");
|
||||
}
|
||||
|
@@ -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'));
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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));
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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(
|
||||
|
@@ -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
|
||||
];
|
||||
|
@@ -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());
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -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) {
|
||||
|
Reference in New Issue
Block a user