require_once("include/Contact.php");
require_once("include/Photo.php");
-/*
- * poco_load
+/**
+ * @brief 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
* pointing to the same global contact id.
*
*/
+function poco_load($cid, $uid = 0, $zcid = 0, $url = null) {
+ // Call the function "poco_load_worker" via the worker
+ proc_run(PRIORITY_LOW, "include/discover_poco.php", "poco_load", intval($cid), intval($uid), intval($zcid), base64_encode($url));
+}
-
-
-
-function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
-
+/**
+ * @brief 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
+ *
+ */
+function poco_load_worker($cid, $uid, $zcid, $url) {
$a = get_app();
if($cid) {
$connect_url = '';
$name = '';
$network = '';
- $updated = '0000-00-00 00:00:00';
+ $updated = NULL_DATE;
$location = '';
$about = '';
$keywords = '';
);
if (count($x)) {
- if (($network == "") AND ($x[0]["network"] != NETWORK_STATUSNET))
+ if (($network == "") AND ($x[0]["network"] != NETWORK_STATUSNET)) {
$network = $x[0]["network"];
-
- if ($updated == "0000-00-00 00:00:00")
+ }
+ if ($updated <= NULL_DATE) {
$updated = $x[0]["updated"];
-
+ }
$created = $x[0]["created"];
$server_url = $x[0]["server_url"];
$nick = $x[0]["nick"];
$alias = $x[0]["alias"];
$notify = $x[0]["notify"];
} else {
- $created = "0000-00-00 00:00:00";
+ $created = NULL_DATE;
$server_url = "";
$urlparts = parse_url($profile_url);
// Wild guess
if ($server_url == "") {
$base = preg_replace("=(https?://)(.*?)/(.*)=ism", "$1$2", $profile);
- if (base != $profile) {
+ if ($base != $profile) {
$server_url = $base;
$network = NETWORK_PHANTOM;
}
$gcontacts = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'",
dbesc(normalise_link($profile)));
- if ($gcontacts[0]["created"] == "0000-00-00 00:00:00")
+ if ($gcontacts[0]["created"] <= NULL_DATE) {
q("UPDATE `gcontact` SET `created` = '%s' WHERE `nurl` = '%s'",
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
-
+ }
if ($gcontacts[0]["server_url"] != "") {
$server_url = $gcontacts[0]["server_url"];
}
}
// Maybe there aren't any entries. Then check if it is a valid feed
- if ($last_updated == "")
- if ($xpath->query('/atom:feed')->length > 0)
- $last_updated = "0000-00-00 00:00:00";
-
+ if ($last_updated == "") {
+ if ($xpath->query('/atom:feed')->length > 0) {
+ $last_updated = NULL_DATE;
+ }
+ }
q("UPDATE `gcontact` SET `updated` = '%s', `last_contact` = '%s' WHERE `nurl` = '%s'",
dbesc(dbm::date($last_updated)), dbesc(dbm::date()), dbesc(normalise_link($profile)));
return false;
}
+/**
+ * @brief Detect server type by using the nodeinfo data
+ *
+ * @param string $server_url address of the server
+ * @return array Server data
+ */
+function poco_fetch_nodeinfo($server_url) {
+ $serverret = z_fetch_url($server_url."/.well-known/nodeinfo");
+ if (!$serverret["success"]) {
+ return false;
+ }
+
+ $nodeinfo = json_decode($serverret['body']);
+
+ if (!is_object($nodeinfo)) {
+ return false;
+ }
+
+ if (!is_array($nodeinfo->links)) {
+ return false;
+ }
+
+ $nodeinfo_url = '';
+
+ foreach ($nodeinfo->links AS $link) {
+ if ($link->rel == 'http://nodeinfo.diaspora.software/ns/schema/1.0') {
+ $nodeinfo_url = $link->href;
+ }
+ }
+
+ if ($nodeinfo_url == '') {
+ return false;
+ }
+
+ $serverret = z_fetch_url($nodeinfo_url);
+ if (!$serverret["success"]) {
+ return false;
+ }
+
+ $nodeinfo = json_decode($serverret['body']);
+
+ if (!is_object($nodeinfo)) {
+ return false;
+ }
+
+ $server = array();
+
+ $server['register_policy'] = REGISTER_CLOSED;
+
+ if (is_bool($nodeinfo->openRegistrations) AND $nodeinfo->openRegistrations) {
+ $server['register_policy'] = REGISTER_OPEN;
+ }
+
+ if (is_object($nodeinfo->software)) {
+ if (isset($nodeinfo->software->name)) {
+ $server['platform'] = $nodeinfo->software->name;
+ }
+
+ if (isset($nodeinfo->software->version)) {
+ $server['version'] = $nodeinfo->software->version;
+ // Version numbers on Nodeinfo are presented with additional info, e.g.:
+ // 0.6.3.0-p1702cc1c, 0.6.99.0-p1b9ab160 or 3.4.3-2-1191.
+ $server['version'] = preg_replace("=(.+)-(.{4,})=ism", "$1", $server['version']);
+ }
+ }
+
+ if (is_object($nodeinfo->metadata)) {
+ if (isset($nodeinfo->metadata->nodeName)) {
+ $server['site_name'] = $nodeinfo->metadata->nodeName;
+ }
+ }
+
+ $diaspora = false;
+ $friendica = false;
+ $gnusocial = false;
+
+ if (is_array($nodeinfo->protocols->inbound)) {
+ foreach ($nodeinfo->protocols->inbound AS $inbound) {
+ if ($inbound == 'diaspora') {
+ $diaspora = true;
+ }
+ if ($inbound == 'friendica') {
+ $friendica = true;
+ }
+ if ($inbound == 'gnusocial') {
+ $gnusocial = true;
+ }
+ }
+ }
+
+ if ($gnusocial) {
+ $server['network'] = NETWORK_OSTATUS;
+ }
+ if ($diaspora) {
+ $server['network'] = NETWORK_DIASPORA;
+ }
+ if ($friendica) {
+ $server['network'] = NETWORK_DFRN;
+ }
+
+ if (!$server) {
+ return false;
+ }
+
+ return $server;
+}
+
/**
* @brief Detect server type (Hubzilla or Friendica) via the front page body
*
return false;
$servers = q("SELECT * FROM `gserver` WHERE `nurl` = '%s'", dbesc(normalise_link($server_url)));
- if ($servers) {
+ if (dbm::is_result($servers)) {
- if ($servers[0]["created"] == "0000-00-00 00:00:00")
+ if ($servers[0]["created"] <= NULL_DATE) {
q("UPDATE `gserver` SET `created` = '%s' WHERE `nurl` = '%s'",
dbesc(datetime_convert()), dbesc(normalise_link($server_url)));
-
+ }
$poco = $servers[0]["poco"];
$noscrape = $servers[0]["noscrape"];
$info = "";
$register_policy = -1;
- $last_contact = "0000-00-00 00:00:00";
- $last_failure = "0000-00-00 00:00:00";
+ $last_contact = NULL_DATE;
+ $last_failure = NULL_DATE;
}
logger("Server ".$server_url." is outdated or unknown. Start discovery. Force: ".$force." Created: ".$servers[0]["created"]." Failure: ".$last_failure." Contact: ".$last_contact, LOGGER_DEBUG);
$orig_last_contact = $last_contact;
// Check if the page is accessible via SSL.
+ $orig_server_url = $server_url;
$server_url = str_replace("http://", "https://", $server_url);
- $serverret = z_fetch_url($server_url."/.well-known/host-meta");
+
+ // We set the timeout to 20 seconds since this operation should be done in no time if the server was vital
+ $serverret = z_fetch_url($server_url."/.well-known/host-meta", false, $redirects, array('timeout' => 20));
+
+ // Quit if there is a timeout.
+ // But we want to make sure to only quit if we are mostly sure that this server url fits.
+ if (dbm::is_result($servers) AND ($orig_server_url == $server_url) AND
+ ($serverret['errno'] == CURLE_OPERATION_TIMEDOUT)) {
+ logger("Connection to server ".$server_url." timed out.", LOGGER_DEBUG);
+ return false;
+ }
// Maybe the page is unencrypted only?
$xmlobj = @simplexml_load_string($serverret["body"],'SimpleXMLElement',0, "http://docs.oasis-open.org/ns/xri/xrd-1.0");
if (!$serverret["success"] OR ($serverret["body"] == "") OR (@sizeof($xmlobj) == 0) OR !is_object($xmlobj)) {
$server_url = str_replace("https://", "http://", $server_url);
- $serverret = z_fetch_url($server_url."/.well-known/host-meta");
+
+ // We set the timeout to 20 seconds since this operation should be done in no time if the server was vital
+ $serverret = z_fetch_url($server_url."/.well-known/host-meta", false, $redirects, array('timeout' => 20));
+
+ // Quit if there is a timeout
+ if ($serverret['errno'] == CURLE_OPERATION_TIMEDOUT) {
+ logger("Connection to server ".$server_url." timed out.", LOGGER_DEBUG);
+ return false;
+ }
$xmlobj = @simplexml_load_string($serverret["body"],'SimpleXMLElement',0, "http://docs.oasis-open.org/ns/xri/xrd-1.0");
}
if (!$serverret["success"] OR ($serverret["body"] == "") OR (sizeof($xmlobj) == 0) OR !is_object($xmlobj)) {
// Workaround for bad configured servers (known nginx problem)
- if ($serverret["debug"]["http_code"] != "403") {
+ if (!in_array($serverret["debug"]["http_code"], array("403", "404"))) {
$last_failure = datetime_convert();
$failure = true;
}
$serverret = z_fetch_url($server_url."/statistics.json");
if ($serverret["success"]) {
$data = json_decode($serverret["body"]);
- if ($version == "")
+ if (isset($data->version)) {
$version = $data->version;
+ // Version numbers on statistics.json are presented with additional info, e.g.:
+ // 0.6.3.0-p1702cc1c, 0.6.99.0-p1b9ab160 or 3.4.3-2-1191.
+ $version = preg_replace("=(.+)-(.{4,})=ism", "$1", $version);
+ }
$site_name = $data->name;
- if (isset($data->network) AND ($platform == ""))
+ if (isset($data->network)) {
$platform = $data->network;
+ }
- if ($platform == "Diaspora")
+ if ($platform == "Diaspora") {
$network = NETWORK_DIASPORA;
+ }
- if ($data->registrations_open)
+ if ($data->registrations_open) {
$register_policy = REGISTER_OPEN;
- else
+ } else {
$register_policy = REGISTER_CLOSED;
+ }
if (isset($data->version))
$last_contact = datetime_convert();
}
}
+ // Query nodeinfo. Working for (at least) Diaspora and Friendica.
+ if (!$failure) {
+ $server = poco_fetch_nodeinfo($server_url);
+ if ($server) {
+ $register_policy = $server['register_policy'];
+
+ if (isset($server['platform'])) {
+ $platform = $server['platform'];
+ }
+
+ if (isset($server['network'])) {
+ $network = $server['network'];
+ }
+
+ if (isset($server['version'])) {
+ $version = $server['version'];
+ }
+
+ if (isset($server['site_name'])) {
+ $site_name = $server['site_name'];
+ }
+
+ $last_contact = datetime_convert();
+ }
+ }
+
// Check for noscrape
// Friendica servers could be detected as OStatus servers
if (!$failure AND in_array($network, array(NETWORK_DFRN, NETWORK_OSTATUS))) {
dbesc($last_failure),
dbesc(normalise_link($server_url))
);
- } elseif (!$failure)
+ } elseif (!$failure) {
q("INSERT INTO `gserver` (`url`, `nurl`, `version`, `site_name`, `info`, `register_policy`, `poco`, `noscrape`, `network`, `platform`, `created`, `last_contact`, `last_failure`)
VALUES ('%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
dbesc($server_url),
dbesc($last_failure),
dbesc(datetime_convert())
);
-
+ }
logger("End discovery for server ".$server_url, LOGGER_DEBUG);
return !$failure;
where uid = %d and not gcontact.nurl in ( select nurl from contact where uid = %d )
AND NOT `gcontact`.`name` IN (SELECT `name` FROM `contact` WHERE `uid` = %d)
AND NOT `gcontact`.`id` IN (SELECT `gcid` FROM `gcign` WHERE `uid` = %d)
- AND `gcontact`.`updated` != '0000-00-00 00:00:00'
+ AND `gcontact`.`updated` >= '%s'
AND `gcontact`.`last_contact` >= `gcontact`.`last_failure`
AND `gcontact`.`network` IN (%s)
GROUP BY `glink`.`gcid` ORDER BY `gcontact`.`updated` DESC,`total` DESC LIMIT %d, %d",
intval($uid),
intval($uid),
intval($uid),
+ dbesc(NULL_DATE),
$sql_network,
intval($start),
intval($limit)
WHERE `glink`.`uid` = 0 AND `glink`.`cid` = 0 AND `glink`.`zcid` = 0 AND NOT `gcontact`.`nurl` IN (SELECT `nurl` FROM `contact` WHERE `uid` = %d)
AND NOT `gcontact`.`name` IN (SELECT `name` FROM `contact` WHERE `uid` = %d)
AND NOT `gcontact`.`id` IN (SELECT `gcid` FROM `gcign` WHERE `uid` = %d)
- AND `gcontact`.`updated` != '0000-00-00 00:00:00'
+ AND `gcontact`.`updated` >= '%s'
AND `gcontact`.`last_contact` >= `gcontact`.`last_failure`
AND `gcontact`.`network` IN (%s)
ORDER BY rand() LIMIT %d, %d",
intval($uid),
intval($uid),
intval($uid),
+ dbesc(NULL_DATE),
$sql_network,
intval($start),
intval($limit)
}
}
+/**
+ * @brief Fetch server list from remote servers and adds them when they are new.
+ *
+ * @param string $poco URL to the POCO endpoint
+ */
+function poco_fetch_serverlist($poco) {
+ $serverret = z_fetch_url($poco."/@server");
+ if (!$serverret["success"]) {
+ return;
+ }
+ $serverlist = json_decode($serverret['body']);
+
+ 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'", dbesc(normalise_link($server_url)));
+ if (!dbm::is_result($r)) {
+ logger("Call server check for server ".$server_url, LOGGER_DEBUG);
+ proc_run(PRIORITY_LOW, "include/discover_poco.php", "server", base64_encode($server_url));
+ }
+ }
+}
+
function poco_discover_federation() {
$last = get_config('poco','last_federation_discovery');
if ($serverdata) {
$servers = json_decode($serverdata);
- foreach($servers->pods AS $server)
- poco_check_server("https://".$server->host);
+ foreach ($servers->pods AS $server) {
+ proc_run(PRIORITY_LOW, "include/discover_poco.php", "server", base64_encode("https://".$server->host));
+ }
}
// Currently disabled, since the service isn't available anymore.
set_config('poco','last_federation_discovery', time());
}
-function poco_discover($complete = false) {
+function poco_discover_single_server($id) {
+ $r = q("SELECT `poco`, `nurl`, `url`, `network` FROM `gserver` WHERE `id` = %d", intval($id));
+ if (!dbm::is_result($r)) {
+ return false;
+ }
- // Update the server list
- poco_discover_federation();
+ $server = $r[0];
- $no_of_queries = 5;
+ // Discover new servers out there (Works from Friendica version 3.5.2)
+ poco_fetch_serverlist($server["poco"]);
- $requery_days = intval(get_config("system", "poco_requery_days"));
+ // Fetch all users from the other server
+ $url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation";
- if ($requery_days == 0)
- $requery_days = 7;
+ logger("Fetch all users from the server ".$server["url"], LOGGER_DEBUG);
- $last_update = date("c", time() - (60 * 60 * 24 * $requery_days));
+ $retdata = z_fetch_url($url);
+ if ($retdata["success"]) {
+ $data = json_decode($retdata["body"]);
- $r = q("SELECT `poco`, `nurl`, `url`, `network` FROM `gserver` WHERE `last_contact` >= `last_failure` AND `poco` != '' AND `last_poco_query` < '%s' ORDER BY RAND()", dbesc($last_update));
- if ($r)
- foreach ($r AS $server) {
+ poco_discover_server($data, 2);
- if (!poco_check_server($server["url"], $server["network"])) {
- // The server is not reachable? Okay, then we will try it later
- q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
- continue;
+ if (get_config('system','poco_discovery') > 1) {
+
+ $timeframe = get_config('system','poco_discovery_since');
+ if ($timeframe == 0) {
+ $timeframe = 30;
}
- // Fetch all users from the other server
- $url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation";
+ $updatedSince = date("Y-m-d H:i:s", 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,gender,contactType,generation";
- logger("Fetch all users from the server ".$server["nurl"], LOGGER_DEBUG);
+ $success = false;
$retdata = z_fetch_url($url);
if ($retdata["success"]) {
- $data = json_decode($retdata["body"]);
+ logger("Fetch all global contacts from the server ".$server["nurl"], LOGGER_DEBUG);
+ $success = poco_discover_server(json_decode($retdata["body"]));
+ }
- poco_discover_server($data, 2);
+ if (!$success AND (get_config('system','poco_discovery') > 2)) {
+ logger("Fetch contacts from users of the server ".$server["nurl"], LOGGER_DEBUG);
+ poco_discover_server_users($data, $server);
+ }
+ }
- if (get_config('system','poco_discovery') > 1) {
+ q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
- $timeframe = get_config('system','poco_discovery_since');
- if ($timeframe == 0)
- $timeframe = 30;
+ return true;
+ } else {
+ // If the server hadn't replied correctly, then force a sanity check
+ poco_check_server($server["url"], $server["network"], true);
- $updatedSince = date("Y-m-d H:i:s", time() - $timeframe * 86400);
+ // If we couldn't reach the server, we will try it some time later
+ q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
- // 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,gender,contactType,generation";
+ return false;
+ }
+}
- $success = false;
+function poco_discover($complete = false) {
- $retdata = z_fetch_url($url);
- if ($retdata["success"]) {
- logger("Fetch all global contacts from the server ".$server["nurl"], LOGGER_DEBUG);
- $success = poco_discover_server(json_decode($retdata["body"]));
- }
+ // Update the server list
+ poco_discover_federation();
- if (!$success AND (get_config('system','poco_discovery') > 2)) {
- logger("Fetch contacts from users of the server ".$server["nurl"], LOGGER_DEBUG);
- poco_discover_server_users($data, $server);
- }
- }
+ $no_of_queries = 5;
- q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
- if (!$complete AND (--$no_of_queries == 0))
- break;
- } else {
- // If the server hadn't replied correctly, then force a sanity check
- poco_check_server($server["url"], $server["network"], true);
+ $requery_days = intval(get_config("system", "poco_requery_days"));
+
+ if ($requery_days == 0) {
+ $requery_days = 7;
+ }
+ $last_update = date("c", time() - (60 * 60 * 24 * $requery_days));
+
+ $r = q("SELECT `id`, `url`, `network` FROM `gserver` WHERE `last_contact` >= `last_failure` AND `poco` != '' AND `last_poco_query` < '%s' ORDER BY RAND()", dbesc($last_update));
+ if (dbm::is_result($r)) {
+ foreach ($r AS $server) {
- // If we couldn't reach the server, we will try it some time later
+ if (!poco_check_server($server["url"], $server["network"])) {
+ // The server is not reachable? Okay, then we will try it later
q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
+ continue;
+ }
+
+ logger('Update directory from server '.$server['url'].' with ID '.$server['id'], LOGGER_DEBUG);
+ proc_run(PRIORITY_LOW, "include/discover_poco.php", "update_server_directory", intval($server['id']));
+
+ if (!$complete AND (--$no_of_queries == 0)) {
+ break;
}
}
+ }
}
function poco_discover_server_users($data, $server) {
$connect_url = '';
$name = '';
$network = '';
- $updated = '0000-00-00 00:00:00';
+ $updated = NULL_DATE;
$location = '';
$about = '';
$keywords = '';
* @return array List of server urls
*/
function poco_serverlist() {
- $r = q("SELECT `id`, `url`, `site_name` AS `displayName`, `network`, `platform`, `version` FROM `gserver`
+ $r = q("SELECT `url`, `site_name` AS `displayName`, `network`, `platform`, `version` FROM `gserver`
WHERE `network` IN ('%s', '%s', '%s') AND `last_contact` > `last_failure`
ORDER BY `last_contact`
LIMIT 1000",
if (!dbm::is_result($r)) {
return false;
}
- $list = array();
- foreach ($r AS $server) {
- $server['id'] = (int)$server['id'];
- $list[] = $server;
- }
- return $list;
+ return $r;
}
?>