Fixed various bugs, improved and refactored some stuff

(remote avatars are working)
This commit is contained in:
Diogo Cordeiro 2018-08-02 18:02:28 +01:00
parent cfc4eece38
commit aa491271f7
15 changed files with 109 additions and 116 deletions

View File

@ -39,7 +39,7 @@ require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "ex
require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "postman.php"; require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "postman.php";
// So that this isn't hardcoded everywhere // So that this isn't hardcoded everywhere
define('ACTIVITYPUB_BASE_INSTANCE_URI', common_root_url().'index.php/user/'); define('ACTIVITYPUB_BASE_ACTOR_URI', common_root_url().'index.php/user/');
const ACTIVITYPUB_PUBLIC_TO = ['https://www.w3.org/ns/activitystreams#Public', const ACTIVITYPUB_PUBLIC_TO = ['https://www.w3.org/ns/activitystreams#Public',
'Public', 'Public',
'as:Public' 'as:Public'
@ -65,7 +65,7 @@ class ActivityPubPlugin extends Plugin
public static function actor_uri($profile) public static function actor_uri($profile)
{ {
if ($profile->isLocal()) { if ($profile->isLocal()) {
return ACTIVITYPUB_BASE_INSTANCE_URI.$profile->getID(); return ACTIVITYPUB_BASE_ACTOR_URI.$profile->getID();
} else { } else {
return $profile->getUri(); return $profile->getUri();
} }
@ -254,7 +254,7 @@ class ActivityPubPlugin extends Plugin
$out->elementStart('dl', 'entity_tags activitypub_profile'); $out->elementStart('dl', 'entity_tags activitypub_profile');
$out->element('dt', null, _m('ActivityPub')); $out->element('dt', null, _m('ActivityPub'));
$out->element('dd', null, _m('Active')); $out->element('dd', null, _m('Remote Profile'));
$out->elementEnd('dl'); $out->elementEnd('dl');
} }
@ -513,7 +513,7 @@ class ActivityPubPlugin extends Plugin
* @param string $uri in/out * @param string $uri in/out
* @return mixed hook return code * @return mixed hook return code
*/ */
public function onStartGetProfileUri($profile, &$uri) public function onStartGetProfileUri(Profile $profile, &$uri)
{ {
$aprofile = Activitypub_profile::getKV('profile_id', $profile->id); $aprofile = Activitypub_profile::getKV('profile_id', $profile->id);
if ($aprofile instanceof Activitypub_profile) { if ($aprofile instanceof Activitypub_profile) {
@ -952,12 +952,18 @@ class ActivityPubReturn
* @param array $mimeTypes Supported Types * @param array $mimeTypes Supported Types
* @return array|null of supported mime types sorted | null if none valid * @return array|null of supported mime types sorted | null if none valid
*/ */
public static function getBestSupportedMimeType($mimeTypes = null) public static function getBestSupportedMimeType($mimeTypes)
{ {
// XXX: This function needs improvement!
if (!isset($_SERVER['HTTP_ACCEPT'])) { if (!isset($_SERVER['HTTP_ACCEPT'])) {
return null; return null;
} }
// This mime type was messing everything, thus the special case
if ($_SERVER['HTTP_ACCEPT'] == 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"') {
return true;
}
// Values will be stored in this array // Values will be stored in this array
$AcceptTypes = []; $AcceptTypes = [];
@ -970,7 +976,7 @@ class ActivityPubReturn
$q = 1; $q = 1;
// check if there is a different quality // check if there is a different quality
if (strpos($a, ';q=')) { if (strpos($a, ';q=')) {
// divide "mime/type;q=X" into two parts: "mime/type" i "X" // divide "mime/type;q=X" into two parts: "mime/type" and "X"
list($a, $q) = explode(';q=', $a); list($a, $q) = explode(';q=', $a);
} }
// mime-type $a is accepted with the quality $q // mime-type $a is accepted with the quality $q
@ -979,12 +985,7 @@ class ActivityPubReturn
} }
arsort($AcceptTypes); arsort($AcceptTypes);
// if no parameter was passed, just return parsed data $mimeTypes = array_map('strtolower', $mimeTypes);
if (!$mimeTypes) {
return $AcceptTypes;
}
$mimeTypes = array_map('strtolower', (array)$mimeTypes);
// lets check our supported types: // lets check our supported types:
foreach ($AcceptTypes as $mime => $q) { foreach ($AcceptTypes as $mime => $q) {
@ -992,6 +993,7 @@ class ActivityPubReturn
return $mime; return $mime;
} }
} }
// no mime-type found // no mime-type found
return null; return null;
} }

View File

@ -68,36 +68,30 @@ class apActorInboxAction extends ManagedAction
common_debug('ActivityPub Inbox: Received a POST request.'); common_debug('ActivityPub Inbox: Received a POST request.');
$data = file_get_contents('php://input'); $data = file_get_contents('php://input');
common_debug('ActivityPub Inbox: Request contents: '.$data); common_debug('ActivityPub Inbox: Request contents: '.$data);
$data = json_decode(file_get_contents('php://input')); $data = json_decode(file_get_contents('php://input'), true);
// Validate data // Validate data
if (!(isset($data->type))) { if (!(isset($data['type']))) {
ActivityPubReturn::error('Type was not specified.'); ActivityPubReturn::error('Type was not specified.');
} }
if (!isset($data->actor)) { if (!isset($data['actor'])) {
ActivityPubReturn::error('Actor was not specified.'); ActivityPubReturn::error('Actor was not specified.');
} }
if (!isset($data->object)) { if (!isset($data['object'])) {
ActivityPubReturn::error('Object was not specified.'); ActivityPubReturn::error('Object was not specified.');
} }
// Get valid Actor object // Get valid Actor object
try { try {
$actor_profile = ActivityPub_explorer::get_profile_from_url($data->actor); $actor_profile = ActivityPub_explorer::get_profile_from_url($data['actor']);
} catch (Exception $e) { } catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 404); ActivityPubReturn::error($e->getMessage(), 404);
} }
// Public To:
$public_to = ['https://www.w3.org/ns/activitystreams#Public',
'Public',
'as:Public'
];
$to_profiles = [$profile]; $to_profiles = [$profile];
// Process request // Process request
switch ($data->type) { switch ($data['type']) {
case "Create": case "Create":
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Create.php"; require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Create.php";
break; break;

0
actions/apnotice.php Normal file → Executable file
View File

View File

@ -58,36 +58,30 @@ class apSharedInboxAction extends ManagedAction
common_debug('ActivityPub Shared Inbox: Received a POST request.'); common_debug('ActivityPub Shared Inbox: Received a POST request.');
$data = file_get_contents('php://input'); $data = file_get_contents('php://input');
common_debug('ActivityPub Shared Inbox: Request contents: '.$data); common_debug('ActivityPub Shared Inbox: Request contents: '.$data);
$data = json_decode(file_get_contents('php://input')); $data = json_decode(file_get_contents('php://input'), true);
// Validate data // Validate data
if (!isset($data->type)) { if (!isset($data['type'])) {
ActivityPubReturn::error('Type was not specified.'); ActivityPubReturn::error('Type was not specified.');
} }
if (!isset($data->actor)) { if (!isset($data['actor'])) {
ActivityPubReturn::error('Actor was not specified.'); ActivityPubReturn::error('Actor was not specified.');
} }
if (!isset($data->object)) { if (!isset($data['object'])) {
ActivityPubReturn::error('Object was not specified.'); ActivityPubReturn::error('Object was not specified.');
} }
// Get valid Actor object // Get valid Actor object
try { try {
$actor_profile = ActivityPub_explorer::get_profile_from_url($data->actor); $actor_profile = ActivityPub_explorer::get_profile_from_url($data['actor']);
} catch (Exception $e) { } catch (Exception $e) {
ActivityPubReturn::error($e->getMessage(), 404); ActivityPubReturn::error($e->getMessage(), 404);
} }
// Public To: $to_profiles = [];
$public_to = ['https://www.w3.org/ns/activitystreams#Public',
'Public',
'as:Public'
];
$to_profiles = ['https://www.w3.org/ns/activitystreams#Public'];
// Process request // Process request
switch ($data->type) { switch ($data['type']) {
// Data available: // Data available:
// Profile $actor_profile // Profile $actor_profile
// string|object $data->object // string|object $data->object

View File

@ -30,20 +30,20 @@ if (!defined('GNUSOCIAL')) {
} }
// Validate data // Validate data
if (!isset($data->type)) { if (!isset($data['object']['type'])) {
ActivityPubReturn::error("Type was not specified."); ActivityPubReturn::error("Type was not specified.");
} }
switch ($data->object->type) { switch ($data['object']['type']) {
case "Follow": case "Follow":
// Validate data // Validate data
if (!isset($data->object->object)) { if (!isset($data['object']['object'])) {
ActivityPubReturn::error("Object Actor URL was not specified."); ActivityPubReturn::error("Object Actor URL was not specified.");
} }
// Get valid Object profile // Get valid Object profile
try { try {
$object_profile = new Activitypub_explorer; $object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($data->object->object)[0]; $object_profile = $object_profile->lookup($data['object']['object'])[0];
} catch (Exception $e) { } catch (Exception $e) {
ActivityPubReturn::error("Invalid Object Actor URL.", 404); ActivityPubReturn::error("Invalid Object Actor URL.", 404);
} }

View File

@ -31,7 +31,7 @@ if (!defined('GNUSOCIAL')) {
try { try {
try { try {
$object_notice = ActivityPubPlugin::grab_notice_from_url($data->object); $object_notice = ActivityPubPlugin::grab_notice_from_url($data['object']);
} catch (Exception $e) { } catch (Exception $e) {
ActivityPubReturn::error('Invalid Object specified.'); ActivityPubReturn::error('Invalid Object specified.');
} }

View File

@ -31,10 +31,10 @@ if (!defined('GNUSOCIAL')) {
$valid_object_types = ['Note']; $valid_object_types = ['Note'];
$res = $data->object; $res = $data['object'];
try { try {
Activitypub_notice::validate_remote_notice((array) $res); Activitypub_notice::validate_remote_notice($res);
} catch (Exception $e) { } catch (Exception $e) {
common_debug('ActivityPub Inbox Create Note: Invalid note: '.$e->getMessage()); common_debug('ActivityPub Inbox Create Note: Invalid note: '.$e->getMessage());
ActivityPubReturn::error($e->getMessage()); ActivityPubReturn::error($e->getMessage());
@ -42,23 +42,23 @@ try {
$settings = []; $settings = [];
if (isset($res->inReplyTo)) { if (isset($res['inReplyTo'])) {
$settings['inReplyTo'] = $res->inReplyTo; $settings['inReplyTo'] = $res['inReplyTo'];
} }
if (isset($res->latitude)) { if (isset($res['latitude'])) {
$settings['latitude'] = $res->latitude; $settings['latitude'] = $res['latitude'];
} }
if (isset($res->longitude)) { if (isset($res['longitude'])) {
$settings['longitude'] = $res->longitude; $settings['longitude'] = $res['longitude'];
} }
try { try {
Activitypub_notice::create_notice( Activitypub_notice::create_notice(
$actor_profile, $actor_profile,
$res->id, $res['id'],
$res->url, $res['url'],
$res->content, $res['content'],
$res->cc, $res['cc'],
$settings $settings
); );
ActivityPubReturn::answer(); ActivityPubReturn::answer();

View File

@ -30,7 +30,7 @@ if (!defined('GNUSOCIAL')) {
} }
try { try {
$notice = ActivityPubPlugin::grab_notice_from_url($data->object->id); $notice = ActivityPubPlugin::grab_notice_from_url($data['object']['id']);
$notice->deleteAs($actor_profile); $notice->deleteAs($actor_profile);
ActivityPubReturn::answer(); ActivityPubReturn::answer();
} catch (Exception $e) { } catch (Exception $e) {

View File

@ -30,7 +30,7 @@ if (!defined('GNUSOCIAL')) {
} }
// Validate Object // Validate Object
if (!filter_var($data->object, FILTER_VALIDATE_URL)) { if (!filter_var($data['object'], FILTER_VALIDATE_URL)) {
ActivityPubReturn::error('Invalid Object Actor URL.'); ActivityPubReturn::error('Invalid Object Actor URL.');
} }
@ -38,7 +38,7 @@ if (!filter_var($data->object, FILTER_VALIDATE_URL)) {
try { try {
if (!isset($profile)) { if (!isset($profile)) {
$object_profile = new Activitypub_explorer; $object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($data->object)[0]; $object_profile = $object_profile->lookup($data['object'])[0];
} else { } else {
$object_profile = $profile; $object_profile = $profile;
unset($profile); unset($profile);
@ -52,14 +52,14 @@ $actor_aprofile = Activitypub_profile::from_profile($actor_profile);
if (!Subscription::exists($actor_profile, $object_profile)) { if (!Subscription::exists($actor_profile, $object_profile)) {
Subscription::start($actor_profile, $object_profile); Subscription::start($actor_profile, $object_profile);
common_debug('ActivityPubPlugin: Accepted Follow request from '.$data->actor.' to '.$data->object); common_debug('ActivityPubPlugin: Accepted Follow request from '.$data['actor'].' to '.$data['object']);
// Notify remote instance that we have accepted their request // Notify remote instance that we have accepted their request
common_debug('ActivityPubPlugin: Notifying remote instance that we have accepted their Follow request request from '.$data->actor.' to '.$data->object); common_debug('ActivityPubPlugin: Notifying remote instance that we have accepted their Follow request request from '.$data['actor'].' to '.$data['object']);
$postman = new Activitypub_postman($actor_profile, [$actor_aprofile]); $postman = new Activitypub_postman($actor_profile, [$actor_aprofile]);
$postman->follow(); $postman->follow();
ActivityPubReturn::answer(); ActivityPubReturn::answer();
} else { } else {
common_debug('ActivityPubPlugin: Received a repeated Follow request from '.$data->actor.' to '.$data->object); common_debug('ActivityPubPlugin: Received a repeated Follow request from '.$data['actor'].' to '.$data['object']);
ActivityPubReturn::error('Already following.', 409); ActivityPubReturn::error('Already following.', 409);
} }

View File

@ -31,7 +31,7 @@ if (!defined('GNUSOCIAL')) {
try { try {
try { try {
$object_notice = ActivityPubPlugin::grab_notice_from_url($data->object); $object_notice = ActivityPubPlugin::grab_notice_from_url($data['object']);
} catch (Exception $e) { } catch (Exception $e) {
ActivityPubReturn::error('Invalid Object specified.'); ActivityPubReturn::error('Invalid Object specified.');
} }

View File

@ -30,18 +30,18 @@ if (!defined('GNUSOCIAL')) {
} }
// Validate data // Validate data
if (!isset($data->type)) { if (!isset($data['type'])) {
ActivityPubReturn::error('Type was not specified.'); ActivityPubReturn::error('Type was not specified.');
} }
switch ($data->object->type) { switch ($data['object']['type']) {
case 'Like': case 'Like':
try { try {
// Validate data // Validate data
if (!isset($data->object->object)) { if (!isset($data['object']['object'])) {
ActivityPubReturn::error('Notice URI was not specified.'); ActivityPubReturn::error('Notice URI was not specified.');
} }
Fave::removeEntry($actor_profile, ActivityPubPlugin::grab_notice_from_url($data->object->object)); Fave::removeEntry($actor_profile, ActivityPubPlugin::grab_notice_from_url($data['object']['object']));
// Notice disfavorited successfully. // Notice disfavorited successfully.
ActivityPubReturn::answer(); ActivityPubReturn::answer();
} catch (Exception $e) { } catch (Exception $e) {
@ -50,13 +50,13 @@ switch ($data->object->type) {
break; break;
case 'Follow': case 'Follow':
// Validate data // Validate data
if (!isset($data->object->object)) { if (!isset($data['object']['object'])) {
ActivityPubReturn::error('Object Actor URL was not specified.'); ActivityPubReturn::error('Object Actor URL was not specified.');
} }
// Get valid Object profile // Get valid Object profile
try { try {
$object_profile = new Activitypub_explorer; $object_profile = new Activitypub_explorer;
$object_profile = $object_profile->lookup($data->object->object)[0]; $object_profile = $object_profile->lookup($data['object']['object'])[0];
} catch (Exception $e) { } catch (Exception $e) {
ActivityPubReturn::error('Invalid Object Actor URL.', 404); ActivityPubReturn::error('Invalid Object Actor URL.', 404);
} }

0
classes/Activitypub_mention_tag.php Normal file → Executable file
View File

View File

@ -318,11 +318,9 @@ class Activitypub_profile extends Managed_DataObject
*/ */
public static function fromUri($url) public static function fromUri($url)
{ {
$explorer = new Activitypub_explorer(); try {
$profiles_found = $explorer->lookup($url); return self::from_profile(Activitypub_explorer::get_profile_from_url($url));
if (!empty($profiles_found)) { } catch (Exception $e) {
return self::from_profile($profiles_found[0]);
} else {
throw new Exception('No valid ActivityPub profile found for given URI.'); throw new Exception('No valid ActivityPub profile found for given URI.');
} }
} }

0
tests/Unit/HTTPSignatureTest.php Normal file → Executable file
View File

View File

@ -56,13 +56,11 @@ class Activitypub_explorer
{ {
$discovery = new Activitypub_explorer; $discovery = new Activitypub_explorer;
// Get valid Actor object // Get valid Actor object
try {
$actor_profile = $discovery->lookup($url); $actor_profile = $discovery->lookup($url);
if (!empty($actor_profile)) {
return $actor_profile[0]; return $actor_profile[0];
} catch (Exception $e) {
throw new Exception('Invalid Actor.');
} }
unset($discovery); throw new Exception('Invalid Actor.');
} }
/** /**
@ -168,10 +166,10 @@ class Activitypub_explorer
common_debug('ActivityPub Explorer: Unable to find a local Aprofile for '.$uri.' - looking for a Profile instead.'); common_debug('ActivityPub Explorer: Unable to find a local Aprofile for '.$uri.' - looking for a Profile instead.');
// Well, maybe it is a pure blood? // Well, maybe it is a pure blood?
// Iff, we are in the same instance: // Iff, we are in the same instance:
$ACTIVITYPUB_BASE_INSTANCE_URI_length = strlen(ACTIVITYPUB_BASE_INSTANCE_URI); $ACTIVITYPUB_BASE_ACTOR_URI_length = strlen(ACTIVITYPUB_BASE_ACTOR_URI);
if (substr($uri, 0, $ACTIVITYPUB_BASE_INSTANCE_URI_length) == ACTIVITYPUB_BASE_INSTANCE_URI) { if (substr($uri, 0, $ACTIVITYPUB_BASE_ACTOR_URI_length) == ACTIVITYPUB_BASE_ACTOR_URI) {
try { try {
$profile = Profile::getByID(intval(substr($uri, $ACTIVITYPUB_BASE_INSTANCE_URI_length))); $profile = Profile::getByID(intval(substr($uri, $ACTIVITYPUB_BASE_ACTOR_URI_length)));
common_debug('ActivityPub Explorer: Found a Profile for '.$uri); common_debug('ActivityPub Explorer: Found a Profile for '.$uri);
// We found something! // We found something!
$this->discovered_actor_profiles[]= $profile; $this->discovered_actor_profiles[]= $profile;
@ -275,14 +273,17 @@ class Activitypub_explorer
* Download and update given avatar image * Download and update given avatar image
* *
* @author GNU Social * @author GNU Social
* @param Profile $profile
* @param string $url * @param string $url
* @return Avatar The Avatar we have on disk. (seldom used) * @return Avatar The Avatar we have on disk.
* @throws Exception in various failure cases * @throws Exception in various failure cases
*/ */
private function _store_avatar($profile, $url) private function _store_avatar(Profile $profile, $url)
{ {
if (!common_valid_http_url($url)) { common_debug('ActivityPub Explorer: Started grabbing remote avatar from: '.$url);
if (!filter_var($url, FILTER_VALIDATE_URL)) {
// TRANS: Server exception. %s is a URL. // TRANS: Server exception. %s is a URL.
common_debug('ActivityPub Explorer: Failed because it is an invalid url: '.$url);
throw new ServerException(sprintf('Invalid avatar URL %s.'), $url); throw new ServerException(sprintf('Invalid avatar URL %s.'), $url);
} }
@ -293,10 +294,12 @@ class Activitypub_explorer
$imgData = HTTPClient::quickGet($url); $imgData = HTTPClient::quickGet($url);
// Make sure it's at least an image file. ImageFile can do the rest. // Make sure it's at least an image file. ImageFile can do the rest.
if (false === getimagesizefromstring($imgData)) { if (false === getimagesizefromstring($imgData)) {
common_debug('ActivityPub Explorer: Failed because the downloaded avatar: '.$url. 'is not a validad image.');
throw new UnsupportedMediaException('Downloaded avatar was not an image.'); throw new UnsupportedMediaException('Downloaded avatar was not an image.');
} }
file_put_contents($temp_filename, $imgData); file_put_contents($temp_filename, $imgData);
unset($imgData); // No need to carry this in memory. unset($imgData); // No need to carry this in memory.
common_debug('ActivityPub Explorer: Stored dowloaded avatar in: '.$temp_filename);
$id = $profile->getID(); $id = $profile->getID();
@ -308,7 +311,9 @@ class Activitypub_explorer
common_timestamp() common_timestamp()
); );
rename($temp_filename, Avatar::path($filename)); rename($temp_filename, Avatar::path($filename));
common_debug('ActivityPub Explorer: Moved avatar from: '.$temp_filename.' to '.$filename);
} catch (Exception $e) { } catch (Exception $e) {
common_debug('ActivityPub Explorer: Something went wrong while processing the avatar from: '.$url.' details: '.$e->getMessage());
unlink($temp_filename); unlink($temp_filename);
throw $e; throw $e;
} }
@ -326,6 +331,7 @@ class Activitypub_explorer
$profile->avatar = $url; $profile->avatar = $url;
$profile->update($orig); $profile->update($orig);
common_debug('ActivityPub Explorer: Seted Avatar from: '.$url.' to profile.');
return Avatar::getUploaded($profile); return Avatar::getUploaded($profile);
} }
@ -416,8 +422,7 @@ class Activitypub_explorer
$response = $client->get($url, $headers); $response = $client->get($url, $headers);
$res = json_decode($response->getBody(), true); $res = json_decode($response->getBody(), true);
if (!isset($res['orderedItems'])) if (!isset($res['orderedItems'])) {
{
return false; return false;
} }