X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FProtocol%2FActivityPub%2FProcessor.php;h=26d953985a5634196a4f97a5ac057df54739f490;hb=9ea34370a60cf6eb47aadd4b8a3e64fb282b577c;hp=2c99132c2004bbfb0a3f90328f3a30623c905fe3;hpb=c6b45a958e3b09bc8f3950a718c181dfc9e0b910;p=friendica.git diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 2c99132c20..26d953985a 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -35,6 +35,7 @@ use Friendica\Model\Event; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; +use Friendica\Model\Search; use Friendica\Model\Tag; use Friendica\Model\User; use Friendica\Protocol\Activity; @@ -42,6 +43,7 @@ use Friendica\Protocol\ActivityPub; use Friendica\Util\DateTimeFormat; use Friendica\Util\JsonLD; use Friendica\Util\Strings; +use Text_LanguageDetect; /** * ActivityPub Processor Protocol class @@ -249,11 +251,18 @@ class Processor $item['isForum'] = false; if (!empty($activity['thread-completion'])) { - // Store the original actor in the "causer" fields to enable the check for ignored or blocked contacts - $item['causer-link'] = $item['owner-link']; - $item['causer-id'] = $item['owner-id']; + if ($activity['thread-completion'] != $item['owner-id']) { + $actor = Contact::getById($activity['thread-completion'], ['url']); + $item['causer-link'] = $actor['url']; + $item['causer-id'] = $activity['thread-completion']; + Logger::info('Use inherited actor as causer.', ['id' => $item['owner-id'], 'activity' => $activity['thread-completion'], 'owner' => $item['owner-link'], 'actor' => $actor['url']]); + } else { + // Store the original actor in the "causer" fields to enable the check for ignored or blocked contacts + $item['causer-link'] = $item['owner-link']; + $item['causer-id'] = $item['owner-id']; + Logger::info('Use actor as causer.', ['id' => $item['owner-id'], 'actor' => $item['owner-link']]); + } - Logger::info('Ignoring actor because of thread completion.', ['actor' => $item['owner-link']]); $item['owner-link'] = $item['author-link']; $item['owner-id'] = $item['author-id']; } else { @@ -486,6 +495,7 @@ class Processor } $stored = false; + ksort($activity['receiver']); foreach ($activity['receiver'] as $receiver) { if ($receiver == -1) { @@ -521,6 +531,12 @@ class Processor $item['post-type'] = Item::PT_ARTICLE; } + if (!empty($activity['from-relay'])) { + $item['post-type'] = Item::PT_RELAY; + } elseif (!empty($activity['thread-completion'])) { + $item['post-type'] = Item::PT_FETCHED; + } + if ($item['isForum'] ?? false) { $item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver); } else { @@ -688,12 +704,13 @@ class Processor /** * Fetches missing posts * - * @param string $url message URL - * @param array $child activity array with the child of this message + * @param string $url message URL + * @param array $child activity array with the child of this message + * @param string $relay_actor Relay actor * @return string fetched message URL * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function fetchMissingActivity(string $url, array $child = []) + public static function fetchMissingActivity(string $url, array $child = [], string $relay_actor = '') { if (!empty($child['receiver'])) { $uid = ActivityPub\Receiver::getFirstUserFromReceivers($child['receiver']); @@ -751,7 +768,17 @@ class Processor $ldactivity = JsonLD::compact($activity); - $ldactivity['thread-completion'] = true; + if (!empty($relay_actor)) { + $ldactivity['thread-completion'] = $ldactivity['from-relay'] = Contact::getIdForURL($relay_actor); + } elseif (!empty($child['thread-completion'])) { + $ldactivity['thread-completion'] = $child['thread-completion']; + } else { + $ldactivity['thread-completion'] = Contact::getIdForURL($actor); + } + + if (!empty($relay_actor) && !self::acceptIncomingMessage($ldactivity, $object['id'])) { + return ''; + } ActivityPub\Receiver::processActivity($ldactivity, json_encode($activity), $uid, true, false, $signer); @@ -760,6 +787,92 @@ class Processor return $activity['id']; } + /** + * Test if incoming relay messages should be accepted + * + * @param array $activity activity array + * @param string $id object ID + * @return boolean true if message is accepted + */ + private static function acceptIncomingMessage(array $activity, string $id) + { + if (empty($activity['as:object'])) { + Logger::info('No object field in activity - accepted', ['id' => $id]); + return true; + } + + $config = DI::config(); + + $subscribe = $config->get('system', 'relay_subscribe', false); + if ($subscribe) { + $scope = $config->get('system', 'relay_scope', SR_SCOPE_ALL); + } else { + $scope = SR_SCOPE_NONE; + } + + if ($scope == SR_SCOPE_ALL) { + Logger::info('Server accept all posts - accepted', ['id' => $id]); + return true; + } + + $replyto = JsonLD::fetchElement($activity['as:object'], 'as:inReplyTo', '@id'); + if (Item::exists(['uri' => $replyto])) { + Logger::info('Post is a reply to an existing post - accepted', ['id' => $id, 'replyto' => $replyto]); + return true; + } + + if ($scope == SR_SCOPE_NONE) { + Logger::info('Server does not accept relay posts - rejected', ['id' => $id]); + return false; + } + + $messageTags = []; + $tags = Receiver::processTags(JsonLD::fetchElementArray($activity['as:object'], 'as:tag') ?? []); + if (!empty($tags)) { + foreach ($tags as $tag) { + if ($tag['type'] != 'Hashtag') { + continue; + } + $messageTags[] = ltrim(mb_strtolower($tag['name']), '#'); + } + } + + $systemTags = []; + $userTags = []; + + if ($scope == SR_SCOPE_TAGS) { + $server_tags = $config->get('system', 'relay_server_tags', []); + $tagitems = explode(',', mb_strtolower($server_tags)); + + foreach ($tagitems AS $tag) { + $systemTags[] = trim($tag, '# '); + } + + if ($config->get('system', 'relay_user_tags')) { + $userTags = Search::getUserTags(); + } + } + + $content = mb_strtolower(BBCode::toPlaintext(HTML::toBBCode(JsonLD::fetchElement($activity['as:object'], 'as:content', '@value')), false)); + + $tagList = array_unique(array_merge($systemTags, $userTags)); + foreach ($messageTags as $tag) { + if (in_array($tag, $tagList)) { + Logger::info('Subscribed hashtag found - accepted', ['id' => $id, 'hashtag' => $tag]); + return true; + } + // We check with "strpos" for performance issues. Only when this is true, the regular expression check is used + // RegExp is taken from here: https://medium.com/@shiba1014/regex-word-boundaries-with-unicode-207794f6e7ed + if ((strpos($content, $tag) !== false) && preg_match('/(?<=[\s,.:;"\']|^)' . preg_quote($tag, '/') . '(?=[\s,.:;"\']|$)/', $content)) { + Logger::info('Subscribed hashtag found in content - accepted', ['id' => $id, 'hashtag' => $tag]); + return true; + } + } + + Logger::info('No matching hashtags found - rejected', ['id' => $id]); + return false; + } + /** * perform a "follow" request *