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;
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;
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']),
}
$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),
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;
Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user());
- // Update the global contact for the user
- GContact::updateForUser(local_user());
-
DI::baseUrl()->redirect('settings');
return; // NOTREACHED
}
$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);
if (!self::update1349()) {
return false;
}
- if (!self::update1350()) {
- return false;
- }
return true;
}
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;
- }
}
$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 ?, ?",
// 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([
'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'],
]);
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;
- }
}
namespace Friendica\Model;
+use DOMDocument;
+use DOMXPath;
use Friendica\App\BaseURL;
use Friendica\Content\Pager;
use Friendica\Core\Hook;
/**
* 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
*/
DBA::update('contact', ['archive' => true], ['id' => $contact['id']]);
DBA::update('contact', ['archive' => true], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
- GContact::updateFromPublicContactURL($contact['url']);
}
}
}
$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']);
}
/**
$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"] = '';
// 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']))
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]);
$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
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'])]);
+ }
}
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
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
*
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;
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 == '') {
}
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]);
{
$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'];
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
*/
$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` != ?
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);
+ }
}
// 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 <em>Auto Discovered Contact Directory</em> 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');
'$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),
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;
// 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);
$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']);
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);
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 = [];
$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 <a href="http://the-federation.info/">the-federation.info</a> for details.')],
// 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']);
}
/**
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
{
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;
}
Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user());
-
- // Update the global contact for the user
- GContact::updateForUser(local_user());
}
public static function content(array $parameters = [])
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;
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;
$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"]),
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;
// 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.");
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;
}
DBA::close($contacts);
- DBA::delete('gcontact', ['addr' => $author]);
-
Logger::log('Removed contacts for ' . $author);
return true;
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;
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;
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 = [];
}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\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;
- }
-}
// 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");
Worker::add(PRIORITY_LOW, 'UpdateGServers');
- Worker::add(PRIORITY_LOW, 'UpdateSuggestions');
-
Worker::add(PRIORITY_LOW, 'Expire');
Worker::add(PRIORITY_MEDIUM, 'DBClean');
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;
// 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
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Worker;
-
-use Friendica\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);
- }
-}
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;
$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');
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]);
}
}
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
{
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);
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Worker;
-
-use Friendica\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]);
- }
-}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Worker;
-
-use Friendica\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);
- }
-}
namespace Friendica\Worker;
use Friendica\DI;
-use Friendica\Model\GContact;
use Friendica\Model\GServer;
-use Friendica\Protocol\PortableContact;
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();
- }
}
}
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']]);
}
}
}
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]);
+ }
}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Worker;
-
-use Friendica\Core\Logger;
-use Friendica\Model\GContact;
-
-class UpdateSuggestions
-{
- /**
- * Discover other servers for their contacts.
- */
- public static function execute()
- {
- GContact::updateSuggestions();
- }
-}
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;
'was' => $data[$translateKey]]);
Worker::add(PRIORITY_LOW, 'ProfileUpdate', $data['id']);
Contact::updateSelfFromUserID($data['id']);
- GContact::updateForUser($data['id']);
$success++;
}
}
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;
}
<canvas id="FederationChart" class="federation-graph" width="320" height="320"></canvas>
<p>{{$intro}}</p>
- {{if not $autoactive}}
- <p class="error-message">{{$hint nofilter}}</p>
- {{/if}}
-
<p>{{$legendtext}}</p>
<ul>
<h2>{{$portable_contacts}}</h2>
{{include file="field_select.tpl" field=$contact_discovery}}
{{include file="field_checkbox.tpl" field=$synchronize_directory}}
- {{include file="field_checkbox.tpl" field=$poco_completion}}
{{include file="field_input.tpl" field=$poco_requery_days}}
- {{include file="field_select.tpl" field=$poco_discovery}}
- {{include file="field_select.tpl" field=$poco_discovery_since}}
+ {{include file="field_checkbox.tpl" field=$poco_discovery}}
{{include file="field_checkbox.tpl" field=$poco_local_search}}
<div class="submit"><input type="submit" name="page_site" value="{{$submit}}"/></div>
<div class="panel-body">
{{include file="field_select.tpl" field=$contact_discovery}}
{{include file="field_checkbox.tpl" field=$synchronize_directory}}
- {{include file="field_checkbox.tpl" field=$poco_completion}}
+ {{include file="field_checkbox.tpl" field=$poco_discovery}}
{{include file="field_input.tpl" field=$poco_requery_days}}
- {{include file="field_select.tpl" field=$poco_discovery}}
- {{include file="field_select.tpl" field=$poco_discovery_since}}
{{include file="field_checkbox.tpl" field=$poco_local_search}}
</div>
<div class="panel-footer">
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
-use Friendica\Model\GContact;
use Friendica\Util\Strings;
function vier_init(App $a)