<?php
namespace Zotlabs\Zot;
/**
* @brief Finger
*
*/
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);
if(strpos($host,'/'))
$host = substr($host,0,strpos($host,'/'));
}
$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 and hubloc_deleted = 0 and hubloc_network = 'zot' 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;
}
$m = parse_url($url);
if($m) {
$parsed_host = strtolower($m['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
);
$headers = [];
$headers['X-Zot-Channel'] = $channel['channel_address'] . '@' . \App::get_hostname();
$headers['X-Zot-Nonce'] = random_string();
$headers['Host'] = $parsed_host;
$xhead = \Zotlabs\Web\HTTPSig::create_sig('',$headers,$channel['channel_prvkey'],
'acct:' . $channel['channel_address'] . '@' . \App::get_hostname(),false);
$retries = 0;
$result = z_post_url($url . $rhs,$postvars,$retries, [ 'headers' => $xhead ]);
if ((! $result['success']) && ($autofallback)) {
if ($https) {
logger('zot_finger: https failed. falling back to http');
$result = z_post_url('http://' . $host . $rhs,$postvars, $retries, [ 'headers' => $xhead ]);
}
}
}
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);
$verify = \Zotlabs\Web\HTTPSig::verify($result,(($x) ? $x['key'] : ''));
if($x && (! $verify['header_valid'])) {
$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);
return $ret;
}
}
return $x;
}
}