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 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; }