use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Database\DBA;
+use Friendica\Model\Contact;
use Friendica\Model\Profile;
+use Friendica\Protocol\ActivityNamespace;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\Email;
use Friendica\Protocol\Feed;
use Friendica\Util\Crypto;
-use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Strings;
use Friendica\Util\XML;
$xrd = null;
$curlResult = Network::curl($ssl_url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']);
+ $ssl_connection_error = ($curlResult->getErrorNumber() == CURLE_COULDNT_CONNECT) || ($curlResult->getReturnCode() == 0);
if ($curlResult->isSuccess()) {
$xml = $curlResult->getBody();
$xrd = XML::parseString($xml, false);
$host_url = 'https://'.$host;
+ } elseif ($curlResult->isTimeout()) {
+ Logger::info('Probing timeout', ['url' => $ssl_url], Logger::DEBUG);
+ self::$istimeout = true;
+ return false;
}
if (!is_object($xrd)) {
$curlResult = Network::curl($url, false, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']);
+ $connection_error = ($curlResult->getErrorNumber() == CURLE_COULDNT_CONNECT) || ($curlResult->getReturnCode() == 0);
if ($curlResult->isTimeout()) {
- Logger::log("Probing timeout for " . $url, Logger::DEBUG);
+ Logger::info('Probing timeout', ['url' => $url], Logger::DEBUG);
+ self::$istimeout = true;
+ return false;
+ } elseif ($connection_error && $ssl_connection_error) {
self::$istimeout = true;
return false;
}
+
$xml = $curlResult->getBody();
$xrd = XML::parseString($xml, false);
$host_url = 'http://'.$host;
return [];
}
- $lrdd = [];
- // The following webfinger path is defined in RFC 7033 https://tools.ietf.org/html/rfc7033
- // Problem is that Hubzilla currently doesn't provide all data in the JSON webfinger
- // compared to the XML webfinger. So this is commented out by now.
- // $lrdd = array("application/jrd+json" => $host_url.'/.well-known/webfinger?resource={uri}');
+ $lrdd = ['application/jrd+json' => $host_url . '/.well-known/webfinger?resource={uri}'];
foreach ($links["xrd"]["link"] as $value => $link) {
if (!empty($link["@attributes"])) {
Logger::log('webfingerDfrn: '.$webbie.':'.print_r($links, true), Logger::DATA);
if (!empty($links) && is_array($links)) {
foreach ($links as $link) {
- if ($link['@attributes']['rel'] === NAMESPACE_DFRN) {
+ if ($link['@attributes']['rel'] === ActivityNamespace::DFRN) {
$profile_link = $link['@attributes']['href'];
}
- if (($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB) && ($profile_link == "")) {
+ if (($link['@attributes']['rel'] === ActivityNamespace::OSTATUSSUB) && ($profile_link == "")) {
$profile_link = 'stat:'.$link['@attributes']['template'];
}
if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') {
if (!self::$istimeout) {
$ap_profile = ActivityPub::probeProfile($uri);
- if (empty($data) || (!empty($ap_profile) && empty($network) && (($data['network'] ?? '') == Protocol::DFRN))) {
+ if (empty($data) || (!empty($ap_profile) && empty($network) && (($data['network'] ?? '') != Protocol::DFRN))) {
$data = $ap_profile;
} elseif (!empty($ap_profile)) {
$ap_profile['batch'] = '';
$data['url'] = $uri;
}
- if (!empty($data['photo'])) {
- $data['baseurl'] = Network::getUrlMatch(Strings::normaliseLink($data['baseurl'] ?? ''), Strings::normaliseLink($data['photo']));
- } else {
+ if (!empty($data['photo']) && !empty($data['baseurl'])) {
+ $data['baseurl'] = Network::getUrlMatch(Strings::normaliseLink($data['baseurl']), Strings::normaliseLink($data['photo']));
+ } elseif (empty($data['photo'])) {
$data['photo'] = System::baseUrl() . '/images/person-300.jpg';
}
$has_key = false;
foreach ($webfinger['links'] as $link) {
- if ($link['rel'] == NAMESPACE_OSTATUSSUB) {
+ if ($link['rel'] == ActivityNamespace::OSTATUSSUB) {
$is_ostatus = true;
}
if ($link['rel'] == 'magic-public-key') {
if ((!$result && ($network == "")) || ($network == Protocol::OSTATUS)) {
$result = self::ostatus($webfinger);
}
+ if (in_array($network, ['', Protocol::ZOT])) {
+ $result = self::zot($webfinger, $result);
+ }
if ((!$result && ($network == "")) || ($network == Protocol::PUMPIO)) {
$result = self::pumpio($webfinger, $addr);
}
Logger::log($uri." is ".$result["network"], Logger::DEBUG);
- if (empty($result["baseurl"])) {
+ if (empty($result["baseurl"]) && ($result["network"] != Protocol::PHANTOM)) {
$pos = strpos($result["url"], $host);
if ($pos) {
$result["baseurl"] = substr($result["url"], 0, $pos).$host;
return $result;
}
+ /**
+ * Check for Zot contact
+ *
+ * @param array $webfinger Webfinger data
+ * @param array $data previously probed data
+ *
+ * @return array Zot data
+ * @throws HTTPException\InternalServerErrorException
+ */
+ private static function zot($webfinger, $data)
+ {
+ if (!empty($webfinger["aliases"]) && is_array($webfinger["aliases"])) {
+ foreach ($webfinger["aliases"] as $alias) {
+ if (substr($alias, 0, 5) == 'acct:') {
+ $data["addr"] = substr($alias, 5);
+ }
+ }
+ }
+
+ if (!empty($webfinger["subject"]) && (substr($webfinger["subject"], 0, 5) == "acct:")) {
+ $data["addr"] = substr($webfinger["subject"], 5);
+ }
+
+ $zot_url = '';
+ foreach ($webfinger['links'] as $link) {
+ if (($link['rel'] == 'http://purl.org/zot/protocol') && !empty($link['href'])) {
+ $zot_url = $link['href'];
+ }
+ }
+
+ if (empty($zot_url) && !empty($data['addr']) && !empty(self::$baseurl)) {
+ $condition = ['nurl' => Strings::normaliseLink(self::$baseurl), 'platform' => ['hubzilla']];
+ if (!DBA::exists('gserver', $condition)) {
+ return $data;
+ }
+ $zot_url = self::$baseurl . '/.well-known/zot-info?address=' . $data['addr'];
+ }
+
+ if (empty($zot_url)) {
+ return $data;
+ }
+
+ $data = self::pollZot($zot_url, $data);
+
+ if (!empty($data['url']) && !empty($webfinger['aliases']) && is_array($webfinger['aliases'])) {
+ foreach ($webfinger['aliases'] as $alias) {
+ if (!strstr($alias, '@') && Strings::normaliseLink($alias) != Strings::normaliseLink($data['url'])) {
+ $data['alias'] = $alias;
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ public static function pollZot($url, $data)
+ {
+ $curlResult = Network::curl($url);
+ if ($curlResult->isTimeout()) {
+ return $data;
+ }
+ $content = $curlResult->getBody();
+ if (!$content) {
+ return $data;
+ }
+
+ $json = json_decode($content, true);
+ if (!is_array($json)) {
+ return $data;
+ }
+
+ if (empty($data['network'])) {
+ if (!empty($json['protocols']) && in_array('zot', $json['protocols'])) {
+ $data['network'] = Protocol::ZOT;
+ } elseif (!isset($json['protocols'])) {
+ $data['network'] = Protocol::ZOT;
+ }
+ }
+
+ if (!empty($json['guid']) && empty($data['guid'])) {
+ $data['guid'] = $json['guid'];
+ }
+ if (!empty($json['key']) && empty($data['pubkey'])) {
+ $data['pubkey'] = $json['key'];
+ }
+ if (!empty($json['name'])) {
+ $data['name'] = $json['name'];
+ }
+ if (!empty($json['photo'])) {
+ $data['photo'] = $json['photo'];
+ if (!empty($json['photo_updated'])) {
+ $data['photo'] .= '?rev=' . urlencode($json['photo_updated']);
+ }
+ }
+ if (!empty($json['address'])) {
+ $data['addr'] = $json['address'];
+ }
+ if (!empty($json['url'])) {
+ $data['url'] = $json['url'];
+ }
+ if (!empty($json['connections_url'])) {
+ $data['poco'] = $json['connections_url'];
+ }
+ if (isset($json['searchable'])) {
+ $data['hide'] = !$json['searchable'];
+ }
+ if (!empty($json['public_forum'])) {
+ $data['community'] = $json['public_forum'];
+ $data['account-type'] = Contact::PAGE_COMMUNITY;
+ }
+
+ if (!empty($json['profile'])) {
+ $profile = $json['profile'];
+ if (!empty($profile['description'])) {
+ $data['about'] = $profile['description'];
+ }
+ if (!empty($profile['gender'])) {
+ $data['gender'] = $profile['gender'];
+ }
+ if (!empty($profile['keywords'])) {
+ $keywords = implode(', ', $profile['keywords']);
+ if (!empty($keywords)) {
+ $data['keywords'] = $keywords;
+ }
+ }
+
+ $loc = [];
+ if (!empty($profile['region'])) {
+ $loc['region'] = $profile['region'];
+ }
+ if (!empty($profile['country'])) {
+ $loc['country-name'] = $profile['country'];
+ }
+ if (!empty($profile['hometown'])) {
+ $loc['locality'] = $profile['hometown'];
+ }
+ $location = Profile::formatLocation($loc);
+ if (!empty($location)) {
+ $data['location'] = $location;
+ }
+ }
+
+ return $data;
+ }
+
/**
* @brief Perform a webfinger request.
*
// The array is reversed to take into account the order of preference for same-rel links
// See: https://tools.ietf.org/html/rfc7033#section-4.4.4
foreach (array_reverse($webfinger["links"]) as $link) {
- if (($link["rel"] == NAMESPACE_DFRN) && !empty($link["href"])) {
+ if (($link["rel"] == ActivityNamespace::DFRN) && !empty($link["href"])) {
$data["network"] = Protocol::DFRN;
- } elseif (($link["rel"] == NAMESPACE_FEED) && !empty($link["href"])) {
+ } elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) {
$data["poll"] = $link["href"];
} elseif (($link["rel"] == "http://webfinger.net/rel/profile-page") && (($link["type"] ?? "") == "text/html") && !empty($link["href"])) {
$data["url"] = $link["href"];
} elseif (($link["rel"] == "http://microformats.org/profile/hcard") && !empty($link["href"])) {
$hcard_url = $link["href"];
- } elseif (($link["rel"] == NAMESPACE_POCO) && !empty($link["href"])) {
+ } elseif (($link["rel"] == ActivityNamespace::POCO) && !empty($link["href"])) {
$data["poco"] = $link["href"];
} elseif (($link["rel"] == "http://webfinger.net/rel/avatar") && !empty($link["href"])) {
$data["photo"] = $link["href"];
{
$hcard_url = "";
$data = [];
+
// The array is reversed to take into account the order of preference for same-rel links
// See: https://tools.ietf.org/html/rfc7033#section-4.4.4
foreach (array_reverse($webfinger["links"]) as $link) {
$data["guid"] = $link["href"];
} elseif (($link["rel"] == "http://webfinger.net/rel/profile-page") && (($link["type"] ?? "") == "text/html") && !empty($link["href"])) {
$data["url"] = $link["href"];
- } elseif (($link["rel"] == NAMESPACE_FEED) && !empty($link["href"])) {
+ } elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) {
$data["poll"] = $link["href"];
- } elseif (($link["rel"] == NAMESPACE_POCO) && !empty($link["href"])) {
+ } elseif (($link["rel"] == ActivityNamespace::POCO) && !empty($link["href"])) {
$data["poco"] = $link["href"];
} elseif (($link["rel"] == "salmon") && !empty($link["href"])) {
$data["notify"] = $link["href"];
}
}
- if (!isset($data["url"]) || ($hcard_url == "")) {
+ if (empty($data["url"]) || empty($hcard_url)) {
return false;
}
return false;
}
- if (isset($data["url"])
- && isset($data["guid"])
- && isset($data["baseurl"])
- && isset($data["pubkey"])
- && ($hcard_url != "")
+ if (!empty($data["url"])
+ && !empty($data["guid"])
+ && !empty($data["baseurl"])
+ && !empty($data["pubkey"])
+ && !empty($hcard_url)
) {
$data["network"] = Protocol::DIASPORA;
$data["url"] = $link["href"];
} elseif (($link["rel"] == "salmon") && !empty($link["href"])) {
$data["notify"] = $link["href"];
- } elseif (($link["rel"] == NAMESPACE_FEED) && !empty($link["href"])) {
+ } elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) {
$data["poll"] = $link["href"];
} elseif (($link["rel"] == "magic-public-key") && !empty($link["href"])) {
$pubkey = $link["href"];
*/
private static function pumpioProfileData($profile_link)
{
+ $curlResult = Network::curl($profile_link);
+ if (!$curlResult->isSuccess()) {
+ return false;
+ }
+
$doc = new DOMDocument();
- if (!@$doc->loadHTMLFile($profile_link)) {
+ if (!@$doc->loadHTML($curlResult->getBody())) {
return false;
}
/**
* @brief Check for pump.io contact
*
- * @param array $webfinger Webfinger data
- *
- * @param $addr
+ * @param array $webfinger Webfinger data
+ * @param string $addr
* @return array pump.io data
*/
private static function pumpio($webfinger, $addr)
*/
private static function getFeedLink($url)
{
- $doc = new DOMDocument();
+ $curlResult = Network::curl($url);
+ if (!$curlResult->isSuccess()) {
+ return false;
+ }
- if (!@$doc->loadHTMLFile($url)) {
+ $doc = new DOMDocument();
+ if (!@$doc->loadHTML($curlResult->getBody())) {
return false;
}
$attr[$attribute->name] = trim($attribute->value);
}
- if ($feed_url == "") {
+ if (empty($feed_url) && !empty($attr['href'])) {
$feed_url = $attr["href"];
}
}