]> git.mxchange.org Git - friendica.git/blobdiff - src/Protocol/ActivityPub/Processor.php
We now inherit the causer for completing a thread
[friendica.git] / src / Protocol / ActivityPub / Processor.php
index 5d5bfc0b1c20b80fb91f1fd7df99220a9f2ed5ba..26d953985a5634196a4f97a5ac057df54739f490 100644 (file)
@@ -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,16 +531,10 @@ class Processor
                                        $item['post-type'] = Item::PT_ARTICLE;
                        }
 
-                       if (in_array($item['post-type'], [Item::PT_COMMENT, Item::PT_GLOBAL, 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 (!empty($activity['from-relay'])) {
-                               $item['causer-id'] = $activity['from-relay'];
+                               $item['post-type'] = Item::PT_RELAY;
+                       } elseif (!empty($activity['thread-completion'])) {
+                               $item['post-type'] = Item::PT_FETCHED;
                        }
 
                        if ($item['isForum'] ?? false) {
@@ -764,8 +768,17 @@ class Processor
 
                $ldactivity = JsonLD::compact($activity);
 
-               $ldactivity['thread-completion'] = true;
-               $ldactivity['from-relay'] = Contact::getIdForURL($relay_actor);
+               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);
 
@@ -774,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
         *