diff --git a/mod/item.php b/mod/item.php index 70c5c9a8b6..e85c08e023 100644 --- a/mod/item.php +++ b/mod/item.php @@ -222,7 +222,7 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat DI::contentItem()->postProcessPost($post, $recipients); if (($post['private'] == Item::PRIVATE) && ($post['thr-parent-id'] != $post['uri-id'])) { - DI::contentItem()->copyPermissions($post['thr-parent-id'], $post['uri-id']); + DI::contentItem()->copyPermissions($post['thr-parent-id'], $post['uri-id'], $post['parent-uri-id']); } Logger::debug('post_complete'); diff --git a/src/Content/Item.php b/src/Content/Item.php index ab5ff1698f..f74a958962 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -1070,17 +1070,32 @@ class Item } } - public function copyPermissions(int $fromUriId, int $toUriId) + public function copyPermissions(int $fromUriId, int $toUriId, int $parentUriId) { - $from = Post::selectFirstPost(['author-id'], ['uri-id' => $fromUriId]); - $from_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $from['author-id']]); - $to = Post::selectFirstPost(['author-id'], ['uri-id' => $toUriId]); - $to_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $to['author-id']]); + $from = Post::selectFirstPost(['author-id'], ['uri-id' => $fromUriId]); + $from_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $from['author-id']]); + $to = Post::selectFirstPost(['author-id'], ['uri-id' => $toUriId]); + $to_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $to['author-id']]); + $parent = Post::selectFirstPost(['author-id'], ['uri-id' => $parentUriId]); + $parent_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $parent['author-id']]); + + $followers = ''; + foreach (array_column(Tag::getByURIId($parentUriId, [Tag::TO, Tag::CC, Tag::BCC]), 'url') as $url) { + if ($url == $parent_author['ap-followers']) { + $followers = $url; + break; + } + } $existing = array_column(Tag::getByURIId($toUriId, [Tag::TO, Tag::CC, Tag::BCC]), 'url'); foreach (Tag::getByURIId($fromUriId, [Tag::TO, Tag::CC, Tag::BCC]) as $receiver) { if ($receiver['url'] == $from_author['ap-followers']) { + if (!empty($followers)) { + $receiver['url'] = $followers; + $receiver['name'] = trim(parse_url($receiver['url'], PHP_URL_PATH), '/'); + Tag::store($toUriId, $receiver['type'], $receiver['name'], $receiver['url']); + } $receiver['url'] = $to_author['ap-followers']; $receiver['name'] = trim(parse_url($receiver['url'], PHP_URL_PATH), '/'); } diff --git a/src/Model/Item.php b/src/Model/Item.php index 27fcfcdd9b..0e741ce816 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1366,7 +1366,7 @@ class Item if ($notify) { DI::contentItem()->postProcessPost($posted_item); if ($copy_permissions && ($posted_item['thr-parent-id'] != $posted_item['uri-id']) && ($posted_item['private'] == self::PRIVATE)) { - DI::contentItem()->copyPermissions($posted_item['thr-parent-id'], $posted_item['uri-id']); + DI::contentItem()->copyPermissions($posted_item['thr-parent-id'], $posted_item['uri-id'], $posted_item['parent-uri-id']); } } else { Hook::callAll('post_remote_end', $posted_item); @@ -3758,7 +3758,7 @@ class Item DI::profiler()->startRecording('rendering'); $trailing = ''; foreach ($PostMedias as $PostMedia) { - if (strpos($item['body'], $PostMedia->url)) { + if (strpos($item['body'], (string)$PostMedia->url)) { continue; } diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index e6371a5975..de3acfea91 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -308,12 +308,13 @@ class Receiver * @param boolean $push Message had been pushed to our system * @param boolean $trust_source Do we trust the source? * @param string $original_actor Actor of the original activity. Used for receiver detection. (Optional) + * @param string $http_signer Actor who has signed the HTTP request * * @return array with object data * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function prepareObjectData(array $activity, int $uid, bool $push, bool &$trust_source, string $original_actor = ''): array + public static function prepareObjectData(array $activity, int $uid, bool $push, bool &$trust_source, string $original_actor = '', string $http_signer = ''): array { $id = JsonLD::fetchElement($activity, '@id'); $type = JsonLD::fetchElement($activity, '@type'); @@ -332,7 +333,7 @@ class Receiver $fetched = false; if (!empty($id) && !$trust_source) { - $fetch_uid = $uid ?: self::getBestUserForActivity($activity, $original_actor); + $fetch_uid = $uid ?: self::getBestUserForActivity($activity, $original_actor, ''); $fetched_activity = Processor::fetchCachedActivity($fetch_id, $fetch_uid); if (!empty($fetched_activity)) { @@ -368,7 +369,7 @@ class Receiver $type = JsonLD::fetchElement($activity, '@type'); // Fetch all receivers from to, cc, bto and bcc - $receiverdata = self::getReceivers($activity, $original_actor ?: $actor, [], false, $push || $fetched); + $receiverdata = self::getReceivers($activity, $original_actor ?: $actor, [], false, $push || $fetched, $http_signer); $receivers = $reception_types = []; foreach ($receiverdata as $key => $data) { $receivers[$key] = $data['uid']; @@ -392,7 +393,7 @@ class Receiver // We possibly need some user to fetch private content, // so we fetch one out of the receivers if no uid is provided. - $fetch_uid = $uid ?: self::getBestUserForActivity($activity, $original_actor); + $fetch_uid = $uid ?: self::getBestUserForActivity($activity, $original_actor, $http_signer); $object_id = JsonLD::fetchElement($activity, 'as:object', '@id'); if (empty($object_id)) { @@ -682,7 +683,7 @@ class Receiver } // $trust_source is called by reference and is set to true if the content was retrieved successfully - $object_data = self::prepareObjectData($activity, $uid, $push, $trust_source, $original_actor); + $object_data = self::prepareObjectData($activity, $uid, $push, $trust_source, $original_actor, $trust_source ? $http_signer : ''); if (empty($object_data)) { Logger::info('No object data found', ['activity' => $activity]); return true; @@ -1094,15 +1095,16 @@ class Receiver * * @param array $activity * @param string $actor + * @param string $http_signer * * @return int user id */ - public static function getBestUserForActivity(array $activity, string $actor = ''): int + public static function getBestUserForActivity(array $activity, string $actor = '', string $http_signer): int { $uid = 0; $actor = $actor ?: JsonLD::fetchElement($activity, 'as:actor', '@id') ?? ''; - $receivers = self::getReceivers($activity, $actor, [], false, false); + $receivers = self::getReceivers($activity, $actor, [], false, false, $http_signer); foreach ($receivers as $receiver) { if ($receiver['type'] == self::TARGET_GLOBAL) { return 0; @@ -1157,11 +1159,12 @@ class Receiver * @param array $tags * @param bool $fetch_unlisted * @param bool $push + * @param string $http_signer * * @return array with receivers (user id) * @throws \Exception */ - private static function getReceivers(array $activity, string $actor, array $tags, bool $fetch_unlisted, bool $push): array + private static function getReceivers(array $activity, string $actor, array $tags, bool $fetch_unlisted, bool $push, string $http_signer): array { $reply = $receivers = $profile = []; @@ -1200,6 +1203,13 @@ class Receiver // We have to prevent false follower assumptions upon thread completions $follower_target = empty($activity['thread-completion']) ? self::TARGET_FOLLOWER : self::TARGET_UNKNOWN; + if (($actor != $http_signer) && ($http_signer != '') && ($follower_target == self::TARGET_FOLLOWER)) { + $signer_profile = APContact::getByURL($http_signer); + $signer_followers = $signer_profile['followers'] ?? ''; + } else { + $signer_followers = ''; + } + foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc','as:audience'] as $element) { $receiver_list = JsonLD::fetchElementArray($activity, $element, '@id'); if (empty($receiver_list)) { @@ -1222,6 +1232,11 @@ class Receiver continue; } + if ($receiver == $signer_followers) { + $receivers = self::getReceiverForActor($http_signer, [], $receivers, $follower_target, $signer_profile); + continue; + } + // Fetching all directly addressed receivers $condition = ['self' => true, 'nurl' => Strings::normaliseLink($receiver)]; $contact = DBA::selectFirst('contact', ['uid', 'contact-type'], $condition); @@ -1949,7 +1964,7 @@ class Receiver $object_data = self::getObjectDataFromActivity($object); - $receiverdata = self::getReceivers($object, $actor ?: $object_data['actor'] ?? '', $object_data['tags'], true, false); + $receiverdata = self::getReceivers($object, $actor ?: $object_data['actor'] ?? '', $object_data['tags'], true, false, ''); $receivers = $reception_types = []; foreach ($receiverdata as $key => $data) { $receivers[$key] = $data['uid'];