aboutsummaryrefslogblamecommitdiffstats
path: root/Zotlabs/Zot6/Finger.php
blob: cec3f98ab1bb0a97277acdfb0f9970304422d6c9 (plain) (tree)

























































































                                                                                                                                      
                                                                                                                 










                                                                                                                                    
                 



















                                                                                                 
                                                                           




















                                                                                                                             
<?php

namespace Zotlabs\Zot6;

/**
 * @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 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
			);

			$headers = [];
			$headers['X-Zot-Channel'] = $channel['channel_address'] . '@' . \App::get_hostname();
			$headers['X-Zot-Nonce']   = random_string();
			$xhead = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel));

			$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 = 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 = zot_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;
	}

}