function webfinger_content(&$a) { $o .= '<h3>Webfinger Diagnostic</h3>'; $o .= '<form action="webfinger" method="get">'; $o .= 'Lookup address: <input type="text" style="width: 250px;" name="addr" value="' . $_GET['addr'] . '" />'; $o .= '<input type="submit" name="submit" value="Submit" /></form>'; $o .= '<br /><br />'; if (x($_GET, 'addr')) { $addr = trim($_GET['addr']); if (strpos($addr, '@') !== false) { $res = webfinger_rfc7033($addr, true); if (!$res) { $res = old_webfinger($addr); } } else { if (function_exists('lrdd')) { $res = lrdd($addr); } } $o .= '<pre>'; $o .= str_replace("\n", '<br />', print_r($res, true)); $o .= '</pre>'; } return $o; }
function get_salmon_key($uri, $keyhash) { $ret = array(); logger('Fetching salmon key for ' . $uri, LOGGER_DEBUG, LOG_INFO); $x = webfinger_rfc7033($uri, true); logger('webfinger returns: ' . print_r($x, true), LOGGER_DATA, LOG_DEBUG); if ($x && array_key_exists('links', $x) && $x['links']) { foreach ($x['links'] as $link) { if (array_key_exists('rel', $link) && $link['rel'] === 'magic-public-key') { $ret[] = $link['href']; } } } else { $arr = old_webfinger($uri); logger('old webfinger returns: ' . print_r($arr, true), LOGGER_DATA, LOG_DEBUG); if (is_array($arr)) { foreach ($arr as $a) { if ($a['@attributes']['rel'] === 'magic-public-key') { $ret[] = $a['@attributes']['href']; } } } else { return ''; } } // We have found at least one key URL // If it's inline, parse it - otherwise get the key if (count($ret)) { for ($x = 0; $x < count($ret); $x++) { if (substr($ret[$x], 0, 5) === 'data:') { $ret[$x] = convert_salmon_key($ret[$x]); } } } logger('Key located: ' . print_r($ret, true), LOGGER_DEBUG, LOG_INFO); if (count($ret) == 1) { // We only found one one key so we don't care if the hash matches. // If it's the wrong key we'll find out soon enough because // message verification will fail. This also covers some older // software which don't supply a keyhash. As long as they only // have one key we'll be right. return $ret[0]; } else { foreach ($ret as $a) { $hash = base64url_encode(hash('sha256', $a)); if ($hash == $keyhash) { return $a; } } } return ''; }
function get() { $o .= '<h3>Webfinger Diagnostic</h3>'; $o .= '<form action="webfinger" method="get">'; $o .= 'Lookup address: <input type="text" style="width: 250px;" name="addr" value="' . $_GET['addr'] . '" />'; $o .= '<input type="submit" name="submit" value="Submit" /></form>'; $o .= '<br /><br />'; $old = false; if (x($_GET, 'addr')) { $addr = trim($_GET['addr']); // if(strpos($addr,'@') !== false) { $res = webfinger_rfc7033($addr, true); if (!$res) { $res = old_webfinger($addr); $old = true; } // } // else { // if(function_exists('lrdd')) // $res = lrdd($addr); // } if ($res && $old) { foreach ($res as $r) { if ($r['@attributes']['rel'] === 'http://microformats.org/profile/hcard') { $hcard = unamp($r['@attributes']['href']); require_once 'library/HTML5/Parser.php'; $res['vcard'] = scrape_vcard($hcard); break; } } } $o .= '<pre>'; $o .= str_replace("\n", '<br />', print_r($res, true)); $o .= '</pre>'; } return $o; }
function diaspora_discover(&$a, &$b) { require_once 'include/network.php'; $result = array(); $network = null; $diaspora = false; $diaspora_base = ''; $diaspora_guid = ''; $diaspora_key = ''; $dfrn = false; $x = old_webfinger($webbie); if ($x) { logger('old_webfinger: ' . print_r($x, true)); foreach ($x as $link) { if ($link['@attributes']['rel'] === NAMESPACE_DFRN) { $dfrn = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'salmon') { $notify = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === NAMESPACE_FEED) { $poll = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') { $hcard = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $profile = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') { $poco = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { $diaspora_base = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { $diaspora_guid = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'diaspora-public-key') { $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); if (strstr($diaspora_key, 'RSA ')) { $pubkey = rsatopem($diaspora_key); } else { $pubkey = $diaspora_key; } $diaspora = true; } } if ($diaspora && $diaspora_base && $diaspora_guid) { $guid = $diaspora_guid; $diaspora_base = trim($diaspora_base, '/'); $notify = $diaspora_base . '/receive'; if (strpos($webbie, '@')) { $addr = str_replace('acct:', '', $webbie); $hostname = substr($webbie, strpos($webbie, '@') + 1); } $network = 'diaspora'; // until we get a dfrn layer, we'll use diaspora protocols for Friendica, // but give it a different network so we can go back and fix these when we get proper support. // It really should be just 'friendica' but we also want to distinguish // between Friendica sites that we can use D* protocols with and those we can't. // Some Friendica sites will have Diaspora disabled. if ($dfrn) { $network = 'friendica-over-diaspora'; } if ($hcard) { $vcard = scrape_vcard($hcard); $vcard['nick'] = substr($webbie, 0, strpos($webbie, '@')); if (!$vcard['fn']) { $vcard['fn'] = $webbie; } } $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($addr)); /** * * Diaspora communications are notoriously unreliable and receiving profile update messages (indeed any messages) * are pretty much random luck. We'll check the timestamp of the xchan_name_date at a higher level and refresh * this record once a month; because if you miss a profile update message and they update their profile photo or name * you're otherwise stuck with stale info until they change their profile again - which could be years from now. * */ if ($r) { $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1", dbesc($vcard['fn']), dbesc($network), dbesc(datetime_convert()), dbesc($addr)); } else { $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($addr), dbesc($guid), dbesc($pubkey), dbesc($addr), dbesc($profile), dbesc($vcard['fn']), dbesc($network), dbescdate(datetime_convert())); } $r = q("select * from hubloc where hubloc_hash = '%s' limit 1", dbesc($webbie)); if (!$r) { $r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)", dbesc($guid), dbesc($addr), dbesc($addr), dbesc($network), dbesc(trim($diaspora_base, '/')), dbesc($hostname), dbesc($notify), dbescdate(datetime_convert())); } $photos = import_xchan_photo($vcard['photo'], $addr); $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbescdate(datetime_convert('UTC', 'UTC', $arr['photo_updated'])), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($addr)); return true; } return false; /* $vcard['fn'] = notags($vcard['fn']); $vcard['nick'] = str_replace(' ','',notags($vcard['nick'])); $result['name'] = $vcard['fn']; $result['nick'] = $vcard['nick']; $result['guid'] = $guid; $result['url'] = $profile; $result['hostname'] = $hostname; $result['addr'] = $addr; $result['batch'] = $batch; $result['notify'] = $notify; $result['poll'] = $poll; $result['request'] = $request; $result['confirm'] = $confirm; $result['poco'] = $poco; $result['photo'] = $vcard['photo']; $result['priority'] = $priority; $result['network'] = $network; $result['alias'] = $alias; $result['pubkey'] = $pubkey; logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG); return $result; */ /* Sample Diaspora result. Array ( [name] => Mike Macgirvin [nick] => macgirvin [guid] => a9174a618f8d269a [url] => https://joindiaspora.com/u/macgirvin [hostname] => joindiaspora.com [addr] => macgirvin@joindiaspora.com [batch] => [notify] => https://joindiaspora.com/receive [poll] => https://joindiaspora.com/public/macgirvin.atom [request] => [confirm] => [poco] => [photo] => https://joindiaspora.s3.amazonaws.com/uploads/images/thumb_large_fec4e6eef13ae5e56207.jpg [priority] => [network] => diaspora [alias] => [pubkey] => -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtihtyIuRDWkDpCA+I1UaQ jI4S7k625+A7EEJm+pL2ZVSJxeCKiFeEgHBQENjLMNNm8l8F6blxgQqE6ZJ9Spa7f tlaXYTRCrfxKzh02L3hR7sNA+JS/nXJaUAIo+IwpIEspmcIRbD9GB7Wv/rr+M28uH 31EeYyDz8QL6InU/bJmnCdFvmEMBQxJOw1ih9tQp7UNJAbUMCje0WYFzBz7sfcaHL OyYcCOqOCBLdGucUoJzTQ9iDBVzB8j1r1JkIHoEb2moUoKUp+tkCylNfd/3IVELF9 7w1Qjmit3m50OrJk2DQOXvCW9KQxaQNdpRPSwhvemIt98zXSeyZ1q/YjjOwG0DWDq AF8aLj3/oQaZndTPy/6tMiZogKaijoxj8xFLuPYDTw5VpKquriVC0z8oxyRbv4t9v 8JZZ9BXqzmayvY3xZGGp8NulrfjW+me2bKh0/df1aHaBwpZdDTXQ6kqAiS2FfsuPN vg57fhfHbL1yJ4oDbNNNeI0kJTGchXqerr8C20khU/cQ2Xt31VyEZtnTB665Ceugv kp3t2qd8UpAVKl430S5Quqx2ymfUIdxdW08CEjnoRNEL3aOWOXfbf4gSVaXmPCR4i LSIeXnd14lQYK/uxW/8cTFjcmddsKxeXysoQxbSa9VdDK+KkpZdgYXYrTTofXs6v+ 4afAEhRaaY+MCAwEAAQ== -----END PUBLIC KEY----- ) */ } }
function discover_by_webbie($webbie) { require_once 'library/HTML5/Parser.php'; $result = array(); $network = null; $diaspora = false; $gnusoc = false; $dfrn = false; $has_salmon = false; $salmon_key = false; $atom_feed = false; $diaspora_base = ''; $diaspora_guid = ''; $diaspora_key = ''; $webbie = strtolower($webbie); $x = webfinger_rfc7033($webbie, true); if ($x && array_key_exists('links', $x) && $x['links']) { foreach ($x['links'] as $link) { if (array_key_exists('rel', $link)) { // If we discover zot - don't search further; grab the info and get out of // here. if ($link['rel'] === PROTOCOL_ZOT) { logger('discover_by_webbie: zot found for ' . $webbie, LOGGER_DEBUG); if (array_key_exists('zot', $x) && $x['zot']['success']) { $i = import_xchan($x['zot']); } else { $z = z_fetch_url($link['href']); if ($z['success']) { $j = json_decode($z['body'], true); $i = import_xchan($j); return true; } } } if ($link['rel'] == NAMESPACE_DFRN) { $dfrn = $link['href']; } if ($link['rel'] == 'magic-public-key') { if (substr($link['href'], 0, 5) === 'data:') { $salmon_key = convert_salmon_key($link['href']); } } if ($link['rel'] == 'salmon') { $has_salmon = true; $salmon = $link['href']; } if ($link['rel'] == 'http://schemas.google.com/g/2010#updates-from') { $atom_feed = $link['href']; } } } } logger('webfinger: ' . print_r($x, true), LOGGER_DATA, LOG_INFO); $arr = array('address' => $webbie, 'success' => false, 'webfinger' => $x); call_hooks('discover_channel_webfinger', $arr); if ($arr['success']) { return true; } $aliases = array(); // Now let's make some decisions on what we may need // to obtain further info $probe_atom = false; $probe_old = false; $probe_hcard = false; $address = ''; $location = ''; $nickname = ''; $fullname = ''; $avatar = ''; $pubkey = ''; if (is_array($x)) { if (array_key_exists('address', $x)) { $address = $x['address']; } if (array_key_exists('location', $x)) { $location = $x['location']; } if (array_key_exists('nickname', $x)) { $nickname = $x['nickname']; } } if (!$x) { $probe_old = true; } if (!$dfrn && !$has_salmon) { $probe_old = true; } if ($probe_old) { $y = old_webfinger($webbie); if ($y) { logger('old_webfinger: ' . print_r($x, true)); foreach ($y as $link) { if ($link['@attributes']['rel'] === NAMESPACE_DFRN) { $dfrn = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'salmon') { $notify = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === NAMESPACE_FEED) { $poll = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') { $hcard = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') { $profile = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') { $poco = unamp($link['@attributes']['href']); } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/seed_location') { $diaspora_base = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'http://joindiaspora.com/guid') { $diaspora_guid = unamp($link['@attributes']['href']); $diaspora = true; } if ($link['@attributes']['rel'] === 'diaspora-public-key') { $diaspora_key = base64_decode(unamp($link['@attributes']['href'])); if (strstr($diaspora_key, 'RSA ')) { $pubkey = rsatopem($diaspora_key); } else { $pubkey = $diaspora_key; } $diaspora = true; } if ($link['@attributes']['rel'] == 'magic-public-key') { if (substr($link['@attributes']['href'], 0, 5) === 'data:') { $salmon_key = convert_salmon_key($link['@attributes']['href']); } } if ($link['@attributes']['rel'] == 'salmon') { $has_salmon = true; $salmon = $link['@attributes']['href']; } if ($link['@attributes']['rel'] == 'http://schemas.google.com/g/2010#updates-from') { $atom_feed = $link['@attributes']['href']; } if ($link['@attributes']['rel'] === 'alias') { $aliases[] = $link['@attributes']['href']; } if ($link['@attributes']['rel'] === 'subject') { $subject = $link['@attributes']['href']; } } } } if ($subject || $aliases) { if (strpos($webbie, '@')) { $rhs = substr($webbie, strpos($webbie, '@') + 1); } else { $m = parse_url($webbie); if ($m) { $rhs = $m['host'] . ($m['port'] ? ':' . $m['port'] : ''); } } $v = array('subject' => $subject, 'aliases' => $aliases); $address = find_webfinger_address($v, $rhs); $location = find_webfinger_location($v, $rhs); if ($address) { $nickname = substr($address, 0, strpos($address, '@')); } } if ($salmon_key && $has_salmon && $atom_feed && !$dfrn && !$diaspora) { $gnusoc = true; $probe_atom = true; } if (!$pubkey) { $pubkey = $salmon_key; } if (($dfrn || $diaspora) && $hcard) { $probe_hcard = true; } if (!$fullname) { $fullname = $nickname; } if ($probe_atom) { $k = z_fetch_url($atom_feed); if ($k['success']) { $feed_meta = feed_meta($k['body']); } if ($feed_meta) { // stash any discovered pubsubhubbub hubs in case we need to follow them // this will save an expensive lookup later if ($feed_meta['hubs'] && $address) { set_xconfig($address, 'system', 'push_hubs', $feed_meta['hubs']); set_xconfig($address, 'system', 'feed_url', $atom_feed); } if ($feed_meta['author']['author_name']) { $fullname = $feed_meta['author']['author_name']; } if (!$avatar) { if ($feed_meta['author']['author_photo']) { $avatar = $feed_meta['author']['author_photo']; } } // for GNU-social over-ride any url aliases we may have picked up in webfinger // The author.uri element in the feed is likely to be more accurate if ($gnusoc && $feed_meta['author']['author_uri']) { $location = $feed_meta['author']['author_uri']; } } } else { if ($probe_hcard) { $vcard = scrape_vcard($hcard); if ($vcard) { logger('vcard: ' . print_r($vcard, true), LOGGER_DATA); if ($vcard['fn']) { $fullname = $vcard['fn']; } if ($vcard['photo'] && strpos($vcard['photo'], 'http') !== 0) { $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; } if (!$avatar) { $avatar = $vcard['photo']; } } } } if ($profile && !$location) { $location = $profile; } if ($location) { $m = parse_url($location); $base = $m['scheme'] . '://' . $m['host']; $host = $m['host']; } if ($diaspora && $diaspora_base && $diaspora_guid) { if ($dfrn) { $network = 'friendica-over-diaspora'; } else { $network = 'diaspora'; } $base = trim($diaspora_base, '/'); $notify = $base . '/receive'; } else { if ($gnusoc) { $network = 'gnusoc'; $notify = $salmon; } } logger('network: ' . $network); logger('address: ' . $address); logger('fullname: ' . $fullname); logger('pubkey: ' . $pubkey); logger('location: ' . $location); // if we have everything we need, let's create the records if ($network && $address && $fullname && $pubkey && $location) { $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($address)); if ($r) { $r = q("update xchan set xchan_name = '%s', xchan_network = '%s', xchan_name_date = '%s' where xchan_hash = '%s' limit 1", dbesc($fullname), dbesc($network), dbesc(datetime_convert()), dbesc($address)); } else { $r = q("insert into xchan ( xchan_hash, xchan_guid, xchan_pubkey, xchan_addr, xchan_url, xchan_name, xchan_network, xchan_name_date ) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($address), dbesc($diaspora_guid ? $diaspora_guid : $location), dbesc($pubkey), dbesc($address), dbesc($location), dbesc($fullname), dbesc($network), dbescdate(datetime_convert())); } $r = q("select * from hubloc where hubloc_hash = '%s' limit 1", dbesc($address)); if (!$r) { $r = q("insert into hubloc ( hubloc_guid, hubloc_hash, hubloc_addr, hubloc_network, hubloc_url, hubloc_host, hubloc_callback, hubloc_updated, hubloc_primary ) values ('%s','%s','%s','%s','%s','%s','%s','%s', 1)", dbesc($diaspora_guid ? $diaspora_guid : $location), dbesc($address), dbesc($address), dbesc($network), dbesc($base), dbesc($host), dbesc($notify), dbescdate(datetime_convert())); } $photos = import_xchan_photo($avatar, $address); $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", dbescdate(datetime_convert()), dbesc($photos[0]), dbesc($photos[1]), dbesc($photos[2]), dbesc($photos[3]), dbesc($address)); return true; } return false; }