X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=include%2FProbe.php;h=a245ef250f2e12b001de453a98d6dca410795761;hb=b50769b6d09549e1641bab1813087fd928602129;hp=4dcbc7841156546c2688e74cc6eb4f1aedef7048;hpb=c625b6aba83fa3ad13d57ae46f8821dd3e747496;p=friendica.git diff --git a/include/Probe.php b/include/Probe.php index 4dcbc78411..a245ef250f 100644 --- a/include/Probe.php +++ b/include/Probe.php @@ -1,6 +1,7 @@ $link) { if (isset($link["@attributes"])) $attributes = $link["@attributes"]; @@ -83,6 +96,122 @@ class Probe { return $xrd_data; } + /** + * @brief Perform Webfinger lookup and return DFRN data + * + * Given an email style address, perform webfinger lookup and + * return the resulting DFRN profile URL, or if no DFRN profile URL + * is located, returns an OStatus subscription template (prefixed + * with the string 'stat:' to identify it as on OStatus template). + * If this isn't an email style address just return $webbie. + * Return an empty string if email-style addresses but webfinger fails, + * or if the resultant personal XRD doesn't contain a supported + * subscription/friend-request attribute. + * + * amended 7/9/2011 to return an hcard which could save potentially loading + * a lengthy content page to scrape dfrn attributes + * + * @param string $webbie Address that should be probed + * @param string $hcard Link to the hcard - is returned by reference + * + * @return string profile link + */ + + public static function webfinger_dfrn($webbie, &$hcard) { + + $profile_link = ''; + + $links = self::lrdd($webbie); + logger('webfinger_dfrn: '.$webbie.':'.print_r($links,true), LOGGER_DATA); + if (count($links)) { + foreach ($links as $link) { + if ($link['@attributes']['rel'] === NAMESPACE_DFRN) + $profile_link = $link['@attributes']['href']; + if (($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB) AND ($profile_link == "")) + $profile_link = 'stat:'.$link['@attributes']['template']; + if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') + $hcard = $link['@attributes']['href']; + } + } + return $profile_link; + } + + /** + * @brief Check an URI for LRDD data + * + * this is a replacement for the "lrdd" function in include/network.php. + * It isn't used in this class and has some redundancies in the code. + * When time comes we can check the existing calls for "lrdd" if we can rework them. + * + * @param string $uri Address that should be probed + * + * @return array uri data + */ + public static function lrdd($uri) { + + $lrdd = self::xrd($uri); + + if (!$lrdd) { + $parts = @parse_url($uri); + if (!$parts) + return array(); + + $host = $parts["host"]; + + $path_parts = explode("/", trim($parts["path"], "/")); + + do { + $lrdd = self::xrd($host); + $host .= "/".array_shift($path_parts); + } while (!$lrdd AND (sizeof($path_parts) > 0)); + } + + if (!$lrdd) + return array(); + + foreach ($lrdd AS $key => $link) { + if ($webfinger) + continue; + + if (!in_array($key, array("lrdd", "lrdd-xml", "lrdd-json"))) + continue; + + $path = str_replace('{uri}', urlencode($uri), $link); + $webfinger = self::webfinger($path); + + if (!$webfinger AND (strstr($uri, "@"))) { + $path = str_replace('{uri}', urlencode("acct:".$uri), $link); + $webfinger = self::webfinger($path); + } + } + + if (!is_array($webfinger["links"])) + return false; + + $data = array(); + + foreach ($webfinger["links"] AS $link) + $data[] = array("@attributes" => $link); + + if (is_array($webfinger["aliases"])) + foreach ($webfinger["aliases"] AS $alias) + $data[] = array("@attributes" => + array("rel" => "alias", + "href" => $alias)); + + return $data; + } + + /** + * @brief Fetch information (protocol endpoints and user information) about a given uri + * + * @param string $uri Address that should be probed + * @param string $network Test for this specific network + * @param integer $uid User ID for the probe (only used for mails) + * @param boolean $cache Use cached values? + * + * @return array uri data + */ public static function uri($uri, $network = "", $uid = 0, $cache = true) { if ($cache) { @@ -106,12 +235,21 @@ class Probe { else $data["photo"] = App::get_baseurl().'/images/person-175.jpg'; - if (!isset($data["name"])) - $data["name"] = $data["url"]; + if (!isset($data["name"]) OR ($data["name"] == "")) { + if (isset($data["nick"])) + $data["name"] = $data["nick"]; - if (!isset($data["nick"])) + if ($data["name"] == "") + $data["name"] = $data["url"]; + } + + if (!isset($data["nick"]) OR ($data["nick"] == "")) { $data["nick"] = strtolower($data["name"]); + if (strpos($data['nick'], ' ')) + $data['nick'] = trim(substr($data['nick'], 0, strpos($data['nick'], ' '))); + } + if (!isset($data["network"])) $data["network"] = NETWORK_PHANTOM; @@ -143,9 +281,20 @@ class Probe { return $data; } + /** + * @brief Fetch information (protocol endpoints and user information) about a given uri + * + * This function is only called by the "uri" function that adds caching and rearranging of data. + * + * @param string $uri Address that should be probed + * @param string $network Test for this specific network + * @param integer $uid User ID for the probe (only used for mails) + * + * @return array uri data + */ private function detect($uri, $network, $uid) { if (strstr($uri, '@')) { - // If the URI starts with "mailto:" then jum directly to the mail detection + // If the URI starts with "mailto:" then jump directly to the mail detection if (strpos($url,'mailto:') !== false) { $uri = str_replace('mailto:', '', $url); return self::mail($uri, $uid); @@ -160,7 +309,11 @@ class Probe { $host = substr($uri,strpos($uri, '@') + 1); $nick = substr($uri,0, strpos($uri, '@')); + if (strpos($uri, '@twitter.com')) + return array("network" => NETWORK_TWITTER); + $lrdd = self::xrd($host); + if (!$lrdd) return self::mail($uri, $uid); @@ -174,6 +327,10 @@ class Probe { // todo: Ports? $host = $parts["host"]; + + if ($host == 'twitter.com') + return array("network" => NETWORK_TWITTER); + $lrdd = self::xrd($host); $path_parts = explode("/", trim($parts["path"], "/")); @@ -188,7 +345,6 @@ class Probe { $nick = array_pop($path_parts); $addr = $nick."@".$host; } - $webfinger = false; /// @todo Do we need the prefix "acct:" or "acct://"? @@ -200,9 +356,28 @@ class Probe { if (!in_array($key, array("lrdd", "lrdd-xml", "lrdd-json"))) continue; + // Try webfinger with the address (user@domain.tld) $path = str_replace('{uri}', urlencode($addr), $link); - $webfinger = self::webfinger($path); + + // Mastodon needs to have it with "acct:" + if (!$webfinger) { + $path = str_replace('{uri}', urlencode("acct:".$addr), $link); + $webfinger = self::webfinger($path); + } + + // If webfinger wasn't successful then try it with the URL - possibly in the format https://... + if (!$webfinger AND ($uri != $addr)) { + $path = str_replace('{uri}', urlencode($uri), $link); + $webfinger = self::webfinger($path); + + // Since the detection with the address wasn't successful, we delete it. + if ($webfinger) { + $nick = ""; + $addr = ""; + } + } + } if (!$webfinger) return self::feed($uri); @@ -224,10 +399,10 @@ class Probe { else { // We overwrite the detected nick with our try if the previois routines hadn't detected it. // Additionally it is overwritten when the nickname doesn't make sense (contains spaces). - if (!isset($result["nick"]) OR ($result["nick"] == "") OR (strstr($result["nick"], " "))) + if ((!isset($result["nick"]) OR ($result["nick"] == "") OR (strstr($result["nick"], " "))) AND ($nick != "")) $result["nick"] = $nick; - if (!isset($result["addr"]) OR ($result["addr"] == "")) + if ((!isset($result["addr"]) OR ($result["addr"] == "")) AND ($addr != "")) $result["addr"] = $addr; } @@ -242,6 +417,15 @@ class Probe { return $result; } + /** + * @brief Perform a webfinger request. + * + * For details see RFC 7033: + * + * @param string $url Address that should be probed + * + * @return array webfinger data + */ private function webfinger($url) { $xrd_timeout = Config::get('system','xrd_timeout', 20); @@ -287,6 +471,17 @@ class Probe { return $webfinger; } + /** + * @brief Poll the Friendica specific noscrape page. + * + * "noscrape" is a faster alternative to fetch the data from the hcard. + * This functionality was originally created for the directory. + * + * @param string $noscrape Link to the noscrape page + * @param array $data The already fetched data + * + * @return array noscrape data + */ private function poll_noscrape($noscrape, $data) { $content = fetch_url($noscrape); if (!$content) @@ -342,6 +537,13 @@ class Probe { return $data; } + /** + * @brief Check for valid DFRN data + * + * @param array $data DFRN data + * + * @return int Number of errors + */ public static function valid_dfrn($data) { $errors = 0; if(!isset($data['key'])) @@ -357,6 +559,13 @@ class Probe { return $errors; } + /** + * @brief Fetch data from a DFRN profile page and via "noscrape" + * + * @param string $profile Link to the profile page + * + * @return array profile data + */ public static function profile($profile) { $data = array(); @@ -386,6 +595,13 @@ class Probe { return $prof_data; } + /** + * @brief Check for DFRN contact + * + * @param array $webfinger Webfinger data + * + * @return array DFRN data + */ private function dfrn($webfinger) { $hcard = ""; @@ -434,6 +650,15 @@ class Probe { return $data; } + /** + * @brief Poll the hcard page (Diaspora and Friendica specific) + * + * @param string $hcard Link to the hcard page + * @param array $data The already fetched data + * @param boolean $dfrn Poll DFRN specific data + * + * @return array hcard data + */ private function poll_hcard($hcard, $data, $dfrn = false) { $doc = new DOMDocument(); @@ -519,6 +744,13 @@ class Probe { return $data; } + /** + * @brief Check for Diaspora contact + * + * @param array $webfinger Webfinger data + * + * @return array Diaspora data + */ private function diaspora($webfinger) { $hcard = ""; @@ -566,6 +798,9 @@ class Probe { isset($data["pubkey"]) AND ($hcard != "")) { $data["network"] = NETWORK_DIASPORA; + // The Diaspora handle must always be lowercase + $data["addr"] = strtolower($data["addr"]); + // We have to overwrite the detected value for "notify" since Hubzilla doesn't send it $data["notify"] = $data["baseurl"]."/receive/users/".$data["guid"]; $data["batch"] = $data["baseurl"]."/receive/public"; @@ -575,10 +810,25 @@ class Probe { return $data; } + /** + * @brief Check for OStatus contact + * + * @param array $webfinger Webfinger data + * + * @return array OStatus data + */ private function ostatus($webfinger) { - $pubkey = ""; $data = array(); + if (is_array($webfinger["aliases"])) + foreach($webfinger["aliases"] AS $alias) + if (strstr($alias, "@")) + $data["addr"] = str_replace('acct:', '', $alias); + + if (is_string($webfinger["subject"]) AND strstr($webfinger["subject"], "@")) + $data["addr"] = str_replace('acct:', '', $webfinger["subject"]); + + $pubkey = ""; foreach ($webfinger["links"] AS $link) { if (($link["rel"] == "http://webfinger.net/rel/profile-page") AND ($link["type"] == "text/html") AND ($link["href"] != "")) @@ -595,7 +845,7 @@ class Probe { $pubkey = substr($pubkey, strpos($pubkey, ',') + 1); else $pubkey = substr($pubkey, 5); - } else + } elseif (normalise_link($pubkey) == 'http://') $pubkey = fetch_url($pubkey); $key = explode(".", $pubkey); @@ -633,6 +883,12 @@ class Probe { if ($feed_data["header"]["author-id"] != "") $data["alias"] = $feed_data["header"]["author-id"]; + if ($feed_data["header"]["author-location"] != "") + $data["location"] = $feed_data["header"]["author-location"]; + + if ($feed_data["header"]["author-about"] != "") + $data["about"] = $feed_data["header"]["author-about"]; + // OStatus has serious issues when the the url doesn't fit (ssl vs. non ssl) // So we take the value that we just fetched, although the other one worked as well if ($feed_data["header"]["author-link"] != "") @@ -642,6 +898,13 @@ class Probe { return $data; } + /** + * @brief Fetch data from a pump.io profile page + * + * @param string $profile Link to the profile page + * + * @return array profile data + */ private function pumpio_profile_data($profile) { $doc = new DOMDocument(); @@ -670,26 +933,32 @@ class Probe { return $data; } + /** + * @brief Check for pump.io contact + * + * @param array $webfinger Webfinger data + * + * @return array pump.io data + */ private function pumpio($webfinger) { + $data = array(); foreach ($webfinger["links"] AS $link) { if (($link["rel"] == "http://webfinger.net/rel/profile-page") AND ($link["type"] == "text/html") AND ($link["href"] != "")) $data["url"] = $link["href"]; elseif (($link["rel"] == "activity-inbox") AND ($link["href"] != "")) - $data["activity-inbox"] = $link["href"]; + $data["notify"] = $link["href"]; elseif (($link["rel"] == "activity-outbox") AND ($link["href"] != "")) - $data["activity-outbox"] = $link["href"]; + $data["poll"] = $link["href"]; elseif (($link["rel"] == "dialback") AND ($link["href"] != "")) $data["dialback"] = $link["href"]; } - if (isset($data["activity-inbox"]) AND isset($data["activity-outbox"]) AND + if (isset($data["poll"]) AND isset($data["notify"]) AND isset($data["dialback"]) AND isset($data["url"])) { // by now we use these fields only for the network type detection // So we unset all data that isn't used at the moment - unset($data["activity-inbox"]); - unset($data["activity-outbox"]); unset($data["dialback"]); $data["network"] = NETWORK_PUMPIO; @@ -706,6 +975,13 @@ class Probe { return $data; } + /** + * @brief Check page for feed link + * + * @param string $url Page link + * + * @return string feed link + */ private function get_feed_link($url) { $doc = new DOMDocument(); @@ -736,6 +1012,14 @@ class Probe { return $feed_url; } + /** + * @brief Check for feed contact + * + * @param string $url Profile link + * @param boolean $probe Do a probe if the page contains a feed link + * + * @return array feed data + */ private function feed($url, $probe = true) { $feed = fetch_url($url); $feed_data = feed_import($feed, $dummy1, $dummy2, $dummy3, true); @@ -777,6 +1061,14 @@ class Probe { return $data; } + /** + * @brief Check for mail contact + * + * @param string $uri Profile link + * @param integer $uid User ID + * + * @return array mail data + */ private function mail($uri, $uid) { if (!validate_email($uri))