((array_key_exists('hubloc_guid', $arr)) ? $arr['hubloc_guid'] : ''), 'hubloc_guid_sig' => ((array_key_exists('hubloc_guid_sig', $arr)) ? $arr['hubloc_guid_sig'] : ''), 'hubloc_id_url' => ((array_key_exists('hubloc_id_url', $arr)) ? $arr['hubloc_id_url'] : ''), 'hubloc_resolver' => ((array_key_exists('hubloc_resolver', $arr)) ? $arr['hubloc_resolver'] : ''), 'hubloc_hash' => ((array_key_exists('hubloc_hash', $arr)) ? $arr['hubloc_hash'] : ''), 'hubloc_addr' => ((array_key_exists('hubloc_addr', $arr)) ? $arr['hubloc_addr'] : ''), 'hubloc_network' => ((array_key_exists('hubloc_network', $arr)) ? $arr['hubloc_network'] : ''), 'hubloc_flags' => ((array_key_exists('hubloc_flags', $arr)) ? $arr['hubloc_flags'] : 0), 'hubloc_status' => ((array_key_exists('hubloc_status', $arr)) ? $arr['hubloc_status'] : 0), 'hubloc_url' => ((array_key_exists('hubloc_url', $arr)) ? $arr['hubloc_url'] : ''), 'hubloc_url_sig' => ((array_key_exists('hubloc_url_sig', $arr)) ? $arr['hubloc_url_sig'] : ''), 'hubloc_site_id' => ((array_key_exists('hubloc_site_id', $arr)) ? $arr['hubloc_site_id'] : ''), 'hubloc_host' => ((array_key_exists('hubloc_host', $arr)) ? $arr['hubloc_host'] : ''), 'hubloc_callback' => ((array_key_exists('hubloc_callback', $arr)) ? $arr['hubloc_callback'] : ''), 'hubloc_connect' => ((array_key_exists('hubloc_connect', $arr)) ? $arr['hubloc_connect'] : ''), 'hubloc_openwebauth' => ((array_key_exists('hubloc_openwebauth', $arr)) ? $arr['hubloc_openwebauth'] : ''), 'hubloc_authredirect' => ((array_key_exists('hubloc_authredirect', $arr)) ? $arr['hubloc_authredirect'] : ''), 'hubloc_sitekey' => ((array_key_exists('hubloc_sitekey', $arr)) ? $arr['hubloc_sitekey'] : ''), 'hubloc_updated' => ((array_key_exists('hubloc_updated', $arr)) ? $arr['hubloc_updated'] : NULL_DATE), 'hubloc_connected' => ((array_key_exists('hubloc_connected', $arr)) ? $arr['hubloc_connected'] : NULL_DATE), 'hubloc_primary' => ((array_key_exists('hubloc_primary', $arr)) ? $arr['hubloc_primary'] : 0), 'hubloc_orphancheck' => ((array_key_exists('hubloc_orphancheck', $arr)) ? $arr['hubloc_orphancheck'] : 0), 'hubloc_error' => ((array_key_exists('hubloc_error', $arr)) ? $arr['hubloc_error'] : 0), 'hubloc_deleted' => ((array_key_exists('hubloc_deleted', $arr)) ? $arr['hubloc_deleted'] : 0) ]; return $update ? update_table_from_array('hubloc', $store, $update) : create_table_from_array('hubloc', $store); } function site_store_lowlevel($arr) { $store = [ 'site_url' => ((array_key_exists('site_url', $arr)) ? $arr['site_url'] : ''), 'site_access' => ((array_key_exists('site_access', $arr)) ? $arr['site_access'] : 0), 'site_flags' => ((array_key_exists('site_flags', $arr)) ? $arr['site_flags'] : 0), 'site_update' => ((array_key_exists('site_update', $arr)) ? $arr['site_update'] : NULL_DATE), 'site_pull' => ((array_key_exists('site_pull', $arr)) ? $arr['site_pull'] : NULL_DATE), 'site_sync' => ((array_key_exists('site_sync', $arr)) ? $arr['site_sync'] : NULL_DATE), 'site_directory' => ((array_key_exists('site_directory', $arr)) ? $arr['site_directory'] : ''), 'site_register' => ((array_key_exists('site_register', $arr)) ? $arr['site_register'] : 0), 'site_sellpage' => ((array_key_exists('site_sellpage', $arr)) ? $arr['site_sellpage'] : ''), 'site_location' => ((array_key_exists('site_location', $arr)) ? $arr['site_location'] : ''), 'site_realm' => ((array_key_exists('site_realm', $arr)) ? $arr['site_realm'] : ''), 'site_valid' => ((array_key_exists('site_valid', $arr)) ? $arr['site_valid'] : 0), 'site_dead' => ((array_key_exists('site_dead', $arr)) ? $arr['site_dead'] : 0), 'site_type' => ((array_key_exists('site_type', $arr)) ? $arr['site_type'] : 0), 'site_project' => ((array_key_exists('site_project', $arr)) ? $arr['site_project'] : ''), 'site_version' => ((array_key_exists('site_version', $arr)) ? $arr['site_version'] : ''), 'site_crypto' => ((array_key_exists('site_crypto', $arr)) ? $arr['site_crypto'] : '') ]; return create_table_from_array('site', $store); } /** * @brief Removes foreign location records which are no longer valid * @return void */ function prune_hub_reinstalls() { $r = q( "select site_url from site where site_type = %d && site_url != '%s'", intval(SITE_TYPE_ZOT), dbesc(z_root()) ); if ($r) { foreach ($r as $rr) { $x = q( "select count(*) as t, hubloc_sitekey, max(hubloc_connected) as c from hubloc where hubloc_url = '%s' group by hubloc_sitekey order by c", dbesc($rr['site_url']) ); // see if this url has more than one sitekey, indicating it has been re-installed. if (count($x) > 1) { $d1 = Time::convert('UTC', 'UTC', $x[0]['c']); $d2 = Time::convert('UTC', 'UTC', 'now - 3 days'); // allow some slop period, say 3 days - just in case this is a glitch or transient occurrence // Then remove any hublocs pointing to the oldest entry. if (($d1 < $d2) && ($x[0]['hubloc_sitekey'])) { logger('prune_hub_reinstalls: removing dead hublocs at ' . $rr['site_url']); hubloc_delete($x[0]); } } } } } /** * @brief checks and repairs local hubloc records * * This shouldn't happen. But it does. */ function check_hublocs() { $sitekey = Config::Get('system', 'pubkey'); $interval = Config::Get('system', 'delivery_interval', 2); $wrong_urls = q( "select hubloc_id from hubloc where hubloc_sitekey = '%s' and hubloc_url != '%s'", dbesc($sitekey), dbesc(z_root()) ); $wrong_keys = q( "select hubloc_id from hubloc where hubloc_sitekey != '%s' and hubloc_url = '%s'", dbesc($sitekey), dbesc(z_root()) ); if (is_array($wrong_urls) && is_array($wrong_keys)) { $invalids = array_merge($wrong_urls, $wrong_keys); } if ($invalids) { foreach ($invalids as $hubloc) { logger('Removing invalid hubloc for ' . $hubloc['hubloc_addr'], LOGGER_DEBUG); hubloc_delete($hubloc); $channel = Channel::from_hash($hubloc['hubloc_hash']); if ($channel) { Run::Summon(['Notifier', 'refresh_all', $channel['channel_id']]); if ($interval) { @time_sleep_until(microtime(true) + (float)$interval); } } } } $channels = q("select * from channel where channel_removed = 0"); if ($channels) { foreach ($channels as $channel) { $hubs = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' and hubloc_sitekey = '%s'", dbesc($channel['channel_hash']), dbesc(z_root()), dbesc($sitekey) ); if (!$hubs) { logger('Repairing hubloc for ' . $channel['channel_address'], LOGGER_DEBUG); // This channel does not have a valid location (hubloc) // set for this site. $hubloc_addr = Channel::get_webfinger($channel); $primary = 1; // if xchan is missing, make this location the primary $xchans = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($channel['channel_hash']) ); if ($xchans) { $primary = (int)$xchans[0]['xchan_addr'] === $hubloc_addr; } $result = hubloc_store_lowlevel( [ 'hubloc_guid' => $channel['channel_guid'], 'hubloc_guid_sig' => $channel['channel_guid_sig'], 'hubloc_hash' => $channel['channel_hash'], 'hubloc_id_url' => Channel::url($channel), 'hubloc_addr' => Channel::get_webfinger($channel), 'hubloc_primary' => $primary, 'hubloc_url' => z_root(), 'hubloc_url_sig' => Libzot::sign(z_root(), $channel['channel_prvkey']), 'hubloc_site_id' => Libzot::make_xchan_hash(z_root(), $sitekey), 'hubloc_host' => App::get_hostname(), 'hubloc_callback' => z_root() . '/nomad', 'hubloc_sitekey' => $sitekey, 'hubloc_network' => 'nomad', 'hubloc_updated' => Time::convert(), 'hubloc_connected' => Time::convert() ] ); if ($result) { q("delete from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' and hubloc_sitekey != '%s'", dbesc($channel['channel_hash']), dbesc(z_root()), dbesc($sitekey) ); Run::Summon([ 'Notifier', 'refresh_all', $channel['channel_id'] ]); if ($interval) { @time_sleep_until(microtime(true) + (float) $interval); } } } } } } /** * @brief Change primary hubloc. * * This actually changes other structures to match the given (presumably current) * hubloc primary selection. * * @param array $hubloc * @return bool */ function hubloc_change_primary($hubloc) { if (! is_array($hubloc)) { logger('no hubloc'); return false; } logger('setting primary: ' . $hubloc['hubloc_url'] . ((intval($hubloc['hubloc_primary'])) ? ' true' : ' false')); // See if this is a local hubloc and if so update the primary for the corresponding channel record. if ($hubloc['hubloc_url'] === z_root()) { $r = Channel::from_hash($hubloc['hubloc_hash']); if ($r) { q( "update channel set channel_primary = %d where channel_id = %d", intval($hubloc['hubloc_primary']), intval($r['channel_id']) ); } } // we only need to proceed further if this particular hubloc is now primary if (! (intval($hubloc['hubloc_primary']))) { logger('not primary: ' . $hubloc['hubloc_url']); return false; } // do we even have an xchan for this hubloc and if so is it already set as primary? $r = q( "select * from xchan where xchan_hash = '%s' limit 1", dbesc($hubloc['hubloc_hash']) ); if (! $r) { logger('xchan not found'); return false; } if ($r[0]['xchan_addr'] === $hubloc['hubloc_addr']) { logger('xchan already changed'); return false; } $url = $hubloc['hubloc_url']; $lwebbie = substr($hubloc['hubloc_addr'], 0, strpos($hubloc['hubloc_addr'], '@')); $r = q( "update xchan set xchan_addr = '%s', xchan_url = '%s', xchan_follow = '%s', xchan_connurl = '%s' where xchan_hash = '%s'", dbesc($hubloc['hubloc_addr']), dbesc($url . '/channel/' . $lwebbie), dbesc($url . '/follow?f=&url=%s'), dbesc($url . '/poco/' . $lwebbie), dbesc($hubloc['hubloc_hash']) ); if (! $r) { logger('xchan_update failed.'); } logger('primary hubloc changed.' . print_r($hubloc, true), LOGGER_DEBUG); return true; } /** * @brief Mark a hubloc as down. * * We use the post url to distinguish between http and https hublocs. * The https might be alive, and the http dead. * Also set site_dead for the corresponding entry in the site table * * @param string $posturl Hubloc callback url which to disable */ function hubloc_mark_as_down($posturl) { $r = q( "update hubloc set hubloc_status = ( hubloc_status | %d ) where hubloc_callback = '%s'", intval(HUBLOC_OFFLINE), dbesc($posturl) ); // extract the baseurl and set site.site_dead to match $m = parse_url($posturl); $h = $m['scheme'] . '://' . $m['host']; $r = q( "update site set site_dead = 1 where site_url = '%s'", dbesc($h) ); } function hubloc_delete($hubloc) { if (is_array($hubloc) && array_key_exists('hubloc_id',$hubloc)) { q("update hubloc set hubloc_deleted = 1, hubloc_updated = '%s' where hubloc_id = %d", dbesc(Time::convert()), intval($hubloc['hubloc_id']), ); } } /** * @brief return comma separated string of non-dead clone locations (net addresses) for a given netid * * @param string $netid network identity (typically xchan_hash or hubloc_hash) * @return string */ function locations_by_netid($netid) { $locs = q( "select hubloc_addr as location from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_deleted = 0 and site_dead = 0", dbesc($netid) ); return array_elm_to_str($locs, 'location', ', ', 'trim_and_unpunify'); } function ping_site($url) { $ret = [ 'success' => false ]; $result = Url::get($url . '/api/z/1.0/version'); if (! $result['success']) { $ret['message'] = 'no answer from ' . $url; return $ret; } $ret['success'] = true; return $ret; } function get_locations_by_hash($hash) { return q( "select hubloc_addr, hubloc_id_url from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", dbesc($hash) ); } function hubloc_id_query($s, $limit = 0) { if ($limit) { $qlimit = 'limit ' . intval($limit); } $r = q("select * from hubloc where (hubloc_id_url = '%s' or hubloc_hash = '%s') and hubloc_deleted = 0 $qlimit", dbesc($s), dbesc($s) ); return $r; } function hublocx_id_query($s, $limit = 0) { if ($limit) { $qlimit = 'limit ' . intval($limit); } $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where (hubloc_id_url = '%s' or hubloc_hash = '%s') and hubloc_deleted = 0 $qlimit", dbesc($s), dbesc($s) ); return $r; } function get_hubloc_id_urls_by_portable_id($xchan) { $r = q("SELECT hubloc_id_url FROM hubloc WHERE hubloc_deleted = 0 AND hubloc_hash = '%s' order by hubloc_primary desc", dbesc($xchan) ); return flatten_array_recursive($r); } function hubloc_id_addr_query($s, $limit = 0) { if ($limit) { $qlimit = 'limit ' . intval($limit); } $r = q("select * from hubloc where (hubloc_id_url = '%s' or hubloc_hash = '%s' or hubloc_addr = '%s') and hubloc_deleted = 0 order by hubloc_id desc $qlimit", dbesc($s), dbesc($s), dbesc(str_replace('acct:' , '', $s)) ); return $r; } /** @noinspection PhpUnused */ function hublocx_id_addr_query($s, $limit = 0) { if ($limit) { $qlimit = 'limit ' . intval($limit); } $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where (hubloc_id_url = '%s' or hubloc_hash = '%s' or hubloc_addr = '%s') and hubloc_deleted = 0 order by hubloc_id desc $qlimit", dbesc($s), dbesc($s), dbesc(str_replace('acct:' , '', $s)) ); return $r; }