docker-tmserver/xaseco/plugins/plugin.access.php

449 lines
14 KiB
PHP
Raw Permalink Normal View History

2022-06-26 17:43:44 +02:00
<?php
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
/**
* Access Control plugin.
* Controls player access by nation (TMN) or zone (TMF).
* Inspired by Apache's mod_access:
* http://httpd.apache.org/docs/2.0/mod/mod_access.html
* Created by Xymph
*
* Dependencies: none
*/
Aseco::registerEvent('onStartup', 'access_initcontrol');
Aseco::registerEvent('onPlayerConnect2', 'access_playerconnect'); // use post event after all join processing
global $access_control;
// ['order'] - boolean: allow,deny = true; deny,allow = false
// ['allowall'] - boolean: Allow from all = true; otherwise false
// ['allow'] - array of nations/zones to allow
// ['denyall'] - boolean: Deny from all = true; otherwise false
// ['deny'] - array of nations/zones to deny
// called @ onStartup
function access_initcontrol($aseco, $reload = false) {
global $access_control;
// initialize access control
$access_control = array();
$access_control['order'] = false;
$access_control['allowall'] = false;
$access_control['allow'] = array();
$access_control['denyall'] = false;
$access_control['deny'] = array();
$access_control['messages'] = array();
$error = '';
// log console message
if (!$reload)
$aseco->console('Load player access control [access.xml]');
// read & parse config file
if (!$settings = $aseco->xml_parser->parseXml('access.xml'))
$error = 'Could not read/parse access control config file access.xml !';
// check/store Order section
if (!$error) {
if (isset($settings['ACCESS']['ORDER'][0])) {
// strip all spaces
$order = str_replace(' ', '', strtolower($settings['ACCESS']['ORDER'][0]));
if ($order == 'allow,deny')
$access_control['order'] = true;
elseif ($order == 'deny,allow')
$access_control['order'] = false;
else {
$error = 'Access control config invalid \'order\' section: "' . $settings['ACCESS']['ORDER'][0] . '"';
}
} else {
$error = 'Access control config missing section: order';
}
}
// check/store Allow section
if (!$error) {
if (isset($settings['ACCESS']['ALLOW'][0])) {
// check/store From entry(ies)
if (isset($settings['ACCESS']['ALLOW'][0]['FROM'])) {
foreach ($settings['ACCESS']['ALLOW'][0]['FROM'] as $from) {
if ($from == 'all') {
if (count($settings['ACCESS']['ALLOW'][0]['FROM']) > 1) {
$error = 'Access control config \'allow\' section contains more besides "all" value';
break;
} else {
$access_control['allowall'] = true;
}
} else { // != 'all'
if (in_array($from, $access_control['allow'])) {
$error = 'Access control config \'allow\' section contains duplicate value: ' . $from;
break;
} else {
if ($from != '') // ignore empty entries
$access_control['allow'][] = $from;
}
}
}
} else {
$error = 'Access control config \'allow\' section must contain at least one \'from\' entry';
}
} else {
$error = 'Access control config missing section: allow';
}
}
// check/store Deny section
if (!$error) {
if (isset($settings['ACCESS']['DENY'][0])) {
// check/store From entry(ies)
if (isset($settings['ACCESS']['DENY'][0]['FROM'])) {
foreach ($settings['ACCESS']['DENY'][0]['FROM'] as $from) {
if ($from == 'all') {
if (count($settings['ACCESS']['DENY'][0]['FROM']) > 1) {
$error = 'Access control config \'deny\' section contains more besides "all" value';
break;
} else {
$access_control['denyall'] = true;
}
} else { // != 'all'
if (in_array($from, $access_control['deny'])) {
$error = 'Access control config \'deny\' section contains duplicate value: ' . $from;
break;
} else {
if ($from != '') // ignore empty entries
$access_control['deny'][] = $from;
}
}
}
} else {
$error = 'Access control config \'deny\' section must contain at least one \'from\' entry';
}
} else {
$error = 'Access control config missing section: deny';
}
}
// final consistency check
if (!$error && $access_control['allowall'] && $access_control['denyall'])
$error = 'Access control config \'allow\' & \'deny\' sections cannot both use "all" value';
// load messages
if (!$error) {
if (isset($settings['ACCESS']['MESSAGES'][0])) {
if (isset($settings['ACCESS']['MESSAGES'][0]['DENIED'][0]))
$access_control['messages']['denied'] = $settings['ACCESS']['MESSAGES'][0]['DENIED'][0];
else
$error = 'Access control config \'messages\' section missing value: denied';
if (isset($settings['ACCESS']['MESSAGES'][0]['DIALOG'][0]))
$access_control['messages']['dialog'] = $settings['ACCESS']['MESSAGES'][0]['DIALOG'][0];
else
$error = 'Access control config \'messages\' section missing value: dialog';
if (isset($settings['ACCESS']['MESSAGES'][0]['RELOAD'][0]))
$access_control['messages']['reload'] = $settings['ACCESS']['MESSAGES'][0]['RELOAD'][0];
else
$error = 'Access control config \'messages\' section missing value: reload';
if (isset($settings['ACCESS']['MESSAGES'][0]['XMLERR'][0]))
$access_control['messages']['xmlerr'] = $settings['ACCESS']['MESSAGES'][0]['XMLERR'][0];
else
$error = 'Access control config \'messages\' section missing value: xmlerr';
if (isset($settings['ACCESS']['MESSAGES'][0]['MISSING'][0]))
$access_control['messages']['missing'] = $settings['ACCESS']['MESSAGES'][0]['MISSING'][0];
else
$error = 'Access control config \'messages\' section missing value: missing';
} else {
$error = 'Access control config missing section: messages';
}
}
if (!$error) {
// sort access lists
sort($access_control['allow']);
sort($access_control['deny']);
// log console message
if ($reload)
$aseco->console('Player access control reloaded from access.xml');
return true;
} else {
// log error message
trigger_error($error, E_USER_WARNING);
$access_control = array();
return false;
}
} // access_initcontrol
function in_zones($access, $zones) {
// check all zones for matching (leading part of) player's zone
foreach ($zones as $zone)
if (strpos($access, $zone) === 0)
return true;
return false;
} // in_zones
// called @ onPlayerConnect2
function access_playerconnect($aseco, $player) {
global $access_control;
// if no access control, bail out immediately
if (!$access_control) return;
// get nation/zone to check for access
if ($aseco->server->getGame() == 'TMF') {
$access = $player->zone;
} elseif ($aseco->server->getGame() == 'TMN') {
$access = $player->nation;
} else { // TMS/TMO
return; // no access control
}
// check for empty nation/zone
if ($access == '') {
if ($access_control['order']) { // Allow,Deny
; // default denied
} else { // Deny,Allow
return; // default allowed
}
} else {
if ($aseco->server->getGame() == 'TMF') {
if ($access_control['order']) { // Allow,Deny
// first check Allow list
if ($access_control['allowall'] || in_zones($access, $access_control['allow'])) {
// then check Deny list
if ($access_control['denyall'] || in_zones($access, $access_control['deny']))
; // deny this nation
else
return; // allow this nation
}
else
; // deny this nation
} else { // Deny,Allow
// first check Deny list
if ($access_control['denyall'] || in_zones($access, $access_control['deny'])) {
// then check Allow list
if ($access_control['allowall'] || in_zones($access, $access_control['allow']))
return; // allow this nation
else
; // deny this nation
}
else
return; // allow this nation
}
} else { // TMN
if ($access_control['order']) { // Allow,Deny
// first check Allow list
if ($access_control['allowall'] || in_array($access, $access_control['allow'])) {
// then check Deny list
if ($access_control['denyall'] || in_array($access, $access_control['deny']))
; // deny this nation
else
return; // allow this nation
}
else
; // deny this nation
} else { // Deny,Allow
// first check Deny list
if ($access_control['denyall'] || in_array($access, $access_control['deny'])) {
// then check Allow list
if ($access_control['allowall'] || in_array($access, $access_control['allow']))
return; // allow this nation
else
; // deny this nation
}
else
return; // allow this nation
}
}
}
// log & kick player
$aseco->console('Player \'{1}\' denied access from "{2}" - kicking...', $player->login, $access);
$message = formatText($access_control['messages']['denied'],
stripColors($player->nickname),
($aseco->server->getGame() == 'TMF' ? 'zone' : 'nation'),
$access);
$aseco->client->query('ChatSendServerMessage', $aseco->formatColors($message));
if ($aseco->server->getGame() == 'TMF') {
$message = formatText($access_control['messages']['dialog'],
$access);
$aseco->client->addCall('Kick', array($player->login, $aseco->formatColors($message)));
} else {
$aseco->client->addCall('Kick', array($player->login));
}
} // access_playerconnect
function admin_access($aseco, $command) {
global $access_control;
$player = $command['author'];
$login = $player->login;
if ($command['params'] == 'help') {
if ($aseco->server->getGame() == 'TMF') {
$header = '{#black}/admin access$g handles player access control:';
$help = array();
$help[] = array('...', '{#black}help',
'Displays this help information');
$help[] = array('...', '{#black}list',
'Displays current access control settings');
$help[] = array('...', '{#black}reload',
'Reloads updated access control settings');
// display ManiaLink message
display_manialink($login, $header, array('Icons64x64_1', 'TrackInfo', -0.01), $help, array(0.8, 0.05, 0.15, 0.6), 'OK');
} elseif ($aseco->server->getGame() == 'TMN') {
$help = '{#black}/admin access$g handles player access control:' . LF;
$help .= ' - {#black}help$g, displays this help information' . LF;
$help .= ' - {#black}list$g, lists current settings' . LF;
$help .= ' - {#black}reload$g, reloads updated settings' . LF;
// display popup message
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $aseco->formatColors($help), 'OK', '', 0);
}
}
elseif ($command['params'] == 'list') {
$player->msgs = array();
if ($aseco->server->getGame() == 'TMF') {
$head = 'Current player access control settings:';
$info = array();
// initialize with Order entry
$info[] = array('Order:', '{#black}' . ($access_control['order'] ? 'Allow,Deny' : 'Deny,Allow'));
$info[] = array();
$lines = 2;
$player->msgs[0] = array(1, $head, array(1.0, 0.2, 0.8), array('Icons128x128_1', 'ManiaZones'));
// collect Allow entries
$info[] = array('Allow:', '');
$lines++;
if ($access_control['allowall']) {
$info[] = array('', '{#black}all');
$lines++;
} else {
foreach ($access_control['allow'] as $from) {
$info[] = array('', '{#black}' . $from);
if (++$lines > 14) {
$player->msgs[] = $info;
$lines = 0;
$info = array();
}
}
}
// insert spacer
$info[] = array();
if (++$lines > 14) {
$player->msgs[] = $info;
$lines = 0;
$info = array();
}
// collect Deny entries
$info[] = array('Deny:', '');
$lines++;
if ($access_control['denyall']) {
$info[] = array('', '{#black}all');
$lines++;
} else {
foreach ($access_control['deny'] as $from) {
$info[] = array('', '{#black}' . $from);
if (++$lines > 14) {
$player->msgs[] = $info;
$lines = 0;
$info = array();
}
}
}
// add if last batch exists
if (count($info) > 1)
$player->msgs[] = $info;
// display ManiaLink message
display_manialink_multi($player);
} elseif ($aseco->server->getGame() == 'TMN') {
$head = 'Current player access control settings:' . LF;
// initialize with Order entry
$info = 'Order: {#black}' . ($access_control['order'] ? 'Allow,Deny' : 'Deny,Allow') . LF . LF;
$lines = 2;
$player->msgs[0] = 1;
// collect Allow entries
$info .= '$gAllow:' . LF;
$lines++;
if ($access_control['allowall']) {
$info .= ' {#black}all' . LF;
$lines++;
} else {
foreach ($access_control['allow'] as $from) {
$info .= ' {#black}' . $from . LF;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $info);
$lines = 0;
$info = '';
}
}
}
// insert spacer
$info .= LF;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $info);
$lines = 0;
$info = '';
}
// collect Deny entries
$info .= '$gDeny:' . LF;
$lines++;
if ($access_control['denyall']) {
$info .= ' {#black}all' . LF;
$lines++;
} else {
foreach ($access_control['deny'] as $from) {
$info .= ' {#black}' . $from . LF;
if (++$lines > 9) {
$player->msgs[] = $aseco->formatColors($head . $info);
$lines = 0;
$info = '';
}
}
}
// add if last batch exists
if ($info != '')
$player->msgs[] = $aseco->formatColors($head . $info);
// display popup message
if (count($player->msgs) == 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'OK', '', 0);
} elseif (count($player->msgs) > 2) {
$aseco->client->query('SendDisplayServerMessageToLogin', $login, $player->msgs[1], 'Close', 'Next', 0);
}
}
}
elseif ($command['params'] == 'reload') {
// reload/check access control
if (access_initcontrol($aseco, true)) {
$message = $access_control['messages']['reload'];
} else {
$access_control = array();
$message = $access_control['messages']['xmlerr'];
}
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
}
else {
$message = $access_control['messages']['missing'];
$aseco->client->query('ChatSendServerMessageToLogin', $aseco->formatColors($message), $login);
}
} // admin_access
?>