449 lines
14 KiB
PHP
449 lines
14 KiB
PHP
|
<?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
|
||
|
?>
|