aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs/Zot6/Finger.php
blob: 22ce4685dc0fb2a1405314ef67964844ee676125 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<?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'],'acct:' . channel_reddress($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;
	}

}