Merge pull request #7339 from annando/gcontact-update
authorHypolite Petovan <hypolite@mrpetovan.com>
Sun, 7 Jul 2019 17:28:20 +0000 (13:28 -0400)
committerGitHub <noreply@github.com>
Sun, 7 Jul 2019 17:28:20 +0000 (13:28 -0400)
Restructured Contact/GContact handling

13 files changed:
config/dbstructure.config.php
database.sql
mod/redir.php
src/Model/APContact.php
src/Model/Contact.php
src/Model/GContact.php
src/Network/Probe.php
src/Protocol/ActivityPub.php
src/Protocol/ActivityPub/Processor.php
src/Protocol/DFRN.php
src/Protocol/Diaspora.php
src/Worker/OnePoll.php
src/Worker/UpdateGContact.php

index 5cf9d85..3c2b813 100755 (executable)
@@ -34,7 +34,7 @@
 use Friendica\Database\DBA;
 
 if (!defined('DB_UPDATE_VERSION')) {
-       define('DB_UPDATE_VERSION', 1315);
+       define('DB_UPDATE_VERSION', 1316);
 }
 
 return [
@@ -253,6 +253,9 @@ return [
                        "pending" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => ""],
                        "deleted" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact has been deleted"],
                        "rating" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => ""],
+                       "unsearchable" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact prefers to not be searchable"],
+                       "sensitive" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact posts sensitive content"],
+                       "baseurl" => ["type" => "varchar(255)", "default" => "", "comment" => "baseurl of the contact"],
                        "reason" => ["type" => "text", "comment" => ""],
                        "closeness" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "99", "comment" => ""],
                        "info" => ["type" => "mediumtext", "comment" => ""],
@@ -423,6 +426,8 @@ return [
                        "updated" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""],
                        "last_contact" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""],
                        "last_failure" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""],
+                       "archive_date" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => ""],
+                       "archived" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
                        "location" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
                        "about" => ["type" => "text", "comment" => ""],
                        "keywords" => ["type" => "text", "comment" => "puplic keywords (interests)"],
index 67ae8b4..7dc24b6 100644 (file)
@@ -1,6 +1,6 @@
 -- ------------------------------------------
 -- Friendica 2019.09-dev (Dalmatian Bellflower)
--- DB_UPDATE_VERSION 1314
+-- DB_UPDATE_VERSION 1316
 -- ------------------------------------------
 
 
@@ -210,6 +210,9 @@ CREATE TABLE IF NOT EXISTS `contact` (
        `pending` boolean NOT NULL DEFAULT '1' COMMENT '',
        `deleted` boolean NOT NULL DEFAULT '0' COMMENT 'Contact has been deleted',
        `rating` tinyint NOT NULL DEFAULT 0 COMMENT '',
+       `unsearchable` boolean NOT NULL DEFAULT '0' COMMENT 'Contact prefers to not be searchable',
+       `sensitive` boolean NOT NULL DEFAULT '0' COMMENT 'Contact posts sensitive content',
+       `baseurl` varchar(255) DEFAULT '' COMMENT 'baseurl of the contact',
        `reason` text COMMENT '',
        `closeness` tinyint unsigned NOT NULL DEFAULT 99 COMMENT '',
        `info` mediumtext COMMENT '',
@@ -372,6 +375,8 @@ CREATE TABLE IF NOT EXISTS `gcontact` (
        `updated` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
        `last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
        `last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
+       `archive_date` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
+       `archived` boolean NOT NULL DEFAULT '0' COMMENT '',
        `location` varchar(255) NOT NULL DEFAULT '' COMMENT '',
        `about` text COMMENT '',
        `keywords` text COMMENT 'puplic keywords (interests)',
index 233ec9b..c99e182 100644 (file)
@@ -85,7 +85,7 @@ function redir_init(App $a) {
 
                // When the remote page does support OWA, then we enforce the use of it
                $basepath = Contact::getBasepath($contact_url);
-               if ($basepath == System::baseUrl()) {
+               if (Strings::compareLink($basepath, System::baseUrl())) {
                        $use_magic = true;
                } else {
                        $serverret = Network::curl($basepath . '/magic');
index b027d6c..82afc07 100644 (file)
@@ -237,44 +237,6 @@ class APContact extends BaseObject
                        DBA::delete('apcontact', ['url' => $url]);
                }
 
-               // Update some data in the contact table with various ways to catch them all
-               $contact_fields = ['name' => $apcontact['name'], 'about' => $apcontact['about'], 'alias' => $apcontact['alias']];
-
-               // Fetch the type and match it with the contact type
-               $contact_types = array_keys(ActivityPub::ACCOUNT_TYPES, $apcontact['type']);
-               if (!empty($contact_types)) {
-                       $contact_type = array_pop($contact_types);
-                       if (is_int($contact_type)) {
-                               $contact_fields['contact-type'] = $contact_type;
-
-                               if ($contact_fields['contact-type'] != User::ACCOUNT_TYPE_COMMUNITY) {
-                                       // Resetting the 'forum' and 'prv' field when it isn't a forum
-                                       $contact_fields['forum'] = false;
-                                       $contact_fields['prv'] = false;
-                               } else {
-                                       // Otherwise set the corresponding forum type
-                                       $contact_fields['forum'] = !$apcontact['manually-approve'];
-                                       $contact_fields['prv'] = $apcontact['manually-approve'];
-                               }
-                       }
-               }
-
-               DBA::update('contact', $contact_fields, ['nurl' => Strings::normaliseLink($url)]);
-
-               if (!empty($apcontact['photo'])) {
-                       $contacts = DBA::select('contact', ['uid', 'id'], ['nurl' => Strings::normaliseLink($url)]);
-                       while ($contact = DBA::fetch($contacts)) {
-                               Contact::updateAvatar($apcontact['photo'], $contact['uid'], $contact['id']);
-                       }
-                       DBA::close($contacts);
-               }
-
-               // Update the gcontact table
-               // These two fields don't exist in the gcontact table
-               unset($contact_fields['forum']);
-               unset($contact_fields['prv']);
-               DBA::update('gcontact', $contact_fields, ['nurl' => Strings::normaliseLink($url)]);
-
                Logger::log('Updated profile for ' . $url, Logger::DEBUG);
 
                return $apcontact;
index a10c0d0..775cc3f 100644 (file)
@@ -176,7 +176,6 @@ class Contact extends BaseObject
 
        /**
         * @brief Get the basepath for a given contact link
-        * @todo  Add functionality to store this value in the contact table
         *
         * @param string $url The contact link
         *
@@ -186,13 +185,19 @@ class Contact extends BaseObject
         */
        public static function getBasepath($url)
        {
-               $data = Probe::uri($url);
-               if (!empty($data['baseurl'])) {
-                       return $data['baseurl'];
+               $contact = DBA::selectFirst('contact', ['baseurl'], ['uid' => 0, 'nurl' => Strings::normaliseLink($url)]);
+               if (!empty($contact['baseurl'])) {
+                       return $contact['baseurl'];
+               }
+
+               self::updateFromProbeByURL($url, true);
+
+               $contact = DBA::selectFirst('contact', ['baseurl'], ['uid' => 0, 'nurl' => Strings::normaliseLink($url)]);
+               if (!empty($contact['baseurl'])) {
+                       return $contact['baseurl'];
                }
 
-               // When we can't probe the server, we use some ugly function that does some pattern matching
-               return PortableContact::detectServer($url);
+               return '';
        }
 
        /**
@@ -792,6 +797,7 @@ class Contact extends BaseObject
                                 */
                                DBA::update('contact', ['archive' => 1], ['id' => $contact['id']]);
                                DBA::update('contact', ['archive' => 1], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
+                               GContact::updateFromPublicContactURL($contact['url']);
                        }
                }
        }
@@ -829,6 +835,7 @@ class Contact extends BaseObject
                $fields = ['term-date' => DBA::NULL_DATETIME, 'archive' => false];
                DBA::update('contact', $fields, ['id' => $contact['id']]);
                DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url'])]);
+               GContact::updateFromPublicContactURL($contact['url']);
 
                if (!empty($contact['batch'])) {
                        $condition = ['batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
@@ -965,7 +972,7 @@ class Contact extends BaseObject
                if ((empty($profile["addr"]) || empty($profile["name"])) && (defaults($profile, "gid", 0) != 0)
                        && in_array($profile["network"], Protocol::FEDERATED)
                ) {
-                       Worker::add(PRIORITY_LOW, "UpdateGContact", $profile["gid"]);
+                       Worker::add(PRIORITY_LOW, "UpdateGContact", $url);
                }
 
                // Show contact details of Diaspora contacts only if connected
@@ -1339,10 +1346,14 @@ class Contact extends BaseObject
                        return 0;
                }
 
-               // When we don't want to update, we look if we know this contact in any way
                if ($no_update && empty($default)) {
+                       // When we don't want to update, we look if we know this contact in any way
                        $data = self::getProbeDataFromDatabase($url, $contact_id);
                        $background_update = true;
+               } elseif ($no_update && !empty($default)) {
+                       // If there are default values, take these
+                       $data = $default;
+                       $background_update = false;
                } else {
                        $data = [];
                        $background_update = false;
@@ -1357,18 +1368,9 @@ class Contact extends BaseObject
                        }
                }
 
-               // Last try in gcontact for unsupported networks
-               if (!in_array($data["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::DIASPORA, Protocol::PUMPIO, Protocol::MAIL, Protocol::FEED])) {
-                       if ($uid != 0) {
-                               return 0;
-                       }
-
-                       $contact = array_merge(self::getProbeDataFromDatabase($url, $contact_id), $default);
-                       if (empty($contact)) {
-                               return 0;
-                       }
-
-                       $data = array_merge($data, $contact);
+               // Take the default values when probing failed
+               if (!empty($default) && !in_array($data["network"], array_merge(Protocol::NATIVE_SUPPORT, [Protocol::PUMPIO]))) {
+                       $data = array_merge($data, $default);
                }
 
                if (empty($data)) {
@@ -1403,6 +1405,7 @@ class Contact extends BaseObject
                                'request'   => defaults($data, 'request', ''),
                                'confirm'   => defaults($data, 'confirm', ''),
                                'poco'      => defaults($data, 'poco', ''),
+                               'baseurl'   => defaults($data, 'baseurl', ''),
                                'name-date' => DateTimeFormat::utcNow(),
                                'uri-date'  => DateTimeFormat::utcNow(),
                                'avatar-date' => DateTimeFormat::utcNow(),
@@ -1456,7 +1459,7 @@ class Contact extends BaseObject
                        self::updateAvatar($data['photo'], $uid, $contact_id);
                }
 
-               $fields = ['url', 'nurl', 'addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'avatar-date', 'pubkey'];
+               $fields = ['url', 'nurl', 'addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'avatar-date', 'pubkey', 'baseurl'];
                $contact = DBA::selectFirst('contact', $fields, ['id' => $contact_id]);
 
                // This condition should always be true
@@ -1473,6 +1476,9 @@ class Contact extends BaseObject
                        'nick' => $data['nick']
                ];
 
+               if (!empty($data['baseurl'])) {
+                       $updated['baseurl'] = $data['baseurl'];
+               }
                if (!empty($data['keywords'])) {
                        $updated['keywords'] = $data['keywords'];
                }
@@ -1501,7 +1507,7 @@ class Contact extends BaseObject
                }
 
                // Only fill the pubkey if it had been empty before. We have to prevent identity theft.
-               if (empty($contact['pubkey'])) {
+               if (empty($contact['pubkey']) && !empty($data['pubkey'])) {
                        $updated['pubkey'] = $data['pubkey'];
                }
 
@@ -1516,6 +1522,11 @@ class Contact extends BaseObject
 
                DBA::update('contact', $updated, ['id' => $contact_id], $contact);
 
+               if (!$background_update && ($uid == 0)) {
+                       // Update the gcontact entry
+                       GContact::updateFromPublicContactID($contact_id);
+               }
+
                return $contact_id;
        }
 
@@ -1770,6 +1781,9 @@ class Contact extends BaseObject
                        return;
                }
 
+               // Update the corresponding gcontact entry
+               GContact::updateFromPublicContactID($id);
+
                // Archive or unarchive the contact. We only need to do this for the public contact.
                // The archive/unarchive function will update the personal contacts by themselves.
                $contact = DBA::selectFirst('contact', [], ['id' => $id]);
@@ -1814,8 +1828,12 @@ class Contact extends BaseObject
                  This will reliably kill your communication with old Friendica contacts.
                 */
 
+               // These fields aren't updated by this routine:
+               // 'location', 'about', 'keywords', 'gender', 'xmpp', 'unsearchable', 'sensitive'];
+
                $fields = ['avatar', 'uid', 'name', 'nick', 'url', 'addr', 'batch', 'notify',
-                       'poll', 'request', 'confirm', 'poco', 'network', 'alias'];
+                       'poll', 'request', 'confirm', 'poco', 'network', 'alias', 'baseurl',
+                       'forum', 'prv', 'contact-type'];
                $contact = DBA::selectFirst('contact', $fields, ['id' => $id]);
                if (!DBA::isResult($contact)) {
                        return false;
@@ -1840,13 +1858,26 @@ class Contact extends BaseObject
                        return false;
                }
 
+               if (isset($ret['account-type'])) {
+                       $ret['forum'] = false;
+                       $ret['prv'] = false;
+                       $ret['contact-type'] = $ret['account-type'];
+                       if ($ret['contact-type'] == User::ACCOUNT_TYPE_COMMUNITY) {
+                               $apcontact = APContact::getByURL($ret['url'], false);
+                               if (isset($apcontact['manually-approve'])) {
+                                       $ret['forum'] = (bool)!$apcontact['manually-approve'];
+                                       $ret['prv'] = (bool)!$ret['forum'];
+                               }
+                       }
+               }
+
                $update = false;
 
                // make sure to not overwrite existing values with blank entries
                foreach ($ret as $key => $val) {
-                       if (!isset($contact[$key])) {
+                       if (!array_key_exists($key, $contact)) {
                                unset($ret[$key]);
-                       } elseif (($contact[$key] != '') && ($val == '')) {
+                       } elseif (($contact[$key] != '') && ($val == '') && !is_bool($ret[$key])) {
                                $ret[$key] = $contact[$key];
                        } elseif ($ret[$key] != $contact[$key]) {
                                $update = true;
@@ -1876,12 +1907,20 @@ class Contact extends BaseObject
 
                self::updateContact($id, $uid, $ret['url'], $ret);
 
-               // Update the corresponding gcontact entry
-               GContact::updateFromProbe($ret['url']);
-
                return true;
        }
 
+       public static function updateFromProbeByURL($url, $force = false)
+       {
+               $id = self::getIdForURL($url);
+
+               if (empty($id)) {
+                       return;
+               }
+
+               self::updateFromProbe($id, '', $force);
+       }
+
        /**
         * Detects if a given contact array belongs to a legacy DFRN connection
         *
@@ -2080,6 +2119,7 @@ class Contact extends BaseObject
                                'name'    => $ret['name'],
                                'nick'    => $ret['nick'],
                                'network' => $ret['network'],
+                               'baseurl' => $ret['baseurl'],
                                'protocol' => $protocol,
                                'pubkey'  => $ret['pubkey'],
                                'rel'     => $new_relation,
index 8c64712..0fae0da 100644 (file)
@@ -140,7 +140,7 @@ class GContact
                }
 
                // Assure that there are no parameter fragments in the profile url
-               if (in_array($gcontact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, ""])) {
+               if (empty($gcontact["network"]) || in_array($gcontact["network"], Protocol::FEDERATED)) {
                        $gcontact['url'] = self::cleanContactUrl($gcontact['url']);
                }
 
@@ -216,7 +216,7 @@ class GContact
                        throw new Exception('No name and photo for URL '.$gcontact['url']);
                }
 
-               if (!in_array($gcontact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::OSTATUS, Protocol::DIASPORA])) {
+               if (!in_array($gcontact['network'], Protocol::FEDERATED)) {
                        throw new Exception('No federated network ('.$gcontact['network'].') detected for URL '.$gcontact['url']);
                }
 
@@ -664,7 +664,7 @@ class GContact
                self::fixAlternateContactAddress($contact);
 
                // Remove unwanted parts from the contact url (e.g. "?zrl=...")
-               if (in_array($contact["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS])) {
+               if (in_array($contact["network"], Protocol::FEDERATED)) {
                        $contact["url"] = self::cleanContactUrl($contact["url"]);
                }
 
@@ -851,55 +851,134 @@ class GContact
                                        'server_url' => $contact['server_url'], 'connect' => $contact['connect']];
 
                        DBA::update('gcontact', $updated, $condition, $fields);
+               }
 
-                       // Now update the contact entry with the user id "0" as well.
-                       // This is used for the shadow copies of public items.
-                       /// @todo Check if we really should do this.
-                       // The quality of the gcontact table is mostly lower than the public contact
-                       $public_contact = DBA::selectFirst('contact', ['id'], ['nurl' => Strings::normaliseLink($contact["url"]), 'uid' => 0]);
-                       if (DBA::isResult($public_contact)) {
-                               Logger::log("Update public contact ".$public_contact["id"], Logger::DEBUG);
-
-                               Contact::updateAvatar($contact["photo"], 0, $public_contact["id"]);
-
-                               $fields = ['name', 'nick', 'addr',
-                                               'network', 'bd', 'gender',
-                                               'keywords', 'alias', 'contact-type',
-                                               'url', 'location', 'about'];
-                               $old_contact = DBA::selectFirst('contact', $fields, ['id' => $public_contact["id"]]);
-
-                               // Update it with the current values
-                               $fields = ['name' => $contact['name'], 'nick' => $contact['nick'],
-                                               'addr' => $contact['addr'], 'network' => $contact['network'],
-                                               'bd' => $contact['birthday'], 'gender' => $contact['gender'],
-                                               'keywords' => $contact['keywords'], 'alias' => $contact['alias'],
-                                               'contact-type' => $contact['contact-type'], 'url' => $contact['url'],
-                                               'location' => $contact['location'], 'about' => $contact['about']];
-
-                               // Don't update the birthday field if not set or invalid
-                               if (empty($contact['birthday']) || ($contact['birthday'] <= DBA::NULL_DATE)) {
-                                       unset($fields['bd']);
-                               }
+               return $gcontact_id;
+       }
+
+       /**
+        * @brief Updates the gcontact entry from a given public contact id
+        *
+        * @param integer $cid contact id
+        * @return void
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        * @throws \ImagickException
+        */
+       public static function updateFromPublicContactID($cid)
+       {
+               self::updateFromPublicContact(['id' => $cid]);
+       }
 
+       /**
+        * @brief Updates the gcontact entry from a given public contact url
+        *
+        * @param string $url contact url
+        * @return integer gcontact id
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        * @throws \ImagickException
+        */
+       public static function updateFromPublicContactURL($url)
+       {
+               return self::updateFromPublicContact(['nurl' => Strings::normaliseLink($url)]);
+       }
 
-                               DBA::update('contact', $fields, ['id' => $public_contact["id"]], $old_contact);
+       /**
+        * @brief Helper function for updateFromPublicContactID and updateFromPublicContactURL
+        *
+        * @param array $condition contact condition
+        * @return integer gcontact id
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        * @throws \ImagickException
+        */
+       private static function updateFromPublicContact($condition)
+       {
+               $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', 'gender',
+                       'bd', 'contact-type', 'network', 'addr', 'notify', 'alias', 'archive', 'term-date',
+                       'created', 'updated', 'avatar', 'success_update', 'failure_update', 'forum', 'prv',
+                       'baseurl', 'sensitive', 'unsearchable'];
+
+               $contact = DBA::selectFirst('contact', $fields, array_merge($condition, ['uid' => 0, 'network' => Protocol::FEDERATED]));
+               if (!DBA::isResult($contact)) {
+                       return 0;
+               }
+
+               $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', 'gender', 'generation',
+                       'birthday', 'contact-type', 'network', 'addr', 'notify', 'alias', 'archived', 'archive_date',
+                       'created', 'updated', 'photo', 'last_contact', 'last_failure', 'community', 'connect',
+                       'server_url', 'nsfw', 'hide', 'id'];
+
+               $old_gcontact = DBA::selectFirst('gcontact', $fields, ['nurl' => $contact['nurl']]);
+               $do_insert = !DBA::isResult($old_gcontact);
+               if ($do_insert) {
+                       $old_gcontact = [];
+               }
+
+               $gcontact = [];
+
+               // These fields are identical in both contact and gcontact
+               $fields = ['name', 'nick', 'url', 'nurl', 'location', 'about', 'keywords', 'gender',
+                       'contact-type', 'network', 'addr', 'notify', 'alias', 'created', 'updated'];
+
+               foreach ($fields as $field) {
+                       $gcontact[$field] = $contact[$field];
+               }
+
+               // These fields are having different names but the same content
+               $gcontact['server_url'] = $contact['baseurl'];
+               $gcontact['nsfw'] = $contact['sensitive'];
+               $gcontact['hide'] = $contact['unsearchable'];
+               $gcontact['archived'] = $contact['archive'];
+               $gcontact['archive_date'] = $contact['term-date'];
+               $gcontact['birthday'] = $contact['bd'];
+               $gcontact['photo'] = $contact['avatar'];
+               $gcontact['last_contact'] = $contact['success_update'];
+               $gcontact['last_failure'] = $contact['failure_update'];
+               $gcontact['community'] = ($contact['forum'] || $contact['prv']);
+
+               foreach (['last_contact', 'last_failure', 'updated'] as $field) {
+                       if (!empty($old_gcontact[$field]) && ($old_gcontact[$field] >= $gcontact[$field])) {
+                               unset($gcontact[$field]);
                        }
                }
 
-               return $gcontact_id;
+               if (!$gcontact['archived']) {
+                       $gcontact['archive_date'] = DBA::NULL_DATETIME;
+               }
+
+               if (!empty($old_gcontact['created']) && ($old_gcontact['created'] > DBA::NULL_DATETIME)
+                       && ($old_gcontact['created'] <= $gcontact['created'])) {
+                       unset($gcontact['created']);
+               }
+
+               if (empty($gcontact['birthday']) && ($gcontact['birthday'] <= DBA::NULL_DATETIME)) {
+                       unset($gcontact['birthday']);
+               }
+
+               if (empty($old_gcontact['generation']) || ($old_gcontact['generation'] > 2)) {
+                       $gcontact['generation'] = 2; // We fetched the data directly from the other server
+               }
+
+               if (!$do_insert) {
+                       DBA::update('gcontact', $gcontact, ['nurl' => $contact['nurl']], $old_gcontact);
+                       return $old_gcontact['id'];
+               } elseif (!$gcontact['archived']) {
+                       DBA::insert('gcontact', $gcontact);
+                       return DBA::lastInsertId();
+               }
        }
 
        /**
         * @brief Updates the gcontact entry from probe
         *
-        * @param string $url profile link
+        * @param string  $url   profile link
+        * @param boolean $force Optional forcing of network probing (otherwise we use the cached data)
         * @return void
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         * @throws \ImagickException
         */
-       public static function updateFromProbe($url)
+       public static function updateFromProbe($url, $force = false)
        {
-               $data = Probe::uri($url);
+               $data = Probe::uri($url, $force);
 
                if (in_array($data["network"], [Protocol::PHANTOM])) {
                        Logger::log("Invalid network for contact url ".$data["url"]." - Called by: ".System::callstack(), Logger::DEBUG);
index a3fe3ca..fae6f8c 100644 (file)
@@ -17,7 +17,6 @@ use Friendica\Core\Logger;
 use Friendica\Core\Protocol;
 use Friendica\Core\System;
 use Friendica\Database\DBA;
-use Friendica\Model\Contact;
 use Friendica\Model\Profile;
 use Friendica\Protocol\ActivityPub;
 use Friendica\Protocol\Email;
@@ -46,8 +45,8 @@ class Probe
         */
        private static function rearrangeData($data)
        {
-               $fields = ["name", "nick", "guid", "url", "addr", "alias",
-                               "photo", "community", "keywords", "location", "about",
+               $fields = ["name", "nick", "guid", "url", "addr", "alias", "photo",
+                               "account-type", "community", "keywords", "location", "about",
                                "batch", "notify", "poll", "request", "confirm", "poco",
                                "priority", "network", "pubkey", "baseurl"];
 
@@ -350,6 +349,8 @@ class Probe
 
                        if (!empty($ap_profile) && empty($network) && (defaults($data, 'network', '') != Protocol::DFRN)) {
                                $data = $ap_profile;
+                       } elseif (!empty($ap_profile)) {
+                               $data = array_merge($ap_profile, $data);
                        }
                } else {
                        Logger::notice('Time out detected. AP will not be probed.', ['uri' => $uri]);
@@ -396,114 +397,6 @@ class Probe
                // Only store into the cache if the value seems to be valid
                if (!in_array($data['network'], [Protocol::PHANTOM, Protocol::MAIL])) {
                        Cache::set('Probe::uri:' . $network . ':' . $uri, $data, Cache::DAY);
-
-                       /// @todo temporary fix - we need a real contact update function that updates only changing fields
-                       /// The biggest problem is the avatar picture that could have a reduced image size.
-                       /// It should only be updated if the existing picture isn't existing anymore.
-                       /// We only update the contact when it is no probing for a specific network.
-                       if (($data['network'] != Protocol::FEED)
-                               && ($network == '')
-                               && $data['name']
-                               && $data['nick']
-                               && $data['url']
-                               && $data['addr']
-                               && $data['poll']
-                       ) {
-                               $fields = [
-                                       'name' => $data['name'],
-                                       'nick' => $data['nick'],
-                                       'url' => $data['url'],
-                                       'addr' => $data['addr'],
-                                       'photo' => $data['photo'],
-                                       'keywords' => $data['keywords'],
-                                       'location' => $data['location'],
-                                       'about' => $data['about'],
-                                       'notify' => $data['notify'],
-                                       'network' => $data['network'],
-                                       'server_url' => $data['baseurl']
-                               ];
-
-                               // This doesn't cover the case when a community isn't a community anymore
-                               if (!empty($data['community']) && $data['community']) {
-                                       $fields['community'] = $data['community'];
-                                       $fields['contact-type'] = Contact::TYPE_COMMUNITY;
-                               }
-
-                               $fieldnames = [];
-
-                               foreach ($fields as $key => $val) {
-                                       if (empty($val)) {
-                                               unset($fields[$key]);
-                                       } else {
-                                               $fieldnames[] = $key;
-                                       }
-                               }
-
-                               $fields['updated'] = DateTimeFormat::utcNow();
-
-                               $condition = ['nurl' => Strings::normaliseLink($data['url'])];
-
-                               $old_fields = DBA::selectFirst('gcontact', $fieldnames, $condition);
-
-                               // When the gcontact doesn't exist, the value "true" will trigger an insert.
-                               // In difference to the public contacts we want to have every contact
-                               // in the world in our global contacts.
-                               if (!$old_fields) {
-                                       $old_fields = true;
-
-                                       // These values have to be set only on insert
-                                       $fields['photo'] = $data['photo'];
-                                       $fields['created'] = DateTimeFormat::utcNow();
-                               }
-
-                               DBA::update('gcontact', $fields, $condition, $old_fields);
-
-                               $fields = [
-                                       'name' => $data['name'],
-                                       'nick' => $data['nick'],
-                                       'url' => $data['url'],
-                                       'addr' => $data['addr'],
-                                       'alias' => $data['alias'],
-                                       'keywords' => $data['keywords'],
-                                       'location' => $data['location'],
-                                       'about' => $data['about'],
-                                       'batch' => $data['batch'],
-                                       'notify' => $data['notify'],
-                                       'poll' => $data['poll'],
-                                       'request' => $data['request'],
-                                       'confirm' => $data['confirm'],
-                                       'poco' => $data['poco'],
-                                       'network' => $data['network'],
-                                       'pubkey' => $data['pubkey'],
-                                       'priority' => $data['priority'],
-                                       'writable' => true,
-                                       'rel' => Contact::SHARING
-                               ];
-
-                               $fieldnames = [];
-
-                               foreach ($fields as $key => $val) {
-                                       if (empty($val)) {
-                                               unset($fields[$key]);
-                                       } else {
-                                               $fieldnames[] = $key;
-                                       }
-                               }
-
-                               $condition = ['nurl' => Strings::normaliseLink($data['url']), 'self' => false, 'uid' => 0];
-
-                               // "$old_fields" will return a "false" when the contact doesn't exist.
-                               // This won't trigger an insert. This is intended, since we only need
-                               // public contacts for everyone we store items from.
-                               // We don't need to store every contact on the planet.
-                               $old_fields = DBA::selectFirst('contact', $fieldnames, $condition);
-
-                               $fields['name-date'] = DateTimeFormat::utcNow();
-                               $fields['uri-date'] = DateTimeFormat::utcNow();
-                               $fields['success_update'] = DateTimeFormat::utcNow();
-
-                               DBA::update('contact', $fields, $condition, $old_fields);
-                       }
                }
 
                return $data;
index 8caf7ac..42b5de9 100644 (file)
@@ -8,6 +8,7 @@ use Friendica\Util\JsonLD;
 use Friendica\Util\Network;
 use Friendica\Core\Protocol;
 use Friendica\Model\APContact;
+use Friendica\Model\User;
 use Friendica\Util\HTTPSignature;
 
 /**
@@ -88,6 +89,31 @@ class ActivityPub
                return $content;
        }
 
+       private static function getAccountType($apcontact)
+       {
+               $accounttype = -1;
+
+               switch($apcontact['type']) {
+                       case 'Person':
+                               $accounttype = User::ACCOUNT_TYPE_PERSON;
+                               break;
+                       case 'Organization':
+                               $accounttype = User::ACCOUNT_TYPE_ORGANISATION;
+                               break;
+                       case 'Service':
+                               $accounttype = User::ACCOUNT_TYPE_NEWS;
+                               break;
+                       case 'Group':
+                               $accounttype = User::ACCOUNT_TYPE_COMMUNITY;
+                               break;
+                       case 'Application':
+                               $accounttype = User::ACCOUNT_TYPE_RELAY;
+                               break;
+               }
+
+               return $accounttype;
+       }
+
        /**
         * Fetches a profile from the given url into an array that is compatible to Probe::uri
         *
@@ -112,7 +138,8 @@ class ActivityPub
                $profile['addr'] = $apcontact['addr'];
                $profile['alias'] = $apcontact['alias'];
                $profile['photo'] = $apcontact['photo'];
-               // $profile['community']
+               $profile['account-type'] = self::getAccountType($apcontact);
+               $profile['community'] = ($profile['account-type'] == User::ACCOUNT_TYPE_COMMUNITY);
                // $profile['keywords']
                // $profile['location']
                $profile['about'] = $apcontact['about'];
index 41aed2f..6ba0c8f 100644 (file)
@@ -611,7 +611,7 @@ class Processor
                }
 
                Logger::log('Updating profile for ' . $activity['object_id'], Logger::DEBUG);
-               APContact::getByURL($activity['object_id'], true);
+               Contact::updateFromProbeByURL($activity['object_id'], true);
        }
 
        /**
index a5deb49..31d363a 100644 (file)
@@ -1684,63 +1684,26 @@ class DFRN
                                Event::createBirthday($contact, $birthday);
                        }
 
-                       // Get all field names
-                       $fields = [];
-                       foreach ($contact_old as $field => $data) {
-                               $fields[$field] = $data;
-                       }
-
-                       unset($fields["id"]);
-                       unset($fields["uid"]);
-                       unset($fields["url"]);
-                       unset($fields["avatar-date"]);
-                       unset($fields["avatar"]);
-                       unset($fields["name-date"]);
-                       unset($fields["uri-date"]);
-
-                       $update = false;
-                       // Update check for this field has to be done differently
-                       $datefields = ["name-date", "uri-date"];
-                       foreach ($datefields as $field) {
-                               // The date fields arrives as '2018-07-17T10:44:45Z' - the database return '2018-07-17 10:44:45'
-                               // The fields have to be in the same format to be comparable, since strtotime does add timezones.
-                               $contact[$field] = DateTimeFormat::utc($contact[$field]);
-
-                               if (strtotime($contact[$field]) > strtotime($contact_old[$field])) {
-                                       Logger::log("Difference for contact " . $contact["id"] . " in field '" . $field . "'. New value: '" . $contact[$field] . "', old value '" . $contact_old[$field] . "'", Logger::DEBUG);
-                                       $update = true;
-                               }
-                       }
+                       $fields = ['name' => $contact['name'], 'nick' => $contact['nick'], 'about' => $contact['about'],
+                               'location' => $contact['location'], 'addr' => $contact['addr'], 'keywords' => $contact['keywords'],
+                               'bdyear' => $contact['bdyear'], 'bd' => $contact['bd'], 'hidden' => $contact['hidden'],
+                               'xmpp' => $contact['xmpp'], 'name-date' => DateTimeFormat::utc($contact['name-date']),
+                               'uri-date' => DateTimeFormat::utc($contact['uri-date'])];
 
-                       foreach ($fields as $field => $data) {
-                               if ($contact[$field] != $contact_old[$field]) {
-                                       Logger::log("Difference for contact " . $contact["id"] . " in field '" . $field . "'. New value: '" . $contact[$field] . "', old value '" . $contact_old[$field] . "'", Logger::DEBUG);
-                                       $update = true;
-                               }
-                       }
+                       DBA::update('contact', $fields, ['id' => $contact['id'], 'network' => $contact['network']], $contact_old);
 
-                       if ($update) {
-                               Logger::log("Update contact data for contact " . $contact["id"] . " (" . $contact["nick"] . ")", Logger::DEBUG);
+                       // Update the public contact. Don't set the "hidden" value, this is used differently for public contacts
+                       unset($fields['hidden']);
+                       $fields['unsearchable'] = $hide;
+                       $condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($contact_old['url'])];
+                       DBA::update('contact', $fields, $condition, true);
 
-                               q(
-                                       "UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s',
-                                       `addr` = '%s', `keywords` = '%s', `bdyear` = '%s', `bd` = '%s', `hidden` = %d,
-                                       `xmpp` = '%s', `name-date`  = '%s', `uri-date` = '%s'
-                                       WHERE `id` = %d AND `network` = '%s'",
-                                       DBA::escape($contact["name"]), DBA::escape($contact["nick"]), DBA::escape($contact["about"]),   DBA::escape($contact["location"]),
-                                       DBA::escape($contact["addr"]), DBA::escape($contact["keywords"]), DBA::escape($contact["bdyear"]),
-                                       DBA::escape($contact["bd"]), intval($contact["hidden"]), DBA::escape($contact["xmpp"]),
-                                       DBA::escape(DateTimeFormat::utc($contact["name-date"])), DBA::escape(DateTimeFormat::utc($contact["uri-date"])),
-                                       intval($contact["id"]), DBA::escape($contact["network"])
-                               );
-                       }
+                       Contact::updateAvatar($author['avatar'], $importer['importer_uid'], $contact['id']);
 
-                       Contact::updateAvatar(
-                               $author['avatar'],
-                               $importer['importer_uid'],
-                               $contact['id'],
-                               (strtotime($contact['avatar-date']) > strtotime($contact_old['avatar-date']) || ($author['avatar'] != $contact_old['avatar']))
-                       );
+                       $pcid = Contact::getIdForURL($contact_old['url']);
+                       if (!empty($pcid)) {
+                               Contact::updateAvatar($author['avatar'], 0, $pcid);
+                       }
 
                        /*
                         * The generation is a sign for the reliability of the provided data.
index f7d9425..31d9efa 100644 (file)
@@ -2258,8 +2258,8 @@ class Diaspora
                $fields = ['name' => $name, 'location' => $location,
                        'name-date' => DateTimeFormat::utcNow(),
                        'about' => $about, 'gender' => $gender,
-                       'addr' => $author, 'nick' => $nick,
-                       'keywords' => $keywords];
+                       'addr' => $author, 'nick' => $nick, 'keywords' => $keywords,
+                       'unsearchable' => !$searchable, 'sensitive' => $nsfw];
 
                if (!empty($birthday)) {
                        $fields['bd'] = $birthday;
index 33a9a51..9af4be9 100644 (file)
@@ -191,11 +191,9 @@ class OnePoll
                        }
 
                        self::updateContact($contact, ['last-update' => $updated, 'success_update' => $updated]);
-                       DBA::update('gcontact', ['last_contact' => $updated], ['nurl' => $contact['nurl']]);
                        Contact::unmarkForArchival($contact);
                } elseif (in_array($contact["network"], [Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, Protocol::FEED])) {
                        self::updateContact($contact, ['last-update' => $updated, 'failure_update' => $updated]);
-                       DBA::update('gcontact', ['last_failure' => $updated], ['nurl' => $contact['nurl']]);
                        Contact::markForArchival($contact);
                } else {
                        self::updateContact($contact, ['last-update' => $updated]);
index dd344ba..aacebcb 100644 (file)
@@ -7,85 +7,17 @@
 namespace Friendica\Worker;
 
 use Friendica\Core\Logger;
-use Friendica\Core\Protocol;
+use Friendica\Model\GContact;
 use Friendica\Database\DBA;
-use Friendica\Network\Probe;
-use Friendica\Protocol\PortableContact;
-use Friendica\Util\DateTimeFormat;
-use Friendica\Util\Strings;
 
 class UpdateGContact
 {
-       public static function execute($contact_id)
+       public static function execute($url, $command = '')
        {
-               Logger::log('update_gcontact: start');
+               $force = ($command == "force");
 
-               if (empty($contact_id)) {
-                       Logger::log('update_gcontact: no contact');
-                       return;
-               }
+               $success = GContact::updateFromProbe($url, $force);
 
-               $r = q("SELECT * FROM `gcontact` WHERE `id` = %d", intval($contact_id));
-
-               if (!DBA::isResult($r)) {
-                       return;
-               }
-
-               if (!in_array($r[0]["network"], Protocol::FEDERATED)) {
-                       return;
-               }
-
-               $data = Probe::uri($r[0]["url"]);
-
-               if (!in_array($data["network"], Protocol::FEDERATED)) {
-                       if ($r[0]["server_url"] != "") {
-                               PortableContact::checkServer($r[0]["server_url"], $r[0]["network"]);
-                       }
-
-                       q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `id` = %d",
-                               DBA::escape(DateTimeFormat::utcNow()), intval($contact_id));
-                       return;
-               }
-
-               if (($data["name"] == "") && ($r[0]['name'] != "")) {
-                       $data["name"] = $r[0]['name'];
-               }
-
-               if (($data["nick"] == "") && ($r[0]['nick'] != "")) {
-                       $data["nick"] = $r[0]['nick'];
-               }
-
-               if (($data["addr"] == "") && ($r[0]['addr'] != "")) {
-                       $data["addr"] = $r[0]['addr'];
-               }
-
-               if (($data["photo"] == "") && ($r[0]['photo'] != "")) {
-                       $data["photo"] = $r[0]['photo'];
-               }
-
-
-               q("UPDATE `gcontact` SET `name` = '%s', `nick` = '%s', `addr` = '%s', `photo` = '%s'
-                                       WHERE `id` = %d",
-                                       DBA::escape($data["name"]),
-                                       DBA::escape($data["nick"]),
-                                       DBA::escape($data["addr"]),
-                                       DBA::escape($data["photo"]),
-                       intval($contact_id)
-               );
-
-               q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `addr` = '%s', `photo` = '%s'
-                                       WHERE `uid` = 0 AND `addr` = '' AND `nurl` = '%s'",
-                                       DBA::escape($data["name"]),
-                                       DBA::escape($data["nick"]),
-                                       DBA::escape($data["addr"]),
-                                       DBA::escape($data["photo"]),
-                                       DBA::escape(Strings::normaliseLink($data["url"]))
-               );
-
-               q("UPDATE `contact` SET `addr` = '%s'
-                                       WHERE `uid` != 0 AND `addr` = '' AND `nurl` = '%s'",
-                                       DBA::escape($data["addr"]),
-                                       DBA::escape(Strings::normaliseLink($data["url"]))
-               );
+               Logger::info('Updated from probe', ['url' => $url, 'force' => $force, 'success' => $success]);
        }
 }