]> git.mxchange.org Git - friendica.git/commitdiff
Asynchronous contact relation check
authorMichael <heluecht@pirati.ca>
Wed, 29 Jul 2020 15:39:45 +0000 (15:39 +0000)
committerMichael <heluecht@pirati.ca>
Wed, 29 Jul 2020 15:39:45 +0000 (15:39 +0000)
src/Model/Contact.php
src/Model/ContactRelation.php
src/Worker/ContactDiscovery.php [new file with mode: 0644]

index bb876405421e0e723457e2bb302cf3e786579937..6dbba6ed408fa4fed5163bcb39c8394f64e8bd12 100644 (file)
@@ -2075,7 +2075,7 @@ class Contact
         * @throws HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function updateFromProbe($id, $network = '', $force = false)
+       public static function updateFromProbe(int $id, string $network = '', bool $force = false)
        {
                /*
                  Warning: Never ever fetch the public key via Probe::uri and write it into the contacts.
@@ -2123,6 +2123,10 @@ class Contact
                        return false;
                }
 
+               if (ContactRelation::isDiscoverable($ret['url'])) {
+                       Worker::add(PRIORITY_LOW, 'ContactDiscovery', $ret['url']);
+               }
+
                if (isset($ret['hide']) && is_bool($ret['hide'])) {
                        $ret['unsearchable'] = $ret['hide'];
                }
@@ -2147,8 +2151,6 @@ class Contact
                        GContact::updateFromPublicContactID($id);
                }
 
-               ContactRelation::discoverByUrl($ret['url']);
-
                $update = false;
 
                // make sure to not overwrite existing values with blank entries except some technical fields
index dfbea17cb45143f67a9fbb7bc159df68a1444e4c..e8b0f81f037bb8a4c47b04124c0fa225e83474b1 100644 (file)
@@ -65,49 +65,16 @@ class ContactRelation
         */
        public static function discoverByUrl(string $url)
        {
-               $contact_discovery = DI::config()->get('system', 'contact_discovery');
-
-               if ($contact_discovery == self::DISCOVERY_NONE) {
-                       return;
-               }
-
                $contact = Contact::getByURL($url);
                if (empty($contact)) {
                        return;
                }
 
-               if ($contact['last-discovery'] > DateTimeFormat::utc('now - 1 month')) {
-                       Logger::info('No discovery - Last was less than a month ago.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['last-discovery']]);
-                       return;
-               }
-
-               if ($contact_discovery != self::DISCOVERY_ALL) {
-                       $local = DBA::exists('contact', ["`nurl` = ? AND `uid` != ?", Strings::normaliseLink($url), 0]);
-                       if (($contact_discovery == self::DISCOVERY_LOCAL) && !$local) {
-                               Logger::info('No discovery - This contact is not followed/following locally.', ['id' => $contact['id'], 'url' => $url]);
-                               return;
-                       }
-
-                       if ($contact_discovery == self::DISCOVERY_INTERACTOR) {
-                               $interactor = DBA::exists('contact-relation', ["`relation-cid` = ? AND `last-interaction` > ?", $contact['id'], DBA::NULL_DATETIME]);
-                               if (!$local && !$interactor) {
-                                       Logger::info('No discovery - This contact is not interacting locally.', ['id' => $contact['id'], 'url' => $url]);
-                                       return;
-                               }
-                       }
-               } elseif ($contact['created'] > DateTimeFormat::utc('now - 1 day')) {
-                       // Newly created contacts are not discovered to avoid DDoS attacks
-                       Logger::info('No discovery - Contact record is less than a day old.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['created']]);
+               if (!self::isDiscoverable($url, $contact)) {
                        return;
                }
 
-               if (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS])) {
-                       // The contact is (most likely) speaking AP, so updating is allowed
-                       $apcontact = APContact::getByURL($url);
-               } else {
-                       // The contact isn't obviously speaking AP, so we don't allow updating
-                       $apcontact = APContact::getByURL($url, false);
-               }
+               $apcontact = APContact::getByURL($url, false);
 
                if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) {
                        $followers = ActivityPub::fetchItems($apcontact['followers']);
@@ -122,6 +89,8 @@ class ContactRelation
                }
 
                if (empty($followers) && empty($followings)) {
+                       DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $contact['id']]);
+                       Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]);
                        return;
                }
 
@@ -142,20 +111,24 @@ class ContactRelation
                }
                $contacts = array_unique($contacts);
 
+               $follower_counter = 0;
+               $following_counter = 0;
+
                Logger::info('Discover contacts', ['id' => $target, 'url' => $url, 'contacts' => count($contacts)]);
                foreach ($contacts as $contact) {
                        $actor = Contact::getIdForURL($contact);
                        if (!empty($actor)) {
-                               $fields = [];
                                if (in_array($contact, $followers)) {
                                        $fields = ['cid' => $target, 'relation-cid' => $actor];
-                               } elseif (in_array($contact, $followings)) {
-                                       $fields = ['cid' => $actor, 'relation-cid' => $target];
-                               } else {
-                                       continue;
+                                       DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true);
+                                       $follower_counter++;
                                }
 
-                               DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true);
+                               if (in_array($contact, $followings)) {
+                                       $fields = ['cid' => $actor, 'relation-cid' => $target];
+                                       DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true);
+                                       $following_counter++;
+                               }
                        }
                }
 
@@ -165,7 +138,66 @@ class ContactRelation
                }
 
                DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $target]);
-               Logger::info('Contacts discovery finished, "last-discovery" set', ['id' => $target, 'url' => $url]);
+               Logger::info('Contacts discovery finished', ['id' => $target, 'url' => $url, 'follower' => $follower_counter, 'following' => $following_counter]);
                return;
        }
+
+       /**
+        * Tests if a given contact url is discoverable
+        *
+        * @param string $url     Contact url
+        * @param array  $contact Contact array
+        * @return boolean True if contact is discoverable
+        */
+       public static function isDiscoverable(string $url, array $contact = [])
+       {
+               $contact_discovery = DI::config()->get('system', 'contact_discovery');
+
+               if ($contact_discovery == self::DISCOVERY_NONE) {
+                       return false;
+               }
+
+               if (empty($contact)) {
+                       $contact = Contact::getByURL($url);
+               }
+
+               if (empty($contact)) {
+                       return false;
+               }
+
+               if ($contact['last-discovery'] > DateTimeFormat::utc('now - 1 month')) {
+                       Logger::info('No discovery - Last was less than a month ago.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['last-discovery']]);
+                       return false;
+               }
+
+               if ($contact_discovery != self::DISCOVERY_ALL) {
+                       $local = DBA::exists('contact', ["`nurl` = ? AND `uid` != ?", Strings::normaliseLink($url), 0]);
+                       if (($contact_discovery == self::DISCOVERY_LOCAL) && !$local) {
+                               Logger::info('No discovery - This contact is not followed/following locally.', ['id' => $contact['id'], 'url' => $url]);
+                               return false;
+                       }
+
+                       if ($contact_discovery == self::DISCOVERY_INTERACTOR) {
+                               $interactor = DBA::exists('contact-relation', ["`relation-cid` = ? AND `last-interaction` > ?", $contact['id'], DBA::NULL_DATETIME]);
+                               if (!$local && !$interactor) {
+                                       Logger::info('No discovery - This contact is not interacting locally.', ['id' => $contact['id'], 'url' => $url]);
+                                       return false;
+                               }
+                       }
+               } elseif ($contact['created'] > DateTimeFormat::utc('now - 1 day')) {
+                       // Newly created contacts are not discovered to avoid DDoS attacks
+                       Logger::info('No discovery - Contact record is less than a day old.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['created']]);
+                       return false;
+               }
+
+               if (!in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS])) {
+                       $apcontact = APContact::getByURL($url, false);
+                       if (empty($apcontact)) {
+                               Logger::info('No discovery - The contact does not seem to speak ActivityPub.', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]);
+                               return false;
+                       }
+               }
+
+               return true;
+       }
 }
diff --git a/src/Worker/ContactDiscovery.php b/src/Worker/ContactDiscovery.php
new file mode 100644 (file)
index 0000000..34d4a7d
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Worker;
+
+use Friendica\Model\ContactRelation;
+
+class ContactDiscovery
+{
+       /**
+        * Discover contact relations
+        * @param string $url
+        */
+       public static function execute(string $url)
+       {
+               ContactRelation::discoverByUrl($url);
+       }
+}