Refactor querying & improve logging
parent
291025bfab
commit
03f88e6c0a
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* Community lists crawled for community links.
|
||||
*/
|
||||
$SOURCES = array(
|
||||
'ASGL' => 'https://raw.githubusercontent.com/GNU-Linux-libre/Awesome-Session-Group-List/main/README.md',
|
||||
'LOKI' => 'https://lokilocker.com/Mods/Session-Groups/wiki/Session-Open-Groups',
|
||||
'SDIR' => 'https://session.directory/?all=groups',
|
||||
'SDIR-BASE' => 'https://session.directory/',
|
||||
'SDIR-PATTERN' => '/view_session_group_user_lokinet\.php\?id=\d+/'
|
||||
);
|
||||
|
||||
/**
|
||||
* List of servers hosts queried individually.
|
||||
*/
|
||||
$KNOWN_SERVERS = array(
|
||||
// Official server
|
||||
"https://open.getsession.org",
|
||||
|
||||
// Kee's server
|
||||
"http://116.203.51.179",
|
||||
|
||||
// Serse with shodan.io:
|
||||
|
||||
// "http://13.233.251.36:8081"
|
||||
"http://164.92.176.135",
|
||||
"http://176.119.147.102",
|
||||
"http://192.227.193.159",
|
||||
"http://60a9fc9.online-server.cloud",
|
||||
"http://93.95.230.10",
|
||||
"http://94.176.239.60",
|
||||
"http://captain.geekgalaxy.com",
|
||||
"http://session.hwreload.it",
|
||||
"http://sogs.k9net.org",
|
||||
);
|
||||
|
||||
$KNOWN_PUBKEYS = array(
|
||||
"116.203.51.179" => "39016f991400c35a46e11e06cb2a64d6d8ab6652e484a556b14f7cf57ed7e73a",
|
||||
"13.233.251.36:8081" => "efcaecf00aebf5b75e62cf1fd550c6052842e1415a9339406e256c8b27cd2039",
|
||||
"164.92.176.135" => "e529311ec8fb6fdb950aaa4fb71fc4da3ea59c6c9ba2886708b9538eea6aa213",
|
||||
"176.119.147.102" => "e093994156ec92e4c13d0387208bfa48ae56dd88b8f60a03980d9ef048af1e3f",
|
||||
"192.227.193.159" => "426453e0e991235b62bc5f35f36d5a204e64b2d8b44e971609add6a10aac6674",
|
||||
"60a9fc9.online-server.cloud" => "7908bcd748313355f99e62f9c1f11c395d04019410edb7ee1618dbe26a423c4f",
|
||||
"93.95.230.10" => "b501f2dc7dc912aa0981b0ba10f2ba739d2f729a7d9b37022aee505aaf72807c",
|
||||
"94.176.239.60" => "2cbde327e9da216af9a69876bc57e16cc0c540b0aa2dfecdd1c115e67993b040",
|
||||
"captain.geekgalaxy.com" => "7242ad657dc2dd20e902a6fa82c34465907b67e80daf50173f38d5745abbaa24",
|
||||
"open.getsession.org" => "a03c383cf63c3c4efe67acc52112a6dd734b3a946b9545f488aaa93da7991238",
|
||||
"session.hwreload.it" => "4b3e75eedd2116b4dab0bcb6443b0e9fbfce7bcf1d35970bdad8a57a0113fb20",
|
||||
"sogs.k9net.org" => "fdcb047eb78520e925fda512a45ae74c6e2de9e0df206b3c0471bf1509919559"
|
||||
);
|
||||
?>
|
@ -0,0 +1,424 @@
|
||||
<?php
|
||||
|
||||
include_once "$PROJECT_ROOT/languages/language_flags.php";
|
||||
|
||||
class CommunityRoom implements JsonSerializable {
|
||||
public readonly object $server;
|
||||
public readonly int $active_users;
|
||||
public readonly int $active_users_cutoff;
|
||||
public readonly string $token;
|
||||
public readonly string $name;
|
||||
public readonly array $admins;
|
||||
public readonly array $moderators;
|
||||
public readonly float $created;
|
||||
public readonly string $description;
|
||||
public readonly ?int $image_id;
|
||||
public readonly int $info_updates;
|
||||
public readonly int $message_sequence;
|
||||
public readonly bool $read;
|
||||
public readonly bool $upload;
|
||||
public readonly bool $write;
|
||||
|
||||
// Custom properties
|
||||
public readonly string $language_flag;
|
||||
|
||||
private function __construct($server, array $data) {
|
||||
global $languages;
|
||||
|
||||
$this->server = $server;
|
||||
$this->active_users = $data['active_users'];
|
||||
$this->active_users_cutoff = $data['active_users_cutoff'];
|
||||
$this->name = $data['name'];
|
||||
$this->token = $data['token'];
|
||||
$this->admins = $data['admins'];
|
||||
$this->moderators = $data['moderators'];
|
||||
$this->created = $data['created'];
|
||||
$this->description = $data['description'] ?? "";
|
||||
$this->image_id = $data['image_id'];
|
||||
$this->info_updates = $data['info_updates'];
|
||||
$this->message_sequence = $data['message_sequence'];
|
||||
$this->read = $data['read'];
|
||||
$this->write = $data['write'];
|
||||
$this->upload = $data['upload'];
|
||||
|
||||
$room_identifier = $this->get_room_identifier();
|
||||
|
||||
$this->language_flag =
|
||||
isset($languages[$room_identifier])
|
||||
? $languages[$room_identifier]
|
||||
: "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CommunityRoom instance from data.
|
||||
* @param CommunityServer $server
|
||||
*/
|
||||
public static function from_data($server, array $data) {
|
||||
return new CommunityRoom($server, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of CommunityRoom instances from data.
|
||||
* @param array[] $data
|
||||
* @return CommunityRoom[]
|
||||
*/
|
||||
public static function from_data_array($server, array $data) {
|
||||
return array_map(function($room_data) use ($server) {
|
||||
return CommunityRoom::from_data($server, $room_data);
|
||||
}, $data);
|
||||
}
|
||||
|
||||
function jsonSerialize(): array {
|
||||
$data = get_object_vars($this);
|
||||
unset($data['server']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of staff Session IDs.
|
||||
* @return string[]
|
||||
*/
|
||||
function get_staff() {
|
||||
return array_unique(
|
||||
[...$this->admins, ...$this->moderators]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns seconds elapsed since room was created.
|
||||
*/
|
||||
function get_age(): float {
|
||||
return time() - $this->created;
|
||||
}
|
||||
|
||||
function get_preview_url(): string {
|
||||
$base_url = $this->server->base_url;
|
||||
$token = $this->token;
|
||||
return "$base_url/r/$token";
|
||||
}
|
||||
|
||||
function get_invite_url(): string {
|
||||
$base_url = $this->server->base_url;
|
||||
$token = $this->token;
|
||||
return "$base_url/r/$token/invite.png";
|
||||
}
|
||||
|
||||
function get_join_url(): string {
|
||||
$base_url = $this->server->base_url;
|
||||
$pubkey = $this->server->pubkey;
|
||||
$token = $this->token;
|
||||
return "$base_url/$token?public_key=$pubkey";
|
||||
}
|
||||
|
||||
function get_icon_url(): string | bool {
|
||||
$image_id = $this->image_id;
|
||||
|
||||
if ($image_id == null)
|
||||
return false;
|
||||
|
||||
$base_url = $this->server->base_url;
|
||||
$token = $this->token;
|
||||
|
||||
return "$base_url/room/$token/file/$image_id";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns our format of room identifier,
|
||||
* i.e. token+pubkey[:4]
|
||||
*/
|
||||
function get_room_identifier(): string {
|
||||
$token = $this->token;
|
||||
$pubkey_4 = substr($this->server->pubkey, 0, 4);
|
||||
return "$token+$pubkey_4";
|
||||
}
|
||||
}
|
||||
|
||||
$SERVER_STRINGIFY_MODE = 0;
|
||||
|
||||
class CommunityServer implements JsonSerializable {
|
||||
// public static int $STRINGIFY_MODE = 0;
|
||||
|
||||
public string $base_url = "";
|
||||
public string $pubkey = "";
|
||||
public ?array $rooms = null;
|
||||
|
||||
/**
|
||||
* Instructs CommunityServer instances
|
||||
* to stringify to their base URL.
|
||||
*/
|
||||
private static function next_dedupe_by_url() {
|
||||
global $SERVER_STRINGIFY_MODE;
|
||||
$SERVER_STRINGIFY_MODE = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs CommunityServer instances
|
||||
* to stringify to their public key.
|
||||
*/
|
||||
private static function next_dedupe_by_pubkey() {
|
||||
global $SERVER_STRINGIFY_MODE;
|
||||
$SERVER_STRINGIFY_MODE = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the given servers to remove URL duplicates.
|
||||
* @param CommunityServer[] $servers
|
||||
* @return CommunityServer[]
|
||||
*/
|
||||
public static function dedupe_by_url($servers) {
|
||||
CommunityServer::next_dedupe_by_url();
|
||||
return array_unique($servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the given servers to remove pubkey duplicates.
|
||||
* @param CommunityServer[] $servers
|
||||
* @return CommunityServer[]
|
||||
*/
|
||||
public static function dedupe_by_pubkey($servers) {
|
||||
CommunityServer::next_dedupe_by_pubkey();
|
||||
return array_unique($servers);
|
||||
}
|
||||
|
||||
private function __construct() { }
|
||||
|
||||
/**
|
||||
* Compare two CommunityServer instances.
|
||||
* @param CommunityServer $a
|
||||
* @param CommunityServer $b
|
||||
*/
|
||||
static function compare($a, $b): int {
|
||||
return strcmp($a->base_url, $b->base_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort an array of servers in-place based on URL.
|
||||
* @param CommunityServer[] &$servers
|
||||
*/
|
||||
static function sort(array &$servers) {
|
||||
usort($servers, 'CommunityServer::compare');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify CommunityServer by custom property
|
||||
* to allow de-duping using array_uniq.
|
||||
*/
|
||||
function __toString(): string {
|
||||
global $SERVER_STRINGIFY_MODE;
|
||||
return match($SERVER_STRINGIFY_MODE) {
|
||||
0 => $this->base_url,
|
||||
1 => $this->pubkey
|
||||
};
|
||||
}
|
||||
|
||||
function jsonSerialize(): array {
|
||||
return get_object_vars($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommunityServer
|
||||
*/
|
||||
static function from_host($host) {
|
||||
$server = new CommunityServer();
|
||||
|
||||
$server->base_url = $host;
|
||||
|
||||
return $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommunityServer
|
||||
*/
|
||||
static function from_known_host($host, $pubkey) {
|
||||
$server = new CommunityServer();
|
||||
|
||||
$server->base_url = $host;
|
||||
|
||||
$server->pubkey = $pubkey;
|
||||
|
||||
return $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommunityServer[]
|
||||
*/
|
||||
static function from_known_hosts($hosts, $pubkeys) {
|
||||
$servers = [];
|
||||
|
||||
foreach ($hosts as $base_url) {
|
||||
$server = new CommunityServer();
|
||||
|
||||
$server->base_url = $base_url;
|
||||
|
||||
$hostname = url_get_base($base_url, false);
|
||||
|
||||
$server->pubkey = $pubkeys[$hostname];
|
||||
|
||||
$servers[] = $server;
|
||||
}
|
||||
|
||||
return $servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommunityServer
|
||||
*/
|
||||
static function from_join_url(string $join_url) {
|
||||
$server = new CommunityServer();
|
||||
|
||||
$server->base_url = url_get_base($join_url);
|
||||
$server->set_pubkey_from_url($join_url);
|
||||
|
||||
return $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommunityServer[]
|
||||
*/
|
||||
static function from_join_urls(array $join_urls) {
|
||||
return array_map(
|
||||
'CommunityServer::from_join_url',
|
||||
$join_urls
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return CommunityServer
|
||||
*/
|
||||
static function from_data(array $data) {
|
||||
$server = new CommunityServer();
|
||||
|
||||
$server->base_url = $data['base_url'];
|
||||
$server->pubkey = $data['pubkey'];
|
||||
$server->rooms = CommunityRoom::from_data_array($server, $data['rooms']);
|
||||
|
||||
return $server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array[] $data
|
||||
* @return CommunityServer[]
|
||||
*/
|
||||
static function from_data_array(array $data) {
|
||||
return array_map(
|
||||
'CommunityServer::from_data',
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CommunityServer[] $servers
|
||||
* @return CommunityRoom[]
|
||||
*/
|
||||
static function enumerate_rooms($servers) {
|
||||
$rooms = [];
|
||||
foreach ($servers as $server) {
|
||||
$rooms[] = $server->rooms;
|
||||
}
|
||||
return array_merge([], ...$rooms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls all servers for rooms.
|
||||
* @param CommunityServer[] $servers Servers to poll.
|
||||
* @return CommunityServer[] Reachable servers.
|
||||
*/
|
||||
public static function poll_reachable(array $servers): array {
|
||||
$reachable_servers = [];
|
||||
|
||||
// Synchronous for-loop for now.
|
||||
foreach ($servers as $server) {
|
||||
if (!($server->fetch_rooms())) continue;
|
||||
// Accept failures to fetch pubkey if already known.
|
||||
// (Has happened.)
|
||||
if (!(
|
||||
$server->fetch_pubkey()
|
||||
|| $server->has_pubkey()
|
||||
)) continue;
|
||||
$reachable_servers[] = $server;
|
||||
}
|
||||
|
||||
return $reachable_servers;
|
||||
}
|
||||
|
||||
function set_pubkey($pubkey) {
|
||||
if ($this->has_pubkey() && $this->pubkey != $pubkey) {
|
||||
$base_url = $this->base_url;
|
||||
throw new ValueError("Pubkey mismatch for $base_url");
|
||||
}
|
||||
|
||||
$this->pubkey = $pubkey;
|
||||
}
|
||||
|
||||
function set_pubkey_from_url($join_url) {
|
||||
$url_components = parse_url($join_url);
|
||||
parse_str($url_components['query'], $query_components);
|
||||
$this->pubkey = $query_components['public_key'];
|
||||
}
|
||||
|
||||
function has_pubkey() {
|
||||
return $this->pubkey != "";
|
||||
}
|
||||
|
||||
function is_reachable() {
|
||||
return url_is_reachable($this->base_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to fetch rooms for self using SOGS API.
|
||||
*
|
||||
* @return bool True if successful, false otherwise.
|
||||
*/
|
||||
function fetch_rooms() {
|
||||
$base_url = $this->base_url;
|
||||
log_info("Fetching rooms for $base_url.");
|
||||
$rooms = curl_get_contents("$base_url/rooms?all=1");
|
||||
if (!$rooms) {
|
||||
log_warning("Could not fetch rooms for $base_url.");
|
||||
return false;
|
||||
}
|
||||
$room_data = json_decode($rooms, true);
|
||||
if ($room_data == null) {
|
||||
log_warning("Could not parse rooms for $base_url.");
|
||||
return false;
|
||||
}
|
||||
$this->rooms = CommunityRoom::from_data_array($this, $room_data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to fetch own public key by parsing SOGS HTML preview.
|
||||
*
|
||||
* @return bool True if successful, false otherwise.
|
||||
*/
|
||||
function fetch_pubkey() {
|
||||
if (empty($this->rooms)) {
|
||||
log_error("Server has no rooms to poll for public key");
|
||||
throw new Error("Server has no rooms to poll for public key");
|
||||
}
|
||||
|
||||
$preview_url = $this->rooms[0]->get_preview_url();
|
||||
|
||||
log_info("Fetching pubkey from $preview_url");
|
||||
$room_view = curl_get_contents($preview_url);
|
||||
|
||||
if (!$room_view) {
|
||||
log_warning("Failed to fetch room preview from $preview_url.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$links = parse_join_links($room_view);
|
||||
|
||||
if (!isset($links[0])) {
|
||||
log_warning("Could not locate join link in preview at $preview_url.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->set_pubkey_from_url($links[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
?>
|
Loading…
Reference in New Issue