Merge pull request #8960 from annando/remove-poco

Poco and gcontact (mostly) removed
This commit is contained in:
Hypolite Petovan 2020-08-03 11:01:40 -04:00 committed by GitHub
commit e033bcd2d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 403 additions and 2099 deletions

View file

@ -27,7 +27,7 @@ use Friendica\Core\Protocol;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Protocol\PortableContact;
use Friendica\Model\GServer;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
use Friendica\Util\XML;
@ -56,16 +56,15 @@ function poco_init(App $a) {
if ($a->argc > 1 && $a->argv[1] === '@server') {
// List of all servers that this server knows
$ret = PortableContact::serverlist();
$ret = GServer::getActive();
header('Content-type: application/json');
echo json_encode($ret);
exit();
}
if ($a->argc > 1 && $a->argv[1] === '@global') {
// List of all profiles that this server recently had data from
$global = true;
$update_limit = date(DateTimeFormat::MYSQL, time() - 30 * 86400);
// Global is not supported anymore
throw new \Friendica\Network\HTTPException\NotFoundException();
}
if ($a->argc > 2 && $a->argv[2] === '@me') {
$justme = true;
@ -99,17 +98,10 @@ function poco_init(App $a) {
if (!empty($_GET['updatedSince'])) {
$update_limit = date(DateTimeFormat::MYSQL, strtotime($_GET['updatedSince']));
}
if ($global) {
$contacts = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND `updated` >= `last_failure` AND NOT `hide` AND `network` IN ('%s', '%s', '%s')",
DBA::escape($update_limit),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::DIASPORA),
DBA::escape(Protocol::OSTATUS)
);
} elseif ($system_mode) {
if ($system_mode) {
$totalResults = DBA::count('profile', ['net-publish' => true]);
} else {
$contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
$contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND NOT `blocked` AND NOT `pending` AND NOT `unsearchable` AND NOT `archive`
AND NOT `failed`
AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra",
intval($user['uid']),
@ -131,24 +123,13 @@ function poco_init(App $a) {
}
$itemsPerPage = ((!empty($_GET['count'])) ? intval($_GET['count']) : $totalResults);
if ($global) {
Logger::log("Start global query", Logger::DEBUG);
$contacts = q("SELECT * FROM `gcontact` WHERE `updated` > '%s' AND NOT `hide` AND `network` IN ('%s', '%s', '%s') AND `updated` > `last_failure`
ORDER BY `updated` DESC LIMIT %d, %d",
DBA::escape($update_limit),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::DIASPORA),
DBA::escape(Protocol::OSTATUS),
intval($startIndex),
intval($itemsPerPage)
);
} elseif ($system_mode) {
if ($system_mode) {
Logger::log("Start system mode query", Logger::DEBUG);
$contacts = DBA::selectToArray('owner-view', [], ['net-publish' => true], ['limit' => [$startIndex, $itemsPerPage]]);
} else {
Logger::log("Start query for user " . $user['nickname'], Logger::DEBUG);
$contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
AND NOT `failed`
$contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND NOT `blocked` AND NOT `pending` AND NOT `hiddden` AND NOT `archive`
AND NOT `failed` AND NOT `unsearchable`
AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra LIMIT %d, %d",
intval($user['uid']),
DBA::escape(Protocol::DFRN),

View file

@ -31,7 +31,6 @@ use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\Group;
use Friendica\Model\Notify\Type;
use Friendica\Model\User;
@ -471,9 +470,6 @@ function settings_post(App $a)
Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user());
// Update the global contact for the user
GContact::updateForUser(local_user());
DI::baseUrl()->redirect('settings');
return; // NOTREACHED
}

View file

@ -76,14 +76,6 @@ class ContactSelector
$server_url = Strings::normaliseLink($contact['baseurl']);
}
if (empty($server_url)) {
// Fetch the server url from the gcontact table
$gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($profile)]);
if (!empty($gcontact) && !empty($gcontact['server_url'])) {
$server_url = Strings::normaliseLink($gcontact['server_url']);
}
}
if (empty($server_url)) {
// Create the server url out of the profile url
$parts = parse_url($profile);

View file

@ -93,9 +93,6 @@ class PostUpdate
if (!self::update1349()) {
return false;
}
if (!self::update1350()) {
return false;
}
return true;
}
@ -997,57 +994,4 @@ class PostUpdate
return false;
}
/**
* update the "gsid" (global server id) field in the gcontact table
*
* @return bool "true" when the job is done
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function update1350()
{
// Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1350) {
return true;
}
$id = DI::config()->get("system", "post_update_version_1350_id", 0);
Logger::info('Start', ['gcontact' => $id]);
$start_id = $id;
$rows = 0;
$condition = ["`id` > ? AND `gsid` IS NULL AND `server_url` != '' AND NOT `server_url` IS NULL", $id];
$params = ['order' => ['id'], 'limit' => 10000];
$gcontacts = DBA::select('gcontact', ['id', 'server_url'], $condition, $params);
if (DBA::errorNo() != 0) {
Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
return false;
}
while ($gcontact = DBA::fetch($gcontacts)) {
$id = $gcontact['id'];
DBA::update('gcontact',
['gsid' => GServer::getID($gcontact['server_url'], true), 'server_url' => GServer::cleanURL($gcontact['server_url'])],
['id' => $gcontact['id']]);
++$rows;
}
DBA::close($gcontacts);
DI::config()->set("system", "post_update_version_1350_id", $id);
Logger::info('Processed', ['rows' => $rows, 'last' => $id]);
if ($start_id == $id) {
DI::config()->set("system", "post_update_version", 1350);
Logger::info('Done');
return true;
}
return false;
}
}

View file

@ -99,17 +99,13 @@ class Introduction extends BaseFactory
$formattedNotifications = [];
try {
/// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact, fcontact and gcontact
/// @todo Fetch contact details by "Contact::getByUrl" instead of queries to contact and fcontact
$stmtNotifications = $this->dba->p(
"SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*,
`fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`, `fcontact`.`addr` AS `faddr`,
`fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest`,
`gcontact`.`location` AS `glocation`, `gcontact`.`about` AS `gabout`,
`gcontact`.`keywords` AS `gkeywords`,
`gcontact`.`network` AS `gnetwork`, `gcontact`.`addr` AS `gaddr`
`fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest`
FROM `intro`
LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id`
LEFT JOIN `gcontact` ON `gcontact`.`nurl` = `contact`.`nurl`
LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id`
WHERE `intro`.`uid` = ? $sql_extra
LIMIT ?, ?",
@ -147,16 +143,14 @@ class Introduction extends BaseFactory
// Normal connection requests
} else {
$notification = $this->getMissingData($notification);
if (empty($notification['url'])) {
continue;
}
// Don't show these data until you are connected. Diaspora is doing the same.
if ($notification['gnetwork'] === Protocol::DIASPORA) {
$notification['glocation'] = "";
$notification['gabout'] = "";
if ($notification['network'] === Protocol::DIASPORA) {
$notification['location'] = "";
$notification['about'] = "";
}
$formattedNotifications[] = new Notification\Introduction([
@ -166,17 +160,17 @@ class Introduction extends BaseFactory
'uid' => $this->session->get('uid'),
'intro_id' => $notification['intro_id'],
'contact_id' => $notification['contact-id'],
'photo' => (!empty($notification['photo']) ? Proxy::proxifyUrl($notification['photo'], false, Proxy::SIZE_SMALL) : "images/person-300.jpg"),
'photo' => Contact::getPhoto($notification),
'name' => $notification['name'],
'location' => BBCode::convert($notification['glocation'], false),
'about' => BBCode::convert($notification['gabout'], false),
'keywords' => $notification['gkeywords'],
'location' => BBCode::convert($notification['location'], false),
'about' => BBCode::convert($notification['about'], false),
'keywords' => $notification['keywords'],
'hidden' => $notification['hidden'] == 1,
'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0),
'url' => $notification['url'],
'zrl' => Contact::magicLink($notification['url']),
'addr' => $notification['gaddr'],
'network' => $notification['gnetwork'],
'addr' => $notification['addr'],
'network' => $notification['network'],
'knowyou' => $notification['knowyou'],
'note' => $notification['note'],
]);
@ -188,41 +182,4 @@ class Introduction extends BaseFactory
return $formattedNotifications;
}
/**
* Check for missing contact data and try to fetch the data from
* from other sources
*
* @param array $intro The input array with the intro data
*
* @return array The array with the intro data
*
* @throws InternalServerErrorException
*/
private function getMissingData(array $intro)
{
// If the network and the addr isn't available from the gcontact
// table entry, take the one of the contact table entry
if (empty($intro['gnetwork']) && !empty($intro['network'])) {
$intro['gnetwork'] = $intro['network'];
}
if (empty($intro['gaddr']) && !empty($intro['addr'])) {
$intro['gaddr'] = $intro['addr'];
}
// If the network and addr is still not available
// get the missing data data from other sources
if (empty($intro['gnetwork']) || empty($intro['gaddr'])) {
$ret = Contact::getByURL($intro['url'], false, ['network', 'addr']);
if (empty($intro['gnetwork']) && !empty($ret['network'])) {
$intro['gnetwork'] = $ret['network'];
}
if (empty($intro['gaddr']) && !empty($ret['addr'])) {
$intro['gaddr'] = $ret['addr'];
}
}
return $intro;
}
}

View file

@ -21,6 +21,8 @@
namespace Friendica\Model;
use DOMDocument;
use DOMXPath;
use Friendica\App\BaseURL;
use Friendica\Content\Pager;
use Friendica\Core\Hook;
@ -88,7 +90,7 @@ class Contact
/**
* Account types
*
* TYPE_UNKNOWN - the account has been imported from gcontact where this is the default type value
* TYPE_UNKNOWN - unknown type
*
* TYPE_PERSON - the account belongs to a person
* Associated page types: PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE
@ -1021,7 +1023,6 @@ class Contact
*/
DBA::update('contact', ['archive' => true], ['id' => $contact['id']]);
DBA::update('contact', ['archive' => true], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
GContact::updateFromPublicContactURL($contact['url']);
}
}
}
@ -1066,7 +1067,6 @@ class Contact
$fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false];
DBA::update('contact', $fields, ['id' => $contact['id']]);
DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
GContact::updateFromPublicContactURL($contact['url']);
}
/**
@ -1269,12 +1269,8 @@ class Contact
$fields = ['url', 'addr', 'alias', 'notify', 'name', 'nick',
'photo', 'keywords', 'location', 'about', 'network'];
$data = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($url)]);
if (!DBA::isResult($data)) {
$condition = ['alias' => [$url, Strings::normaliseLink($url), $ssl_url]];
$data = DBA::selectFirst('contact', $fields, $condition);
}
$condition = ['alias' => [$url, Strings::normaliseLink($url), $ssl_url]];
$data = DBA::selectFirst('contact', $fields, $condition);
if (DBA::isResult($data)) {
$data["pubkey"] = '';
@ -1706,7 +1702,6 @@ class Contact
// There are several fields that indicate that the contact or user is a forum
// "page-flags" is a field in the user table,
// "forum" and "prv" are used in the contact table. They stand for User::PAGE_FLAGS_COMMUNITY and User::PAGE_FLAGS_PRVGROUP.
// "community" is used in the gcontact table and is true if the contact is User::PAGE_FLAGS_COMMUNITY or User::PAGE_FLAGS_PRVGROUP.
if ((isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_COMMUNITY))
|| (isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_PRVGROUP))
|| (isset($contact['forum']) && intval($contact['forum']))
@ -1994,9 +1989,6 @@ class Contact
return;
}
// Update the corresponding gcontact entry
GContact::updateFromPublicContactID($id);
// Archive or unarchive the contact. We only need to do this for the public contact.
// The archive/unarchive function will update the personal contacts by themselves.
$contact = DBA::selectFirst('contact', [], ['id' => $id]);
@ -2155,11 +2147,6 @@ class Contact
$new_pubkey = $ret['pubkey'];
// Update the gcontact entry
if ($uid == 0) {
GContact::updateFromPublicContactID($id);
}
$update = false;
// make sure to not overwrite existing values with blank entries except some technical fields
@ -3068,4 +3055,228 @@ class Contact
return array_slice($contacts, $start, $limit);
}
/**
* Add public contacts from an array
*
* @param array $urls
* @return array result "count", "added" and "updated"
*/
public static function addByUrls(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'])]);
}
/**
* Returns a random, global contact of the current node
*
* @return string The profile URL
* @throws Exception
*/
public static function getRandomUrl()
{
$r = DBA::selectFirst('contact', ['url'], [
"`uid` = ? AND `network` = ? AND NOT `failed` AND `last-item` > ?",
0, Protocol::DFRN, DateTimeFormat::utc('now - 1 month'),
], ['order' => ['RAND()']]);
if (DBA::isResult($r)) {
return $r['url'];
}
return '';
}
}

View file

@ -21,171 +21,16 @@
namespace Friendica\Model;
use DOMDocument;
use DOMXPath;
use Exception;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\Search;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Network\Probe;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\PortableContact;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
/**
* This class handles GlobalContact related functions
*/
class GContact
{
/**
* Link the gcontact entry with user, contact and global contact
*
* @param integer $gcid Global contact ID
* @param integer $uid User ID
* @param integer $cid Contact ID
* @param integer $zcid Global Contact ID
* @return void
* @throws Exception
*/
public static function link($gcid, $uid = 0, $cid = 0, $zcid = 0)
{
if ($gcid <= 0) {
return;
}
$condition = ['cid' => $cid, 'uid' => $uid, 'gcid' => $gcid, 'zcid' => $zcid];
DBA::update('glink', ['updated' => DateTimeFormat::utcNow()], $condition, true);
}
/**
* Sanitize the given gcontact data
*
* Generation:
* 0: No definition
* 1: Profiles on this server
* 2: Contacts of profiles on this server
* 3: Contacts of contacts of profiles on this server
* 4: ...
*
* @param array $gcontact array with gcontact data
* @return array $gcontact
* @throws Exception
*/
public static function sanitize($gcontact)
{
if (empty($gcontact['url'])) {
throw new Exception('URL is empty');
}
$gcontact['server_url'] = $gcontact['server_url'] ?? '';
$urlparts = parse_url($gcontact['url']);
if (empty($urlparts['scheme'])) {
throw new Exception('This (' . $gcontact['url'] . ") doesn't seem to be an url.");
}
if (in_array($urlparts['host'], ['twitter.com', 'identi.ca'])) {
throw new Exception('Contact from a non federated network ignored. (' . $gcontact['url'] . ')');
}
// Don't store the statusnet connector as network
// We can't simply set this to Protocol::OSTATUS since the connector could have fetched posts from friendica as well
if ($gcontact['network'] == Protocol::STATUSNET) {
$gcontact['network'] = '';
}
// Assure that there are no parameter fragments in the profile url
if (empty($gcontact['*network']) || in_array($gcontact['network'], Protocol::FEDERATED)) {
$gcontact['url'] = self::cleanContactUrl($gcontact['url']);
}
// The global contacts should contain the original picture, not the cached one
if (($gcontact['generation'] != 1) && stristr(Strings::normaliseLink($gcontact['photo']), Strings::normaliseLink(DI::baseUrl() . '/photo/'))) {
$gcontact['photo'] = '';
}
if (empty($gcontact['network'])) {
$gcontact['network'] = '';
$condition = ["`uid` = 0 AND `nurl` = ? AND `network` != '' AND `network` != ?",
Strings::normaliseLink($gcontact['url']), Protocol::STATUSNET];
$contact = DBA::selectFirst('contact', ['network'], $condition);
if (DBA::isResult($contact)) {
$gcontact['network'] = $contact['network'];
}
if (($gcontact['network'] == '') || ($gcontact['network'] == Protocol::OSTATUS)) {
$condition = ["`uid` = 0 AND `alias` IN (?, ?) AND `network` != '' AND `network` != ?",
$gcontact['url'], Strings::normaliseLink($gcontact['url']), Protocol::STATUSNET];
$contact = DBA::selectFirst('contact', ['network'], $condition);
if (DBA::isResult($contact)) {
$gcontact['network'] = $contact['network'];
}
}
}
$fields = ['network', 'updated', 'server_url', 'url', 'addr'];
$gcnt = DBA::selectFirst('gcontact', $fields, ['nurl' => Strings::normaliseLink($gcontact['url'])]);
if (DBA::isResult($gcnt)) {
if (!isset($gcontact['network']) && ($gcnt['network'] != Protocol::STATUSNET)) {
$gcontact['network'] = $gcnt['network'];
}
if ($gcontact['updated'] <= DBA::NULL_DATETIME) {
$gcontact['updated'] = $gcnt['updated'];
}
if (!isset($gcontact['server_url']) && (Strings::normaliseLink($gcnt['server_url']) != Strings::normaliseLink($gcnt['url']))) {
$gcontact['server_url'] = $gcnt['server_url'];
}
if (!isset($gcontact['addr'])) {
$gcontact['addr'] = $gcnt['addr'];
}
}
if ((!isset($gcontact['network']) || !isset($gcontact['name']) || !isset($gcontact['addr']) || !isset($gcontact['photo']) || !isset($gcontact['server_url']))
&& GServer::reachable($gcontact['url'], $gcontact['server_url'], $gcontact['network'], false)
) {
$data = Probe::uri($gcontact['url']);
if ($data['network'] == Protocol::PHANTOM) {
throw new Exception('Probing for URL ' . $gcontact['url'] . ' failed');
}
$gcontact['server_url'] = $data['baseurl'];
$gcontact['failed'] = false;
$gcontact = array_merge($gcontact, $data);
}
if (!isset($gcontact['name']) || !isset($gcontact['photo'])) {
throw new Exception('No name and photo for URL '.$gcontact['url']);
}
if (!in_array($gcontact['network'], Protocol::FEDERATED)) {
throw new Exception('No federated network (' . $gcontact['network'] . ') detected for URL ' . $gcontact['url']);
}
if (empty($gcontact['server_url'])) {
// We check the server url to be sure that it is a real one
$server_url = self::getBasepath($gcontact['url']);
// We are now sure that it is a correct URL. So we use it in the future
if ($server_url != '') {
$gcontact['server_url'] = $server_url;
}
}
// The server URL doesn't seem to be valid, so we don't store it.
if (!GServer::check($gcontact['server_url'], $gcontact['network'])) {
$gcontact['server_url'] = '';
}
return $gcontact;
}
/**
* @param integer $uid id
* @param integer $cid id
@ -360,792 +205,4 @@ class GContact
/// @TODO Check all calling-findings of this function if they properly use DBA::isResult()
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
*
* @return string The profile URL
* @throws Exception
*/
public static function getRandomUrl()
{
$r = DBA::selectFirst('gcontact', ['url'], [
'`network` = ?
AND NOT `failed`
AND `updated` > ?',
Protocol::DFRN,
DateTimeFormat::utc('now - 1 month'),
], ['order' => ['RAND()']]);
if (DBA::isResult($r)) {
return $r['url'];
}
return '';
}
}

View file

@ -32,7 +32,6 @@ use Friendica\DI;
use Friendica\Module\Register;
use Friendica\Network\CurlResult;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\PortableContact;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Strings;
@ -111,7 +110,10 @@ class GServer
public static function reachable(string $profile, string $server = '', string $network = '', bool $force = false)
{
if ($server == '') {
$server = GContact::getBasepath($profile);
$contact = Contact::getByURL($profile, null, ['baseurl']);
if (!empty($contact['baseurl'])) {
$server = $contact['baseurl'];
}
}
if ($server == '') {
@ -471,10 +473,9 @@ class GServer
}
if (!empty($serverdata['network']) && !empty($id) && ($serverdata['network'] != Protocol::PHANTOM)) {
$gcontacts = DBA::count('gcontact', ['gsid' => $id]);
$apcontacts = DBA::count('apcontact', ['gsid' => $id]);
$contacts = DBA::count('contact', ['uid' => 0, 'gsid' => $id]);
$max_users = max($gcontacts, $apcontacts, $contacts, $registeredUsers);
$max_users = max($apcontacts, $contacts, $registeredUsers);
if ($max_users > $registeredUsers) {
Logger::info('Update registered users', ['id' => $id, 'url' => $serverdata['nurl'], 'registered-users' => $max_users]);
DBA::update('gserver', ['registered-users' => $max_users], ['id' => $id]);
@ -959,12 +960,6 @@ class GServer
{
$contacts = [];
$gcontacts = DBA::select('gcontact', ['url', 'nurl'], ['server_url' => [$url, $serverdata['nurl']]]);
while ($gcontact = DBA::fetch($gcontacts)) {
$contacts[$gcontact['nurl']] = $gcontact['url'];
}
DBA::close($gcontacts);
$apcontacts = DBA::select('apcontact', ['url'], ['baseurl' => [$url, $serverdata['nurl']]]);
while ($apcontact = DBA::fetch($apcontacts)) {
$contacts[Strings::normaliseLink($apcontact['url'])] = $apcontact['url'];
@ -1556,20 +1551,6 @@ class GServer
return !strpos($body, '>');
}
/**
* Update the user directory of a given gserver record
*
* @param array $gserver gserver record
*/
public static function updateDirectory(array $gserver)
{
/// @todo Add Mastodon API directory
if (!empty($gserver['poco'])) {
PortableContact::discoverSingleServer($gserver['id']);
}
}
/**
* Update GServer entries
*/
@ -1588,7 +1569,7 @@ class GServer
$last_update = date('c', time() - (60 * 60 * 24 * $requery_days));
$gservers = DBA::p("SELECT `id`, `url`, `nurl`, `network`, `poco`
$gservers = DBA::p("SELECT `id`, `url`, `nurl`, `network`, `poco`, `directory-type`
FROM `gserver`
WHERE NOT `failed`
AND `directory-type` != ?
@ -1672,4 +1653,18 @@ class GServer
DI::config()->set('poco', 'last_federation_discovery', time());
}
/**
* Returns a list of 1,000 active servers order by the last contact
*
* @return array List of server urls
* @throws Exception
*/
public static function getActive()
{
$result = DBA::p("SELECT `url`, `site_name` AS `displayName`, `network`, `platform`, `version` FROM `gserver`
WHERE `network` IN (?, ?, ?, ?) AND NOT `failed` ORDER BY `last_contact` LIMIT ?",
Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, 1000);
return DBA::toArray($result);
}
}

View file

@ -133,7 +133,6 @@ class Federation extends BaseAdmin
// some helpful text
$intro = DI::l10n()->t('This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of.');
$hint = DI::l10n()->t('The <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');
@ -141,8 +140,6 @@ class Federation extends BaseAdmin
'$title' => DI::l10n()->t('Administration'),
'$page' => DI::l10n()->t('Federation Statistics'),
'$intro' => $intro,
'$hint' => $hint,
'$autoactive' => DI::config()->get('system', 'poco_completion'),
'$counts' => $counts,
'$version' => FRIENDICA_VERSION,
'$legendtext' => DI::l10n()->t('Currently this node is aware of %d nodes with %d registered users from the following platforms:', $total, $users),

View file

@ -31,7 +31,6 @@ use Friendica\DI;
use Friendica\Model\ContactRelation;
use Friendica\Module\BaseAdmin;
use Friendica\Module\Register;
use Friendica\Protocol\PortableContact;
use Friendica\Util\BasePath;
use Friendica\Util\EMailer\MailBuilder;
use Friendica\Util\Strings;
@ -104,12 +103,10 @@ class Site extends BaseAdmin
// update profile links in the format "http://server.tld"
update_table($a, "profile", ['photo', 'thumb'], $old_url, $new_url);
update_table($a, "contact", ['photo', 'thumb', 'micro', 'url', 'nurl', 'alias', 'request', 'notify', 'poll', 'confirm', 'poco', 'avatar'], $old_url, $new_url);
update_table($a, "gcontact", ['url', 'nurl', 'photo', 'server_url', 'notify', 'alias'], $old_url, $new_url);
update_table($a, "item", ['owner-link', 'author-link', 'body', 'plink', 'tag'], $old_url, $new_url);
// update profile addresses in the format "user@server.tld"
update_table($a, "contact", ['addr'], $old_host, $new_host);
update_table($a, "gcontact", ['connect', 'addr'], $old_host, $new_host);
// update config
DI::config()->set('system', 'url', $new_url);
@ -180,10 +177,8 @@ class Site extends BaseAdmin
$optimize_fragmentation = (!empty($_POST['optimize_fragmentation']) ? intval(trim($_POST['optimize_fragmentation'])) : 30);
$contact_discovery = (!empty($_POST['contact_discovery']) ? intval(trim($_POST['contact_discovery'])) : ContactRelation::DISCOVERY_NONE);
$synchronize_directory = (!empty($_POST['synchronize_directory']) ? intval(trim($_POST['synchronize_directory'])) : false);
$poco_completion = (!empty($_POST['poco_completion']) ? intval(trim($_POST['poco_completion'])) : false);
$poco_requery_days = (!empty($_POST['poco_requery_days']) ? intval(trim($_POST['poco_requery_days'])) : 7);
$poco_discovery = (!empty($_POST['poco_discovery']) ? intval(trim($_POST['poco_discovery'])) : PortableContact::DISABLED);
$poco_discovery_since = (!empty($_POST['poco_discovery_since']) ? intval(trim($_POST['poco_discovery_since'])) : 30);
$poco_discovery = (!empty($_POST['poco_discovery']) ? intval(trim($_POST['poco_discovery'])) : false);
$poco_local_search = !empty($_POST['poco_local_search']);
$nodeinfo = !empty($_POST['nodeinfo']);
$dfrn_only = !empty($_POST['dfrn_only']);
@ -308,12 +303,10 @@ class Site extends BaseAdmin
DI::config()->set('system', 'min_memory' , $min_memory);
DI::config()->set('system', 'optimize_max_tablesize', $optimize_max_tablesize);
DI::config()->set('system', 'optimize_fragmentation', $optimize_fragmentation);
DI::config()->set('system', 'poco_completion' , $poco_completion);
DI::config()->set('system', 'contact_discovery' , $contact_discovery);
DI::config()->set('system', 'synchronize_directory' , $synchronize_directory);
DI::config()->set('system', 'poco_requery_days' , $poco_requery_days);
DI::config()->set('system', 'poco_discovery' , $poco_discovery);
DI::config()->set('system', 'poco_discovery_since' , $poco_discovery_since);
DI::config()->set('system', 'poco_local_search' , $poco_local_search);
DI::config()->set('system', 'nodeinfo' , $nodeinfo);
DI::config()->set('config', 'sitename' , $sitename);
@ -490,20 +483,6 @@ class Site extends BaseAdmin
CP_USERS_AND_GLOBAL => DI::l10n()->t('Public postings from local users and the federated network')
];
$poco_discovery_choices = [
PortableContact::DISABLED => DI::l10n()->t('Disabled'),
PortableContact::USERS => DI::l10n()->t('Users'),
PortableContact::USERS_GCONTACTS => DI::l10n()->t('Users, Global Contacts'),
PortableContact::USERS_GCONTACTS_FALLBACK => DI::l10n()->t('Users, Global Contacts/fallback'),
];
$poco_discovery_since_choices = [
'30' => DI::l10n()->t('One month'),
'91' => DI::l10n()->t('Three months'),
'182' => DI::l10n()->t('Half a year'),
'365' => DI::l10n()->t('One year'),
];
/* get user names to make the install a personal install of X */
// @TODO Move to Model\User::getNames()
$user_names = [];
@ -688,10 +667,8 @@ class Site extends BaseAdmin
$discovery_choices],
'$synchronize_directory' => ['synchronize_directory', DI::l10n()->t('Synchronize the contacts with the directory server'), DI::config()->get('system', 'synchronize_directory'), DI::l10n()->t('if enabled, the system will check periodically for new contacts on the defined directory server.')],
'$poco_completion' => ['poco_completion', DI::l10n()->t('Periodical check of global contacts'), DI::config()->get('system', 'poco_completion'), DI::l10n()->t('If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers.')],
'$poco_requery_days' => ['poco_requery_days', DI::l10n()->t('Days between requery'), DI::config()->get('system', 'poco_requery_days'), DI::l10n()->t('Number of days after which a server is requeried for his contacts.')],
'$poco_discovery' => ['poco_discovery', DI::l10n()->t('Discover contacts from other servers'), DI::config()->get('system', 'poco_discovery'), DI::l10n()->t('Periodically query other servers for contacts. You can choose between "Users": the users on the remote system, "Global Contacts": active contacts that are known on the system. The fallback is meant for Redmatrix servers and older friendica servers, where global contacts weren\'t available. The fallback increases the server load, so the recommended setting is "Users, Global Contacts".'), $poco_discovery_choices],
'$poco_discovery_since' => ['poco_discovery_since', DI::l10n()->t('Timeframe for fetching global contacts'), DI::config()->get('system', 'poco_discovery_since'), DI::l10n()->t('When the discovery is activated, this value defines the timeframe for the activity of the global contacts that are fetched from other servers.'), $poco_discovery_since_choices],
'$poco_discovery' => ['poco_discovery', DI::l10n()->t('Discover contacts from other servers'), DI::config()->get('system', 'poco_discovery'), DI::l10n()->t('Periodically query other servers for contacts. The system queries Friendica, Mastodon and Hubzilla servers.')],
'$poco_local_search' => ['poco_local_search', DI::l10n()->t('Search the local directory'), DI::config()->get('system', 'poco_local_search'), DI::l10n()->t('Search the local directory instead of the global directory. When searching locally, every search will be executed on the global directory in the background. This improves the search results when the search is repeated.')],
'$nodeinfo' => ['nodeinfo', DI::l10n()->t('Publish server information'), DI::config()->get('system', 'nodeinfo'), DI::l10n()->t('If enabled, general server and usage data will be published. The data contains the name and version of the server, number of users with public profiles, number of posts and the activated protocols and connectors. See <a href="http://the-federation.info/">the-federation.info</a> for details.')],

View file

@ -186,9 +186,6 @@ class Contact extends BaseModule
// Update the entry in the contact table
Model\Contact::updateFromProbe($contact_id, '', true);
// Update the entry in the gcontact table
Model\GContact::updateFromProbe($contact['url']);
}
/**

View file

@ -26,14 +26,13 @@ use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\GContact;
use Friendica\Model\Profile;
use Friendica\Model\User;
/**
* Endpoint for getting current user infos
*
* @see GContact::updateFromNoScrape() for usage
* @see Contact::updateFromNoScrape() for usage
*/
class NoScrape extends BaseModule
{

View file

@ -24,7 +24,6 @@ namespace Friendica\Module;
use Friendica\BaseModule;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
/**
* Redirects to a random Friendica profile this node knows about
@ -35,7 +34,7 @@ class RandomProfile extends BaseModule
{
$a = DI::app();
$contactUrl = GContact::getRandomUrl();
$contactUrl = Contact::getRandomUrl();
if ($contactUrl) {
$link = Contact::magicLink($contactUrl);

View file

@ -31,7 +31,6 @@ use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\Profile;
use Friendica\Model\ProfileField;
use Friendica\Model\User;
@ -151,9 +150,6 @@ class Index extends BaseSettings
}
Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user());
// Update the global contact for the user
GContact::updateForUser(local_user());
}
public static function content(array $parameters = [])

View file

@ -33,7 +33,6 @@ use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\Event;
use Friendica\Model\GContact;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Model\Mail;
@ -1686,21 +1685,6 @@ class DFRN
if (!empty($pcid)) {
Contact::updateAvatar($pcid, $author['avatar']);
}
/*
* The generation is a sign for the reliability of the provided data.
* It is used in the socgraph.php to prevent that old contact data
* that was relayed over several servers can overwrite contact
* data that we received directly.
*/
$poco["generation"] = 2;
$poco["photo"] = $author["avatar"];
$poco["hide"] = $hide;
$poco["contact-type"] = $contact["contact-type"];
$gcid = GContact::update($poco);
GContact::link($gcid, $importer["importer_uid"], $contact["id"]);
}
return $author;
@ -1943,15 +1927,6 @@ class DFRN
$old = $r[0];
// Update the gcontact entry
$relocate["server_url"] = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$1$2", $relocate["url"]);
$fields = ['name' => $relocate["name"], 'photo' => $relocate["avatar"],
'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]),
'addr' => $relocate["addr"], 'connect' => $relocate["addr"],
'notify' => $relocate["notify"], 'server_url' => $relocate["server_url"]];
DBA::update('gcontact', $fields, ['nurl' => Strings::normaliseLink($old["url"])]);
// Update the contact table. We try to find every entry.
$fields = ['name' => $relocate["name"], 'avatar' => $relocate["avatar"],
'url' => $relocate["url"], 'nurl' => Strings::normaliseLink($relocate["url"]),

View file

@ -34,7 +34,6 @@ use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\GContact;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Model\Mail;
@ -1656,7 +1655,7 @@ class Diaspora
// Update the profile
self::receiveProfile($importer, $data->profile);
// change the technical stuff in contact and gcontact
// change the technical stuff in contact
$data = Probe::uri($new_handle);
if ($data['network'] == Protocol::PHANTOM) {
Logger::log('Account for '.$new_handle." couldn't be probed.");
@ -1671,14 +1670,6 @@ class Diaspora
DBA::update('contact', $fields, ['addr' => $old_handle]);
$fields = ['url' => $data['url'], 'nurl' => Strings::normaliseLink($data['url']),
'name' => $data['name'], 'nick' => $data['nick'],
'addr' => $data['addr'], 'connect' => $data['addr'],
'notify' => $data['notify'], 'photo' => $data['photo'],
'server_url' => $data['baseurl'], 'network' => $data['network']];
DBA::update('gcontact', $fields, ['addr' => $old_handle]);
Logger::log('Contacts are updated.');
return true;
@ -1702,8 +1693,6 @@ class Diaspora
}
DBA::close($contacts);
DBA::delete('gcontact', ['addr' => $author]);
Logger::log('Removed contacts for ' . $author);
return true;
@ -2438,18 +2427,6 @@ class Diaspora
DBA::update('contact', $fields, ['id' => $contact['id']]);
// @todo Update the public contact, then update the gcontact from that
$gcontact = ["url" => $contact["url"], "network" => Protocol::DIASPORA, "generation" => 2,
"photo" => $image_url, "name" => $name, "location" => $location,
"about" => $about, "birthday" => $birthday,
"addr" => $author, "nick" => $nick, "keywords" => $keywords,
"hide" => !$searchable, "nsfw" => $nsfw];
$gcid = GContact::update($gcontact);
GContact::link($gcid, $importer["uid"], $contact["id"]);
Logger::log("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], Logger::DEBUG);
return true;

View file

@ -34,7 +34,6 @@ use Friendica\DI;
use Friendica\Model\APContact;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\GContact;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Model\Tag;
@ -240,15 +239,6 @@ class OStatus
Contact::updateAvatar($cid, $author["author-avatar"]);
}
}
$contact["generation"] = 2;
$contact["hide"] = false; // OStatus contacts are never hidden
if (!empty($author["author-avatar"])) {
$contact["photo"] = $author["author-avatar"];
}
$gcid = GContact::update($contact);
GContact::link($gcid, $contact["uid"], $contact["id"]);
} elseif (empty($contact["network"]) || ($contact["network"] != Protocol::DFRN)) {
$contact = [];
}

View file

@ -1,486 +0,0 @@
<?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;
}
}

View file

@ -57,9 +57,6 @@ class Cron
// run the process to update server directories in the background
Worker::add(PRIORITY_LOW, 'UpdateServerDirectories');
// run the process to update locally stored global contacts in the background
Worker::add(PRIORITY_LOW, 'UpdateGContacts');
// Expire and remove user entries
Worker::add(PRIORITY_MEDIUM, "CronJobs", "expire_and_remove_users");
@ -88,8 +85,6 @@ class Cron
Worker::add(PRIORITY_LOW, 'UpdateGServers');
Worker::add(PRIORITY_LOW, 'UpdateSuggestions');
Worker::add(PRIORITY_LOW, 'Expire');
Worker::add(PRIORITY_MEDIUM, 'DBClean');

View file

@ -29,7 +29,6 @@ use Friendica\Database\DBA;
use Friendica\Database\PostUpdate;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\Nodeinfo;
use Friendica\Model\Photo;
use Friendica\Model\User;
@ -258,14 +257,6 @@ class CronJobs
// There was an issue where the nick vanishes from the contact table
q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''");
// Update the global contacts for local users
$r = q("SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`");
if (DBA::isResult($r)) {
foreach ($r AS $user) {
GContact::updateForUser($user["uid"]);
}
}
/// @todo
/// - remove thread entries without item
/// - remove sign entries without item

View file

@ -1,41 +0,0 @@
<?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);
}
}

View file

@ -32,7 +32,6 @@ use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\Email;
use Friendica\Protocol\Feed;
use Friendica\Protocol\PortableContact;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
use Friendica\Util\XML;
@ -95,13 +94,6 @@ class OnePoll
$contact = DBA::selectFirst('contact', [], ['id' => $contact_id]);
}
// load current friends if possible.
if (!empty($contact['poco']) && !$contact['failed']) {
if (!DBA::exists('glink', ["`cid` = ? AND updated > UTC_TIMESTAMP() - INTERVAL 1 DAY", $contact['id']])) {
PortableContact::loadWorker($contact['id'], $importer_uid, 0, $contact['poco']);
}
}
// Don't poll if polling is deactivated (But we poll feeds and mails anyway)
if (!in_array($protocol, [Protocol::FEED, Protocol::MAIL]) && DI::config()->get('system', 'disable_polling')) {
Logger::log('Polling is disabled');

View file

@ -60,22 +60,11 @@ class PullDirectory
return;
}
$result = Contact::addByUrls($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]);
}
}

View file

@ -23,14 +23,9 @@ namespace Friendica\Worker;
use Friendica\Core\Cache\Duration;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\Search;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\GServer;
use Friendica\Util\Strings;
class SearchDirectory
{
@ -56,43 +51,7 @@ class SearchDirectory
if (!empty($j->results)) {
foreach ($j->results as $jj) {
// Check if the contact already exists
$gcontact = DBA::selectFirst('gcontact', ['failed'], ['nurl' => Strings::normaliseLink($jj->url)]);
if (DBA::isResult($gcontact)) {
Logger::info('Profile already exists', ['profile' => $jj->url, 'search' => $search]);
if ($gcontact['failed']) {
continue;
}
// Update the contact
GContact::updateFromProbe($jj->url);
continue;
}
$server_url = GContact::getBasepath($jj->url, true);
if ($server_url != '') {
if (!GServer::check($server_url)) {
Logger::info("Friendica server doesn't answer.", ['server' => $server_url]);
continue;
}
Logger::info('Friendica server seems to be okay.', ['server' => $server_url]);
}
$data = Contact::getByURL($jj->url);
if ($data['network'] == Protocol::DFRN) {
Logger::info('Add profile to local directory', ['profile' => $jj->url]);
if ($jj->tags != '') {
$data['keywords'] = $jj->tags;
}
$data['server_url'] = $data['baseurl'];
GContact::update($data);
} else {
Logger::info('Profile is not responding or no Friendica contact', ['profile' => $jj->url, 'network' => $data['network']]);
}
Contact::getByURL($jj->url);
}
}
DI::cache()->set('SearchDirectory:' . $search, time(), Duration::DAY);

View file

@ -1,44 +0,0 @@
<?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]);
}
}

View file

@ -1,101 +0,0 @@
<?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);
}
}

View file

@ -22,9 +22,7 @@
namespace Friendica\Worker;
use Friendica\DI;
use Friendica\Model\GContact;
use Friendica\Model\GServer;
use Friendica\Protocol\PortableContact;
class UpdateServerDirectories
{
@ -33,16 +31,10 @@ class UpdateServerDirectories
*/
public static function execute()
{
if (DI::config()->get('system', 'poco_discovery') == PortableContact::DISABLED) {
if (!DI::config()->get('system', 'poco_discovery')) {
return;
}
// Query Friendica and Hubzilla servers for their users
GServer::discover();
// Query GNU Social servers for their users ("statistics" addon has to be enabled on the GS server)
if (!DI::config()->get('system', 'ostatus_disabled')) {
GContact::discoverGsUsers();
}
}
}

View file

@ -21,16 +21,86 @@
namespace Friendica\Worker;
use Friendica\Core\Logger;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\GServer;
class UpdateServerDirectory
{
/**
* Query the given server for their users
*
* @param array $gserver Server record
*/
public static function execute(array $gserver)
{
GServer::updateDirectory($gserver);
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::addByUrls($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::addByUrls($urls);
Logger::info('Account discovery ended', ['count' => $result['count'], 'added' => $result['added'], 'updated' => $result['updated'], 'url' => $gserver['url']]);
}
}

View file

@ -63,4 +63,41 @@ class UpdateServerPeers
}
Logger::info('Server peer update ended', ['total' => $total, 'added' => $added, 'url' => $url]);
}
/**
* Fetch server list from remote servers and adds them when they are new.
*
* @param string $poco URL to the POCO endpoint
*/
private static function fetchServerlist($poco)
{
$curlResult = DI::httpRequest()->get($poco . '/@server');
if (!$curlResult->isSuccess()) {
Logger::info('Server is not reachable or does not offer the "poco" endpoint', ['poco' => $poco]);
return;
}
$serverlist = json_decode($curlResult->getBody(), true);
if (!is_array($serverlist)) {
Logger::info('Server does not have any servers listed', ['poco' => $poco]);
return;
}
Logger::info('PoCo Server update start', ['poco' => $poco]);
$total = 0;
$added = 0;
foreach ($serverlist as $server) {
++$total;
if (DBA::exists('gserver', ['nurl' => Strings::normaliseLink($server['url'])])) {
// We already know this server
continue;
}
// This endpoint doesn't offer the schema. So we assume that it is HTTPS.
Worker::add(PRIORITY_LOW, 'UpdateGServer', $server['url']);
++$added;
}
Logger::info('PoCo Server update ended', ['total' => $total, 'added' => $added, 'poco' => $poco]);
}
}

View file

@ -1,36 +0,0 @@
<?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();
}
}

View file

@ -48,7 +48,6 @@ use Friendica\Database\DBA;
use Friendica\Database\DBStructure;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\Item;
use Friendica\Model\User;
use Friendica\Model\Storage;
@ -315,7 +314,6 @@ function update_1298()
'was' => $data[$translateKey]]);
Worker::add(PRIORITY_LOW, 'ProfileUpdate', $data['id']);
Contact::updateSelfFromUserID($data['id']);
GContact::updateForUser($data['id']);
$success++;
}
}
@ -562,16 +560,5 @@ function update_1357()
return Update::FAILED;
}
if (!DBA::e("UPDATE `gcontact` SET `failed` = true WHERE `last_contact` < `last_failure` AND `failed` IS NULL")) {
return Update::FAILED;
}
if (!DBA::e("UPDATE `gcontact` SET `failed` = false WHERE `last_contact` > `last_failure` AND `failed` IS NULL")) {
return Update::FAILED;
}
if (!DBA::e("UPDATE `gcontact` SET `failed` = false WHERE `updated` > `last_failure` AND `failed` IS NULL")) {
return Update::FAILED;
}
return Update::SUCCESS;
}

View file

@ -5,10 +5,6 @@
<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>

View file

@ -99,10 +99,8 @@
<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>

View file

@ -220,10 +220,8 @@
<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">

View file

@ -16,6 +16,7 @@
{{* Additional links *}}
<div class="side-link" id="side-match-link"><a href="match" >{{$nv.similar}}</a></div>
<div class="side-link" id="side-suggest-link"><a href="suggest" >{{$nv.suggest}}</a></div>
<div class="side-link" id="side-random-profile-link" ><a href="randprof" target="extlink" >{{$nv.random}}</a></div>
{{if $nv.inv}}
<div class="side-link" id="side-invite-link" ><button type="button" class="btn-link" onclick="addToModal('invite'); return false;">{{$nv.inv}}</button></div>

View file

@ -17,6 +17,7 @@ use Friendica\Core\Session;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model;
use Friendica\Model\Contact;
use Friendica\Module;
use Friendica\Util\Strings;
@ -190,55 +191,28 @@ function frio_remote_nav($a, &$nav)
$homelink = Session::get('visitor_home', '');
}
// split up the url in it's parts (protocol,domain/directory, /profile/, nickname
// I'm not familiar with regex, so someone might find a better solutionen
//
// E.g $homelink = 'https://friendica.domain.com/profile/mickey' should result in an array
// with 0 => 'https://friendica.domain.com/profile/mickey' 1 => 'https://',
// 2 => 'friendica.domain.com' 3 => '/profile/' 4 => 'mickey'
//
//$server_url = preg_match('/^(https?:\/\/.*?)\/profile\//2', $homelink);
preg_match('/^(https?:\/\/)?(.*?)(\/profile\/)(.*)/', $homelink, $url_parts);
// Construct the server url of the visitor. So we could link back to his/her own menu.
// And construct a webbie (e.g. mickey@friendica.domain.com for the search in gcontact
// We use the webbie for search in gcontact because we don't know if gcontact table stores
// the right value if its http or https protocol
$webbie = '';
if (count($url_parts)) {
$server_url = $url_parts[1] . $url_parts[2];
$webbie = $url_parts[4] . '@' . $url_parts[2];
}
// since $userinfo isn't available for the hook we write it to the nav array
// this isn't optimal because the contact query will be done now twice
if (local_user() && !empty($a->user['uid'])) {
// empty the server url for local user because we won't need it
$server_url = '';
// user info
$r = q("SELECT `micro` FROM `contact` WHERE `uid` = %d AND `self`", intval($a->user['uid']));
$r[0]['photo'] = (DBA::isResult($r) ? DI::baseUrl()->remove($r[0]['micro']) : 'images/person-48.jpg');
$r[0]['name'] = $a->user['username'];
$remoteUser = Contact::getById($a->user['uid'], ['id', 'url', 'avatar', 'micro', 'name', 'nick']);
$remoteUser['name'] = $a->user['username'];
} elseif (!local_user() && remote_user()) {
$r = q("SELECT `name`, `nick`, `micro` AS `photo` FROM `contact` WHERE `id` = %d", intval(remote_user()));
$remoteUser = Contact::getById(remote_user(), ['id', 'url', 'avatar', 'micro', 'name', 'nick']);
$nav['remote'] = DI::l10n()->t('Guest');
} elseif (Model\Profile::getMyURL()) {
$r = q("SELECT `name`, `nick`, `photo` FROM `gcontact`
WHERE `addr` = '%s' AND `network` = 'dfrn'",
DBA::escape($webbie));
$remoteUser = Contact::getByURL($homelink, ['id', 'url', 'avatar', 'micro', 'name', 'nick']);
$nav['remote'] = DI::l10n()->t('Visitor');
} else {
$r = false;
$remoteUser = null;
}
$remoteUser = null;
if (DBA::isResult($r)) {
if (DBA::isResult($remoteUser)) {
$nav['userinfo'] = [
'icon' => (DBA::isResult($r) ? $r[0]['photo'] : 'images/person-48.jpg'),
'name' => $r[0]['name'],
'icon' => Contact::getMicro($remoteUser),
'name' => $remoteUser['name'],
];
$remoteUser = $r[0];
$server_url = $remoteUser['baseurl'];
}
if (!local_user() && !empty($server_url) && !is_null($remoteUser)) {

View file

@ -17,7 +17,6 @@ use Friendica\Core\Search;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Util\Strings;
function vier_init(App $a)
@ -240,16 +239,10 @@ function vier_community_info()
$helpers = explode(",", $helperlist);
if ($helpers) {
$query = "";
foreach ($helpers as $index => $helper) {
if ($query != "") {
$query .= ",";
}
$query .= "'".DBA::escape(Strings::normaliseLink(trim($helper)))."'";
foreach ($helpers as $helper) {
$urls[] = Strings::normaliseLink(trim($helper));
}
$r = q("SELECT `url`, `name` FROM `gcontact` WHERE `nurl` IN (%s)", $query);
$r = DBA::selectToArray('contact', ['url', 'name'], ['uid' => 0, 'nurl' => $urls]);
}
foreach ($r as $index => $helper) {