<?php namespace Zotlabs\Zot; class Finger { static private $token; /** * @brief Look up information about channel. * * @param string $webbie * does not have to be host qualified e.g. 'foo' is treated as 'foo\@thishub' * @param array $channel * (optional), if supplied permissions will be enumerated specifically for $channel * @param boolean $autofallback * fallback/failover to http if https connection cannot be established. Default is true. * * @return zotinfo array (with 'success' => true) or array('success' => false); */ static public function run($webbie, $channel = null, $autofallback = true) { $ret = array('success' => false); self::$token = random_string(); if (strpos($webbie,'@') === false) { $address = $webbie; $host = App::get_hostname(); } else { $address = substr($webbie,0,strpos($webbie,'@')); $host = substr($webbie,strpos($webbie,'@')+1); } $xchan_addr = $address . '@' . $host; if ((! $address) || (! $xchan_addr)) { logger('zot_finger: no address :' . $webbie); return $ret; } logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA, LOG_DEBUG); // potential issue here; the xchan_addr points to the primary hub. // The webbie we were called with may not, so it might not be found // unless we query for hubloc_addr instead of xchan_addr $r = q("select xchan.*, hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_addr = '%s' and hubloc_primary = 1 limit 1", dbesc($xchan_addr) ); if ($r) { $url = $r[0]['hubloc_url']; if ($r[0]['hubloc_network'] && $r[0]['hubloc_network'] !== 'zot') { logger('zot_finger: alternate network: ' . $webbie); logger('url: '.$url.', net: '.var_export($r[0]['hubloc_network'],true), LOGGER_DATA, LOG_DEBUG); return $ret; } } else { $url = 'https://' . $host; } $rhs = '/.well-known/zot-info'; $https = ((strpos($url,'https://') === 0) ? true : false); logger('zot_finger: ' . $address . ' at ' . $url, LOGGER_DEBUG); if ($channel) { $postvars = array( 'address' => $address, 'target' => $channel['channel_guid'], 'target_sig' => $channel['channel_guid_sig'], 'key' => $channel['channel_pubkey'], 'token' => self::$token ); $result = z_post_url($url . $rhs,$postvars); if ((! $result['success']) && ($autofallback)) { if ($https) { logger('zot_finger: https failed. falling back to http'); $result = z_post_url('http://' . $host . $rhs,$postvars); } } } else { $rhs .= '?f=&address=' . urlencode($address) . '&token=' . self::$token; $result = z_fetch_url($url . $rhs); if ((! $result['success']) && ($autofallback)) { if ($https) { logger('zot_finger: https failed. falling back to http'); $result = z_fetch_url('http://' . $host . $rhs); } } } if(! $result['success']) { logger('zot_finger: no results'); return $ret; } $x = json_decode($result['body'],true); if($x) { $signed_token = ((is_array($x) && array_key_exists('signed_token',$x)) ? $x['signed_token'] : null); if($signed_token) { $valid = rsa_verify('token.' . self::$token,base64url_decode($signed_token),$x['key']); if(! $valid) { logger('invalid signed token: ' . $url . $rhs, LOGGER_NORMAL, LOG_ERR); return $ret; } } else { logger('No signed token from ' . $url . $rhs, LOGGER_NORMAL, LOG_WARNING); // after 2017-01-01 this will be a hard error unless you over-ride it. if((time() > 1483228800) && (! get_config('system','allow_unsigned_zotfinger'))) return $ret; } } return $x; } }