]> git.mxchange.org Git - friendica.git/commitdiff
Merge pull request #10323 from annando/issue-10306
authorHypolite Petovan <hypolite@mrpetovan.com>
Wed, 26 May 2021 18:24:24 +0000 (14:24 -0400)
committerGitHub <noreply@github.com>
Wed, 26 May 2021 18:24:24 +0000 (14:24 -0400)
Issue 10306: Improve local delivery

src/Model/Contact.php
src/Model/Item.php
src/Model/Post.php
src/Model/User.php
src/Protocol/ActivityPub/Transmitter.php
src/Util/Network.php
src/Worker/Delivery.php
src/Worker/Notifier.php

index 4af1d9bb6d74347913248d19230fe60a30227da3..c3c75a8d438ede705687c9fb9edd423889e283da 100644 (file)
@@ -1941,7 +1941,7 @@ class Contact
                        return false;
                }
 
-               if (Contact::isLocal($ret['url'])) {
+               if (self::isLocal($ret['url'])) {
                        Logger::info('Local contacts are not updated here.');
                        return true;
                }
index 4dbea3eb8b73110593548afb5081ad0460e3f4ab..ac9af32a6968b24d39db3ffd6a597d877be0f41f 100644 (file)
@@ -1018,6 +1018,30 @@ class Item
 
                if (empty($item['event-id'])) {
                        unset($item['event-id']);
+
+                       $ev = Event::fromBBCode($item['body']);
+                       if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) {
+                               Logger::info('Event found.');
+                               $ev['cid']       = $item['contact-id'];
+                               $ev['uid']       = $item['uid'];
+                               $ev['uri']       = $item['uri'];
+                               $ev['edited']    = $item['edited'];
+                               $ev['private']   = $item['private'];
+                               $ev['guid']      = $item['guid'];
+                               $ev['plink']     = $item['plink'];
+                               $ev['network']   = $item['network'];
+                               $ev['protocol']  = $item['protocol'];
+                               $ev['direction'] = $item['direction'];
+                               $ev['source']    = $item['source'];
+
+                               $event = DBA::selectFirst('event', ['id'], ['uri' => $item['uri'], 'uid' => $item['uid']]);
+                               if (DBA::isResult($event)) {
+                                       $ev['id'] = $event['id'];
+                               }
+
+                               $item['event-id'] = Event::store($ev);
+                               Logger::info('Event was stored', ['id' => $item['event-id']]);
+                       }
                }
 
                if (empty($item['causer-id'])) {
@@ -1322,19 +1346,26 @@ class Item
        /**
         * Store a public item defined by their URI-ID for the given users
         *
-        * @param integer $uri_id URI-ID of the given item
-        * @param integer $uid    The user that will receive the item entry
-        * @param array   $fields Additional fields to be stored
+        * @param integer $uri_id     URI-ID of the given item
+        * @param integer $uid        The user that will receive the item entry
+        * @param array   $fields     Additional fields to be stored
+        * @param integer $source_uid User id of the source post
         * @return integer stored item id
         */
-       public static function storeForUserByUriId(int $uri_id, int $uid, array $fields = [])
+       public static function storeForUserByUriId(int $uri_id, int $uid, array $fields = [], int $source_uid = 0)
        {
-               $item = Post::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $uri_id, 'uid' => 0]);
+               if ($uid == $source_uid) {
+                       Logger::warning('target UID must not be be equal to the source UID', ['uri-id' => $uri_id, 'uid' => $uid]);
+                       return 0;
+               }
+
+               $item = Post::selectFirst(self::ITEM_FIELDLIST, ['uri-id' => $uri_id, 'uid' => $source_uid]);
                if (!DBA::isResult($item)) {
+                       Logger::warning('Item could not be fetched', ['uri-id' => $uri_id, 'uid' => $source_uid]);
                        return 0;
                }
 
-               if (($item['private'] == self::PRIVATE) || !in_array($item['network'], Protocol::FEDERATED)) {
+               if (($source_uid == 0) && (($item['private'] == self::PRIVATE) || !in_array($item['network'], Protocol::FEDERATED))) {
                        Logger::notice('Item is private or not from a federated network. It will not be stored for the user.', ['uri-id' => $uri_id, 'uid' => $uid, 'private' => $item['private'], 'network' => $item['network']]);
                        return 0;
                }
@@ -1343,8 +1374,25 @@ class Item
 
                $item = array_merge($item, $fields);
 
+               $is_reshare = ($item['gravity'] == GRAVITY_ACTIVITY) && ($item['verb'] == Activity::ANNOUNCE);
+
+               if ((($item['gravity'] == GRAVITY_PARENT) || $is_reshare) &&
+                       DI::pConfig()->get($uid, 'system', 'accept_only_sharer') &&
+                       !Contact::isSharingByURL($item['author-link'], $uid) &&
+                       !Contact::isSharingByURL($item['owner-link'], $uid)) {
+                       Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid]);
+                       return 0;
+               }
+
+               if ((($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
+                       // Only do an auto complete with the source uid "0" to prevent privavy problems
+                       $causer = $item['causer-id'] ?: $item['author-id'];
+                       $result = self::storeForUserByUriId($item['thr-parent-id'], $uid, ['causer-id' => $causer, 'post-reason' => self::PR_FETCHED]);
+                       Logger::info('Fetched thread parent', ['uri-id' => $item['thr-parent-id'], 'uid' => $uid, 'causer' => $causer, 'result' => $result]);
+               }
+
                $stored = self::storeForUser($item, $uid);
-               Logger::info('Public item stored for user', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'stored' => $stored]);
+               Logger::info('Item stored for user', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'source-uid' => $source_uid, 'stored' => $stored]);
                return $stored;
        }
 
@@ -1364,11 +1412,18 @@ class Item
                }
 
                unset($item['id']);
-               unset($item['parent']);
                unset($item['mention']);
                unset($item['starred']);
                unset($item['unseen']);
                unset($item['psid']);
+               unset($item['pinned']);
+               unset($item['ignored']);
+               unset($item['pubmail']);
+               unset($item['forum_mode']);
+
+               unset($item['event-id']);
+               unset($item['hidden']);
+               unset($item['notification-type']);
 
                $item['uid'] = $uid;
                $item['origin'] = 0;
@@ -1394,8 +1449,6 @@ class Item
                        $item['contact-id'] = $self['id'];
                }
 
-               /// @todo Handling of "event-id"
-
                $notify = false;
                if ($item['gravity'] == GRAVITY_PARENT) {
                        $contact = DBA::selectFirst('contact', [], ['id' => $item['contact-id'], 'self' => false]);
@@ -1407,9 +1460,9 @@ class Item
                $distributed = self::insert($item, $notify, true);
 
                if (!$distributed) {
-                       Logger::info("Distributed public item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]);
+                       Logger::info("Distributed item wasn't stored", ['uri-id' => $item['uri-id'], 'user' => $uid]);
                } else {
-                       Logger::info('Distributed public item was stored', ['uri-id' => $item['uri-id'], 'user' => $uid, 'stored' => $distributed]);
+                       Logger::info('Distributed item was stored', ['uri-id' => $item['uri-id'], 'user' => $uid, 'stored' => $distributed]);
                }
                return $distributed;
        }
@@ -3295,18 +3348,18 @@ class Item
        {
                $shared = BBCode::fetchShareAttributes($item['body']);
                if (empty($shared['link'])) {
-                       return $item['body'];   
+                       return $item['body'];
                }
-               
+
                $id = self::fetchByLink($shared['link']);
                Logger::info('Fetched shared post', ['uri-id' => $item['uri-id'], 'id' => $id, 'author' => $shared['profile'], 'url' => $shared['link'], 'guid' => $shared['guid'], 'callstack' => System::callstack()]);
                if (!$id) {
-                       return $item['body'];   
+                       return $item['body'];
                }
 
                $shared_item = Post::selectFirst(['author-name', 'author-link', 'author-avatar', 'plink', 'created', 'guid', 'title', 'body'], ['id' => $id]);
                if (!DBA::isResult($shared_item)) {
-                       return $item['body'];   
+                       return $item['body'];
                }
 
                $shared_content = BBCode::getShareOpeningTag($shared_item['author-name'], $shared_item['author-link'], $shared_item['author-avatar'], $shared_item['plink'], $shared_item['created'], $shared_item['guid']);
index aae847664db2093efee10ebc45575a2fa44a39ad..2dc73446be188955ff7d38d48bc6da3f0f7eabbf 100644 (file)
@@ -164,15 +164,16 @@ class Post
         * @param array $fields
         * @param array $condition
         * @param array $params
+        * @param bool  $user_mode true = post-user-view, false = post-view
         * @return bool|array
         * @throws \Exception
         * @see   DBA::select
         */
-       public static function selectFirst(array $fields = [], array $condition = [], $params = [])
+       public static function selectFirst(array $fields = [], array $condition = [], $params = [], bool $user_mode = true)
        {
                $params['limit'] = 1;
 
-               $result = self::select($fields, $condition, $params);
+               $result = self::select($fields, $condition, $params, $user_mode);
 
                if (is_bool($result)) {
                        return $result;
index 38a22f70f9a72e944d1a0eb18c7c94d43afc091b..2131406d4a5f8cf2be7432c66b57f2839936224c 100644 (file)
@@ -312,8 +312,8 @@ class User
         */
        public static function getIdForURL(string $url)
        {
-               // Avoid any database requests when the hostname isn't even part of the url.
-               if (!strpos($url, DI::baseUrl()->getHostname())) {
+               // Avoid database queries when the local node hostname isn't even part of the url.
+               if (!Contact::isLocal($url)) {
                        return 0;
                }
 
index 83e3d35a9782c971ec4b84d085aaefdf27995c3b..bfb1287c8295ceed0a8d75ab247cb057d72f7530 100644 (file)
@@ -748,10 +748,6 @@ class Transmitter
 
                $contacts = DBA::select('contact', ['id', 'url', 'network', 'protocol', 'gsid'], $condition);
                while ($contact = DBA::fetch($contacts)) {
-                       if (Contact::isLocal($contact['url'])) {
-                               continue;
-                       }
-
                        if (!self::isAPContact($contact, $networks)) {
                                continue;
                        }
@@ -766,7 +762,7 @@ class Transmitter
 
                        $profile = APContact::getByURL($contact['url'], false);
                        if (!empty($profile)) {
-                               if (empty($profile['sharedinbox']) || $personal) {
+                               if (empty($profile['sharedinbox']) || $personal || Contact::isLocal($contact['url'])) {
                                        $target = $profile['inbox'];
                                } else {
                                        $target = $profile['sharedinbox'];
@@ -829,15 +825,11 @@ class Transmitter
                                if ($item_profile && ($receiver == $item_profile['followers']) && ($uid == $profile_uid)) {
                                        $inboxes = array_merge($inboxes, self::fetchTargetInboxesforUser($uid, $personal, self::isAPPost($last_id)));
                                } else {
-                                       if (Contact::isLocal($receiver)) {
-                                               continue;
-                                       }
-
                                        $profile = APContact::getByURL($receiver, false);
                                        if (!empty($profile)) {
                                                $contact = Contact::getByURLForUser($receiver, $uid, false, ['id']);
 
-                                               if (empty($profile['sharedinbox']) || $personal || $blindcopy) {
+                                               if (empty($profile['sharedinbox']) || $personal || $blindcopy || Contact::isLocal($receiver)) {
                                                        $target = $profile['inbox'];
                                                } else {
                                                        $target = $profile['sharedinbox'];
index 2c9949ee23bd93061304b0eeb883f5cafb4630c1..0b10687c9d5a64f3a5e8ceef166d04002cec7293 100644 (file)
@@ -548,4 +548,15 @@ class Network
                        exit;
                }
        }
+
+       /**
+        * Check if the given URL is a local link
+        *
+        * @param string $url 
+        * @return bool 
+        */
+       public static function isLocalLink(string $url)
+       {
+               return (strpos(Strings::normaliseLink($url), Strings::normaliseLink(DI::baseUrl())) !== false);
+       }
 }
index db7b8c26a5352cea62941e6bac699a954dd734fb..dad2ff7b3d0cf75819e777a53a6b58aaf5da781a 100644 (file)
@@ -30,13 +30,11 @@ use Friendica\Protocol\DFRN;
 use Friendica\Protocol\Diaspora;
 use Friendica\Protocol\Email;
 use Friendica\Protocol\Activity;
-use Friendica\Util\Strings;
 use Friendica\Util\Network;
 use Friendica\Core\Worker;
 use Friendica\Model\Conversation;
 use Friendica\Model\FContact;
 use Friendica\Model\Item;
-use Friendica\Model\Post;
 use Friendica\Protocol\Relay;
 
 class Delivery
@@ -216,11 +214,6 @@ class Delivery
                        $contact['network'] = Protocol::DIASPORA;
                }
 
-               // Ensure that local contacts are delivered locally
-               if (Model\Contact::isLocal($contact['url'])) {
-                       $contact['network'] = Protocol::DFRN;
-               }
-
                Logger::notice('Delivering', ['cmd' => $cmd, 'uri-id' => $post_uriid, 'followup' => $followup, 'network' => $contact['network']]);
 
                switch ($contact['network']) {
@@ -316,40 +309,6 @@ class Delivery
 
                Logger::debug('Notifier entry: ' . $contact["url"] . ' ' . (($target_item['guid'] ?? '') ?: $target_item['id']) . ' entry: ' . $atom);
 
-               // perform local delivery if we are on the same site
-               if (Model\Contact::isLocal($contact['url'])) {
-                       $condition = ['nurl' => Strings::normaliseLink($contact['url']), 'self' => true];
-                       $target_self = DBA::selectFirst('contact', ['uid'], $condition);
-                       if (!DBA::isResult($target_self)) {
-                               return;
-                       }
-                       $target_uid = $target_self['uid'];
-
-                       // Check if the user has got this contact
-                       $cid = Model\Contact::getIdForURL($owner['url'], $target_uid);
-                       if (!$cid) {
-                               // Otherwise there should be a public contact
-                               $cid = Model\Contact::getIdForURL($owner['url']);
-                               if (!$cid) {
-                                       return;
-                               }
-                       }
-
-                       $target_importer = DFRN::getImporter($cid, $target_uid);
-                       if (empty($target_importer)) {
-                               // This should never happen
-                               return;
-                       }
-
-                       DFRN::import($atom, $target_importer, Conversation::PARCEL_LOCAL_DFRN, Conversation::PUSH);
-
-                       if (in_array($cmd, [Delivery::POST, Delivery::POKE])) {
-                               Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::DFRN);
-                       }
-
-                       return;
-               }
-
                $protocol = Model\Post\DeliveryData::DFRN;
 
                // We don't have a relationship with contacts on a public post.
index 60a5572ca5035c09a634d7676a03b97c42d9d59a..de2774594d7972ee9f5b8c124d9667ea795d525e 100644 (file)
@@ -41,6 +41,7 @@ use Friendica\Protocol\Diaspora;
 use Friendica\Protocol\OStatus;
 use Friendica\Protocol\Relay;
 use Friendica\Protocol\Salmon;
+use Friendica\Util\Network;
 
 /*
  * The notifier is typically called with:
@@ -313,7 +314,7 @@ class Notifier
                                /// @todo Possibly we should not uplink when the author is the forum itself?
 
                                if ((intval($parent['forum_mode']) == 1) && !$top_level && ($cmd !== Delivery::UPLINK)
-                                       && ($target_item['verb'] != Activity::ANNOUNCE)) {                                              
+                                       && ($target_item['verb'] != Activity::ANNOUNCE)) {
                                        Worker::add($a->queue['priority'], 'Notifier', Delivery::UPLINK, $post_uriid, $sender_uid);
                                }
 
@@ -494,29 +495,32 @@ class Notifier
        /**
         * Deliver the message to the contacts
         *
-        * @param string $cmd 
-        * @param int $post_uriid 
+        * @param string $cmd
+        * @param int $post_uriid
         * @param int $sender_uid
-        * @param array $target_item 
-        * @param array $thr_parent 
-        * @param array $owner 
-        * @param bool $batch_delivery 
-        * @param array $contacts 
-        * @param array $ap_contacts 
-        * @param array $conversants 
-        * @return int 
-        * @throws InternalServerErrorException 
-        * @throws Exception 
+        * @param array $target_item
+        * @param array $thr_parent
+        * @param array $owner
+        * @param bool $batch_delivery
+        * @param array $contacts
+        * @param array $ap_contacts
+        * @param array $conversants
+        * @return int
+        * @throws InternalServerErrorException
+        * @throws Exception
         */
        private static function delivery(string $cmd, int $post_uriid, int $sender_uid, array $target_item, array $thr_parent, array $owner, bool $batch_delivery, bool $in_batch, array $contacts, array $ap_contacts, array $conversants = [])
        {
-               $a = DI::app(); 
+               $a = DI::app();
                $delivery_queue_count = 0;
 
                foreach ($contacts as $contact) {
-                       // Ensure that local contacts are delivered via DFRN
-                       if (Contact::isLocal($contact['url'])) {
-                               $contact['network'] = Protocol::DFRN;
+                       // Direct delivery of local contacts
+                       if ($target_uid = User::getIdForURL($contact['url'])) {
+                               Logger::info('Direct delivery', ['uri-id' => $target_item['uri-id'], 'target' => $target_uid]);
+                               $fields = ['protocol' => Conversation::PARCEL_LOCAL_DFRN, 'direction' => Conversation::PUSH];
+                               Item::storeForUserByUriId($target_item['uri-id'], $target_uid, $fields, $target_item['uid']);
+                               continue;
                        }
 
                        // Deletions are always sent via DFRN as well.
@@ -575,19 +579,19 @@ class Notifier
        /**
         * Deliver the message via OStatus
         *
-        * @param int $target_id 
-        * @param array $target_item 
-        * @param array $owner 
-        * @param array $url_recipients 
-        * @param bool $public_message 
-        * @param bool $push_notify 
-        * @return int 
-        * @throws InternalServerErrorException 
-        * @throws Exception 
+        * @param int $target_id
+        * @param array $target_item
+        * @param array $owner
+        * @param array $url_recipients
+        * @param bool $public_message
+        * @param bool $push_notify
+        * @return int
+        * @throws InternalServerErrorException
+        * @throws Exception
         */
        private static function deliverOStatus(int $target_id, array $target_item, array $owner, array $url_recipients, bool $public_message, bool $push_notify)
        {
-               $a = DI::app(); 
+               $a = DI::app();
                $delivery_queue_count = 0;
 
                $url_recipients = array_filter($url_recipients);
@@ -775,6 +779,16 @@ class Notifier
                foreach ($inboxes as $inbox => $receivers) {
                        $contacts = array_merge($contacts, $receivers);
 
+                       if ((count($receivers) == 1) && Network::isLocalLink($inbox)) {
+                               $contact = Contact::getById($receivers[0], ['url']);
+                               if ($target_uid = User::getIdForURL($contact['url'])) {
+                                       $fields = ['protocol' => Conversation::PARCEL_LOCAL_DFRN, 'direction' => Conversation::PUSH, 'post-reason' => Item::PR_BCC];
+                                       Item::storeForUserByUriId($target_item['uri-id'], $target_uid, $fields, $target_item['uid']);
+                                       Logger::info('Delivered locally', ['cmd' => $cmd, 'id' => $target_item['id'], 'inbox' => $inbox]);
+                                       continue;
+                               }
+                       }
+
                        Logger::info('Delivery via ActivityPub', ['cmd' => $cmd, 'id' => $target_item['id'], 'inbox' => $inbox]);
 
                        if (Worker::add(['priority' => $priority, 'created' => $created, 'dont_fork' => true],