$ldactivity = JsonLD::compact($activity);
- $actor = JsonLD::fetchElement($ldactivity, 'as:actor');
+ $actor = JsonLD::fetchElement($ldactivity, 'as:actor', '@id');
Logger::info('Message for user ' . $uid . ' is from actor ' . $actor);
*/
private static function prepareObjectData($activity, $uid, &$trust_source)
{
- $actor = JsonLD::fetchElement($activity, 'as:actor');
+ $actor = JsonLD::fetchElement($activity, 'as:actor', '@id');
if (empty($actor)) {
Logger::log('Empty actor', Logger::DEBUG);
return [];
Logger::log('Receivers: ' . $uid . ' - ' . json_encode($receivers), Logger::DEBUG);
- $object_id = JsonLD::fetchElement($activity, 'as:object');
+ $object_id = JsonLD::fetchElement($activity, 'as:object', '@id');
if (empty($object_id)) {
Logger::log('No object found', Logger::DEBUG);
return [];
}
+ if (!is_string($object_id)) {
+ Logger::info('Invalid object id', ['object' => $object_id]);
+ return [];
+ }
+
$object_type = self::fetchObjectType($activity, $object_id, $uid);
// Fetch the content only on activities where this matters
Logger::log("Object data couldn't be processed", Logger::DEBUG);
return [];
}
+ $object_data['object_id'] = $object_id;
+
+ // Test if it is an answer to a mail
+ if (DBA::exists('mail', ['uri' => $object_data['reply-to-id']])) {
+ $object_data['directmessage'] = true;
+ } else {
+ $object_data['directmessage'] = JsonLD::fetchElement($activity, 'litepub:directMessage');
+ }
+
// We had been able to retrieve the object data - so we can trust the source
$trust_source = true;
- } elseif (in_array($type, ['as:Like', 'as:Dislike']) ||
- (($type == 'as:Follow') && in_array($object_type, self::CONTENT_TYPES))) {
+ } elseif (in_array($type, array_merge(self::ACTIVITY_TYPES, ['as:Follow'])) && in_array($object_type, self::CONTENT_TYPES)) {
// Create a mostly empty array out of the activity data (instead of the object).
// This way we later don't have to check for the existence of ech individual array element.
$object_data = self::processObject($activity);
$object_data['name'] = $type;
- $object_data['author'] = JsonLD::fetchElement($activity, 'as:actor');
+ $object_data['author'] = JsonLD::fetchElement($activity, 'as:actor', '@id');
$object_data['object_id'] = $object_id;
$object_data['object_type'] = ''; // Since we don't fetch the object, we don't know the type
+ } elseif (in_array($type, ['as:Add'])) {
+ $object_data = [];
+ $object_data['id'] = JsonLD::fetchElement($activity, '@id');
+ $object_data['target_id'] = JsonLD::fetchElement($activity, 'as:target', '@id');
+ $object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
+ $object_data['object_type'] = JsonLD::fetchElement($activity['as:object'], '@type');
+ $object_data['object_content'] = JsonLD::fetchElement($activity['as:object'], 'as:content', '@type');
} else {
$object_data = [];
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
- $object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object');
- $object_data['object_actor'] = JsonLD::fetchElement($activity['as:object'], 'as:actor');
+ $object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
+ $object_data['object_actor'] = JsonLD::fetchElement($activity['as:object'], 'as:actor', '@id');
$object_data['object_object'] = JsonLD::fetchElement($activity['as:object'], 'as:object');
$object_data['object_type'] = JsonLD::fetchElement($activity['as:object'], '@type');
return;
}
- if (!JsonLD::fetchElement($activity, 'as:object')) {
+ if (!JsonLD::fetchElement($activity, 'as:object', '@id')) {
Logger::log('Empty object', Logger::DEBUG);
return;
}
- if (!JsonLD::fetchElement($activity, 'as:actor')) {
+ if (!JsonLD::fetchElement($activity, 'as:actor', '@id')) {
Logger::log('Empty actor', Logger::DEBUG);
return;
// Don't trust the source if "actor" differs from "attributedTo". The content could be forged.
if ($trust_source && ($type == 'as:Create') && is_array($activity['as:object'])) {
- $actor = JsonLD::fetchElement($activity, 'as:actor');
- $attributed_to = JsonLD::fetchElement($activity['as:object'], 'as:attributedTo');
+ $actor = JsonLD::fetchElement($activity, 'as:actor', '@id');
+ $attributed_to = JsonLD::fetchElement($activity['as:object'], 'as:attributedTo', '@id');
$trust_source = ($actor == $attributed_to);
if (!$trust_source) {
Logger::log('Not trusting actor: ' . $actor . '. It differs from attributedTo: ' . $attributed_to, Logger::DEBUG);
switch ($type) {
case 'as:Create':
+ if (in_array($object_data['object_type'], self::CONTENT_TYPES)) {
+ ActivityPub\Processor::createItem($object_data);
+ }
+ break;
+
+ case 'as:Add':
+ if ($object_data['object_type'] == 'as:tag') {
+ ActivityPub\Processor::addTag($object_data);
+ }
+ break;
+
case 'as:Announce':
if (in_array($object_data['object_type'], self::CONTENT_TYPES)) {
+ $profile = APContact::getByURL($object_data['actor']);
+ // Reshared posts from persons appear as summary at the bottom
+ // If this isn't set, then a single reshare appears on top. This is used for groups.
+ $object_data['thread-completion'] = ($profile['type'] != 'Group');
+
ActivityPub\Processor::createItem($object_data);
+
+ // Add the bottom reshare information only for persons
+ if ($profile['type'] != 'Group') {
+ $announce_object_data = self::processObject($activity);
+ $announce_object_data['name'] = $type;
+ $announce_object_data['author'] = JsonLD::fetchElement($activity, 'as:actor', '@id');
+ $announce_object_data['object_id'] = $object_data['object_id'];
+ $announce_object_data['object_type'] = $object_data['object_type'];
+
+ ActivityPub\Processor::createActivity($announce_object_data, ACTIVITY2_ANNOUNCE);
+ }
}
break;
$receivers = [];
// When it is an answer, we inherite the receivers from the parent
- $replyto = JsonLD::fetchElement($activity, 'as:inReplyTo');
+ $replyto = JsonLD::fetchElement($activity, 'as:inReplyTo', '@id');
if (!empty($replyto)) {
$parents = Item::select(['uid'], ['uri' => $replyto]);
while ($parent = Item::fetch($parents)) {
}
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc'] as $element) {
- $receiver_list = JsonLD::fetchElementArray($activity, $element);
+ $receiver_list = JsonLD::fetchElementArray($activity, $element, '@id');
if (empty($receiver_list)) {
continue;
}
// Check if the potential receiver is following the actor
// Exception: The receiver is targetted via "to" or this is a comment
if ((($element != 'as:to') && empty($replyto)) || ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) {
- $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS];
+ $networks = Protocol::FEDERATED;
$condition = ['nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND],
'network' => $networks, 'archive' => false, 'pending' => false, 'uid' => $contact['uid']];
public static function getReceiverForActor($actor, $tags)
{
$receivers = [];
- $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS];
+ $networks = Protocol::FEDERATED;
$condition = ['nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
'network' => $networks, 'archive' => false, 'pending' => false];
$contacts = DBA::select('contact', ['uid', 'rel'], $condition);
*/
public static function switchContact($cid, $uid, $url)
{
- $profile = ActivityPub::probeProfile($url);
- if (empty($profile)) {
- return;
- }
-
- Logger::log('Switch contact ' . $cid . ' (' . $profile['url'] . ') for user ' . $uid . ' to ActivityPub');
+ Contact::updateFromProbe($cid, '', true);
- $photo = defaults($profile, 'photo', null);
- unset($profile['photo']);
- unset($profile['baseurl']);
- unset($profile['guid']);
-
- $profile['nurl'] = Strings::normaliseLink($profile['url']);
- DBA::update('contact', $profile, ['id' => $cid]);
-
- Contact::updateAvatar($photo, $uid, $cid);
+ Logger::log('Switch contact ' . $cid . ' (' . $url . ') for user ' . $uid . ' to ActivityPub');
// Send a new follow request to be sure that the connection still exists
if (($uid != 0) && DBA::exists('contact', ['id' => $cid, 'rel' => [Contact::SHARING, Contact::FRIEND]])) {
- ActivityPub\Transmitter::sendActivity('Follow', $profile['url'], $uid);
- Logger::log('Send a new follow request to ' . $profile['url'] . ' for user ' . $uid, Logger::DEBUG);
+ ActivityPub\Transmitter::sendActivity('Follow', $url, $uid);
+ Logger::log('Send a new follow request to ' . $url . ' for user ' . $uid, Logger::DEBUG);
}
}
}
if (!empty($activity['diaspora:guid']) && empty($object_data['diaspora:guid'])) {
- $object_data['diaspora:guid'] = JsonLD::fetchElement($activity, 'diaspora:guid');
+ $object_data['diaspora:guid'] = JsonLD::fetchElement($activity, 'diaspora:guid', '@value');
}
$object_data['service'] = JsonLD::fetchElement($activity, 'as:instrument', 'as:name', '@type', 'as:Service');
+ $object_data['service'] = JsonLD::fetchElement($object_data, 'service', '@value');
return $object_data;
}
* @param boolean $trust_source Do we trust the provided object?
* @param integer $uid User ID for the signature that we use to fetch data
*
- * @return array with trusted and valid object data
+ * @return array|false with trusted and valid object data
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- private static function fetchObject($object_id, $object = [], $trust_source = false, $uid = 0)
+ private static function fetchObject(string $object_id, array $object = [], bool $trust_source = false, int $uid = 0)
{
// By fetching the type we check if the object is complete.
$type = JsonLD::fetchElement($object, '@type');
}
if ($type == 'as:Announce') {
- $object_id = JsonLD::fetchElement($object, 'object');
- if (empty($object_id)) {
+ $object_id = JsonLD::fetchElement($object, 'object', '@id');
+ if (empty($object_id) || !is_string($object_id)) {
return false;
}
return self::fetchObject($object_id, [], false, $uid);
}
Logger::log('Unhandled object type: ' . $type, Logger::DEBUG);
+ return false;
}
/**
}
$element = ['type' => str_replace('as:', '', JsonLD::fetchElement($tag, '@type')),
- 'href' => JsonLD::fetchElement($tag, 'as:href'),
- 'name' => JsonLD::fetchElement($tag, 'as:name')];
+ 'href' => JsonLD::fetchElement($tag, 'as:href', '@id'),
+ 'name' => JsonLD::fetchElement($tag, 'as:name', '@value')];
if (empty($element['type'])) {
continue;
continue;
}
- $url = JsonLD::fetchElement($emoji['as:icon'], 'as:url');
- $element = ['name' => JsonLD::fetchElement($emoji, 'as:name'),
+ $url = JsonLD::fetchElement($emoji['as:icon'], 'as:url', '@id');
+ $element = ['name' => JsonLD::fetchElement($emoji, 'as:name', '@value'),
'href' => $url];
$emojilist[] = $element;
}
$attachlist[] = ['type' => str_replace('as:', '', JsonLD::fetchElement($attachment, '@type')),
- 'mediaType' => JsonLD::fetchElement($attachment, 'as:mediaType'),
- 'name' => JsonLD::fetchElement($attachment, 'as:name'),
- 'url' => JsonLD::fetchElement($attachment, 'as:url')];
+ 'mediaType' => JsonLD::fetchElement($attachment, 'as:mediaType', '@value'),
+ 'name' => JsonLD::fetchElement($attachment, 'as:name', '@value'),
+ 'url' => JsonLD::fetchElement($attachment, 'as:url', '@id')];
}
return $attachlist;
}
$object_data = [];
$object_data['object_type'] = JsonLD::fetchElement($object, '@type');
$object_data['id'] = JsonLD::fetchElement($object, '@id');
- $object_data['reply-to-id'] = JsonLD::fetchElement($object, 'as:inReplyTo');
+ $object_data['reply-to-id'] = JsonLD::fetchElement($object, 'as:inReplyTo', '@id');
// An empty "id" field is translated to "./" by the compactor, so we have to check for this content
if (empty($object_data['reply-to-id']) || ($object_data['reply-to-id'] == './')) {
$object_data['published'] = $object_data['updated'];
}
- $actor = JsonLD::fetchElement($object, 'as:attributedTo');
+ $actor = JsonLD::fetchElement($object, 'as:attributedTo', '@id');
if (empty($actor)) {
- $actor = JsonLD::fetchElement($object, 'as:actor');
+ $actor = JsonLD::fetchElement($object, 'as:actor', '@id');
}
- $object_data['diaspora:guid'] = JsonLD::fetchElement($object, 'diaspora:guid');
- $object_data['diaspora:comment'] = JsonLD::fetchElement($object, 'diaspora:comment');
- $object_data['diaspora:like'] = JsonLD::fetchElement($object, 'diaspora:like');
+ $object_data['diaspora:guid'] = JsonLD::fetchElement($object, 'diaspora:guid', '@value');
+ $object_data['diaspora:comment'] = JsonLD::fetchElement($object, 'diaspora:comment', '@value');
+ $object_data['diaspora:like'] = JsonLD::fetchElement($object, 'diaspora:like', '@value');
$object_data['actor'] = $object_data['author'] = $actor;
- $object_data['context'] = JsonLD::fetchElement($object, 'as:context');
- $object_data['conversation'] = JsonLD::fetchElement($object, 'ostatus:conversation');
+ $object_data['context'] = JsonLD::fetchElement($object, 'as:context', '@id');
+ $object_data['conversation'] = JsonLD::fetchElement($object, 'ostatus:conversation', '@id');
$object_data['sensitive'] = JsonLD::fetchElement($object, 'as:sensitive');
- $object_data['name'] = JsonLD::fetchElement($object, 'as:name');
- $object_data['summary'] = JsonLD::fetchElement($object, 'as:summary');
- $object_data['content'] = JsonLD::fetchElement($object, 'as:content');
+ $object_data['name'] = JsonLD::fetchElement($object, 'as:name', '@value');
+ $object_data['summary'] = JsonLD::fetchElement($object, 'as:summary', '@value');
+ $object_data['content'] = JsonLD::fetchElement($object, 'as:content', '@value');
$object_data['source'] = JsonLD::fetchElement($object, 'as:source', 'as:content', 'as:mediaType', 'text/bbcode');
+ $object_data['source'] = JsonLD::fetchElement($object_data, 'source', '@value');
$object_data['start-time'] = JsonLD::fetchElement($object, 'as:startTime', '@value');
$object_data['end-time'] = JsonLD::fetchElement($object, 'as:endTime', '@value');
$object_data['location'] = JsonLD::fetchElement($object, 'as:location', 'as:name', '@type', 'as:Place');
+ $object_data['location'] = JsonLD::fetchElement($object_data, 'location', '@value');
$object_data['latitude'] = JsonLD::fetchElement($object, 'as:location', 'as:latitude', '@type', 'as:Place');
$object_data['latitude'] = JsonLD::fetchElement($object_data, 'latitude', '@value');
$object_data['longitude'] = JsonLD::fetchElement($object, 'as:location', 'as:longitude', '@type', 'as:Place');
$object_data['tags'] = self::processTags(JsonLD::fetchElementArray($object, 'as:tag'));
$object_data['emojis'] = self::processEmojis(JsonLD::fetchElementArray($object, 'as:tag', 'toot:Emoji'));
$object_data['generator'] = JsonLD::fetchElement($object, 'as:generator', 'as:name', '@type', 'as:Application');
- $object_data['alternate-url'] = JsonLD::fetchElement($object, 'as:url');
+ $object_data['generator'] = JsonLD::fetchElement($object_data, 'generator', '@value');
+ $object_data['alternate-url'] = JsonLD::fetchElement($object, 'as:url', '@id');
// Special treatment for Hubzilla links
if (is_array($object_data['alternate-url'])) {
- $object_data['alternate-url'] = JsonLD::fetchElement($object_data['alternate-url'], 'as:href');
+ $object_data['alternate-url'] = JsonLD::fetchElement($object_data['alternate-url'], 'as:href', '@id');
if (!is_string($object_data['alternate-url'])) {
- $object_data['alternate-url'] = JsonLD::fetchElement($object['as:url'], 'as:href');
+ $object_data['alternate-url'] = JsonLD::fetchElement($object['as:url'], 'as:href', '@id');
}
}