diff --git a/mod/poco.php b/mod/poco.php index abe10ee39c..41a1627271 100644 --- a/mod/poco.php +++ b/mod/poco.php @@ -27,7 +27,7 @@ use Friendica\Core\Protocol; use Friendica\Core\Renderer; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Protocol\PortableContact; +use Friendica\Model\GServer; use Friendica\Util\DateTimeFormat; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -56,16 +56,15 @@ function poco_init(App $a) { if ($a->argc > 1 && $a->argv[1] === '@server') { // List of all servers that this server knows - $ret = PortableContact::serverlist(); + $ret = GServer::getActive(); header('Content-type: application/json'); echo json_encode($ret); exit(); } if ($a->argc > 1 && $a->argv[1] === '@global') { - // List of all profiles that this server recently had data from - $global = true; - $update_limit = date(DateTimeFormat::MYSQL, time() - 30 * 86400); + // Global is not supported anymore + throw new \Friendica\Network\HTTPException\NotFoundException(); } if ($a->argc > 2 && $a->argv[2] === '@me') { $justme = true; @@ -99,17 +98,10 @@ function poco_init(App $a) { if (!empty($_GET['updatedSince'])) { $update_limit = date(DateTimeFormat::MYSQL, strtotime($_GET['updatedSince'])); } - if ($global) { - $contacts = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND `updated` >= `last_failure` AND NOT `hide` AND `network` IN ('%s', '%s', '%s')", - DBA::escape($update_limit), - DBA::escape(Protocol::DFRN), - DBA::escape(Protocol::DIASPORA), - DBA::escape(Protocol::OSTATUS) - ); - } elseif ($system_mode) { + if ($system_mode) { $totalResults = DBA::count('profile', ['net-publish' => true]); } else { - $contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 + $contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND NOT `blocked` AND NOT `pending` AND NOT `unsearchable` AND NOT `archive` AND NOT `failed` AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra", intval($user['uid']), @@ -131,24 +123,13 @@ function poco_init(App $a) { } $itemsPerPage = ((!empty($_GET['count'])) ? intval($_GET['count']) : $totalResults); - if ($global) { - Logger::log("Start global query", Logger::DEBUG); - $contacts = q("SELECT * FROM `gcontact` WHERE `updated` > '%s' AND NOT `hide` AND `network` IN ('%s', '%s', '%s') AND `updated` > `last_failure` - ORDER BY `updated` DESC LIMIT %d, %d", - DBA::escape($update_limit), - DBA::escape(Protocol::DFRN), - DBA::escape(Protocol::DIASPORA), - DBA::escape(Protocol::OSTATUS), - intval($startIndex), - intval($itemsPerPage) - ); - } elseif ($system_mode) { + if ($system_mode) { Logger::log("Start system mode query", Logger::DEBUG); $contacts = DBA::selectToArray('owner-view', [], ['net-publish' => true], ['limit' => [$startIndex, $itemsPerPage]]); } else { Logger::log("Start query for user " . $user['nickname'], Logger::DEBUG); - $contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0 - AND NOT `failed` + $contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND NOT `blocked` AND NOT `pending` AND NOT `hiddden` AND NOT `archive` + AND NOT `failed` AND NOT `unsearchable` AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra LIMIT %d, %d", intval($user['uid']), DBA::escape(Protocol::DFRN), diff --git a/mod/settings.php b/mod/settings.php index d02375ed7c..e147144e21 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -31,7 +31,6 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Model\Group; use Friendica\Model\Notify\Type; use Friendica\Model\User; @@ -471,9 +470,6 @@ function settings_post(App $a) Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); - // Update the global contact for the user - GContact::updateForUser(local_user()); - DI::baseUrl()->redirect('settings'); return; // NOTREACHED } diff --git a/src/Content/ContactSelector.php b/src/Content/ContactSelector.php index c834f8c514..ec7bcab95c 100644 --- a/src/Content/ContactSelector.php +++ b/src/Content/ContactSelector.php @@ -76,14 +76,6 @@ class ContactSelector $server_url = Strings::normaliseLink($contact['baseurl']); } - if (empty($server_url)) { - // Fetch the server url from the gcontact table - $gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($profile)]); - if (!empty($gcontact) && !empty($gcontact['server_url'])) { - $server_url = Strings::normaliseLink($gcontact['server_url']); - } - } - if (empty($server_url)) { // Create the server url out of the profile url $parts = parse_url($profile); diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 6d8b197db1..6a8d663c5a 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -93,9 +93,6 @@ class PostUpdate if (!self::update1349()) { return false; } - if (!self::update1350()) { - return false; - } return true; } @@ -997,57 +994,4 @@ class PostUpdate return false; } - - /** - * update the "gsid" (global server id) field in the gcontact table - * - * @return bool "true" when the job is done - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - private static function update1350() - { - // Was the script completed? - if (DI::config()->get("system", "post_update_version") >= 1350) { - return true; - } - - $id = DI::config()->get("system", "post_update_version_1350_id", 0); - - Logger::info('Start', ['gcontact' => $id]); - - $start_id = $id; - $rows = 0; - $condition = ["`id` > ? AND `gsid` IS NULL AND `server_url` != '' AND NOT `server_url` IS NULL", $id]; - $params = ['order' => ['id'], 'limit' => 10000]; - $gcontacts = DBA::select('gcontact', ['id', 'server_url'], $condition, $params); - - if (DBA::errorNo() != 0) { - Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); - return false; - } - - while ($gcontact = DBA::fetch($gcontacts)) { - $id = $gcontact['id']; - - DBA::update('gcontact', - ['gsid' => GServer::getID($gcontact['server_url'], true), 'server_url' => GServer::cleanURL($gcontact['server_url'])], - ['id' => $gcontact['id']]); - - ++$rows; - } - DBA::close($gcontacts); - - DI::config()->set("system", "post_update_version_1350_id", $id); - - Logger::info('Processed', ['rows' => $rows, 'last' => $id]); - - if ($start_id == $id) { - DI::config()->set("system", "post_update_version", 1350); - Logger::info('Done'); - return true; - } - - return false; - } } diff --git a/src/Factory/Notification/Introduction.php b/src/Factory/Notification/Introduction.php index 21aef9297a..a16926f967 100644 --- a/src/Factory/Notification/Introduction.php +++ b/src/Factory/Notification/Introduction.php @@ -99,17 +99,13 @@ class Introduction extends BaseFactory $formattedNotifications = []; try { - /// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact, fcontact and gcontact + /// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact and fcontact $stmtNotifications = $this->dba->p( "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`, `fcontact`.`addr` AS `faddr`, - `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest`, - `gcontact`.`location` AS `glocation`, `gcontact`.`about` AS `gabout`, - `gcontact`.`keywords` AS `gkeywords`, - `gcontact`.`network` AS `gnetwork`, `gcontact`.`addr` AS `gaddr` + `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest` FROM `intro` LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` - LEFT JOIN `gcontact` ON `gcontact`.`nurl` = `contact`.`nurl` LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id` WHERE `intro`.`uid` = ? $sql_extra LIMIT ?, ?", @@ -147,16 +143,14 @@ class Introduction extends BaseFactory // Normal connection requests } else { - $notification = $this->getMissingData($notification); - if (empty($notification['url'])) { continue; } // Don't show these data until you are connected. Diaspora is doing the same. - if ($notification['gnetwork'] === Protocol::DIASPORA) { - $notification['glocation'] = ""; - $notification['gabout'] = ""; + if ($notification['network'] === Protocol::DIASPORA) { + $notification['location'] = ""; + $notification['about'] = ""; } $formattedNotifications[] = new Notification\Introduction([ @@ -166,17 +160,17 @@ class Introduction extends BaseFactory 'uid' => $this->session->get('uid'), 'intro_id' => $notification['intro_id'], 'contact_id' => $notification['contact-id'], - 'photo' => (!empty($notification['photo']) ? Proxy::proxifyUrl($notification['photo'], false, Proxy::SIZE_SMALL) : "images/person-300.jpg"), + 'photo' => Contact::getPhoto($notification), 'name' => $notification['name'], - 'location' => BBCode::convert($notification['glocation'], false), - 'about' => BBCode::convert($notification['gabout'], false), - 'keywords' => $notification['gkeywords'], + 'location' => BBCode::convert($notification['location'], false), + 'about' => BBCode::convert($notification['about'], false), + 'keywords' => $notification['keywords'], 'hidden' => $notification['hidden'] == 1, 'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), 'url' => $notification['url'], 'zrl' => Contact::magicLink($notification['url']), - 'addr' => $notification['gaddr'], - 'network' => $notification['gnetwork'], + 'addr' => $notification['addr'], + 'network' => $notification['network'], 'knowyou' => $notification['knowyou'], 'note' => $notification['note'], ]); @@ -188,41 +182,4 @@ class Introduction extends BaseFactory return $formattedNotifications; } - - /** - * Check for missing contact data and try to fetch the data from - * from other sources - * - * @param array $intro The input array with the intro data - * - * @return array The array with the intro data - * - * @throws InternalServerErrorException - */ - private function getMissingData(array $intro) - { - // If the network and the addr isn't available from the gcontact - // table entry, take the one of the contact table entry - if (empty($intro['gnetwork']) && !empty($intro['network'])) { - $intro['gnetwork'] = $intro['network']; - } - if (empty($intro['gaddr']) && !empty($intro['addr'])) { - $intro['gaddr'] = $intro['addr']; - } - - // If the network and addr is still not available - // get the missing data data from other sources - if (empty($intro['gnetwork']) || empty($intro['gaddr'])) { - $ret = Contact::getByURL($intro['url'], false, ['network', 'addr']); - - if (empty($intro['gnetwork']) && !empty($ret['network'])) { - $intro['gnetwork'] = $ret['network']; - } - if (empty($intro['gaddr']) && !empty($ret['addr'])) { - $intro['gaddr'] = $ret['addr']; - } - } - - return $intro; - } } diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 61b4c9d05a..18c378179e 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -21,6 +21,8 @@ namespace Friendica\Model; +use DOMDocument; +use DOMXPath; use Friendica\App\BaseURL; use Friendica\Content\Pager; use Friendica\Core\Hook; @@ -88,7 +90,7 @@ class Contact /** * Account types * - * TYPE_UNKNOWN - the account has been imported from gcontact where this is the default type value + * TYPE_UNKNOWN - unknown type * * TYPE_PERSON - the account belongs to a person * Associated page types: PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE @@ -1021,7 +1023,6 @@ class Contact */ DBA::update('contact', ['archive' => true], ['id' => $contact['id']]); DBA::update('contact', ['archive' => true], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]); - GContact::updateFromPublicContactURL($contact['url']); } } } @@ -1066,7 +1067,6 @@ class Contact $fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false]; DBA::update('contact', $fields, ['id' => $contact['id']]); DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]); - GContact::updateFromPublicContactURL($contact['url']); } /** @@ -1269,12 +1269,8 @@ class Contact $fields = ['url', 'addr', 'alias', 'notify', 'name', 'nick', 'photo', 'keywords', 'location', 'about', 'network']; - $data = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($url)]); - - if (!DBA::isResult($data)) { - $condition = ['alias' => [$url, Strings::normaliseLink($url), $ssl_url]]; - $data = DBA::selectFirst('contact', $fields, $condition); - } + $condition = ['alias' => [$url, Strings::normaliseLink($url), $ssl_url]]; + $data = DBA::selectFirst('contact', $fields, $condition); if (DBA::isResult($data)) { $data["pubkey"] = ''; @@ -1706,7 +1702,6 @@ class Contact // There are several fields that indicate that the contact or user is a forum // "page-flags" is a field in the user table, // "forum" and "prv" are used in the contact table. They stand for User::PAGE_FLAGS_COMMUNITY and User::PAGE_FLAGS_PRVGROUP. - // "community" is used in the gcontact table and is true if the contact is User::PAGE_FLAGS_COMMUNITY or User::PAGE_FLAGS_PRVGROUP. if ((isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_COMMUNITY)) || (isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_PRVGROUP)) || (isset($contact['forum']) && intval($contact['forum'])) @@ -1994,9 +1989,6 @@ class Contact 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]); @@ -2155,11 +2147,6 @@ class Contact $new_pubkey = $ret['pubkey']; - // Update the gcontact entry - if ($uid == 0) { - GContact::updateFromPublicContactID($id); - } - $update = false; // make sure to not overwrite existing values with blank entries except some technical fields @@ -3068,4 +3055,208 @@ class Contact return array_slice($contacts, $start, $limit); } + + /** + * Add public contacts from an array + * + * @param array $urls + * @return array result "count", "added" and "updated" + */ + public static function addContactsByArray(array $urls) + { + $added = 0; + $updated = 0; + $count = 0; + + foreach ($urls as $url) { + $contact = Contact::getByURL($url, false, ['id']); + if (empty($contact['id'])) { + Worker::add(PRIORITY_LOW, 'AddContact', 0, $url); + ++$added; + } else { + Worker::add(PRIORITY_LOW, 'UpdateContact', $contact['id']); + ++$updated; + } + ++$count; + } + + return ['count' => $count, 'added' => $added, 'updated' => $updated]; + } + + /** + * Set the last date that the contact had posted something + * + * This functionality is currently unused + * + * @param string $data probing result + * @param bool $force force updating + */ + private static function setLastUpdate(array $data, bool $force = false) + { + $contact = self::getByURL($data['url'], false, []); + if (empty($contact)) { + return; + } + if (!$force && !GServer::updateNeeded($contact['created'], $contact['updated'], $contact['last_failure'], $contact['last_contact'])) { + Logger::info("Don't update profile", ['url' => $data['url'], 'updated' => $contact['updated']]); + return; + } + + if (self::updateFromNoScrape($data)) { + return; + } + + if (!empty($data['outbox'])) { + self::updateFromOutbox($data['outbox'], $data); + } elseif (!empty($data['poll']) && ($data['network'] == Protocol::ACTIVITYPUB)) { + self::updateFromOutbox($data['poll'], $data); + } elseif (!empty($data['poll'])) { + self::updateFromFeed($data); + } + } + + /** + * Update a global contact via the "noscrape" endpoint + * + * @param string $data Probing result + * + * @return bool 'true' if update was successful or the server was unreachable + */ + private static function updateFromNoScrape(array $data) + { + // Check the 'noscrape' endpoint when it is a Friendica server + $gserver = DBA::selectFirst('gserver', ['noscrape'], ["`nurl` = ? AND `noscrape` != ''", + Strings::normaliseLink($data['baseurl'])]); + if (!DBA::isResult($gserver)) { + return false; + } + + $curlResult = DI::httpRequest()->get($gserver['noscrape'] . '/' . $data['nick']); + + if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { + $noscrape = json_decode($curlResult->getBody(), true); + if (!empty($noscrape) && !empty($noscrape['updated'])) { + $noscrape['updated'] = DateTimeFormat::utc($noscrape['updated'], DateTimeFormat::MYSQL); + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $noscrape['updated']]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + return true; + } + } elseif ($curlResult->isTimeout()) { + // On a timeout return the existing value, but mark the contact as failure + $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + return true; + } + return false; + } + + /** + * Update a global contact via an ActivityPub Outbox + * + * @param string $feed + * @param array $data Probing result + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function updateFromOutbox(string $feed, array $data) + { + $outbox = ActivityPub::fetchContent($feed); + if (empty($outbox)) { + return; + } + + if (!empty($outbox['orderedItems'])) { + $items = $outbox['orderedItems']; + } elseif (!empty($outbox['first']['orderedItems'])) { + $items = $outbox['first']['orderedItems']; + } elseif (!empty($outbox['first']['href']) && ($outbox['first']['href'] != $feed)) { + self::updateFromOutbox($outbox['first']['href'], $data); + return; + } elseif (!empty($outbox['first'])) { + if (is_string($outbox['first']) && ($outbox['first'] != $feed)) { + self::updateFromOutbox($outbox['first'], $data); + } else { + Logger::warning('Unexpected data', ['outbox' => $outbox]); + } + return; + } else { + $items = []; + } + + $last_updated = ''; + foreach ($items as $activity) { + if (!empty($activity['published'])) { + $published = DateTimeFormat::utc($activity['published']); + } elseif (!empty($activity['object']['published'])) { + $published = DateTimeFormat::utc($activity['object']['published']); + } else { + continue; + } + + if ($last_updated < $published) { + $last_updated = $published; + } + } + + if (empty($last_updated)) { + return; + } + + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + } + + /** + * Update a global contact via an XML feed + * + * @param string $data Probing result + */ + private static function updateFromFeed(array $data) + { + // Search for the newest entry in the feed + $curlResult = DI::httpRequest()->get($data['poll']); + if (!$curlResult->isSuccess()) { + $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + + Logger::info("Profile wasn't reachable (no feed)", ['url' => $data['url']]); + return; + } + + $doc = new DOMDocument(); + @$doc->loadXML($curlResult->getBody()); + + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); + + $entries = $xpath->query('/atom:feed/atom:entry'); + + $last_updated = ''; + + foreach ($entries as $entry) { + $published_item = $xpath->query('atom:published/text()', $entry)->item(0); + $updated_item = $xpath->query('atom:updated/text()' , $entry)->item(0); + $published = !empty($published_item->nodeValue) ? DateTimeFormat::utc($published_item->nodeValue) : null; + $updated = !empty($updated_item->nodeValue) ? DateTimeFormat::utc($updated_item->nodeValue) : null; + + if (empty($published) || empty($updated)) { + Logger::notice('Invalid entry for XPath.', ['entry' => $entry, 'url' => $data['url']]); + continue; + } + + if ($last_updated < $published) { + $last_updated = $published; + } + + if ($last_updated < $updated) { + $last_updated = $updated; + } + } + + if (empty($last_updated)) { + return; + } + + $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; + DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); + } } diff --git a/src/Model/GContact.php b/src/Model/GContact.php index fdf2d14077..6549e9efe2 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -21,171 +21,16 @@ namespace Friendica\Model; -use DOMDocument; -use DOMXPath; use Exception; -use Friendica\Core\Logger; use Friendica\Core\Protocol; -use Friendica\Core\Search; -use Friendica\Core\System; use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Network\Probe; -use Friendica\Protocol\ActivityPub; -use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; /** * This class handles GlobalContact related functions */ class GContact { - /** - * Link the gcontact entry with user, contact and global contact - * - * @param integer $gcid Global contact ID - * @param integer $uid User ID - * @param integer $cid Contact ID - * @param integer $zcid Global Contact ID - * @return void - * @throws Exception - */ - public static function link($gcid, $uid = 0, $cid = 0, $zcid = 0) - { - if ($gcid <= 0) { - return; - } - - $condition = ['cid' => $cid, 'uid' => $uid, 'gcid' => $gcid, 'zcid' => $zcid]; - DBA::update('glink', ['updated' => DateTimeFormat::utcNow()], $condition, true); - } - - /** - * Sanitize the given gcontact data - * - * Generation: - * 0: No definition - * 1: Profiles on this server - * 2: Contacts of profiles on this server - * 3: Contacts of contacts of profiles on this server - * 4: ... - * - * @param array $gcontact array with gcontact data - * @return array $gcontact - * @throws Exception - */ - public static function sanitize($gcontact) - { - if (empty($gcontact['url'])) { - throw new Exception('URL is empty'); - } - - $gcontact['server_url'] = $gcontact['server_url'] ?? ''; - - $urlparts = parse_url($gcontact['url']); - if (empty($urlparts['scheme'])) { - throw new Exception('This (' . $gcontact['url'] . ") doesn't seem to be an url."); - } - - if (in_array($urlparts['host'], ['twitter.com', 'identi.ca'])) { - throw new Exception('Contact from a non federated network ignored. (' . $gcontact['url'] . ')'); - } - - // Don't store the statusnet connector as network - // We can't simply set this to Protocol::OSTATUS since the connector could have fetched posts from friendica as well - if ($gcontact['network'] == Protocol::STATUSNET) { - $gcontact['network'] = ''; - } - - // Assure that there are no parameter fragments in the profile url - if (empty($gcontact['*network']) || in_array($gcontact['network'], Protocol::FEDERATED)) { - $gcontact['url'] = self::cleanContactUrl($gcontact['url']); - } - - // The global contacts should contain the original picture, not the cached one - if (($gcontact['generation'] != 1) && stristr(Strings::normaliseLink($gcontact['photo']), Strings::normaliseLink(DI::baseUrl() . '/photo/'))) { - $gcontact['photo'] = ''; - } - - if (empty($gcontact['network'])) { - $gcontact['network'] = ''; - - $condition = ["`uid` = 0 AND `nurl` = ? AND `network` != '' AND `network` != ?", - Strings::normaliseLink($gcontact['url']), Protocol::STATUSNET]; - $contact = DBA::selectFirst('contact', ['network'], $condition); - if (DBA::isResult($contact)) { - $gcontact['network'] = $contact['network']; - } - - if (($gcontact['network'] == '') || ($gcontact['network'] == Protocol::OSTATUS)) { - $condition = ["`uid` = 0 AND `alias` IN (?, ?) AND `network` != '' AND `network` != ?", - $gcontact['url'], Strings::normaliseLink($gcontact['url']), Protocol::STATUSNET]; - $contact = DBA::selectFirst('contact', ['network'], $condition); - if (DBA::isResult($contact)) { - $gcontact['network'] = $contact['network']; - } - } - } - - $fields = ['network', 'updated', 'server_url', 'url', 'addr']; - $gcnt = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($gcontact['url'])]); - if (DBA::isResult($gcnt)) { - if (!isset($gcontact['network']) && ($gcnt['network'] != Protocol::STATUSNET)) { - $gcontact['network'] = $gcnt['network']; - } - if ($gcontact['updated'] <= DBA::NULL_DATETIME) { - $gcontact['updated'] = $gcnt['updated']; - } - if (!isset($gcontact['server_url']) && (Strings::normaliseLink($gcnt['server_url']) != Strings::normaliseLink($gcnt['url']))) { - $gcontact['server_url'] = $gcnt['server_url']; - } - if (!isset($gcontact['addr'])) { - $gcontact['addr'] = $gcnt['addr']; - } - } - - if ((!isset($gcontact['network']) || !isset($gcontact['name']) || !isset($gcontact['addr']) || !isset($gcontact['photo']) || !isset($gcontact['server_url'])) - && GServer::reachable($gcontact['url'], $gcontact['server_url'], $gcontact['network'], false) - ) { - $data = Probe::uri($gcontact['url']); - - if ($data['network'] == Protocol::PHANTOM) { - throw new Exception('Probing for URL ' . $gcontact['url'] . ' failed'); - } - - $gcontact['server_url'] = $data['baseurl']; - $gcontact['failed'] = false; - - $gcontact = array_merge($gcontact, $data); - } - - if (!isset($gcontact['name']) || !isset($gcontact['photo'])) { - throw new Exception('No name and photo for URL '.$gcontact['url']); - } - - if (!in_array($gcontact['network'], Protocol::FEDERATED)) { - throw new Exception('No federated network (' . $gcontact['network'] . ') detected for URL ' . $gcontact['url']); - } - - if (empty($gcontact['server_url'])) { - // We check the server url to be sure that it is a real one - $server_url = self::getBasepath($gcontact['url']); - - // We are now sure that it is a correct URL. So we use it in the future - if ($server_url != '') { - $gcontact['server_url'] = $server_url; - } - } - - // The server URL doesn't seem to be valid, so we don't store it. - if (!GServer::check($gcontact['server_url'], $gcontact['network'])) { - $gcontact['server_url'] = ''; - } - - return $gcontact; - } - /** * @param integer $uid id * @param integer $cid id @@ -361,771 +206,6 @@ class GContact return $r; } - /** - * @return void - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function updateSuggestions() - { - $done = []; - - /// @TODO Check if it is really neccessary to poll the own server - PortableContact::loadWorker(0, 0, 0, DI::baseUrl() . '/poco'); - - $done[] = DI::baseUrl() . '/poco'; - - if (strlen(DI::config()->get('system', 'directory'))) { - $x = DI::httpRequest()->fetch(Search::getGlobalDirectory() . '/pubsites'); - if (!empty($x)) { - $j = json_decode($x); - if (!empty($j->entries)) { - foreach ($j->entries as $entry) { - GServer::check($entry->url); - - $url = $entry->url . '/poco'; - if (!in_array($url, $done)) { - PortableContact::loadWorker(0, 0, 0, $url); - $done[] = $url; - } - } - } - } - } - - // Query your contacts from Friendica and Redmatrix/Hubzilla for their contacts - $contacts = DBA::p("SELECT DISTINCT(`poco`) AS `poco` FROM `contact` WHERE `network` IN (?, ?)", Protocol::DFRN, Protocol::DIASPORA); - while ($contact = DBA::fetch($contacts)) { - $base = substr($contact['poco'], 0, strrpos($contact['poco'], '/')); - if (!in_array($base, $done)) { - PortableContact::loadWorker(0, 0, 0, $base); - } - } - DBA::close($contacts); - } - - /** - * Removes unwanted parts from a contact url - * - * @param string $url Contact url - * - * @return string Contact url with the wanted parts - * @throws Exception - */ - public static function cleanContactUrl($url) - { - $parts = parse_url($url); - - if (empty($parts['scheme']) || empty($parts['host'])) { - return $url; - } - - $new_url = $parts['scheme'] . '://' . $parts['host']; - - if (!empty($parts['port'])) { - $new_url .= ':' . $parts['port']; - } - - if (!empty($parts['path'])) { - $new_url .= $parts['path']; - } - - if ($new_url != $url) { - Logger::info('Cleaned contact url', ['url' => $url, 'new_url' => $new_url, 'callstack' => System::callstack()]); - } - - return $new_url; - } - - /** - * Fetch the gcontact id, add an entry if not existed - * - * @param array $contact contact array - * - * @return bool|int Returns false if not found, integer if contact was found - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function getId($contact) - { - if (empty($contact['network'])) { - Logger::notice('Empty network', ['url' => $contact['url'], 'callstack' => System::callstack()]); - return false; - } - - if (in_array($contact['network'], [Protocol::PHANTOM])) { - Logger::notice('Invalid network', ['url' => $contact['url'], 'callstack' => System::callstack()]); - return false; - } - - if ($contact['network'] == Protocol::STATUSNET) { - $contact['network'] = Protocol::OSTATUS; - } - - // Remove unwanted parts from the contact url (e.g. '?zrl=...') - if (in_array($contact['network'], Protocol::FEDERATED)) { - $contact['url'] = self::cleanContactUrl($contact['url']); - } - - $condition = ['nurl' => Strings::normaliseLink($contact['url'])]; - $gcontact = DBA::selectFirst('gcontact', ['id'], $condition, ['order' => ['id']]); - if (DBA::isResult($gcontact)) { - return $gcontact['id']; - } - - $contact['location'] = $contact['location'] ?? ''; - $contact['about'] = $contact['about'] ?? ''; - $contact['generation'] = $contact['generation'] ?? 0; - $contact['hide'] = $contact['hide'] ?? true; - - $fields = ['name' => $contact['name'], 'nick' => $contact['nick'] ?? '', 'addr' => $contact['addr'] ?? '', 'network' => $contact['network'], - 'url' => $contact['url'], 'nurl' => Strings::normaliseLink($contact['url']), 'photo' => $contact['photo'], - 'created' => DateTimeFormat::utcNow(), 'updated' => DateTimeFormat::utcNow(), 'location' => $contact['location'], - 'about' => $contact['about'], 'hide' => $contact['hide'], 'generation' => $contact['generation'], 'failed' => false]; - - DBA::insert('gcontact', $fields); - - // We intentionally aren't using lastInsertId here. There is a chance for duplicates. - $gcontact = DBA::selectFirst('gcontact', ['id'], $condition, ['order' => ['id']]); - if (!DBA::isResult($gcontact)) { - Logger::info('GContact creation failed', $fields); - // Shouldn't happen - return 0; - } - return $gcontact['id']; - } - - /** - * Updates the gcontact table from a given array - * - * @param array $contact contact array - * - * @return bool|int Returns false if not found, integer if contact was found - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function update($contact) - { - // Check for invalid "contact-type" value - if (isset($contact['contact-type']) && (intval($contact['contact-type']) < 0)) { - $contact['contact-type'] = 0; - } - - /// @todo update contact table as well - - $gcontact_id = self::getId($contact); - - if (!$gcontact_id) { - return false; - } - - $public_contact = DBA::selectFirst('gcontact', [ - 'name', 'nick', 'photo', 'location', 'about', 'addr', 'generation', 'birthday', 'keywords', 'gsid', 'failed', - 'contact-type', 'hide', 'nsfw', 'network', 'alias', 'notify', 'server_url', 'connect', 'updated', 'url' - ], ['id' => $gcontact_id]); - - if (!DBA::isResult($public_contact)) { - return false; - } - - // Get all field names - $fields = []; - foreach ($public_contact as $field => $data) { - $fields[$field] = $data; - } - - unset($fields['url']); - unset($fields['updated']); - unset($fields['hide']); - - // Bugfix: We had an error in the storing of keywords which lead to the "0" - // This value is still transmitted via poco. - if (isset($contact['keywords']) && ($contact['keywords'] == '0')) { - unset($contact['keywords']); - } - - if (isset($public_contact['keywords']) && ($public_contact['keywords'] == '0')) { - $public_contact['keywords'] = ''; - } - - // assign all unassigned fields from the database entry - foreach ($fields as $field => $data) { - if (empty($contact[$field])) { - $contact[$field] = $public_contact[$field]; - } - } - - if (!isset($contact['hide'])) { - $contact['hide'] = $public_contact['hide']; - } - - $fields['hide'] = $public_contact['hide']; - - if ($contact['network'] == Protocol::STATUSNET) { - $contact['network'] = Protocol::OSTATUS; - } - - if (!isset($contact['updated'])) { - $contact['updated'] = DateTimeFormat::utcNow(); - } - - if ($contact['network'] == Protocol::TWITTER) { - $contact['server_url'] = 'http://twitter.com'; - } - - if (empty($contact['server_url'])) { - $data = Probe::uri($contact['url']); - if ($data['network'] != Protocol::PHANTOM) { - $contact['server_url'] = $data['baseurl']; - } - } else { - $contact['server_url'] = Strings::normaliseLink($contact['server_url']); - } - - if (!empty($contact['server_url']) && empty($contact['gsid'])) { - $contact['gsid'] = GServer::getID($contact['server_url']); - } - - if (empty($contact['addr']) && !empty($contact['server_url']) && !empty($contact['nick'])) { - $hostname = str_replace('http://', '', $contact['server_url']); - $contact['addr'] = $contact['nick'] . '@' . $hostname; - } - - // Check if any field changed - $update = false; - unset($fields['generation']); - - if ((($contact['generation'] > 0) && ($contact['generation'] <= $public_contact['generation'])) || ($public_contact['generation'] == 0)) { - foreach ($fields as $field => $data) { - if ($contact[$field] != $public_contact[$field]) { - Logger::debug('Difference found.', ['contact' => $contact['url'], 'field' => $field, 'new' => $contact[$field], 'old' => $public_contact[$field]]); - $update = true; - } - } - - if ($contact['generation'] < $public_contact['generation']) { - Logger::debug('Difference found.', ['contact' => $contact['url'], 'field' => 'generation', 'new' => $contact['generation'], 'old' => $public_contact['generation']]); - $update = true; - } - } - - if ($update) { - Logger::debug('Update gcontact.', ['contact' => $contact['url']]); - $condition = ["`nurl` = ? AND (`generation` = 0 OR `generation` >= ?)", - Strings::normaliseLink($contact['url']), $contact['generation']]; - $contact['updated'] = DateTimeFormat::utc($contact['updated']); - - $updated = [ - 'photo' => $contact['photo'], 'name' => $contact['name'], - 'nick' => $contact['nick'], 'addr' => $contact['addr'], - 'network' => $contact['network'], 'birthday' => $contact['birthday'], - 'keywords' => $contact['keywords'], - 'hide' => $contact['hide'], 'nsfw' => $contact['nsfw'], - 'contact-type' => $contact['contact-type'], 'alias' => $contact['alias'], - 'notify' => $contact['notify'], 'url' => $contact['url'], - 'location' => $contact['location'], 'about' => $contact['about'], - 'generation' => $contact['generation'], 'updated' => $contact['updated'], - 'server_url' => $contact['server_url'], 'connect' => $contact['connect'], - 'failed' => $contact['failed'], 'gsid' => $contact['gsid'] - ]; - - DBA::update('gcontact', $updated, $condition, $fields); - } - - return $gcontact_id; - } - - /** - * Set the last date that the contact had posted something - * - * @param string $data Probing result - * @param bool $force force updating - */ - public static function setLastUpdate(array $data, bool $force = false) - { - // Fetch the global contact - $gcontact = DBA::selectFirst('gcontact', ['created', 'updated', 'last_contact', 'last_failure'], - ['nurl' => Strings::normaliseLink($data['url'])]); - if (!DBA::isResult($gcontact)) { - return; - } - - if (!$force && !GServer::updateNeeded($gcontact['created'], $gcontact['updated'], $gcontact['last_failure'], $gcontact['last_contact'])) { - Logger::info("Don't update profile", ['url' => $data['url'], 'updated' => $gcontact['updated']]); - return; - } - - if (self::updateFromNoScrape($data)) { - return; - } - - if (!empty($data['outbox'])) { - self::updateFromOutbox($data['outbox'], $data); - } elseif (!empty($data['poll']) && ($data['network'] == Protocol::ACTIVITYPUB)) { - self::updateFromOutbox($data['poll'], $data); - } elseif (!empty($data['poll'])) { - self::updateFromFeed($data); - } - } - - /** - * Update a global contact via the "noscrape" endpoint - * - * @param string $data Probing result - * - * @return bool 'true' if update was successful or the server was unreachable - */ - private static function updateFromNoScrape(array $data) - { - // Check the 'noscrape' endpoint when it is a Friendica server - $gserver = DBA::selectFirst('gserver', ['noscrape'], ["`nurl` = ? AND `noscrape` != ''", - Strings::normaliseLink($data['baseurl'])]); - if (!DBA::isResult($gserver)) { - return false; - } - - $curlResult = DI::httpRequest()->get($gserver['noscrape'] . '/' . $data['nick']); - - if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { - $noscrape = json_decode($curlResult->getBody(), true); - if (!empty($noscrape) && !empty($noscrape['updated'])) { - $noscrape['updated'] = DateTimeFormat::utc($noscrape['updated'], DateTimeFormat::MYSQL); - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $noscrape['updated']]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - return true; - } - } elseif ($curlResult->isTimeout()) { - // On a timeout return the existing value, but mark the contact as failure - $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - return true; - } - return false; - } - - /** - * Update a global contact via an ActivityPub Outbox - * - * @param string $feed - * @param array $data Probing result - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private static function updateFromOutbox(string $feed, array $data) - { - $outbox = ActivityPub::fetchContent($feed); - if (empty($outbox)) { - return; - } - - if (!empty($outbox['orderedItems'])) { - $items = $outbox['orderedItems']; - } elseif (!empty($outbox['first']['orderedItems'])) { - $items = $outbox['first']['orderedItems']; - } elseif (!empty($outbox['first']['href']) && ($outbox['first']['href'] != $feed)) { - self::updateFromOutbox($outbox['first']['href'], $data); - return; - } elseif (!empty($outbox['first'])) { - if (is_string($outbox['first']) && ($outbox['first'] != $feed)) { - self::updateFromOutbox($outbox['first'], $data); - } else { - Logger::warning('Unexpected data', ['outbox' => $outbox]); - } - return; - } else { - $items = []; - } - - $last_updated = ''; - foreach ($items as $activity) { - if (!empty($activity['published'])) { - $published = DateTimeFormat::utc($activity['published']); - } elseif (!empty($activity['object']['published'])) { - $published = DateTimeFormat::utc($activity['object']['published']); - } else { - continue; - } - - if ($last_updated < $published) { - $last_updated = $published; - } - } - - if (empty($last_updated)) { - return; - } - - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - } - - /** - * Update a global contact via an XML feed - * - * @param string $data Probing result - */ - private static function updateFromFeed(array $data) - { - // Search for the newest entry in the feed - $curlResult = DI::httpRequest()->get($data['poll']); - if (!$curlResult->isSuccess()) { - $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - - Logger::info("Profile wasn't reachable (no feed)", ['url' => $data['url']]); - return; - } - - $doc = new DOMDocument(); - @$doc->loadXML($curlResult->getBody()); - - $xpath = new DOMXPath($doc); - $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom'); - - $entries = $xpath->query('/atom:feed/atom:entry'); - - $last_updated = ''; - - foreach ($entries as $entry) { - $published_item = $xpath->query('atom:published/text()', $entry)->item(0); - $updated_item = $xpath->query('atom:updated/text()' , $entry)->item(0); - $published = !empty($published_item->nodeValue) ? DateTimeFormat::utc($published_item->nodeValue) : null; - $updated = !empty($updated_item->nodeValue) ? DateTimeFormat::utc($updated_item->nodeValue) : null; - - if (empty($published) || empty($updated)) { - Logger::notice('Invalid entry for XPath.', ['entry' => $entry, 'url' => $data['url']]); - continue; - } - - if ($last_updated < $published) { - $last_updated = $published; - } - - if ($last_updated < $updated) { - $last_updated = $updated; - } - } - - if (empty($last_updated)) { - return; - } - - $fields = ['failed' => false, 'last_contact' => DateTimeFormat::utcNow(), 'updated' => $last_updated]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($data['url'])]); - } - /** - * 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]); - } - - /** - * 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)]); - } - - /** - * 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', - 'bd', 'contact-type', 'network', 'addr', 'notify', 'alias', 'archive', 'term-date', - 'created', 'updated', 'avatar', 'success_update', 'failure_update', 'forum', 'prv', - 'baseurl', 'gsid', 'sensitive', 'unsearchable', 'failed']; - - $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', 'generation', - 'birthday', 'contact-type', 'network', 'addr', 'notify', 'alias', 'archived', 'archive_date', - 'created', 'updated', 'photo', 'last_contact', 'last_failure', 'community', 'connect', - 'server_url', 'gsid', 'nsfw', 'hide', 'id', 'failed']; - - $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', 'gsid', - 'contact-type', 'network', 'addr', 'notify', 'alias', 'created', 'updated', 'failed']; - - foreach ($fields as $field) { - $gcontact[$field] = $contact[$field]; - } - - // These fields are having different names but the same content - $gcontact['server_url'] = $contact['baseurl'] ?? ''; // "baseurl" can be null, "server_url" not - $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]); - } - } - - 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(); - } - } - - /** - * Updates the gcontact entry from probe - * - * @param string $url profile link - * @param boolean $force Optional forcing of network probing (otherwise we use the cached data) - * - * @return boolean 'true' when contact had been updated - * - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function updateFromProbe($url, $force = false) - { - $data = Probe::uri($url, $force); - - if (in_array($data['network'], [Protocol::PHANTOM])) { - $fields = ['failed' => true, 'last_failure' => DateTimeFormat::utcNow()]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($url)]); - Logger::info('Invalid network for contact', ['url' => $data['url'], 'callstack' => System::callstack()]); - return false; - } - - $data['server_url'] = $data['baseurl']; - $data['failed'] = false; - - self::update($data); - - // Set the date of the latest post - self::setLastUpdate($data, $force); - - return true; - } - - /** - * Update the gcontact entry for a given user id - * - * @param int $uid User ID - * @return bool - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function updateForUser($uid) - { - $profile = Profile::getByUID($uid); - if (empty($profile)) { - Logger::error('Cannot find profile', ['uid' => $uid]); - return false; - } - - $user = User::getOwnerDataById($uid); - if (empty($user)) { - Logger::error('Cannot find user', ['uid' => $uid]); - return false; - } - - $userdata = array_merge($profile, $user); - - $location = Profile::formatLocation( - ['locality' => $userdata['locality'], 'region' => $userdata['region'], 'country-name' => $userdata['country-name']] - ); - - $gcontact = ['name' => $userdata['name'], 'location' => $location, 'about' => $userdata['about'], - 'keywords' => $userdata['pub_keywords'], - 'birthday' => $userdata['dob'], 'photo' => $userdata['photo'], - "notify" => $userdata['notify'], 'url' => $userdata['url'], - "hide" => !$userdata['net-publish'], - 'nick' => $userdata['nickname'], 'addr' => $userdata['addr'], - "connect" => $userdata['addr'], "server_url" => DI::baseUrl(), - "generation" => 1, 'network' => Protocol::DFRN]; - - self::update($gcontact); - } - - /** - * Get the basepath for a given contact link - * - * @param string $url The gcontact link - * @param boolean $dont_update Don't update the contact - * - * @return string basepath - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function getBasepath($url, $dont_update = false) - { - $gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($url)]); - if (!empty($gcontact['server_url'])) { - return $gcontact['server_url']; - } elseif ($dont_update) { - return ''; - } - - self::updateFromProbe($url, true); - - // Fetch the result - $gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($url)]); - if (empty($gcontact['server_url'])) { - Logger::info('No baseurl for gcontact', ['url' => $url]); - return ''; - } - - Logger::info('Found baseurl for gcontact', ['url' => $url, 'baseurl' => $gcontact['server_url']]); - return $gcontact['server_url']; - } - - /** - * Fetches users of given GNU Social server - * - * If the "Statistics" addon is enabled (See http://gstools.org/ for details) we query user data with this. - * - * @param string $server Server address - * @return bool - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function fetchGsUsers($server) - { - Logger::info('Fetching users from GNU Social server', ['server' => $server]); - - $url = $server . '/main/statistics'; - - $curlResult = DI::httpRequest()->get($url); - if (!$curlResult->isSuccess()) { - return false; - } - - $statistics = json_decode($curlResult->getBody()); - - if (!empty($statistics->config->instance_address)) { - if (!empty($statistics->config->instance_with_ssl)) { - $server = 'https://'; - } else { - $server = 'http://'; - } - - $server .= $statistics->config->instance_address; - - $hostname = $statistics->config->instance_address; - } elseif (!empty($statistics->instance_address)) { - if (!empty($statistics->instance_with_ssl)) { - $server = 'https://'; - } else { - $server = 'http://'; - } - - $server .= $statistics->instance_address; - - $hostname = $statistics->instance_address; - } - - if (!empty($statistics->users)) { - foreach ($statistics->users as $nick => $user) { - $profile_url = $server . '/' . $user->nickname; - - $contact = ['url' => $profile_url, - 'name' => $user->fullname, - 'addr' => $user->nickname . '@' . $hostname, - 'nick' => $user->nickname, - "network" => Protocol::OSTATUS, - 'photo' => DI::baseUrl() . '/images/person-300.jpg']; - - if (isset($user->bio)) { - $contact['about'] = $user->bio; - } - - self::getId($contact); - } - } - } - - /** - * Asking GNU Social server on a regular base for their user data - * - * @return void - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function discoverGsUsers() - { - $requery_days = intval(DI::config()->get('system', 'poco_requery_days')); - - $last_update = date("c", time() - (60 * 60 * 24 * $requery_days)); - - $r = DBA::select('gserver', ['nurl', 'url'], [ - '`network` = ? - AND NOT `failed` - AND `last_poco_query` < ?', - Protocol::OSTATUS, - $last_update - ], [ - 'limit' => 5, - 'order' => ['RAND()'] - ]); - - if (!DBA::isResult($r)) { - return; - } - - foreach ($r as $server) { - self::fetchGsUsers($server['url']); - DBA::update('gserver', ['last_poco_query' => DateTimeFormat::utcNow()], ['nurl' => $server['nurl']]); - } - } - /** * Returns a random, global contact of the current node * diff --git a/src/Model/GServer.php b/src/Model/GServer.php index 07a5eaad3e..aa598a729a 100644 --- a/src/Model/GServer.php +++ b/src/Model/GServer.php @@ -32,7 +32,6 @@ use Friendica\DI; use Friendica\Module\Register; use Friendica\Network\CurlResult; use Friendica\Protocol\Diaspora; -use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -111,7 +110,10 @@ class GServer public static function reachable(string $profile, string $server = '', string $network = '', bool $force = false) { if ($server == '') { - $server = GContact::getBasepath($profile); + $contact = Contact::getByURL($profile, null, ['baseurl']); + if (!empty($contact['baseurl'])) { + $server = $contact['baseurl']; + } } if ($server == '') { @@ -471,10 +473,9 @@ class GServer } if (!empty($serverdata['network']) && !empty($id) && ($serverdata['network'] != Protocol::PHANTOM)) { - $gcontacts = DBA::count('gcontact', ['gsid' => $id]); $apcontacts = DBA::count('apcontact', ['gsid' => $id]); $contacts = DBA::count('contact', ['uid' => 0, 'gsid' => $id]); - $max_users = max($gcontacts, $apcontacts, $contacts, $registeredUsers); + $max_users = max($apcontacts, $contacts, $registeredUsers); if ($max_users > $registeredUsers) { Logger::info('Update registered users', ['id' => $id, 'url' => $serverdata['nurl'], 'registered-users' => $max_users]); DBA::update('gserver', ['registered-users' => $max_users], ['id' => $id]); @@ -959,12 +960,6 @@ class GServer { $contacts = []; - $gcontacts = DBA::select('gcontact', ['url', 'nurl'], ['server_url' => [$url, $serverdata['nurl']]]); - while ($gcontact = DBA::fetch($gcontacts)) { - $contacts[$gcontact['nurl']] = $gcontact['url']; - } - DBA::close($gcontacts); - $apcontacts = DBA::select('apcontact', ['url'], ['baseurl' => [$url, $serverdata['nurl']]]); while ($apcontact = DBA::fetch($apcontacts)) { $contacts[Strings::normaliseLink($apcontact['url'])] = $apcontact['url']; @@ -1556,20 +1551,6 @@ class GServer return !strpos($body, '>'); } - /** - * Update the user directory of a given gserver record - * - * @param array $gserver gserver record - */ - public static function updateDirectory(array $gserver) - { - /// @todo Add Mastodon API directory - - if (!empty($gserver['poco'])) { - PortableContact::discoverSingleServer($gserver['id']); - } - } - /** * Update GServer entries */ @@ -1588,7 +1569,7 @@ class GServer $last_update = date('c', time() - (60 * 60 * 24 * $requery_days)); - $gservers = DBA::p("SELECT `id`, `url`, `nurl`, `network`, `poco` + $gservers = DBA::p("SELECT `id`, `url`, `nurl`, `network`, `poco`, `directory-type` FROM `gserver` WHERE NOT `failed` AND `directory-type` != ? @@ -1672,4 +1653,18 @@ class GServer DI::config()->set('poco', 'last_federation_discovery', time()); } + + /** + * Returns a list of 1,000 active servers order by the last contact + * + * @return array List of server urls + * @throws Exception + */ + public static function getActive() + { + $result = DBA::p("SELECT `url`, `site_name` AS `displayName`, `network`, `platform`, `version` FROM `gserver` + WHERE `network` IN (?, ?, ?, ?) AND NOT `failed` ORDER BY `last_contact` LIMIT ?", + Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, 1000); + return DBA::toArray($result); + } } diff --git a/src/Module/Admin/Federation.php b/src/Module/Admin/Federation.php index fd60b07152..d0d30e85e1 100644 --- a/src/Module/Admin/Federation.php +++ b/src/Module/Admin/Federation.php @@ -132,7 +132,6 @@ class Federation extends BaseAdmin // some helpful text $intro = DI::l10n()->t('This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of.'); - $hint = DI::l10n()->t('The Auto Discovered Contact Directory feature is not enabled, it will improve the data displayed here.'); // load the template, replace the macros and return the page content $t = Renderer::getMarkupTemplate('admin/federation.tpl'); @@ -140,8 +139,6 @@ class Federation extends BaseAdmin '$title' => DI::l10n()->t('Administration'), '$page' => DI::l10n()->t('Federation Statistics'), '$intro' => $intro, - '$hint' => $hint, - '$autoactive' => DI::config()->get('system', 'poco_completion'), '$counts' => $counts, '$version' => FRIENDICA_VERSION, '$legendtext' => DI::l10n()->t('Currently this node is aware of %d nodes with %d registered users from the following platforms:', $total, $users), diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 3bdcd71147..7b298cd937 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -31,7 +31,6 @@ use Friendica\DI; use Friendica\Model\ContactRelation; use Friendica\Module\BaseAdmin; use Friendica\Module\Register; -use Friendica\Protocol\PortableContact; use Friendica\Util\BasePath; use Friendica\Util\EMailer\MailBuilder; use Friendica\Util\Strings; @@ -104,12 +103,10 @@ class Site extends BaseAdmin // update profile links in the format "http://server.tld" update_table($a, "profile", ['photo', 'thumb'], $old_url, $new_url); update_table($a, "contact", ['photo', 'thumb', 'micro', 'url', 'nurl', 'alias', 'request', 'notify', 'poll', 'confirm', 'poco', 'avatar'], $old_url, $new_url); - update_table($a, "gcontact", ['url', 'nurl', 'photo', 'server_url', 'notify', 'alias'], $old_url, $new_url); update_table($a, "item", ['owner-link', 'author-link', 'body', 'plink', 'tag'], $old_url, $new_url); // update profile addresses in the format "user@server.tld" update_table($a, "contact", ['addr'], $old_host, $new_host); - update_table($a, "gcontact", ['connect', 'addr'], $old_host, $new_host); // update config DI::config()->set('system', 'url', $new_url); @@ -180,10 +177,8 @@ class Site extends BaseAdmin $optimize_fragmentation = (!empty($_POST['optimize_fragmentation']) ? intval(trim($_POST['optimize_fragmentation'])) : 30); $contact_discovery = (!empty($_POST['contact_discovery']) ? intval(trim($_POST['contact_discovery'])) : ContactRelation::DISCOVERY_NONE); $synchronize_directory = (!empty($_POST['synchronize_directory']) ? intval(trim($_POST['synchronize_directory'])) : false); - $poco_completion = (!empty($_POST['poco_completion']) ? intval(trim($_POST['poco_completion'])) : false); $poco_requery_days = (!empty($_POST['poco_requery_days']) ? intval(trim($_POST['poco_requery_days'])) : 7); - $poco_discovery = (!empty($_POST['poco_discovery']) ? intval(trim($_POST['poco_discovery'])) : PortableContact::DISABLED); - $poco_discovery_since = (!empty($_POST['poco_discovery_since']) ? intval(trim($_POST['poco_discovery_since'])) : 30); + $poco_discovery = (!empty($_POST['poco_discovery']) ? intval(trim($_POST['poco_discovery'])) : false); $poco_local_search = !empty($_POST['poco_local_search']); $nodeinfo = !empty($_POST['nodeinfo']); $dfrn_only = !empty($_POST['dfrn_only']); @@ -308,12 +303,10 @@ class Site extends BaseAdmin DI::config()->set('system', 'min_memory' , $min_memory); DI::config()->set('system', 'optimize_max_tablesize', $optimize_max_tablesize); DI::config()->set('system', 'optimize_fragmentation', $optimize_fragmentation); - DI::config()->set('system', 'poco_completion' , $poco_completion); DI::config()->set('system', 'contact_discovery' , $contact_discovery); DI::config()->set('system', 'synchronize_directory' , $synchronize_directory); DI::config()->set('system', 'poco_requery_days' , $poco_requery_days); DI::config()->set('system', 'poco_discovery' , $poco_discovery); - DI::config()->set('system', 'poco_discovery_since' , $poco_discovery_since); DI::config()->set('system', 'poco_local_search' , $poco_local_search); DI::config()->set('system', 'nodeinfo' , $nodeinfo); DI::config()->set('config', 'sitename' , $sitename); @@ -490,20 +483,6 @@ class Site extends BaseAdmin CP_USERS_AND_GLOBAL => DI::l10n()->t('Public postings from local users and the federated network') ]; - $poco_discovery_choices = [ - PortableContact::DISABLED => DI::l10n()->t('Disabled'), - PortableContact::USERS => DI::l10n()->t('Users'), - PortableContact::USERS_GCONTACTS => DI::l10n()->t('Users, Global Contacts'), - PortableContact::USERS_GCONTACTS_FALLBACK => DI::l10n()->t('Users, Global Contacts/fallback'), - ]; - - $poco_discovery_since_choices = [ - '30' => DI::l10n()->t('One month'), - '91' => DI::l10n()->t('Three months'), - '182' => DI::l10n()->t('Half a year'), - '365' => DI::l10n()->t('One year'), - ]; - /* get user names to make the install a personal install of X */ // @TODO Move to Model\User::getNames() $user_names = []; @@ -688,10 +667,8 @@ class Site extends BaseAdmin $discovery_choices], '$synchronize_directory' => ['synchronize_directory', DI::l10n()->t('Synchronize the contacts with the directory server'), DI::config()->get('system', 'synchronize_directory'), DI::l10n()->t('if enabled, the system will check periodically for new contacts on the defined directory server.')], - '$poco_completion' => ['poco_completion', DI::l10n()->t('Periodical check of global contacts'), DI::config()->get('system', 'poco_completion'), DI::l10n()->t('If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers.')], '$poco_requery_days' => ['poco_requery_days', DI::l10n()->t('Days between requery'), DI::config()->get('system', 'poco_requery_days'), DI::l10n()->t('Number of days after which a server is requeried for his contacts.')], - '$poco_discovery' => ['poco_discovery', DI::l10n()->t('Discover contacts from other servers'), DI::config()->get('system', 'poco_discovery'), DI::l10n()->t('Periodically query other servers for contacts. You can choose between "Users": the users on the remote system, "Global Contacts": active contacts that are known on the system. The fallback is meant for Redmatrix servers and older friendica servers, where global contacts weren\'t available. The fallback increases the server load, so the recommended setting is "Users, Global Contacts".'), $poco_discovery_choices], - '$poco_discovery_since' => ['poco_discovery_since', DI::l10n()->t('Timeframe for fetching global contacts'), DI::config()->get('system', 'poco_discovery_since'), DI::l10n()->t('When the discovery is activated, this value defines the timeframe for the activity of the global contacts that are fetched from other servers.'), $poco_discovery_since_choices], + '$poco_discovery' => ['poco_discovery', DI::l10n()->t('Discover contacts from other servers'), DI::config()->get('system', 'poco_discovery'), DI::l10n()->t('Periodically query other servers for contacts. The system queries Friendica, Mastodon and Hubzilla servers.')], '$poco_local_search' => ['poco_local_search', DI::l10n()->t('Search the local directory'), DI::config()->get('system', 'poco_local_search'), DI::l10n()->t('Search the local directory instead of the global directory. When searching locally, every search will be executed on the global directory in the background. This improves the search results when the search is repeated.')], '$nodeinfo' => ['nodeinfo', DI::l10n()->t('Publish server information'), DI::config()->get('system', 'nodeinfo'), DI::l10n()->t('If enabled, general server and usage data will be published. The data contains the name and version of the server, number of users with public profiles, number of posts and the activated protocols and connectors. See the-federation.info for details.')], diff --git a/src/Module/Contact.php b/src/Module/Contact.php index 40148c50d1..bf22470ed4 100644 --- a/src/Module/Contact.php +++ b/src/Module/Contact.php @@ -186,9 +186,6 @@ class Contact extends BaseModule // Update the entry in the contact table Model\Contact::updateFromProbe($contact_id, '', true); - - // Update the entry in the gcontact table - Model\GContact::updateFromProbe($contact['url']); } /** diff --git a/src/Module/NoScrape.php b/src/Module/NoScrape.php index 1457a1125f..4ad0e53069 100644 --- a/src/Module/NoScrape.php +++ b/src/Module/NoScrape.php @@ -26,14 +26,13 @@ use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Model\GContact; use Friendica\Model\Profile; use Friendica\Model\User; /** * Endpoint for getting current user infos * - * @see GContact::updateFromNoScrape() for usage + * @see Contact::updateFromNoScrape() for usage */ class NoScrape extends BaseModule { diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php index a7e02f4299..f4c902b829 100644 --- a/src/Module/Settings/Profile/Index.php +++ b/src/Module/Settings/Profile/Index.php @@ -31,7 +31,6 @@ use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Model\Profile; use Friendica\Model\ProfileField; use Friendica\Model\User; @@ -151,9 +150,6 @@ class Index extends BaseSettings } Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); - - // Update the global contact for the user - GContact::updateForUser(local_user()); } public static function content(array $parameters = []) diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index f213af8db3..281220bc34 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -33,7 +33,6 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; use Friendica\Model\Event; -use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; @@ -1686,21 +1685,6 @@ class DFRN if (!empty($pcid)) { Contact::updateAvatar($pcid, $author['avatar']); } - - /* - * The generation is a sign for the reliability of the provided data. - * It is used in the socgraph.php to prevent that old contact data - * that was relayed over several servers can overwrite contact - * data that we received directly. - */ - - $poco["generation"] = 2; - $poco["photo"] = $author["avatar"]; - $poco["hide"] = $hide; - $poco["contact-type"] = $contact["contact-type"]; - $gcid = GContact::update($poco); - - GContact::link($gcid, $importer["importer_uid"], $contact["id"]); } return $author; @@ -1943,15 +1927,6 @@ class DFRN $old = $r[0]; - // Update the gcontact entry - $relocate["server_url"] = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$1$2", $relocate["url"]); - - $fields = ['name' => $relocate["name"], 'photo' => $relocate["avatar"], - 'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]), - 'addr' => $relocate["addr"], 'connect' => $relocate["addr"], - 'notify' => $relocate["notify"], 'server_url' => $relocate["server_url"]]; - DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($old["url"])]); - // Update the contact table. We try to find every entry. $fields = ['name' => $relocate["name"], 'avatar' => $relocate["avatar"], 'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]), diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index f3b95db68f..feb1111443 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -34,7 +34,6 @@ use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Conversation; -use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Mail; @@ -1656,7 +1655,7 @@ class Diaspora // Update the profile self::receiveProfile($importer, $data->profile); - // change the technical stuff in contact and gcontact + // change the technical stuff in contact $data = Probe::uri($new_handle); if ($data['network'] == Protocol::PHANTOM) { Logger::log('Account for '.$new_handle." couldn't be probed."); @@ -1671,14 +1670,6 @@ class Diaspora DBA::update('contact', $fields, ['addr' => $old_handle]); - $fields = ['url' => $data['url'], 'nurl' => Strings::normaliseLink($data['url']), - 'name' => $data['name'], 'nick' => $data['nick'], - 'addr' => $data['addr'], 'connect' => $data['addr'], - 'notify' => $data['notify'], 'photo' => $data['photo'], - 'server_url' => $data['baseurl'], 'network' => $data['network']]; - - DBA::update('gcontact', $fields, ['addr' => $old_handle]); - Logger::log('Contacts are updated.'); return true; @@ -1702,8 +1693,6 @@ class Diaspora } DBA::close($contacts); - DBA::delete('gcontact', ['addr' => $author]); - Logger::log('Removed contacts for ' . $author); return true; @@ -2438,18 +2427,6 @@ class Diaspora DBA::update('contact', $fields, ['id' => $contact['id']]); - // @todo Update the public contact, then update the gcontact from that - - $gcontact = ["url" => $contact["url"], "network" => Protocol::DIASPORA, "generation" => 2, - "photo" => $image_url, "name" => $name, "location" => $location, - "about" => $about, "birthday" => $birthday, - "addr" => $author, "nick" => $nick, "keywords" => $keywords, - "hide" => !$searchable, "nsfw" => $nsfw]; - - $gcid = GContact::update($gcontact); - - GContact::link($gcid, $importer["uid"], $contact["id"]); - Logger::log("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], Logger::DEBUG); return true; diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 1cf2894eb5..d9e18fa613 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -34,7 +34,6 @@ use Friendica\DI; use Friendica\Model\APContact; use Friendica\Model\Contact; use Friendica\Model\Conversation; -use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; use Friendica\Model\Tag; @@ -240,15 +239,6 @@ class OStatus Contact::updateAvatar($cid, $author["author-avatar"]); } } - - $contact["generation"] = 2; - $contact["hide"] = false; // OStatus contacts are never hidden - if (!empty($author["author-avatar"])) { - $contact["photo"] = $author["author-avatar"]; - } - $gcid = GContact::update($contact); - - GContact::link($gcid, $contact["uid"], $contact["id"]); } elseif (empty($contact["network"]) || ($contact["network"] != Protocol::DFRN)) { $contact = []; } diff --git a/src/Protocol/PortableContact.php b/src/Protocol/PortableContact.php deleted file mode 100644 index 8109ef2373..0000000000 --- a/src/Protocol/PortableContact.php +++ /dev/null @@ -1,486 +0,0 @@ -. - * - */ - -namespace Friendica\Protocol; - -use Exception; -use Friendica\Content\Text\HTML; -use Friendica\Core\Logger; -use Friendica\Core\Protocol; -use Friendica\Core\Worker; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\GContact; -use Friendica\Model\GServer; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; - -/** - * - * @todo Move GNU Social URL schemata (http://server.tld/user/number) to http://server.tld/username - * @todo Fetch profile data from profile page for Redmatrix users - * @todo Detect if it is a forum - */ -class PortableContact -{ - const DISABLED = 0; - const USERS = 1; - const USERS_GCONTACTS = 2; - const USERS_GCONTACTS_FALLBACK = 3; - - /** - * Fetch POCO data - * - * @param integer $cid Contact ID - * @param integer $uid User ID - * @param integer $zcid Global Contact ID - * @param integer $url POCO address that should be polled - * - * Given a contact-id (minimum), load the PortableContacts friend list for that contact, - * and add the entries to the gcontact (Global Contact) table, or update existing entries - * if anything (name or photo) has changed. - * We use normalised urls for comparison which ignore http vs https and www.domain vs domain - * - * Once the global contact is stored add (if necessary) the contact linkage which associates - * the given uid, cid to the global contact entry. There can be many uid/cid combinations - * pointing to the same global contact id. - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function loadWorker($cid, $uid = 0, $zcid = 0, $url = null) - { - // Call the function "load" via the worker - Worker::add(PRIORITY_LOW, 'FetchPoCo', (int)$cid, (int)$uid, (int)$zcid, $url); - } - - /** - * Fetch POCO data from the worker - * - * @param integer $cid Contact ID - * @param integer $uid User ID - * @param integer $zcid Global Contact ID - * @param integer $url POCO address that should be polled - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public static function load($cid, $uid, $zcid, $url) - { - if ($cid) { - if (!$url || !$uid) { - $contact = DBA::selectFirst('contact', ['poco', 'uid'], ['id' => $cid]); - if (DBA::isResult($contact)) { - $url = $contact['poco']; - $uid = $contact['uid']; - } - } - if (!$uid) { - return; - } - } - - if (!$url) { - return; - } - - $url = $url . (($uid) ? '/@me/@all?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation' : '?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation'); - - Logger::log('load: ' . $url, Logger::DEBUG); - - $fetchresult = DI::httpRequest()->fetchFull($url); - $s = $fetchresult->getBody(); - - Logger::log('load: returns ' . $s, Logger::DATA); - - Logger::log('load: return code: ' . $fetchresult->getReturnCode(), Logger::DEBUG); - - if (($fetchresult->getReturnCode() > 299) || (! $s)) { - return; - } - - $j = json_decode($s, true); - - Logger::debug('load', ['json' => $j]); - - if (!isset($j['entry'])) { - return; - } - - $total = 0; - foreach ($j['entry'] as $entry) { - $total ++; - $profile_url = ''; - $profile_photo = ''; - $connect_url = ''; - $name = ''; - $network = ''; - $updated = DBA::NULL_DATETIME; - $location = ''; - $about = ''; - $keywords = ''; - $contact_type = -1; - $generation = 0; - - if (!empty($entry['displayName'])) { - $name = $entry['displayName']; - } - - if (isset($entry['urls'])) { - foreach ($entry['urls'] as $url) { - if ($url['type'] == 'profile') { - $profile_url = $url['value']; - continue; - } - if ($url['type'] == 'webfinger') { - $connect_url = str_replace('acct:', '', $url['value']); - continue; - } - } - } - if (isset($entry['photos'])) { - foreach ($entry['photos'] as $photo) { - if ($photo['type'] == 'profile') { - $profile_photo = $photo['value']; - continue; - } - } - } - - if (isset($entry['updated'])) { - $updated = date(DateTimeFormat::MYSQL, strtotime($entry['updated'])); - } - - if (isset($entry['network'])) { - $network = $entry['network']; - } - - if (isset($entry['currentLocation'])) { - $location = $entry['currentLocation']; - } - - if (isset($entry['aboutMe'])) { - $about = HTML::toBBCode($entry['aboutMe']); - } - - if (isset($entry['generation']) && ($entry['generation'] > 0)) { - $generation = ++$entry['generation']; - } - - if (isset($entry['tags'])) { - foreach ($entry['tags'] as $tag) { - $keywords = implode(", ", $tag); - } - } - - if (isset($entry['contactType']) && ($entry['contactType'] >= 0)) { - $contact_type = $entry['contactType']; - } - - $gcontact = ["url" => $profile_url, - "name" => $name, - "network" => $network, - "photo" => $profile_photo, - "about" => $about, - "location" => $location, - "keywords" => $keywords, - "connect" => $connect_url, - "updated" => $updated, - "contact-type" => $contact_type, - "generation" => $generation]; - - try { - $gcontact = GContact::sanitize($gcontact); - $gcid = GContact::update($gcontact); - - GContact::link($gcid, $uid, $cid, $zcid); - } catch (Exception $e) { - Logger::log($e->getMessage(), Logger::DEBUG); - } - } - Logger::log("load: loaded $total entries", Logger::DEBUG); - - $condition = ["`cid` = ? AND `uid` = ? AND `zcid` = ? AND `updated` < UTC_TIMESTAMP - INTERVAL 2 DAY", $cid, $uid, $zcid]; - DBA::delete('glink', $condition); - } - - /** - * Returns a list of all known servers - * @return array List of server urls - * @throws Exception - */ - public static function serverlist() - { - $r = q( - "SELECT `url`, `site_name` AS `displayName`, `network`, `platform`, `version` FROM `gserver` - WHERE `network` IN ('%s', '%s', '%s') AND NOT `failed` - ORDER BY `last_contact` - LIMIT 1000", - DBA::escape(Protocol::DFRN), - DBA::escape(Protocol::DIASPORA), - DBA::escape(Protocol::OSTATUS) - ); - - if (!DBA::isResult($r)) { - return false; - } - - return $r; - } - - /** - * Fetch server list from remote servers and adds them when they are new. - * - * @param string $poco URL to the POCO endpoint - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private static function fetchServerlist($poco) - { - $curlResult = DI::httpRequest()->get($poco . "/@server"); - - if (!$curlResult->isSuccess()) { - return; - } - - $serverlist = json_decode($curlResult->getBody(), true); - - if (!is_array($serverlist)) { - return; - } - - foreach ($serverlist as $server) { - $server_url = str_replace("/index.php", "", $server['url']); - - $r = q("SELECT `nurl` FROM `gserver` WHERE `nurl` = '%s'", DBA::escape(Strings::normaliseLink($server_url))); - - if (!DBA::isResult($r)) { - Logger::log("Call server check for server ".$server_url, Logger::DEBUG); - Worker::add(PRIORITY_LOW, 'UpdateGServer', $server_url); - } - } - } - - public static function discoverSingleServer($id) - { - $server = DBA::selectFirst('gserver', ['poco', 'nurl', 'url', 'network'], ['id' => $id]); - - if (!DBA::isResult($server)) { - return false; - } - - // Discover new servers out there (Works from Friendica version 3.5.2) - self::fetchServerlist($server["poco"]); - - // Fetch all users from the other server - $url = $server["poco"] . "/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation"; - - Logger::info("Fetch all users from the server " . $server["url"]); - - $curlResult = DI::httpRequest()->get($url); - - if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { - $data = json_decode($curlResult->getBody(), true); - - if (!empty($data)) { - self::discoverServer($data, 2); - } - - if (DI::config()->get('system', 'poco_discovery') >= self::USERS_GCONTACTS) { - $timeframe = DI::config()->get('system', 'poco_discovery_since'); - - if ($timeframe == 0) { - $timeframe = 30; - } - - $updatedSince = date(DateTimeFormat::MYSQL, time() - $timeframe * 86400); - - // Fetch all global contacts from the other server (Not working with Redmatrix and Friendica versions before 3.3) - $url = $server["poco"]."/@global?updatedSince=".$updatedSince."&fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation"; - - $success = false; - - $curlResult = DI::httpRequest()->get($url); - - if ($curlResult->isSuccess() && !empty($curlResult->getBody())) { - Logger::info("Fetch all global contacts from the server " . $server["nurl"]); - $data = json_decode($curlResult->getBody(), true); - - if (!empty($data)) { - $success = self::discoverServer($data); - } - } - - if (!$success && !empty($data) && DI::config()->get('system', 'poco_discovery') >= self::USERS_GCONTACTS_FALLBACK) { - Logger::info("Fetch contacts from users of the server " . $server["nurl"]); - self::discoverServerUsers($data, $server); - } - } - - return true; - } else { - // If the server hadn't replied correctly, then force a sanity check - GServer::check($server["url"], $server["network"], true); - - return false; - } - } - - private static function discoverServerUsers(array $data, array $server) - { - if (!isset($data['entry'])) { - return; - } - - foreach ($data['entry'] as $entry) { - $username = ''; - - if (isset($entry['urls'])) { - foreach ($entry['urls'] as $url) { - if ($url['type'] == 'profile') { - $profile_url = $url['value']; - $path_array = explode('/', parse_url($profile_url, PHP_URL_PATH)); - $username = end($path_array); - } - } - } - - if ($username != '') { - Logger::log('Fetch contacts for the user ' . $username . ' from the server ' . $server['nurl'], Logger::DEBUG); - - // Fetch all contacts from a given user from the other server - $url = $server['poco'] . '/' . $username . '/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,contactType,generation'; - - $curlResult = DI::httpRequest()->get($url); - - if ($curlResult->isSuccess()) { - $data = json_decode($curlResult->getBody(), true); - - if (!empty($data)) { - self::discoverServer($data, 3); - } - } - } - } - } - - private static function discoverServer(array $data, $default_generation = 0) - { - if (empty($data['entry'])) { - return false; - } - - $success = false; - - foreach ($data['entry'] as $entry) { - $profile_url = ''; - $profile_photo = ''; - $connect_url = ''; - $name = ''; - $network = ''; - $updated = DBA::NULL_DATETIME; - $location = ''; - $about = ''; - $keywords = ''; - $contact_type = -1; - $generation = $default_generation; - - if (!empty($entry['displayName'])) { - $name = $entry['displayName']; - } - - if (isset($entry['urls'])) { - foreach ($entry['urls'] as $url) { - if ($url['type'] == 'profile') { - $profile_url = $url['value']; - continue; - } - if ($url['type'] == 'webfinger') { - $connect_url = str_replace('acct:' , '', $url['value']); - continue; - } - } - } - - if (isset($entry['photos'])) { - foreach ($entry['photos'] as $photo) { - if ($photo['type'] == 'profile') { - $profile_photo = $photo['value']; - continue; - } - } - } - - if (isset($entry['updated'])) { - $updated = date(DateTimeFormat::MYSQL, strtotime($entry['updated'])); - } - - if (isset($entry['network'])) { - $network = $entry['network']; - } - - if (isset($entry['currentLocation'])) { - $location = $entry['currentLocation']; - } - - if (isset($entry['aboutMe'])) { - $about = HTML::toBBCode($entry['aboutMe']); - } - - if (isset($entry['generation']) && ($entry['generation'] > 0)) { - $generation = ++$entry['generation']; - } - - if (isset($entry['contactType']) && ($entry['contactType'] >= 0)) { - $contact_type = $entry['contactType']; - } - - if (isset($entry['tags'])) { - foreach ($entry['tags'] as $tag) { - $keywords = implode(", ", $tag); - } - } - - if ($generation > 0) { - $success = true; - - Logger::log("Store profile ".$profile_url, Logger::DEBUG); - - $gcontact = ["url" => $profile_url, - "name" => $name, - "network" => $network, - "photo" => $profile_photo, - "about" => $about, - "location" => $location, - "keywords" => $keywords, - "connect" => $connect_url, - "updated" => $updated, - "contact-type" => $contact_type, - "generation" => $generation]; - - try { - $gcontact = GContact::sanitize($gcontact); - GContact::update($gcontact); - } catch (Exception $e) { - Logger::log($e->getMessage(), Logger::DEBUG); - } - - Logger::log("Done for profile ".$profile_url, Logger::DEBUG); - } - } - return $success; - } -} diff --git a/src/Worker/Cron.php b/src/Worker/Cron.php index 5bdd22f330..4a49103e09 100644 --- a/src/Worker/Cron.php +++ b/src/Worker/Cron.php @@ -57,9 +57,6 @@ class Cron // run the process to update server directories in the background Worker::add(PRIORITY_LOW, 'UpdateServerDirectories'); - // run the process to update locally stored global contacts in the background - Worker::add(PRIORITY_LOW, 'UpdateGContacts'); - // Expire and remove user entries Worker::add(PRIORITY_MEDIUM, "CronJobs", "expire_and_remove_users"); @@ -88,8 +85,6 @@ class Cron Worker::add(PRIORITY_LOW, 'UpdateGServers'); - Worker::add(PRIORITY_LOW, 'UpdateSuggestions'); - Worker::add(PRIORITY_LOW, 'Expire'); Worker::add(PRIORITY_MEDIUM, 'DBClean'); diff --git a/src/Worker/CronJobs.php b/src/Worker/CronJobs.php index 4f988b6e14..5b5c01acab 100644 --- a/src/Worker/CronJobs.php +++ b/src/Worker/CronJobs.php @@ -29,7 +29,6 @@ use Friendica\Database\DBA; use Friendica\Database\PostUpdate; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Model\Nodeinfo; use Friendica\Model\Photo; use Friendica\Model\User; @@ -258,14 +257,6 @@ class CronJobs // There was an issue where the nick vanishes from the contact table q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''"); - // Update the global contacts for local users - $r = q("SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`"); - if (DBA::isResult($r)) { - foreach ($r AS $user) { - GContact::updateForUser($user["uid"]); - } - } - /// @todo /// - remove thread entries without item /// - remove sign entries without item diff --git a/src/Worker/FetchPoCo.php b/src/Worker/FetchPoCo.php deleted file mode 100644 index 477d001d29..0000000000 --- a/src/Worker/FetchPoCo.php +++ /dev/null @@ -1,41 +0,0 @@ -. - * - */ - -namespace Friendica\Worker; - -use Friendica\Core\Logger; -use Friendica\Protocol\PortableContact; - -class FetchPoCo -{ - /** - * Fetch PortableContacts from a given PoCo server address - * - * @param integer $cid Contact ID - * @param integer $uid User ID - * @param integer $zcid Global Contact ID - * @param integer $url PoCo address that should be polled - */ - public static function execute($cid, $uid, $zcid, $url) - { - PortableContact::load($cid, $uid, $zcid, $url); - } -} diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index fbd1ab4e59..40681175d4 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -32,7 +32,6 @@ use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Email; use Friendica\Protocol\Feed; -use Friendica\Protocol\PortableContact; use Friendica\Util\DateTimeFormat; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -95,13 +94,6 @@ class OnePoll $contact = DBA::selectFirst('contact', [], ['id' => $contact_id]); } - // load current friends if possible. - if (!empty($contact['poco']) && !$contact['failed']) { - if (!DBA::exists('glink', ["`cid` = ? AND updated > UTC_TIMESTAMP() - INTERVAL 1 DAY", $contact['id']])) { - PortableContact::loadWorker($contact['id'], $importer_uid, 0, $contact['poco']); - } - } - // Don't poll if polling is deactivated (But we poll feeds and mails anyway) if (!in_array($protocol, [Protocol::FEED, Protocol::MAIL]) && DI::config()->get('system', 'disable_polling')) { Logger::log('Polling is disabled'); diff --git a/src/Worker/PullDirectory.php b/src/Worker/PullDirectory.php index a83b0a13be..250496009d 100644 --- a/src/Worker/PullDirectory.php +++ b/src/Worker/PullDirectory.php @@ -60,22 +60,11 @@ class PullDirectory return; } + $result = Contact::addContactsByArray($contacts['results']); + $now = $contacts['now'] ?? 0; - $count = $contacts['count'] ?? 0; - $added = 0; - $updated = 0; - foreach ($contacts['results'] as $url) { - $contact = Contact::getByURL($url, false, ['id']); - if (empty($contact['id'])) { - Worker::add(PRIORITY_LOW, 'AddContact', 0, $url); - ++$added; - } else { - Worker::add(PRIORITY_LOW, "UpdateContact", $contact['id']); - ++$updated; - } - } DI::config()->set('system', 'last-directory-sync', $now); - Logger::info('Synchronization ended.', ['now' => $now, 'count' => $count, 'added' => $added, 'updated' => $updated, 'directory' => $directory]); + Logger::info('Synchronization ended', ['now' => $now, 'count' => $result['count'], 'added' => $result['added'], 'updated' => $result['updated'], 'directory' => $directory]); } } diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index 546c369b2c..3bb5f1b8b2 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -23,14 +23,9 @@ namespace Friendica\Worker; use Friendica\Core\Cache\Duration; use Friendica\Core\Logger; -use Friendica\Core\Protocol; use Friendica\Core\Search; -use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; -use Friendica\Model\GServer; -use Friendica\Util\Strings; class SearchDirectory { @@ -56,43 +51,7 @@ class SearchDirectory if (!empty($j->results)) { foreach ($j->results as $jj) { - // Check if the contact already exists - $gcontact = DBA::selectFirst('gcontact', ['failed'], ['nurl' => Strings::normaliseLink($jj->url)]); - if (DBA::isResult($gcontact)) { - Logger::info('Profile already exists', ['profile' => $jj->url, 'search' => $search]); - - if ($gcontact['failed']) { - continue; - } - - // Update the contact - GContact::updateFromProbe($jj->url); - continue; - } - - $server_url = GContact::getBasepath($jj->url, true); - if ($server_url != '') { - if (!GServer::check($server_url)) { - Logger::info("Friendica server doesn't answer.", ['server' => $server_url]); - continue; - } - Logger::info('Friendica server seems to be okay.', ['server' => $server_url]); - } - - $data = Contact::getByURL($jj->url); - if ($data['network'] == Protocol::DFRN) { - Logger::info('Add profile to local directory', ['profile' => $jj->url]); - - if ($jj->tags != '') { - $data['keywords'] = $jj->tags; - } - - $data['server_url'] = $data['baseurl']; - - GContact::update($data); - } else { - Logger::info('Profile is not responding or no Friendica contact', ['profile' => $jj->url, 'network' => $data['network']]); - } + Contact::getByURL($jj->url); } } DI::cache()->set('SearchDirectory:' . $search, time(), Duration::DAY); diff --git a/src/Worker/UpdateGContact.php b/src/Worker/UpdateGContact.php deleted file mode 100644 index 3f71241fa5..0000000000 --- a/src/Worker/UpdateGContact.php +++ /dev/null @@ -1,44 +0,0 @@ -. - * - */ - -namespace Friendica\Worker; - -use Friendica\Core\Logger; -use Friendica\DI; -use Friendica\Model\GContact; - -class UpdateGContact -{ - /** - * Update global contact via probe - * @param string $url Global contact url - * @param string $command - */ - public static function execute(string $url, string $command = '') - { - $force = ($command == "force"); - $nodiscover = ($command == "nodiscover"); - - $success = GContact::updateFromProbe($url, $force); - - Logger::info('Updated from probe', ['url' => $url, 'force' => $force, 'success' => $success]); - } -} diff --git a/src/Worker/UpdateGContacts.php b/src/Worker/UpdateGContacts.php deleted file mode 100644 index 9d9519241b..0000000000 --- a/src/Worker/UpdateGContacts.php +++ /dev/null @@ -1,101 +0,0 @@ -. - * - */ - -namespace Friendica\Worker; - -use Friendica\Core\Logger; -use Friendica\Core\Protocol; -use Friendica\Core\Worker; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\GContact; -use Friendica\Model\GServer; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; - -class UpdateGContacts -{ - /** - * Updates global contacts - */ - public static function execute() - { - if (!DI::config()->get('system', 'poco_completion')) { - return; - } - - Logger::info('Update global contacts'); - - $starttime = time(); - - $contacts = DBA::p("SELECT `url`, `created`, `updated`, `last_failure`, `last_contact`, `server_url`, `network` FROM `gcontact` - WHERE `last_contact` < UTC_TIMESTAMP - INTERVAL 1 MONTH AND - `last_failure` < UTC_TIMESTAMP - INTERVAL 1 MONTH AND - `network` IN (?, ?, ?, ?, ?, '') ORDER BY rand()", - Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, Protocol::FEED); - - $checked = 0; - - while ($contact = DBA::fetch($contacts)) { - $urlparts = parse_url($contact['url']); - if (empty($urlparts['scheme'])) { - DBA::update('gcontact', ['network' => Protocol::PHANTOM], - ['nurl' => Strings::normaliseLink($contact['url'])]); - continue; - } - - if (in_array($urlparts['host'], ['twitter.com', 'identi.ca'])) { - $networks = ['twitter.com' => Protocol::TWITTER, 'identi.ca' => Protocol::PUMPIO]; - - DBA::update('gcontact', ['network' => $networks[$urlparts['host']]], - ['nurl' => Strings::normaliseLink($contact['url'])]); - continue; - } - - $server_url = GContact::getBasepath($contact['url'], true); - $force_update = false; - - if (!empty($contact['server_url'])) { - $force_update = (Strings::normaliseLink($contact['server_url']) != Strings::normaliseLink($server_url)); - - $server_url = $contact['server_url']; - } - - if ((empty($server_url) && ($contact['network'] == Protocol::FEED)) || $force_update || GServer::check($server_url, $contact['network'])) { - Logger::info('Check profile', ['profile' => $contact['url']]); - Worker::add(PRIORITY_LOW, 'UpdateGContact', $contact['url'], 'force'); - - if (++$checked > 100) { - return; - } - } else { - DBA::update('gcontact', ['last_failure' => DateTimeFormat::utcNow()], - ['nurl' => Strings::normaliseLink($contact['url'])]); - } - - // Quit the loop after 3 minutes - if (time() > ($starttime + 180)) { - return; - } - } - DBA::close($contacts); - } -} diff --git a/src/Worker/UpdateServerDirectories.php b/src/Worker/UpdateServerDirectories.php index 74f75fcd76..ba5ef1017e 100644 --- a/src/Worker/UpdateServerDirectories.php +++ b/src/Worker/UpdateServerDirectories.php @@ -22,9 +22,7 @@ namespace Friendica\Worker; use Friendica\DI; -use Friendica\Model\GContact; use Friendica\Model\GServer; -use Friendica\Protocol\PortableContact; class UpdateServerDirectories { @@ -33,16 +31,10 @@ class UpdateServerDirectories */ public static function execute() { - if (DI::config()->get('system', 'poco_discovery') == PortableContact::DISABLED) { + if (!DI::config()->get('system', 'poco_discovery')) { return; } - // Query Friendica and Hubzilla servers for their users GServer::discover(); - - // Query GNU Social servers for their users ("statistics" addon has to be enabled on the GS server) - if (!DI::config()->get('system', 'ostatus_disabled')) { - GContact::discoverGsUsers(); - } } } diff --git a/src/Worker/UpdateServerDirectory.php b/src/Worker/UpdateServerDirectory.php index 87bbee7e8c..2066ce7cb9 100644 --- a/src/Worker/UpdateServerDirectory.php +++ b/src/Worker/UpdateServerDirectory.php @@ -22,17 +22,86 @@ namespace Friendica\Worker; use Friendica\Core\Logger; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Contact; use Friendica\Model\GServer; class UpdateServerDirectory { /** * Query the given server for their users - * @param string $gserver Server URL + * + * @param array $gserver Server record */ - public static function execute($gserver) + public static function execute(array $gserver) { - GServer::updateDirectory($gserver); - return; + $gserver = DBA::selectFirst('gserver', [], ['url' => $gserver['url']]); + if ($gserver['directory-type'] == GServer::DT_MASTODON) { + self::discoverMastodonDirectory($gserver); + } elseif (!empty($gserver['poco'])) { + self::discoverPoCo($gserver); + } + } + + private static function discoverPoCo(array $gserver) + { + $result = DI::httpRequest()->fetch($gserver['poco'] . '?fields=urls'); + if (empty($result)) { + Logger::info('Empty result', ['url' => $gserver['url']]); + return; + } + + $contacts = json_decode($result, true); + if (empty($contacts['entry'])) { + Logger::info('No contacts', ['url' => $gserver['url']]); + return; + } + + Logger::info('PoCo discovery started', ['poco' => $gserver['poco']]); + + $urls = []; + foreach ($contacts['entry'] as $entry) { + foreach ($entry['urls'] as $url_entry) { + if (empty($url_entry['type']) || empty($url_entry['value'])) { + continue; + } + if ($url_entry['type'] == 'profile') { + $urls[] = $url_entry['value']; + } + } + } + + $result = Contact::addContactsByArray($urls); + + Logger::info('PoCo discovery ended', ['count' => $result['count'], 'added' => $result['added'], 'updated' => $result['updated'], 'poco' => $gserver['poco']]); + } + + private static function discoverMastodonDirectory(array $gserver) + { + $result = DI::httpRequest()->fetch($gserver['url'] . '/api/v1/directory?order=new&local=true&limit=200&offset=0'); + if (empty($result)) { + Logger::info('Empty result', ['url' => $gserver['url']]); + return; + } + + $accounts = json_decode($result, true); + if (empty($accounts)) { + Logger::info('No contacts', ['url' => $gserver['url']]); + return; + } + + Logger::info('Account discovery started', ['url' => $gserver['url']]); + + $urls = []; + foreach ($accounts as $account) { + if (!empty($account['url'])) { + $urls[] = $account['url']; + } + } + + $result = Contact::addContactsByArray($urls); + + Logger::info('Account discovery ended', ['count' => $result['count'], 'added' => $result['added'], 'updated' => $result['updated'], 'url' => $gserver['url']]); } } diff --git a/src/Worker/UpdateServerPeers.php b/src/Worker/UpdateServerPeers.php index ff0cdfa730..ca45344459 100644 --- a/src/Worker/UpdateServerPeers.php +++ b/src/Worker/UpdateServerPeers.php @@ -63,4 +63,41 @@ class UpdateServerPeers } Logger::info('Server peer update ended', ['total' => $total, 'added' => $added, 'url' => $url]); } + + /** + * Fetch server list from remote servers and adds them when they are new. + * + * @param string $poco URL to the POCO endpoint + */ + private static function fetchServerlist($poco) + { + $curlResult = DI::httpRequest()->get($poco . '/@server'); + if (!$curlResult->isSuccess()) { + Logger::info('Server is not reachable or does not offer the "poco" endpoint', ['poco' => $poco]); + return; + } + + $serverlist = json_decode($curlResult->getBody(), true); + if (!is_array($serverlist)) { + Logger::info('Server does not have any servers listed', ['poco' => $poco]); + return; + } + + Logger::info('PoCo Server update start', ['poco' => $poco]); + + $total = 0; + $added = 0; + foreach ($serverlist as $server) { + ++$total; + if (DBA::exists('gserver', ['nurl' => Strings::normaliseLink($server['url'])])) { + // We already know this server + continue; + } + // This endpoint doesn't offer the schema. So we assume that it is HTTPS. + Worker::add(PRIORITY_LOW, 'UpdateGServer', $server['url']); + ++$added; + } + + Logger::info('PoCo Server update ended', ['total' => $total, 'added' => $added, 'poco' => $poco]); + } } diff --git a/src/Worker/UpdateSuggestions.php b/src/Worker/UpdateSuggestions.php deleted file mode 100644 index 103a3cf4ca..0000000000 --- a/src/Worker/UpdateSuggestions.php +++ /dev/null @@ -1,36 +0,0 @@ -. - * - */ - -namespace Friendica\Worker; - -use Friendica\Core\Logger; -use Friendica\Model\GContact; - -class UpdateSuggestions -{ - /** - * Discover other servers for their contacts. - */ - public static function execute() - { - GContact::updateSuggestions(); - } -} diff --git a/update.php b/update.php index a4d71bc4fd..f335b5292e 100644 --- a/update.php +++ b/update.php @@ -48,7 +48,6 @@ use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Model\Contact; -use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\User; use Friendica\Model\Storage; @@ -315,7 +314,6 @@ function update_1298() 'was' => $data[$translateKey]]); Worker::add(PRIORITY_LOW, 'ProfileUpdate', $data['id']); Contact::updateSelfFromUserID($data['id']); - GContact::updateForUser($data['id']); $success++; } } @@ -562,16 +560,5 @@ function update_1357() return Update::FAILED; } - if (!DBA::e("UPDATE `gcontact` SET `failed` = true WHERE `last_contact` < `last_failure` AND `failed` IS NULL")) { - return Update::FAILED; - } - - if (!DBA::e("UPDATE `gcontact` SET `failed` = false WHERE `last_contact` > `last_failure` AND `failed` IS NULL")) { - return Update::FAILED; - } - - if (!DBA::e("UPDATE `gcontact` SET `failed` = false WHERE `updated` > `last_failure` AND `failed` IS NULL")) { - return Update::FAILED; - } return Update::SUCCESS; } diff --git a/view/templates/admin/federation.tpl b/view/templates/admin/federation.tpl index 177d5406f6..37e3cb847a 100644 --- a/view/templates/admin/federation.tpl +++ b/view/templates/admin/federation.tpl @@ -5,10 +5,6 @@

{{$intro}}

- {{if not $autoactive}} -

{{$hint nofilter}}

- {{/if}} -

{{$legendtext}}