Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
friendica/include/socgraph.php

2286 lines
68 KiB

<?php
/**
* @file include/socgraph.php
*
* @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
*/
11 years ago
require_once('include/datetime.php');
require_once("include/Scrape.php");
require_once("include/network.php");
require_once("include/html2bbcode.php");
require_once("include/Contact.php");
require_once("include/Photo.php");
/*
* poco_load
*
* 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.
*
*/
function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
$a = get_app();
if($cid) {
if((! $url) || (! $uid)) {
$r = q("select `poco`, `uid` from `contact` where `id` = %d limit 1",
intval($cid)
);
if (dbm::is_result($r)) {
$url = $r[0]['poco'];
$uid = $r[0]['uid'];
}
}
if(! $uid)
return;
}
if(! $url)
return;
$url = $url . (($uid) ? '/@me/@all?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation' : '?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation') ;
logger('poco_load: ' . $url, LOGGER_DEBUG);
$s = fetch_url($url);
logger('poco_load: returns ' . $s, LOGGER_DATA);
logger('poco_load: return code: ' . $a->get_curl_code(), LOGGER_DEBUG);
if(($a->get_curl_code() > 299) || (! $s))
return;
$j = json_decode($s);
logger('poco_load: json: ' . print_r($j,true),LOGGER_DATA);
if(! isset($j->entry))
return;
$total = 0;
foreach($j->entry as $entry) {
$total ++;
$profile_url = '';
$profile_photo = '';
$connect_url = '';
$name = '';
$network = '';
$updated = '0000-00-00 00:00:00';
$location = '';
$about = '';
$keywords = '';
$gender = '';
$contact_type = -1;
$generation = 0;
$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("Y-m-d H:i:s", strtotime($entry->updated));
}
if (isset($entry->network)) {
$network = $entry->network;
}
if (isset($entry->currentLocation)) {
$location = $entry->currentLocation;
}
if (isset($entry->aboutMe)) {
$about = html2bbcode($entry->aboutMe);
}
if (isset($entry->gender)) {
$gender = $entry->gender;
}
if (isset($entry->generation) AND ($entry->generation > 0)) {
$generation = ++$entry->generation;
}
if (isset($entry->tags)) {
foreach($entry->tags as $tag) {
$keywords = implode(", ", $tag);
}
}
if (isset($entry->contactType) AND ($entry->contactType >= 0))
$contact_type = $entry->contactType;
// If you query a Friendica server for its profiles, the network has to be Friendica
/// TODO It could also be a Redmatrix server
//if ($uid == 0)
// $network = NETWORK_DFRN;
poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
$gcontact = array("url" => $profile_url, "contact-type" => $contact_type, "generation" => $generation);
update_gcontact($gcontact);
// Update the Friendica contacts. Diaspora is doing it via a message. (See include/diaspora.php)
// Deactivated because we now update Friendica contacts in dfrn.php
//if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
// q("UPDATE `contact` SET `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s'
// WHERE `nurl` = '%s' AND NOT `self` AND `network` = '%s'",
// dbesc($location),
// dbesc($about),
// dbesc($keywords),
// dbesc($gender),
// dbesc(normalise_link($profile_url)),
// dbesc(NETWORK_DFRN));
}
logger("poco_load: loaded $total entries",LOGGER_DEBUG);
q("DELETE FROM `glink` WHERE `cid` = %d AND `uid` = %d AND `zcid` = %d AND `updated` < UTC_TIMESTAMP - INTERVAL 2 DAY",
intval($cid),
intval($uid),
intval($zcid)
);
11 years ago
}
function poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid = 0, $uid = 0, $zcid = 0) {
// 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: ...
$gcid = "";
if ($profile_url == "")
return $gcid;
$urlparts = parse_url($profile_url);
if (!isset($urlparts["scheme"]))
return $gcid;
if (in_array($urlparts["host"], array("www.facebook.com", "facebook.com", "twitter.com",
"identi.ca", "alpha.app.net")))
return $gcid;
// Don't store the statusnet connector as network
// We can't simply set this to NETWORK_OSTATUS since the connector could have fetched posts from friendica as well
if ($network == NETWORK_STATUSNET)
$network = "";
// Assure that there are no parameter fragments in the profile url
if (in_array($network, array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, "")))
$profile_url = clean_contact_url($profile_url);
$alternate = poco_alternate_ostatus_url($profile_url);
$orig_updated = $updated;
// The global contacts should contain the original picture, not the cached one
if (($generation != 1) AND stristr(normalise_link($profile_photo), normalise_link(App::get_baseurl()."/photo/"))) {
$profile_photo = "";
}
$r = q("SELECT `network` FROM `contact` WHERE `nurl` = '%s' AND `network` != '' AND `network` != '%s' LIMIT 1",
dbesc(normalise_link($profile_url)), dbesc(NETWORK_STATUSNET)
);
if (dbm::is_result($r)) {
$network = $r[0]["network"];
}
if (($network == "") OR ($network == NETWORK_OSTATUS)) {
$r = q("SELECT `network`, `url` FROM `contact` WHERE `alias` IN ('%s', '%s') AND `network` != '' AND `network` != '%s' LIMIT 1",
dbesc($profile_url), dbesc(normalise_link($profile_url)), dbesc(NETWORK_STATUSNET)
);
if (dbm::is_result($r)) {
$network = $r[0]["network"];
//$profile_url = $r[0]["url"];
}
}
$x = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
dbesc(normalise_link($profile_url))
);
if (count($x)) {
if (($network == "") AND ($x[0]["network"] != NETWORK_STATUSNET))
$network = $x[0]["network"];
if ($updated == "0000-00-00 00:00:00")
$updated = $x[0]["updated"];
$created = $x[0]["created"];
$server_url = $x[0]["server_url"];
$nick = $x[0]["nick"];
$addr = $x[0]["addr"];
$alias = $x[0]["alias"];
$notify = $x[0]["notify"];
} else {
$created = "0000-00-00 00:00:00";
$server_url = "";
$urlparts = parse_url($profile_url);
$nick = end(explode("/", $urlparts["path"]));
$addr = "";
$alias = "";
$notify = "";
}
if ((($network == "") OR ($name == "") OR ($addr == "") OR ($profile_photo == "") OR ($server_url == "") OR $alternate)
AND poco_reachable($profile_url, $server_url, $network, false)) {
$data = probe_url($profile_url);
$orig_profile = $profile_url;
$network = $data["network"];
$name = $data["name"];
$nick = $data["nick"];
$addr = $data["addr"];
$alias = $data["alias"];
$notify = $data["notify"];
$profile_url = $data["url"];
$profile_photo = $data["photo"];
$server_url = $data["baseurl"];
if ($alternate AND ($network == NETWORK_OSTATUS)) {
// Delete the old entry - if it exists
$r = q("SELECT `id` FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($orig_profile)));
if ($r) {
q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($orig_profile)));
q("DELETE FROM `glink` WHERE `gcid` = %d", intval($r[0]["id"]));
}
// possibly create a new entry
poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
}
}
if ($alternate AND ($network == NETWORK_OSTATUS))
return $gcid;
if (count($x) AND ($x[0]["network"] == "") AND ($network != "")) {
q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
dbesc($network),
dbesc(normalise_link($profile_url))
);
}
if (($name == "") OR ($profile_photo == ""))
return $gcid;
if (!in_array($network, array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA)))
return $gcid;
logger("profile-check generation: ".$generation." Network: ".$network." URL: ".$profile_url." name: ".$name." avatar: ".$profile_photo, LOGGER_DEBUG);
// We check the server url to be sure that it is a real one
$server_url2 = poco_detect_server($profile_url);
// We are no sure that it is a correct URL. So we use it in the future
if ($server_url2 != "") {
$server_url = $server_url2;
}
// The server URL doesn't seem to be valid, so we don't store it.
if (!poco_check_server($server_url, $network)) {
$server_url = "";
}
7 years ago
$gcontact = array("url" => $profile_url,
"addr" => $addr,
"alias" => $alias,
"name" => $name,
"network" => $network,
"photo" => $profile_photo,
"about" => $about,
"location" => $location,
"gender" => $gender,
"keywords" => $keywords,
"server_url" => $server_url,
"connect" => $connect_url,
"notify" => $notify,
"updated" => $updated,
"generation" => $generation);
$gcid = update_gcontact($gcontact);
if(!$gcid)
return $gcid;
$r = q("SELECT * FROM `glink` WHERE `cid` = %d AND `uid` = %d AND `gcid` = %d AND `zcid` = %d LIMIT 1",
intval($cid),
intval($uid),
intval($gcid),
intval($zcid)
);
if (! dbm::is_result($r)) {
q("INSERT INTO `glink` (`cid`,`uid`,`gcid`,`zcid`, `updated`) VALUES (%d,%d,%d,%d, '%s') ",
intval($cid),
intval($uid),
intval($gcid),
intval($zcid),
dbesc(datetime_convert())
);
} else {
q("UPDATE `glink` SET `updated` = '%s' WHERE `cid` = %d AND `uid` = %d AND `gcid` = %d AND `zcid` = %d",
dbesc(datetime_convert()),
intval($cid),
intval($uid),
intval($gcid),
intval($zcid)
);
}
return $gcid;
}
11 years ago
function poco_reachable($profile, $server = "", $network = "", $force = false) {
if ($server == "")
$server = poco_detect_server($profile);
if ($server == "")
return true;
return poco_check_server($server, $network, $force);
}
function poco_detect_server($profile) {
// Try to detect the server path based upon some known standard paths
$server_url = "";
if ($server_url == "") {
$friendica = preg_replace("=(https?://)(.*)/profile/(.*)=ism", "$1$2", $profile);
if ($friendica != $profile) {
$server_url = $friendica;
$network = NETWORK_DFRN;
}
}
if ($server_url == "") {
$diaspora = preg_replace("=(https?://)(.*)/u/(.*)=ism", "$1$2", $profile);
if ($diaspora != $profile) {
$server_url = $diaspora;
$network = NETWORK_DIASPORA;
}
}
if ($server_url == "") {
$red = preg_replace("=(https?://)(.*)/channel/(.*)=ism", "$1$2", $profile);
if ($red != $profile) {
$server_url = $red;
$network = NETWORK_DIASPORA;
}
}
// Mastodon
if ($server_url == "") {
$mastodon = preg_replace("=(https?://)(.*)/users/(.*)=ism", "$1$2", $profile);
if ($mastodon != $profile) {
$server_url = $mastodon;
$network = NETWORK_OSTATUS;
}
}
// Numeric OStatus variant
if ($server_url == "") {
$ostatus = preg_replace("=(https?://)(.*)/user/(.*)=ism", "$1$2", $profile);
if ($ostatus != $profile) {
$server_url = $ostatus;
$network = NETWORK_OSTATUS;
}
}
// Wild guess
if ($server_url == "") {
$base = preg_replace("=(https?://)(.*?)/(.*)=ism", "$1$2", $profile);
if ($base != $profile) {
$server_url = $base;
$network = NETWORK_PHANTOM;
}
}
if ($server_url == "") {
return "";
}
$r = q("SELECT `id` FROM `gserver` WHERE `nurl` = '%s' AND `last_contact` > `last_failure`",
dbesc(normalise_link($server_url)));
if (dbm::is_result($r)) {
return $server_url;
}
// Fetch the host-meta to check if this really is a server
$serverret = z_fetch_url($server_url."/.well-known/host-meta");
if (!$serverret["success"]) {
return "";
}
return $server_url;
}
function poco_alternate_ostatus_url($url) {
return(preg_match("=https?://.+/user/\d+=ism", $url, $matches));
}
function poco_last_updated($profile, $force = false) {
7 years ago
$gcontacts = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'",
dbesc(normalise_link($profile)));
if ($gcontacts[0]["created"] == "0000-00-00 00:00:00")
q("UPDATE `gcontact` SET `created` = '%s' WHERE `nurl` = '%s'",
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
7 years ago
if ($gcontacts[0]["server_url"] != "") {
$server_url = $gcontacts[0]["server_url"];
}
if (($server_url == '') OR ($gcontacts[0]["server_url"] == $gcontacts[0]["nurl"])) {
$server_url = poco_detect_server($profile);
}
if (!in_array($gcontacts[0]["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_FEED, NETWORK_OSTATUS, ""))) {
logger("Profile ".$profile.": Network type ".$gcontacts[0]["network"]." can't be checked", LOGGER_DEBUG);
return false;
}
if ($server_url != "") {
if (!poco_check_server($server_url, $gcontacts[0]["network"], $force)) {
if ($force)
q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
logger("Profile ".$profile.": Server ".$server_url." wasn't reachable.", LOGGER_DEBUG);
7 years ago
return false;
}
q("UPDATE `gcontact` SET `server_url` = '%s' WHERE `nurl` = '%s'",
dbesc($server_url), dbesc(normalise_link($profile)));
}
if (in_array($gcontacts[0]["network"], array("", NETWORK_FEED))) {
$server = q("SELECT `network` FROM `gserver` WHERE `nurl` = '%s' AND `network` != ''",
dbesc(normalise_link($server_url)));
if ($server)
q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
dbesc($server[0]["network"]), dbesc(normalise_link($profile)));
else
return false;
}
// noscrape is really fast so we don't cache the call.
if (($gcontacts[0]["server_url"] != "") AND ($gcontacts[0]["nick"] != "")) {
// Use noscrape if possible
$server = q("SELECT `noscrape`, `network` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($gcontacts[0]["server_url"])));
if ($server) {
$noscraperet = z_fetch_url($server[0]["noscrape"]."/".$gcontacts[0]["nick"]);
if ($noscraperet["success"] AND ($noscraperet["body"] != "")) {
$noscrape = json_decode($noscraperet["body"], true);
if (is_array($noscrape)) {
$contact = array("url" => $profile,
"network" => $server[0]["network"],
"generation" => $gcontacts[0]["generation"]);
if (isset($noscrape["fn"]))
$contact["name"] = $noscrape["fn"];
if (isset($noscrape["comm"]))
$contact["community"] = $noscrape["comm"];
if (isset($noscrape["tags"])) {
$keywords = implode(" ", $noscrape["tags"]);
if ($keywords != "")
$contact["keywords"] = $keywords;
}
$location = formatted_location($noscrape);
if ($location)
$contact["location"] = $location;
if (isset($noscrape["dfrn-notify"]))
$contact["notify"] = $noscrape["dfrn-notify"];
// Remove all fields that are not present in the gcontact table
unset($noscrape["fn"]);
unset($noscrape["key"]);
unset($noscrape["homepage"]);
unset($noscrape["comm"]);
unset($noscrape["tags"]);
unset($noscrape["locality"]);
unset($noscrape["region"]);
unset($noscrape["country-name"]);
unset($noscrape["contacts"]);
unset($noscrape["dfrn-request"]);
unset($noscrape["dfrn-confirm"]);
unset($noscrape["dfrn-notify"]);
unset($noscrape["dfrn-poll"]);
// Set the date of the last contact
/// @todo By now the function "update_gcontact" doesn't work with this field
//$contact["last_contact"] = datetime_convert();
$contact = array_merge($contact, $noscrape);
update_gcontact($contact);
if (trim($noscrape["updated"]) != "") {
q("UPDATE `gcontact` SET `last_contact` = '%s' WHERE `nurl` = '%s'",
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
logger("Profile ".$profile." was last updated at ".$noscrape["updated"]." (noscrape)", LOGGER_DEBUG);
return $noscrape["updated"];
}
}
}
}
7 years ago
}
// If we only can poll the feed, then we only do this once a while
if (!$force AND !poco_do_update($gcontacts[0]["created"], $gcontacts[0]["updated"], $gcontacts[0]["last_failure"], $gcontacts[0]["last_contact"])) {
logger("Profile ".$profile." was last updated at ".$gcontacts[0]["updated"]." (cached)", LOGGER_DEBUG);
return $gcontacts[0]["updated"];
}
$data = probe_url($profile);
// Is the profile link the alternate OStatus link notation? (http://domain.tld/user/4711)
// Then check the other link and delete this one
if (($data["network"] == NETWORK_OSTATUS) AND poco_alternate_ostatus_url($profile) AND
(normalise_link($profile) == normalise_link($data["alias"])) AND
(normalise_link($profile) != normalise_link($data["url"]))) {
// Delete the old entry
q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($profile)));
q("DELETE FROM `glink` WHERE `gcid` = %d", intval($gcontacts[0]["id"]));
poco_check($data["url"], $data["name"], $data["network"], $data["photo"], $gcontacts[0]["about"], $gcontacts[0]["location"],
$gcontacts[0]["gender"], $gcontacts[0]["keywords"], $data["addr"], $gcontacts[0]["updated"], $gcontacts[0]["generation"]);
poco_last_updated($data["url"], $force);
logger("Profile ".$profile." was deleted", LOGGER_DEBUG);
return false;
}
if (($data["poll"] == "") OR (in_array($data["network"], array(NETWORK_FEED, NETWORK_PHANTOM)))) {
7 years ago
q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
logger("Profile ".$profile." wasn't reachable (profile)", LOGGER_DEBUG);
return false;
7 years ago
}
$contact = array("generation" => $gcontacts[0]["generation"]);
7 years ago
$contact = array_merge($contact, $data);
$contact["server_url"] = $data["baseurl"];
7 years ago
unset($contact["batch"]);
unset($contact["poll"]);
unset($contact["request"]);
unset($contact["confirm"]);
unset($contact["poco"]);
unset($contact["priority"]);
unset($contact["pubkey"]);
unset($contact["baseurl"]);
7 years ago
update_gcontact($contact);
7 years ago
$feedret = z_fetch_url($data["poll"]);
7 years ago
if (!$feedret["success"]) {
q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
logger("Profile ".$profile." wasn't reachable (no feed)", LOGGER_DEBUG);
return false;
7 years ago
}
$doc = new DOMDocument();
@$doc->loadXML($feedret["body"]);
$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 = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue;
$updated = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue;
if ($last_updated < $published)
$last_updated = $published;
if ($last_updated < $updated)
$last_updated = $updated;
}
// 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";
7 years ago
q("UPDATE `gcontact` SET `updated` = '%s', `last_contact` = '%s' WHERE `nurl` = '%s'",
dbesc(dbm::date($last_updated)), dbesc(dbm::date()), dbesc(normalise_link($profile)));
7 years ago
if (($gcontacts[0]["generation"] == 0))
q("UPDATE `gcontact` SET `generation` = 9 WHERE `nurl` = '%s'",
dbesc(normalise_link($profile)));
logger("Profile ".$profile." was last updated at ".$last_updated, LOGGER_DEBUG);
return($last_updated);
}
function poco_do_update($created, $updated, $last_failure, $last_contact) {
$now = strtotime(datetime_convert());
if ($updated > $last_contact)
$contact_time = strtotime($updated);
else
$contact_time = strtotime($last_contact);
$failure_time = strtotime($last_failure);
$created_time = strtotime($created);
// If there is no "created" time then use the current time
if ($created_time <= 0)
$created_time = $now;
// If the last contact was less than 24 hours then don't update
if (($now - $contact_time) < (60 * 60 * 24))
return false;
// If the last failure was less than 24 hours then don't update
if (($now - $failure_time) < (60 * 60 * 24))
return false;
// If the last contact was less than a week ago and the last failure is older than a week then don't update
//if ((($now - $contact_time) < (60 * 60 * 24 * 7)) AND ($contact_time > $failure_time))
// return false;
// If the last contact time was more than a week ago and the contact was created more than a week ago, then only try once a week
if ((($now - $contact_time) > (60 * 60 * 24 * 7)) AND (($now - $created_time) > (60 * 60 * 24 * 7)) AND (($now - $failure_time) < (60 * 60 * 24 * 7)))
return false;
// If the last contact time was more than a month ago and the contact was created more than a month ago, then only try once a month
if ((($now - $contact_time) > (60 * 60 * 24 * 30)) AND (($now - $created_time) > (60 * 60 * 24 * 30)) AND (($now - $failure_time) < (60 * 60 * 24 * 30)))
return false;
return true;
}
function poco_to_boolean($val) {
if (($val == "true") OR ($val == 1))
return(true);
if (($val == "false") OR ($val == 0))
return(false);
return ($val);
}
/**
* @brief Detect server type (Hubzilla or Friendica) via the poco data
*
* @param object $data POCO data
* @return array Server data
*/
function poco_detect_poco_data($data) {
$server = false;
if (!isset($data->entry)) {
return false;
}
if (count($data->entry) == 0) {
return false;
}
if (!isset($data->entry[0]->urls)) {
return false;
}
if (count($data->entry[0]->urls) == 0) {
return false;
}
foreach ($data->entry[0]->urls AS $url) {
if ($url->type == 'zot') {
$server = array();
$server["platform"] = 'Hubzilla';
$server["network"] = NETWORK_DIASPORA;
return $server;
}
}
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') {