Added full source code
This commit is contained in:
commit
33f8586698
1377 changed files with 123808 additions and 0 deletions
scripts/lib
account.incactions.incajax.incclassloader.incconfig.incdata_tree.incdb.incdb_accessor.incdb_connection.incdb_copy.incengine.inc
engines
game.inchandler.incinput.inclibrary.inclog.incoutput.incpcheck.incpcheck_manager.incpcheck_thread.incprefs.incresource.incsession.inctick.inctick_manager.inctracking.incversion.incxml_config.inc
13
scripts/lib/account.inc
Normal file
13
scripts/lib/account.inc
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* ACCOUNT MANAGEMENT FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
function accountLog($what, $uid) {
|
||||
$game = config::getGame('main');
|
||||
$lib = $game->getLib('main/account');
|
||||
$lib->call('log', $uid, $what);
|
||||
}
|
||||
|
||||
?>
|
93
scripts/lib/actions.inc
Normal file
93
scripts/lib/actions.inc
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* GAME ACTIONS
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/** The game_action class should be used as a base class for all classes
|
||||
* implementing specific game actions.
|
||||
*
|
||||
* It initializes the object by storing the game instance and loading the
|
||||
* required libraries.
|
||||
*/
|
||||
abstract class game_action {
|
||||
|
||||
/** A reference to the game instance the action is called on.
|
||||
*/
|
||||
protected $game;
|
||||
|
||||
/** Libraries loaded and instanciated. These libraries will
|
||||
* be accessed to the __get() method.
|
||||
*/
|
||||
private $__libraries;
|
||||
|
||||
/** The constructor initialises the instance by storing the
|
||||
* associated game instance and loading libraries.
|
||||
*/
|
||||
protected function __construct($game, $libraries = null) {
|
||||
$this->game = $game;
|
||||
|
||||
if (is_null($libraries)) {
|
||||
$libraries = array();
|
||||
}
|
||||
$this->__initLibraries($libraries);
|
||||
}
|
||||
|
||||
/** This method loads libraries from the game instance and
|
||||
* stores them inside the $__libraries array.
|
||||
*/
|
||||
private function __initLibraries($libraries) {
|
||||
$libs = array();
|
||||
foreach ($libraries as $key => $name) {
|
||||
$libs[$key] = $this->game->getLib($name);
|
||||
}
|
||||
$this->__libraries = $libs;
|
||||
}
|
||||
|
||||
/** The __get() overload method is useful to access the
|
||||
* libraries associated with this game action.
|
||||
*/
|
||||
protected function __get($var) {
|
||||
if (array_key_exists($var, $this->__libraries)) {
|
||||
return $this->__libraries[$var];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This function allows the user to call a game action function; it must
|
||||
* have at least one argument, which may be either a string (the name of the
|
||||
* action function to call for the current game version) or an array
|
||||
* containing two strings (the version identifier and the name of the action).
|
||||
* Other arguments may follow; they will be passed to the action function.
|
||||
*/
|
||||
function gameAction() {
|
||||
$sitePath = input::$path;
|
||||
|
||||
$n = func_num_args();
|
||||
if ($n == 0) {
|
||||
l::fatal(22, "Requested game was '$sitePath'");
|
||||
}
|
||||
$args = func_get_args();
|
||||
$gas = array_shift($args);
|
||||
|
||||
if (is_array($gas)) {
|
||||
list($v,$a) = $gas;
|
||||
} elseif (is_string($gas)) {
|
||||
$v = $sitePath;
|
||||
$a = $gas;
|
||||
} else {
|
||||
l::fatal(23, "Requested game was '$sitePath'");
|
||||
}
|
||||
array_unshift($args, $a);
|
||||
|
||||
$g = config::getGame($v);
|
||||
l::deprecated("gameAction($v, $a, ...)");
|
||||
return call_user_func_array(array($g, 'deprecatedAction'), $args);
|
||||
}
|
||||
|
||||
|
||||
?>
|
84
scripts/lib/ajax.inc
Normal file
84
scripts/lib/ajax.inc
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
|
||||
class ajax {
|
||||
|
||||
static $method = array();
|
||||
static $fHandler = array();
|
||||
static $fTheme = array();
|
||||
static $init = "";
|
||||
|
||||
function getTheme() {
|
||||
$f = getLayoutDirectory(input::$game->version->id) . "/ajax.inc";
|
||||
if (!file_exists($f)) {
|
||||
return array();
|
||||
}
|
||||
return include($f);
|
||||
}
|
||||
|
||||
static function init() {
|
||||
$handler = handler::$h;
|
||||
|
||||
// Get AJAX functions from handler
|
||||
$a1 = is_array($handler->ajax) ? $handler->ajax : array();
|
||||
if (is_array($a1['func'])) {
|
||||
ajax::$fHandler = $a1['func'];
|
||||
$ml = is_array($a1['method']) ? $a1['method'] : array();
|
||||
foreach ($a1['func'] as $f) {
|
||||
$m = ($ml[$f] == "") ? ($handler->ajaxPOST ? "POST" : "GET") : $ml[$f];
|
||||
ajax::$method[$f] = $m;
|
||||
}
|
||||
}
|
||||
|
||||
// Get theme-specific AJAX functions
|
||||
$a2 = ajax::getTheme();
|
||||
if (is_array($a2['func'])) {
|
||||
ajax::$fTheme = $a2['func'];
|
||||
$ml = is_array($a2['method']) ? $a2['method'] : array();
|
||||
foreach ($a2['func'] as $f) {
|
||||
$m = ($ml[$f] == "") ? ($handler->ajaxPOST ? "POST" : "GET") : $ml[$f];
|
||||
ajax::$method[$f] = $m;
|
||||
}
|
||||
}
|
||||
|
||||
// Create init string
|
||||
ajax::$init = $a2['init'] . $a1['init'];
|
||||
}
|
||||
|
||||
|
||||
static function canCall($function) {
|
||||
return in_array($function, ajax::$fTheme) || in_array($function, ajax::$fHandler);
|
||||
}
|
||||
|
||||
static function call($function, $args) {
|
||||
// We don't want an error in that part of the code to make the
|
||||
// client-side JS script go insane
|
||||
ob_start();
|
||||
|
||||
// Call the function
|
||||
if (in_array($function, ajax::$fTheme)) {
|
||||
$res = call_user_func_array("thm_{$function}", $args);
|
||||
} else if (in_array($function, ajax::$fHandler)) {
|
||||
$res = call_user_func_array(array(handler::$h, $function), $args);
|
||||
} else {
|
||||
$res = null;
|
||||
}
|
||||
|
||||
// Log any error / warning / whatever
|
||||
$buffer = ob_get_contents();
|
||||
if ($buffer != '') {
|
||||
$b = explode("\n", $buffer);
|
||||
foreach ($b as $line) {
|
||||
if ($line != '') {
|
||||
l::warning("AJAX ($function): $line");
|
||||
}
|
||||
}
|
||||
}
|
||||
ob_end_clean();
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
25
scripts/lib/classloader.inc
Normal file
25
scripts/lib/classloader.inc
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
class loader {
|
||||
private static $loadedClasses = array();
|
||||
|
||||
static function load($file, $className) {
|
||||
if (in_array($className, loader::$loadedClasses)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(file_exists($file) && is_readable($file) && is_file($file))) {
|
||||
l::fatal(19, "File '$file' (for class $className) not found");
|
||||
}
|
||||
if (!include_once($file)) {
|
||||
l::fatal(20, "File '$file' (for class $className) returned false");
|
||||
}
|
||||
if (!class_exists($className)) {
|
||||
l::fatal(21, "Class '$className' not found in file '$file'");
|
||||
}
|
||||
|
||||
array_push(loader::$loadedClasses, $className);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
130
scripts/lib/config.inc
Normal file
130
scripts/lib/config.inc
Normal file
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* MAIN CONFIGURATION MANAGEMENT
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
class config {
|
||||
|
||||
static $config = null;
|
||||
static $main = null;
|
||||
|
||||
function __construct($versions, $games, $defGame) {
|
||||
$this->versions = $versions;
|
||||
$this->games = $games;
|
||||
$this->defGame = $defGame;
|
||||
}
|
||||
|
||||
private static function parseXML($xmlData) {
|
||||
require_once('xml_config.inc');
|
||||
return xml_config::parse($xmlData);
|
||||
}
|
||||
|
||||
private static function tryLoadSerialized() {
|
||||
$config = &config::$main;
|
||||
|
||||
if (!file_exists("{$config['cachedir']}/config.ser")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$f = @file_get_contents("{$config['cachedir']}/config.ser");
|
||||
if ($f === false) {
|
||||
return false;
|
||||
}
|
||||
return @unserialize($f);
|
||||
}
|
||||
|
||||
private static function writeCache($cObj) {
|
||||
$config = &config::$main;
|
||||
|
||||
$text = serialize($cObj);
|
||||
$mask = umask(0002);
|
||||
$w = @file_put_contents("{$config['cachedir']}/config.ser", $text, LOCK_EX);
|
||||
umask($mask);
|
||||
if ($w === false) {
|
||||
l::warn("CONFIG: failed to cache configuration");
|
||||
}
|
||||
}
|
||||
|
||||
private static function checkUpdate($checksum = null) {
|
||||
$config = &config::$main;
|
||||
|
||||
$f = @file_get_contents("{$config['scriptdir']}/legacyworlds.xml");
|
||||
if ($f === false) {
|
||||
l::error("CONFIG: could not open file '{$config['scriptdir']}/legacyworlds.xml'");
|
||||
return false;
|
||||
}
|
||||
|
||||
$md5 = md5($f);
|
||||
if (!is_null($checksum) && $checksum == $md5) {
|
||||
return false;
|
||||
}
|
||||
l::notice("CONFIG: XML configuration file modified, updating");
|
||||
|
||||
$data = config::parseXML($f);
|
||||
if (is_object($data)) {
|
||||
$data->checksum = $md5;
|
||||
config::writeCache($data);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
static function load() {
|
||||
global $config;
|
||||
if (!is_null($config) && is_null(config::$main)) {
|
||||
config::$main = $config;
|
||||
}
|
||||
|
||||
$sData = config::tryLoadSerialized();
|
||||
if (!is_object($sData)) {
|
||||
$sData = config::checkUpdate();
|
||||
} else {
|
||||
$nData = config::checkUpdate($sData->checksum);
|
||||
if (is_object($nData)) {
|
||||
$sData = $nData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_object($sData)) {
|
||||
l::fatal(0, "The XML configuration could not be parsed and serialized data could not be read");
|
||||
}
|
||||
config::$config = $sData;
|
||||
}
|
||||
|
||||
|
||||
static function reload() {
|
||||
$nData = config::checkUpdate(config::$config->checksum);
|
||||
if (is_object($nData)) {
|
||||
config::$config = $nData;
|
||||
l::info("CONFIG: configuration updated");
|
||||
}
|
||||
return is_object($nData);
|
||||
}
|
||||
|
||||
|
||||
static function getGames() {
|
||||
return config::$config->games;
|
||||
}
|
||||
|
||||
static function getGame($name) {
|
||||
return config::$config->games[$name];
|
||||
}
|
||||
|
||||
static function getDefaultGame() {
|
||||
return config::$config->games[config::$config->defGame];
|
||||
}
|
||||
|
||||
static function hasGame($name) {
|
||||
return is_object(config::$config->games[$name]);
|
||||
}
|
||||
|
||||
static function getParam($name) {
|
||||
return config::$config->games['main']->params[$name];
|
||||
}
|
||||
}
|
||||
|
||||
config::load();
|
||||
|
||||
?>
|
130
scripts/lib/data_tree.inc
Normal file
130
scripts/lib/data_tree.inc
Normal file
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
|
||||
abstract class data_gen {
|
||||
private $name;
|
||||
private $attributes;
|
||||
|
||||
function __construct($name) {
|
||||
$this->name = $name;
|
||||
$this->attributes = array();
|
||||
}
|
||||
|
||||
function getName() {
|
||||
return $name;
|
||||
}
|
||||
|
||||
abstract function getContents();
|
||||
|
||||
abstract function addContents($contents);
|
||||
|
||||
function getAttribute($name) {
|
||||
return isset($this->attributes[$name]) ? $this->attributes[$name] : null;
|
||||
}
|
||||
|
||||
function setAttribute($name, $value) {
|
||||
$this->attributes[$name] = $value;
|
||||
}
|
||||
|
||||
function toXML($document) {
|
||||
$element = $document->createElement($this->name);
|
||||
foreach ($this->attributes as $k => $v) {
|
||||
$element->setAttribute($k, $v);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
function toLWData($document) {
|
||||
$root = $document->createElement("Node");
|
||||
$root->setAttribute("name", $this->name);
|
||||
foreach ($this->attributes as $k => $v) {
|
||||
$node = $document->createElement("Attr");
|
||||
$root->appendChild($node);
|
||||
$node->setAttribute("name", $k);
|
||||
$text = $document->createTextNode($v);
|
||||
$node->appendChild($text);
|
||||
}
|
||||
return $root;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class data_leaf extends data_gen {
|
||||
|
||||
private $contents;
|
||||
|
||||
function __construct($name, $contents = "") {
|
||||
parent::__construct($name);
|
||||
$this->contents = $contents;
|
||||
}
|
||||
|
||||
function getContents() {
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
function addContents($contents) {
|
||||
$this->contents .= $contents;
|
||||
}
|
||||
|
||||
function toXML($document) {
|
||||
$element = parent::toXML($document);
|
||||
$value = $document->createTextNode($this->contents);
|
||||
$element->appendChild($value);
|
||||
return $element;
|
||||
}
|
||||
|
||||
function toLWData($document) {
|
||||
$element = parent::toLWData($document);
|
||||
$element->setAttribute("node", 0);
|
||||
if ($this->contents != '') {
|
||||
$node = $document->createElement("Text");
|
||||
$value = $document->createTextNode($this->contents);
|
||||
$node->appendChild($value);
|
||||
$element->appendChild($node);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class data_node extends data_gen {
|
||||
|
||||
private $contents;
|
||||
|
||||
function __construct($name) {
|
||||
parent::__construct($name);
|
||||
$this->contents = array();
|
||||
}
|
||||
|
||||
function getContents() {
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
function addContents($data) {
|
||||
if ($data instanceof data_gen) {
|
||||
array_push($this->contents, $data);
|
||||
}
|
||||
}
|
||||
|
||||
function toXML($document) {
|
||||
$element = parent::toXML($document);
|
||||
foreach ($this->contents as $sub) {
|
||||
$node = $sub->toXML($document);
|
||||
$element->appendChild($node);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
function toLWData($document) {
|
||||
$element = parent::toLWData($document);
|
||||
$element->setAttribute("node", 1);
|
||||
foreach ($this->contents as $sub) {
|
||||
$node = $sub->toLWData($document);
|
||||
$element->appendChild($node);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
69
scripts/lib/db.inc
Normal file
69
scripts/lib/db.inc
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* DATABASE FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
function dbConnect($fatal = true) {
|
||||
if (is_null(db::$database)) {
|
||||
try {
|
||||
new db();
|
||||
$rv = true;
|
||||
} catch (Exception $e) {
|
||||
$rv = false;
|
||||
}
|
||||
} else {
|
||||
$rv = db::$database->open();
|
||||
}
|
||||
if ($fatal && !$rv) {
|
||||
l::fatal(1, array("SQL: Database connection failed", pg_last_error()));
|
||||
}
|
||||
return $rv;
|
||||
}
|
||||
|
||||
function dbClose() {
|
||||
db::$database->close();
|
||||
}
|
||||
|
||||
function dbQuery($q) {
|
||||
return db::$database->query($q);
|
||||
}
|
||||
|
||||
function dbBegin() {
|
||||
db::$database->begin();
|
||||
}
|
||||
|
||||
function dbEnd() {
|
||||
db::$database->end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** This function returns the results count for the $qr database query
|
||||
* result
|
||||
*/
|
||||
function dbCount($qr) {
|
||||
return @pg_num_rows($qr);
|
||||
}
|
||||
|
||||
|
||||
/** This function returns the next row from the $qr query result as a hash
|
||||
* table
|
||||
*/
|
||||
function dbFetchHash($qr) {
|
||||
return @pg_fetch_assoc($qr);
|
||||
}
|
||||
|
||||
|
||||
/** This function returns the next row from the $qr query result as an array */
|
||||
function dbFetchArray($qr) {
|
||||
return @pg_fetch_row($qr);
|
||||
}
|
||||
|
||||
|
||||
/** This function turns a value into a DB-specific boolean value. */
|
||||
function dbBool($v) {
|
||||
return $v ? "true" : "false";
|
||||
}
|
||||
|
||||
?>
|
127
scripts/lib/db_accessor.inc
Normal file
127
scripts/lib/db_accessor.inc
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
class db_accessor {
|
||||
private static $lastNamespace = null;
|
||||
private $db;
|
||||
private $namespace;
|
||||
|
||||
public function __construct($db, $namespace) {
|
||||
$this->db = $db;
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
public function setNamespace() {
|
||||
if (self::$lastNamespace === $this->namespace) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($this->namespace) || $this->namespace == '') {
|
||||
$this->db->query('SET search_path=public,main');
|
||||
} else {
|
||||
$this->db->query('SET search_path=public,main,' . $this->namespace);
|
||||
}
|
||||
|
||||
self::$lastNamespace = $this->namespace;
|
||||
}
|
||||
|
||||
public function enableExceptions() {
|
||||
$this->db->enableExceptions();
|
||||
}
|
||||
|
||||
public function disableExceptions() {
|
||||
$this->db->disableExceptions();
|
||||
}
|
||||
|
||||
public function query($q) {
|
||||
$this->setNamespace();
|
||||
return $this->db->query($q);
|
||||
}
|
||||
|
||||
public function begin() {
|
||||
$this->db->begin();
|
||||
}
|
||||
|
||||
public function end($rb = false) {
|
||||
return $this->db->end($rb);
|
||||
}
|
||||
|
||||
public function needsReset() {
|
||||
return $this->db->needsReset();
|
||||
}
|
||||
|
||||
public function reset($attempts = 5, $delay = 1) {
|
||||
if ($rv = $this->db->reset($attempts, $delay)) {
|
||||
self::$lastNamespace = false;
|
||||
}
|
||||
return $rv;
|
||||
}
|
||||
|
||||
|
||||
/** This method starts a transaction and runs in safely, trying to reset the connection
|
||||
* if it fails.
|
||||
*/
|
||||
public function safeTransaction($callback, $args = null, $maxResets = 1,
|
||||
$attempts = 5, $delay = 1, $maxDeadlocks = 10) {
|
||||
|
||||
if (is_null($args)) {
|
||||
$args = array();
|
||||
} elseif (!is_array($args)) {
|
||||
$args = array($args);
|
||||
}
|
||||
|
||||
$resets = $deadlocks = 0;
|
||||
$this->enableExceptions();
|
||||
|
||||
try {
|
||||
do {
|
||||
$ok = false;
|
||||
try {
|
||||
$this->begin();
|
||||
$returnValue = call_user_func_array($callback, $args);
|
||||
$this->end();
|
||||
$ok = true;
|
||||
} catch (db_deadlock_exception $e) {
|
||||
/* $this->end(false);
|
||||
self::$lastNamespace = false;
|
||||
if ($deadlocks == $maxDeadlocks) { */
|
||||
throw $e;
|
||||
// }
|
||||
// $deadlocks ++;
|
||||
} catch (db_sql_exception $e) {
|
||||
$this->end();
|
||||
throw $e;
|
||||
} catch (db_srv_exception $e) {
|
||||
if ($resets == $maxResets) {
|
||||
throw $e;
|
||||
}
|
||||
$this->reset($attempts, $delay);
|
||||
$resets ++;
|
||||
}
|
||||
} while (!$ok);
|
||||
} catch (Exception $e) {
|
||||
$this->disableExceptions();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->disableExceptions();
|
||||
return $returnValue;
|
||||
}
|
||||
|
||||
|
||||
/** This method copies raw data into a table.
|
||||
*/
|
||||
public function copyTo($tableName, $data) {
|
||||
$this->setNamespace();
|
||||
return $this->db->copyTo($tableName, $data);
|
||||
}
|
||||
|
||||
|
||||
/** This method copies raw data from a table.
|
||||
*/
|
||||
public function copyFrom($tableName) {
|
||||
$this->setNamespace();
|
||||
return $this->db->copyFrom($tableName);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
446
scripts/lib/db_connection.inc
Normal file
446
scripts/lib/db_connection.inc
Normal file
|
@ -0,0 +1,446 @@
|
|||
<?php
|
||||
|
||||
|
||||
class db_srv_exception extends Exception {
|
||||
|
||||
public function __construct($message, $code = 0) {
|
||||
parent::__construct("Database server error ($message)", $code);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class db_sql_exception extends Exception {
|
||||
|
||||
private $query;
|
||||
|
||||
public function __construct($query, $message = "") {
|
||||
parent::__construct("SQL query exception" . ($message != '' ? ": $message" : ""));
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
|
||||
public function getQuery() {
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class db_deadlock_exception extends Exception {
|
||||
|
||||
public function __construct($error) {
|
||||
parent::__construct("Deadlock detected ($error)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class db {
|
||||
|
||||
static $database = null;
|
||||
static $accessors = array();
|
||||
|
||||
private $isOpen = false;
|
||||
private $cString = '';
|
||||
private $conn = null;
|
||||
private $inTrans = false;
|
||||
private $queries = 0;
|
||||
private $__needsReset = false;
|
||||
private $useExceptions = 0;
|
||||
private $trace = false;
|
||||
|
||||
|
||||
static function connect() {
|
||||
if (self::$database && ! self::$database->open()) {
|
||||
throw new db_srv_exception("unable to connect");
|
||||
} else {
|
||||
new db();
|
||||
}
|
||||
return self::$database;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
if (!$this->open()) {
|
||||
throw new db_srv_exception("unable to connect");
|
||||
}
|
||||
self::$database = $this;
|
||||
}
|
||||
|
||||
public function open() {
|
||||
if ($this->isOpen) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get access to the 'main' game
|
||||
$main = config::getGame('main');
|
||||
if (!$main) {
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception("failed to fetch 'main' pseudo-game");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate the connection string
|
||||
if ($this->cString == '') {
|
||||
$cString = "dbname='{$main->params['dbname']}'";
|
||||
if ($main->params['dbhost'] != '') {
|
||||
$cString .= " host='{$main->params['dbhost']}' sslmode='disable'";
|
||||
}
|
||||
if ($main->params['dbuser'] != '') {
|
||||
$cString .= " user='{$main->params['dbuser']}'";
|
||||
}
|
||||
if ($main->params['dbpass'] != '') {
|
||||
$cString .= " password='{$main->params['dbpass']}'";
|
||||
}
|
||||
$this->cString = $cString;
|
||||
}
|
||||
|
||||
// Connect to the database
|
||||
ob_start();
|
||||
$this->conn = pg_connect($this->cString);
|
||||
$error = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// Check status
|
||||
if (!$this->conn || pg_connection_status($this->conn) == PGSQL_CONNECTION_BAD) {
|
||||
$this->conn = null;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception("database connection failed: $error");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->isOpen = true;
|
||||
$this->inTrans = false;
|
||||
$this->queries = 1;
|
||||
if (!@pg_query($this->conn, 'SET search_path=public,main')) {
|
||||
$error = pg_last_error();
|
||||
@pg_close($this->conn);
|
||||
$this->conn = null;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_sql_exception('SET search_path=public,main',
|
||||
"failed to initialise search path: $error");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$this->begin();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function close() {
|
||||
if (! $this->isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->end();
|
||||
@pg_close($this->conn);
|
||||
$this->isOpen = false;
|
||||
self::$accessors = array();
|
||||
|
||||
if ($this->queries >= 20) {
|
||||
l::debug("SQL: connection closed after {$this->queries} queries");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function enableExceptions() {
|
||||
$this->useExceptions ++;
|
||||
}
|
||||
|
||||
public function disableExceptions() {
|
||||
if ($this->useExceptions > 0) {
|
||||
$this->useExceptions --;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function fail($logTxt, $query) {
|
||||
if ($this->error) {
|
||||
if ($this->useExceptions) {
|
||||
throw new db_sql_exception($query, $logText);
|
||||
}
|
||||
return;
|
||||
}
|
||||
l::error("SQL: $logTxt");
|
||||
l::info("QUERY: $query");
|
||||
l::backtrace();
|
||||
$this->error = true;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_sql_exception($query, $logTxt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function begin() {
|
||||
if ($this->inTrans || $this->__needsReset) {
|
||||
return;
|
||||
}
|
||||
if (@pg_query($this->conn, "BEGIN TRANSACTION")) {
|
||||
$this->queries ++;
|
||||
$this->inTrans = true;
|
||||
$this->error = false;
|
||||
} else {
|
||||
l::error("SQL: could not start transaction");
|
||||
l::info("SQL: error was " . ($error = pg_last_error($this->conn)));
|
||||
if (pg_connection_status($this->conn) == PGSQL_CONNECTION_BAD) {
|
||||
$this->__needsReset = true;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception($error);
|
||||
}
|
||||
} else {
|
||||
if ($this->useExceptions) {
|
||||
throw new db_sql_exception("BEGIN TRANSACTION", $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function end($rollback = false) {
|
||||
if (!$this->inTrans) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$r = ($rollback || $this->error);
|
||||
$query = $r ? "ROLLBACK" : "COMMIT";
|
||||
if (@pg_query($this->conn, $query)) {
|
||||
$this->queries ++;
|
||||
$this->inTrans = false;
|
||||
} else {
|
||||
$cStat = pg_connection_status($this->conn);
|
||||
$error = pg_last_error($this->conn);
|
||||
if ($cStat == PGSQL_CONNECTION_BAD) {
|
||||
l::notice("SQL: could not end transaction, connection is in an invalid status");
|
||||
$this->__needsReset = true;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception($error);
|
||||
}
|
||||
} else {
|
||||
l::error("SQL: could not end transaction while " . ($r ? "rolling back" : "comitting"));
|
||||
l::info("SQL ERROR: $error");
|
||||
l::backtrace();
|
||||
if ($this->useExceptions) {
|
||||
throw new db_sql_exception($query, $error);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function query($query) {
|
||||
if (!$this->inTrans) {
|
||||
$this->fail("query executed outside of transaction", $query);
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->queries ++;
|
||||
if ($this->trace) {
|
||||
l::trace("EXECUTE: $query");
|
||||
}
|
||||
$r = @pg_query($this->conn, $query);
|
||||
if (!$r) {
|
||||
$cStat = pg_connection_status($this->conn);
|
||||
$error = pg_last_error($this->conn);
|
||||
if ($cStat == PGSQL_CONNECTION_BAD) {
|
||||
l::notice("SQL: connection is gone while executing query");
|
||||
l::debug("QUERY: $query");
|
||||
$this->__needsReset = true;
|
||||
$this->error = true;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception($error);
|
||||
}
|
||||
return;
|
||||
} elseif (preg_match('/deadlock/i', $error)) {
|
||||
l::error("SQL: deadlock detected");
|
||||
if ($this->useExceptions) {
|
||||
throw new db_deadlock_exception($error);
|
||||
}
|
||||
$this->error = true;
|
||||
return;
|
||||
} else {
|
||||
$this->fail("an error has occured: $error", $query);
|
||||
}
|
||||
} elseif (preg_match('/^\s*insert\s+into ("?\w+"?)/i', $query, $match)) {
|
||||
pg_free_result($r);
|
||||
|
||||
$tn = $match[1];
|
||||
if ($tn{0} == '"') {
|
||||
$tn = str_replace('"', '', $tn);
|
||||
} else {
|
||||
$tn = strtolower($tn);
|
||||
}
|
||||
|
||||
$r2 = @pg_query("SELECT last_inserted('$tn')");
|
||||
if ($r2) {
|
||||
$rv = pg_fetch_row($r2);
|
||||
if (is_null($rv[0])) {
|
||||
$r = true;
|
||||
} else {
|
||||
$r = $rv[0];
|
||||
}
|
||||
pg_free_result($r2);
|
||||
} elseif (preg_match('/deadlock/i', $error)) {
|
||||
l::error("SQL: deadlock detected");
|
||||
if ($this->useExceptions) {
|
||||
throw new db_deadlock_exception($error);
|
||||
}
|
||||
$this->error = true;
|
||||
return;
|
||||
} else {
|
||||
$cStat = pg_connection_status($this->conn);
|
||||
$error = pg_last_error($this->conn);
|
||||
if ($cStat == PGSQL_CONNECTION_BAD) {
|
||||
l::notice("SQL: connection is gone while fetching last ID");
|
||||
$this->__needsReset = true;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception($error);
|
||||
}
|
||||
} else {
|
||||
$this->fail("failed to fetch last ID: $error", "SELECT last_inserted('$tn')");
|
||||
}
|
||||
$r = null;
|
||||
}
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function getGameAccess($prefix) {
|
||||
if (is_null($this->accessors[$prefix])) {
|
||||
$this->accessors[$prefix] = new db_accessor($this, $prefix);
|
||||
}
|
||||
return $this->accessors[$prefix];
|
||||
}
|
||||
|
||||
public function needsReset() {
|
||||
return $this->__needsReset;
|
||||
}
|
||||
|
||||
public function reset($attempts = 5, $delay = 1) {
|
||||
l::notice("SQL: resetting connection after {$this->queries} queries");
|
||||
$this->__needsReset = false;
|
||||
$i = 0;
|
||||
do {
|
||||
$rv = @pg_connection_reset($this->conn);
|
||||
if (!$rv) {
|
||||
$error = pg_last_error($this->conn);
|
||||
$i ++;
|
||||
l::warn("Reconnection attempt #$i failed" . ($i < $attempts ? ", retrying" : ""));
|
||||
sleep($delay);
|
||||
}
|
||||
} while ($i < $attempts && !$rv);
|
||||
if ($rv) {
|
||||
$this->error = false;
|
||||
$this->inTrans = false;
|
||||
$this->queries = 0;
|
||||
$this->trace = true;
|
||||
} else {
|
||||
$this->isOpen = false;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception($error);
|
||||
}
|
||||
}
|
||||
return $rv;
|
||||
}
|
||||
|
||||
|
||||
public function copyTo($table, $data) {
|
||||
$actualData = array();
|
||||
foreach ($data as $row) {
|
||||
$actualRow = array();
|
||||
foreach ($row as $column) {
|
||||
if (is_null($column)) {
|
||||
$output = "\\N";
|
||||
} elseif (is_string($column)) {
|
||||
$output = preg_replace(
|
||||
array('/\\\\/', '/\x08/', '/\f/', '/\r/', '/\\n/', '/\t/', '/\v/'),
|
||||
array('\\\\\\', '\\b', '\\f', '\\r', '\\n', '\\t', '\\v'),
|
||||
$column
|
||||
);
|
||||
} else {
|
||||
$output = $column;
|
||||
}
|
||||
array_push($actualRow, $output);
|
||||
}
|
||||
array_push($actualData, join("\t", $actualRow));
|
||||
}
|
||||
|
||||
$result = @pg_copy_from($this->conn, $table, $actualData);
|
||||
if (!$result) {
|
||||
$cStat = pg_connection_status($this->conn);
|
||||
$error = pg_last_error($this->conn);
|
||||
if ($cStat == PGSQL_CONNECTION_BAD) {
|
||||
l::notice("SQL: connection is gone while copying into table '$table'");
|
||||
$this->__needsReset = true;
|
||||
$this->error = true;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception($error);
|
||||
}
|
||||
return false;
|
||||
} elseif (preg_match('/deadlock/i', $error)) {
|
||||
l::error("SQL: deadlock detected while copying into table '$table'");
|
||||
if ($this->useExceptions) {
|
||||
throw new db_deadlock_exception($error);
|
||||
}
|
||||
$this->error = true;
|
||||
return false;
|
||||
} else {
|
||||
$this->fail("an error has occured: $error", "COPY \"$table\" FROM STDIN");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function copyFrom($table) {
|
||||
$result = pg_copy_to($this->conn, $table);
|
||||
if ($result === FALSE) {
|
||||
$cStat = pg_connection_status($this->conn);
|
||||
$error = pg_last_error($this->conn);
|
||||
if ($cStat == PGSQL_CONNECTION_BAD) {
|
||||
l::notice("SQL: connection is gone while copying from table '$table'");
|
||||
$this->__needsReset = true;
|
||||
$this->error = true;
|
||||
if ($this->useExceptions) {
|
||||
throw new db_srv_exception($error);
|
||||
}
|
||||
return false;
|
||||
} elseif (preg_match('/deadlock/i', $error)) {
|
||||
l::error("SQL: deadlock detected while copying from table '$table'");
|
||||
if ($this->useExceptions) {
|
||||
throw new db_deadlock_exception($error);
|
||||
}
|
||||
$this->error = true;
|
||||
return false;
|
||||
} else {
|
||||
$this->fail("an error has occured: $error", "COPY \"$table\" TO STDOUT");
|
||||
}
|
||||
}
|
||||
|
||||
$actualData = array();
|
||||
foreach ($result as $row) {
|
||||
$actualRow = array();
|
||||
$splitRow = explode("\t", $row);
|
||||
foreach ($splitRow as $column) {
|
||||
if ($column == "\\N") {
|
||||
$output = null;
|
||||
} else {
|
||||
$output = preg_replace(
|
||||
array('/\\\\b/', '/\\\\f/',
|
||||
'/\\\\r/', '/\\\\n/', '/\\\\t/', '/\\\\v/', '/\\\\\\\\/'),
|
||||
array("\x08", "\x0c", "\r", "\n", "\t", "\x0b", "\\"),
|
||||
$column
|
||||
);
|
||||
}
|
||||
array_push($actualRow, $output);
|
||||
}
|
||||
array_push($actualData, $actualRow);
|
||||
}
|
||||
|
||||
return $actualData;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
210
scripts/lib/db_copy.inc
Normal file
210
scripts/lib/db_copy.inc
Normal file
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// LegacyWorlds Beta 5
|
||||
// Game libraries
|
||||
//
|
||||
// lib/db_copy.inc
|
||||
//
|
||||
// This library adds support for mass insertions into the database.
|
||||
//
|
||||
// Copyright(C) 2004-2008, DeepClone Development
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
|
||||
class db_copy {
|
||||
|
||||
/* COPY MODES */
|
||||
const copyTo = 0; // Mass-insert data into the table
|
||||
const copyToClean = 1; // Similar to copyTo, but delete the table's contents first
|
||||
const copyFrom = 2; // Mass-reads the table's contents
|
||||
/**************/
|
||||
|
||||
private $table;
|
||||
private $mode;
|
||||
private $db;
|
||||
private $data;
|
||||
private $colNames;
|
||||
|
||||
|
||||
/** Initialises the instance by specifying a table name and an optional copy mode.
|
||||
* If no mode is specified, it defaults to copyFrom (mass read).
|
||||
* The constructor can also be called with another db_copy instance as its first
|
||||
* argument; in that case, it copies this instance's data; the mode must be either
|
||||
* copyTo or copyToClean.
|
||||
*/
|
||||
public function __construct($table, $mode = self::copyFrom) {
|
||||
if (is_string($table)) {
|
||||
$this->table = $table;
|
||||
$this->mode = $mode;
|
||||
$this->db = $this->data = $this->colNames = null;
|
||||
} elseif (is_object($table) && $table instanceof db_copy) {
|
||||
$this->table = $table->table;
|
||||
$this->mode = ($mode != self::copyFrom ? $mode : self::copyTo);
|
||||
$this->db = $table->db;
|
||||
$this->data = $table->data;
|
||||
$this->colNames = $table->colNames;
|
||||
} else {
|
||||
throw new Exception("Invalid parameters to the db_copy constructor");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method sets the database accessor to use when performing the copy
|
||||
* operation.
|
||||
*/
|
||||
public function setAccessor($db) {
|
||||
if (!is_object($db) || !(($db instanceof db) || ($db instanceof db_accessor))) {
|
||||
throw new Exception("Invalid database accessor: " . gettype($db)
|
||||
. (is_object($db) ? (" (" . get_class($db) . ")") : "") );
|
||||
}
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
|
||||
/** This method executes the operation. It returns true on success or false
|
||||
* on error. However, if database exceptions are enabled, SQL failure will
|
||||
* cause an exception to be thrown.
|
||||
*/
|
||||
public function execute() {
|
||||
if (is_null($this->db)) {
|
||||
l::debug("db_copy::execute() called but no accessor set");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->mode == self::copyFrom) {
|
||||
if (!is_null($this->data)) {
|
||||
return true;
|
||||
}
|
||||
$this->data = $this->db->copyFrom($this->table);
|
||||
return !is_null($this->data);
|
||||
}
|
||||
|
||||
if (is_null($this->data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->mode == self::copyToClean) {
|
||||
$this->db->query("DELETE FROM \"{$this->table}\"");
|
||||
}
|
||||
return $this->db->copyTo($this->table, $this->data);
|
||||
}
|
||||
|
||||
|
||||
/** This method is called with an arbitrary number of arguments. It sets
|
||||
* the names for the columns, which is required for some of the operations.
|
||||
*/
|
||||
public function setColumnNames() {
|
||||
$n = func_num_args();
|
||||
if ($n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$args = func_get_args();
|
||||
$names = array();
|
||||
for ($i = 0; $i < $n; $i ++) {
|
||||
if (!is_string($args[$i])) {
|
||||
return;
|
||||
}
|
||||
$names[$args[$i]] = $i;
|
||||
}
|
||||
|
||||
$this->colNames = $names;
|
||||
}
|
||||
|
||||
|
||||
/** This method returns the amount of rows in the object.
|
||||
*/
|
||||
public function rows() {
|
||||
return is_null($this->data) ? 0 : count($this->data);
|
||||
}
|
||||
|
||||
|
||||
/** This method appends a row to the current data. The row must be passed
|
||||
* as an array of values. This method will fail and return FALSE if the
|
||||
* current mode is copyFrom or if the data is not an array.
|
||||
*/
|
||||
public function appendRow($data) {
|
||||
if (!is_array($data) || $this->mode == self::copyFrom) {
|
||||
return false;
|
||||
}
|
||||
if (!is_array($this->data)) {
|
||||
$this->data = array();
|
||||
}
|
||||
array_push($this->data, $data);
|
||||
}
|
||||
|
||||
|
||||
/** This method removes a row using its index. It will return FALSE if the
|
||||
* index is invalid or if the current mode is copyFrom. In any other case
|
||||
* it will return the row that has been removed.
|
||||
*/
|
||||
public function removeRow($index) {
|
||||
if ($this->mode == self::copyFrom || !is_int($index) || $index < 0
|
||||
|| is_null($this->data) || $index >= count($this->data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_splice($this->data, $index, 1);
|
||||
}
|
||||
|
||||
|
||||
/** This method returns a row from the current data. It will return FALSE
|
||||
* if the index is invalid, or the row on success.
|
||||
*/
|
||||
public function getRow($index) {
|
||||
if (!is_int($index) || $index < 0 || is_null($this->data) || $index >= count($this->data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->data[$index];
|
||||
}
|
||||
|
||||
|
||||
/** This method searches for rows using a column identifier (either an
|
||||
* integer or, if the column names have been specified, a column name)
|
||||
* and a value to look for. It returns FALSE if the column identifier
|
||||
* is invalid, or an array of row indices if rows are found. The
|
||||
* additional $unique parameter can specify that the value will be
|
||||
* unique to prevent the method from going through all rows in that
|
||||
* case; it is false by default.
|
||||
*/
|
||||
public function lookupRow($column, $value, $unique = false) {
|
||||
if (is_null($this->data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_int($column)) {
|
||||
if ($column < 0) {
|
||||
return false;
|
||||
}
|
||||
} elseif (is_string($column)) {
|
||||
if (is_null($this->colNames) || !array_key_exists($column, $this->colNames)) {
|
||||
return false;
|
||||
}
|
||||
$column = $this->colNames[$column];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
$results = array();
|
||||
for ($i = 0; $i < count($this->data); $i ++) {
|
||||
if (count($this->data[$i]) < $column) {
|
||||
return false;
|
||||
}
|
||||
if ($this->data[$i][$column] != $value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
array_push($results, $i);
|
||||
if ($unique) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
57
scripts/lib/engine.inc
Normal file
57
scripts/lib/engine.inc
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* DISPLAY ENGINE MANAGEMENT
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
class engine {
|
||||
|
||||
static $e = null;
|
||||
|
||||
/** This function loads a display engine. It then checks the configuration for
|
||||
* the maintenance mode data; if such data is found, the display engine's
|
||||
* maintenance() method is called.
|
||||
* A fatal error is displayed if the engine isn't found in the engines/
|
||||
* subdirectory of the script directory.
|
||||
*/
|
||||
function load() {
|
||||
$eType = input::$eType;
|
||||
|
||||
// Check for a valid type
|
||||
if (preg_match('/[^A-Za-z0-9]/', $eType)) {
|
||||
l::fatal(26);
|
||||
}
|
||||
$eType = strtolower($eType);
|
||||
|
||||
// Get the path
|
||||
$ePath = config::$main['scriptdir'] . "/lib/engines/$eType.inc";
|
||||
|
||||
// Load the engine
|
||||
if (!file_exists($ePath)) {
|
||||
l::fatal(27, "Unhandled engine type: $eType");
|
||||
} elseif (!include($ePath)) {
|
||||
l::fatal(28, "Could not open engine file '$ePath'");
|
||||
} elseif (!class_exists('display_engine')) {
|
||||
l::fatal(29, "Invalid engine file for $eType");
|
||||
} else {
|
||||
$engine = new display_engine();
|
||||
}
|
||||
|
||||
// Check for maintenance mode
|
||||
if (is_array(config::$main['maintenance'])) {
|
||||
$engine->maintenance(config::$main['maintenance']);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
engine::$e = $engine;
|
||||
l::setFatalHandler('engine::fatalError');
|
||||
}
|
||||
|
||||
static function fatalError($errno, $errorText, $information) {
|
||||
die(engine::$e->displayFatalError($errno, $errorText, $information));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
71
scripts/lib/engines/css.inc
Normal file
71
scripts/lib/engines/css.inc
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
class display_engine {
|
||||
|
||||
function maintenance($maintenance) {
|
||||
echo "/* Server is under maintenance: " . $maintenance['reason'] . " */";
|
||||
}
|
||||
|
||||
|
||||
function displayFatalError($code, $i, $i2) {
|
||||
echo "/* Fatal error $code */";
|
||||
}
|
||||
|
||||
|
||||
function initSession() {
|
||||
}
|
||||
|
||||
|
||||
function checkAuth() {
|
||||
}
|
||||
|
||||
|
||||
function handleInput() {
|
||||
$this->id = (int) input::$input['id'];
|
||||
}
|
||||
|
||||
|
||||
function initRPC() {
|
||||
}
|
||||
|
||||
|
||||
function outputData() {
|
||||
header("Content-Type: text/css");
|
||||
|
||||
if (!is_array(tracking::$data['cssId'])) {
|
||||
tracking::$data['cssId'] = array();
|
||||
} else {
|
||||
$now = time();
|
||||
foreach (tracking::$data['cssId'] as $ck => $t) {
|
||||
if ($now - $t > 345600) {
|
||||
unset(tracking::$data['cssId'][$ck]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cssId = input::$path . "/" . input::$page . "/{$this->id}";
|
||||
if (!isset(tracking::$data['cssId'][$cssId])) {
|
||||
tracking::$data['cssId'][$cssId] = time();
|
||||
}
|
||||
|
||||
$time = tracking::$data['cssId'][$cssId];
|
||||
$lastModified = substr(date('r', $time), 0, -5).'GMT';
|
||||
$etag = '"'.md5($lastModified).'"';
|
||||
header("Last-Modified: $lastModified GMT");
|
||||
header("ETag: $etag");
|
||||
if (!input::$IE) {
|
||||
header("Cache-Control: no-cache, must-revalidate");
|
||||
}
|
||||
$ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false;
|
||||
$ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false;
|
||||
if (($ifNoneMatch || $ifModifiedSince) && (!$ifNoneMatch || $ifNoneMatch == $etag) && (!$ifModifiedSince || $ifModifiedSince != $lastModified)) {
|
||||
header('HTTP/1.0 304 Not Modified');
|
||||
return;
|
||||
}
|
||||
|
||||
displayResource($this->id, "css");
|
||||
l::fatal(30, "CSS resource {$this->id}");
|
||||
}
|
||||
}
|
||||
|
||||
|
71
scripts/lib/engines/js.inc
Normal file
71
scripts/lib/engines/js.inc
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
class display_engine {
|
||||
|
||||
function maintenance($maintenance) {
|
||||
echo "/* Server is under maintenance: " . $maintenance['reason'] . " */";
|
||||
}
|
||||
|
||||
|
||||
function displayFatalError($code, $i, $i2) {
|
||||
echo "/* Fatal error $code */";
|
||||
}
|
||||
|
||||
|
||||
function initSession() {
|
||||
}
|
||||
|
||||
|
||||
function checkAuth() {
|
||||
}
|
||||
|
||||
|
||||
function handleInput() {
|
||||
$this->id = (int) input::$input['id'];
|
||||
}
|
||||
|
||||
|
||||
function initRPC() {
|
||||
}
|
||||
|
||||
|
||||
function outputData() {
|
||||
header("Content-Type: text/javascript");
|
||||
|
||||
if (!is_array(tracking::$data['jsId'])) {
|
||||
tracking::$data['jsId'] = array();
|
||||
} else {
|
||||
$now = time();
|
||||
foreach (tracking::$data['jsId'] as $ck => $t) {
|
||||
if ($now - $t > 345600) {
|
||||
unset(tracking::$data['jsId'][$ck]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$jsId = input::$path . "/" . input::$page . "/{$this->id}";
|
||||
if (!isset(tracking::$data['jsId'][$jsId])) {
|
||||
tracking::$data['jsId'][$jsId] = time();
|
||||
}
|
||||
|
||||
$time = tracking::$data['jsId'][$jsId];
|
||||
$lastModified = substr(date('r', $time), 0, -5).'GMT';
|
||||
$etag = '"'.md5($lastModified).'"';
|
||||
header("Last-Modified: $lastModified GMT");
|
||||
header("ETag: $etag");
|
||||
if (!input::$IE) {
|
||||
header("Cache-Control: no-cache, must-revalidate");
|
||||
}
|
||||
$ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false;
|
||||
$ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false;
|
||||
if (($ifNoneMatch || $ifModifiedSince) && (!$ifNoneMatch || $ifNoneMatch == $etag) && (!$ifModifiedSince || $ifModifiedSince != $lastModified)) {
|
||||
header('HTTP/1.0 304 Not Modified');
|
||||
return;
|
||||
}
|
||||
|
||||
displayResource($this->id, "js");
|
||||
l::fatal(30, "JS resource {$this->id}");
|
||||
}
|
||||
}
|
||||
|
||||
|
452
scripts/lib/engines/page.inc
Normal file
452
scripts/lib/engines/page.inc
Normal file
|
@ -0,0 +1,452 @@
|
|||
<?php
|
||||
|
||||
// Standard page output engine
|
||||
|
||||
class display_engine {
|
||||
|
||||
public static $version;
|
||||
|
||||
function maintenance($maintenance) {
|
||||
include(config::$main['scriptdir'] . "/site/main/maintenance.inc");
|
||||
}
|
||||
|
||||
|
||||
function displayFatalError($errno, $errorText, $information) {
|
||||
ob_end_clean();
|
||||
ob_start();
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<title>Legacy Worlds</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<b>The LegacyWorlds game engine encountered a fatal error:</b><br/>
|
||||
<?=$errorText?><br/>
|
||||
Please report this error by sending an e-mail to the staff at
|
||||
<a href='mailto:webmaster@legacyworlds.com'>webmaster@legacyworlds.com</a>
|
||||
explaining what happened. Please make sure you include information such
|
||||
as the name of the Web browser you're using, the address of the page on
|
||||
which you got this message, etc ... The more information you give us, the
|
||||
better we'll be able to fix the problem.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
$contents = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $contents;
|
||||
}
|
||||
|
||||
|
||||
function displayLogin($default = "", $error = false) {
|
||||
input::$path = 'main';
|
||||
input::$page = 'login';
|
||||
$this->outputPage('main', 'login', array('login' => $default, 'error' => $error));
|
||||
endRequest();
|
||||
}
|
||||
|
||||
|
||||
function displayConfirm($error = false) {
|
||||
input::$path = 'main';
|
||||
input::$page = 'confirm';
|
||||
$this->outputPage('main', 'confirm', array('error' => $error));
|
||||
endRequest();
|
||||
}
|
||||
|
||||
function displayKicked() {
|
||||
input::$path = 'main';
|
||||
input::$page = 'kicked';
|
||||
$this->outputPage('main', 'kicked', null);
|
||||
endRequest();
|
||||
}
|
||||
|
||||
|
||||
function initSession() {
|
||||
$create = handler::$h->needsAuth || input::$input['userlogin'] == 1 || input::$input['authcode'] != '' || input::$input['ac_restart'] != "";
|
||||
$r = session::handle($create);
|
||||
if ($r == 1 && $create) {
|
||||
$this->displayLogin();
|
||||
} elseif ($r == 2 && $create) {
|
||||
l::trace("tracknew? " . (tracking::$new ? "yes" : "no"));
|
||||
l::fatal(12);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function checkGameRegistration() {
|
||||
if (input::$path == "main") {
|
||||
return;
|
||||
}
|
||||
|
||||
$game = input::$game;
|
||||
if ($game->status() == 'PRE' || $game->status() == 'FINISHED') {
|
||||
input::$path = 'main';
|
||||
input::$page = 'notfound';
|
||||
$this->outputPage('main', 'notfound', array());
|
||||
endRequest();
|
||||
}
|
||||
|
||||
$lib = $game->getLib();
|
||||
$pid = $lib->call("doesUserPlay", $_SESSION['userid']);
|
||||
if (is_null($pid)) {
|
||||
input::$path = 'main';
|
||||
input::$page = 'notregistered';
|
||||
$this->outputPage('main', 'notregistered', array(
|
||||
"id" => $game->name,
|
||||
"name" => $game->text
|
||||
));
|
||||
endRequest();
|
||||
}
|
||||
if (!is_array($_SESSION["{$game->name}_data"])) {
|
||||
$_SESSION["{$game->name}_data"] = array("player" => $pid);
|
||||
}
|
||||
dbQuery("UPDATE main.credits SET resources_used = resources_used + 1 WHERE account = {$_SESSION['userid']}");
|
||||
}
|
||||
|
||||
function checkCredits() {
|
||||
if (! is_null($_SESSION['annoy_until'])) {
|
||||
if (time() >= $_SESSION['annoy_until']) {
|
||||
$_SESSION['annoy_until'] = null;
|
||||
return;
|
||||
}
|
||||
$this->outputPage('main', 'annoy', array(
|
||||
"time" => $_SESSION['annoy_until'] - time()
|
||||
));
|
||||
endRequest();
|
||||
}
|
||||
|
||||
$q = dbQuery("SELECT resources_used, credits_obtained FROM credits WHERE account = {$_SESSION['userid']}");
|
||||
list($used, $cred) = dbFetchArray($q);
|
||||
if ($used < $cred) {
|
||||
return;
|
||||
}
|
||||
|
||||
$time = min(60, round(5 + ($used - $cred) / 1000));
|
||||
$_SESSION['annoy_until'] = time() + $time;
|
||||
|
||||
$this->outputPage('main', 'annoy', array(
|
||||
"time" => $time + 1
|
||||
));
|
||||
endRequest();
|
||||
}
|
||||
|
||||
function checkConfirmationCode($code) {
|
||||
$q = "SELECT status,conf_code FROM account WHERE id=" . $_SESSION['userid'];
|
||||
$qr = dbQuery($q);
|
||||
|
||||
if (!$qr || dbCount($qr) != 1) {
|
||||
l::warn("Reading the {$_SESSION['userid']} account's confirmation code failed");
|
||||
killSession();
|
||||
$this->displayLogin();
|
||||
}
|
||||
|
||||
list($status, $cc) = dbFetchArray($qr);
|
||||
if ($cc != strtolower($code)) {
|
||||
l::notice("Account {$_SESSION['userid']} entered an invalid confirmation code");
|
||||
$this->displayConfirm(true);
|
||||
}
|
||||
|
||||
// Validate account
|
||||
dbQuery("UPDATE account SET conf_code=NULL,status='STD' WHERE id={$_SESSION['userid']}");
|
||||
$_SESSION['authok'] = true;
|
||||
accountLog('v', $_SESSION['userid']);
|
||||
|
||||
if ($status == 'NEW') {
|
||||
// New accounts -> register to default game
|
||||
$main = config::getGame('main');
|
||||
$game = $main->getLib()->call('preJoin', $_SESSION['userid']);
|
||||
input::$path = 'main';
|
||||
input::$page = 'play';
|
||||
if (is_null($game)) {
|
||||
$this->outputPage('main', 'play', 2);
|
||||
} else {
|
||||
$this->outputPage('main', 'play', array('registered' => $game));
|
||||
}
|
||||
endRequest();
|
||||
} else {
|
||||
$input = $_SESSION['original_request'];
|
||||
$this->checkGameRegistration();
|
||||
}
|
||||
}
|
||||
|
||||
function doRestart($uid) {
|
||||
$conf = substr(md5(uniqid(rand())), 0, 16);
|
||||
|
||||
$q = dbQuery("SELECT name,email FROM account WHERE id='$uid' AND conf_code IS NULL");
|
||||
if (!($q && dbCount($q) == 1)) {
|
||||
return false;
|
||||
}
|
||||
list($u, $e) = dbFetchArray($q);
|
||||
|
||||
if (!dbQuery("UPDATE account SET conf_code='$conf' WHERE id='$uid'")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbQuery(
|
||||
"INSERT INTO account_log(tracking,account,ip_addr,action) VALUES("
|
||||
. tracking::$dbId . ",$uid,'".$_SERVER['REMOTE_ADDR']."','CREATE')"
|
||||
);
|
||||
|
||||
$mail = @file_get_contents(config::$main['scriptdir'] . "/game/main/mail/mail-restart.en.txt");
|
||||
if (is_bool($mail)) {
|
||||
return false;
|
||||
}
|
||||
$tmp = explode("\n", $mail);
|
||||
$sub = array_shift($tmp);
|
||||
$mail = preg_replace(
|
||||
array('/_USER_/', '/_CCODE_/'),
|
||||
array($u, $conf), join("\n", $tmp)
|
||||
);
|
||||
$hdr = "From: webmaster@legacyworlds.com\r\n"
|
||||
. "Reply-To: webmaster@legacyworlds.com\r\n"
|
||||
. "X-Mailer: LegacyWorlds\r\n"
|
||||
. "Mime-Version: 1.0\r\n"
|
||||
. "Content-Type: text/plain; charset=utf-8";
|
||||
if (!mail($e, $sub, $mail, $hdr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function restartAccount($mail) {
|
||||
if (is_null(input::$input['ac_restart'])) {
|
||||
input::$path = 'main';
|
||||
input::$page = 'restart';
|
||||
$this->outputPage('main', 'restart', array("email"=>$mail));
|
||||
endRequest();
|
||||
} else {
|
||||
$this->doRestart($_SESSION['userid']);
|
||||
$this->displayLogin();
|
||||
}
|
||||
}
|
||||
|
||||
function accountValidation($loggedIn = false) {
|
||||
$q = dbQuery("SELECT status,reason,email,conf_code FROM account WHERE id={$_SESSION['userid']}");
|
||||
|
||||
if (!$q || dbCount($q) != 1) {
|
||||
$engine->displayLogin($_SESSION['login'], true);
|
||||
}
|
||||
|
||||
list($status,$reason,$mail,$code) = dbFetchArray($q);
|
||||
if ($status == 'KICKED') {
|
||||
l::notice("Got a login attempt from kicked user {$_SESSION['userid']}; setting banned attempt flag");
|
||||
dbQuery("INSERT INTO banned_attempt (ip_addr) VALUES ('{$_SERVER['REMOTE_ADDR']}')");
|
||||
tracking::$data['bat'] = true;
|
||||
tracking::$data['uid'] = $_SESSION['userid'];
|
||||
session::kill();
|
||||
$this->displayKicked();
|
||||
}
|
||||
|
||||
if ($status == 'STD' || $status == 'VAC') {
|
||||
if (! $_SESSION['authok']) {
|
||||
$_SESSION['authok'] = true;
|
||||
$this->checkCredits();
|
||||
} elseif (! is_null($_SESSION['annoy_until'])) {
|
||||
$this->checkCredits();
|
||||
}
|
||||
$this->checkGameRegistration();
|
||||
$_SESSION['last_page'] = time();
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null(input::$input['authcode']) && !is_null($code)) {
|
||||
$_SESSION['original_request'] = input::$input;
|
||||
$this->displayConfirm(false);
|
||||
} elseif ($loggedIn) {
|
||||
session::kill();
|
||||
$this->displayLogin();
|
||||
} elseif (!is_null(input::$input['authcode']) && !is_null($code)) {
|
||||
$this->checkConfirmationCode(input::$input['authcode']);
|
||||
} else {
|
||||
$this->restartAccount($mail);
|
||||
}
|
||||
}
|
||||
|
||||
function checkAccountAuth($l, $p) {
|
||||
$l1 = addslashes($l); $p1 = addslashes($p);
|
||||
$q = "SELECT id,name,password FROM account WHERE lower(name) = lower('$l1')";
|
||||
$qr = dbQuery($q);
|
||||
|
||||
if (!$qr || dbCount($qr) != 1) {
|
||||
l::info("Account authentication failed, login '".urlencode($l)."' not found");
|
||||
if ($qr) {
|
||||
l::debug("Query returned " . dbCount($qr) . " row(s)", LOG_DEBUG);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
list($uid,$l2,$pw) = dbFetchArray($qr);
|
||||
if ($p != $pw) {
|
||||
l::info("Account authentication failed, login '".urlencode($l)."' used invalid password");
|
||||
l::debug("User ID: $uid ; real user name: $l2", LOG_DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Close existing sessions for the same user
|
||||
$q = dbQuery("SELECT id,tracking FROM web_session WHERE account=$uid");
|
||||
if ($q && dbCount($q)) {
|
||||
list($sid, $tid) = dbFetchArray($q);
|
||||
dbQuery(
|
||||
"INSERT INTO account_log(tracking,account,ip_addr,action) "
|
||||
. "VALUES ($tid,$uid,'AUTO','OUT')"
|
||||
);
|
||||
dbQuery("DELETE FROM web_session WHERE id=$sid");
|
||||
}
|
||||
|
||||
$_SESSION['login'] = $l2;
|
||||
$_SESSION['userid'] = $uid;
|
||||
l::info("Player '$l2' (#$uid) logged in");
|
||||
tracking::$data['previous_login'] = $l;
|
||||
session::setAccount($uid);
|
||||
|
||||
accountLog('i', $uid);
|
||||
$q = dbQuery("UPDATE account SET last_login=UNIX_TIMESTAMP(NOW()) WHERE id=$uid");
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkAuth() {
|
||||
$create = handler::$h->needsAuth || input::$input['userlogin'] == 1 || input::$input['authcode'] != '' || input::$input['ac_restart'] != "";
|
||||
|
||||
if (!$create) {
|
||||
if ($_SESSION['authok']) {
|
||||
$this->accountValidation(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (tracking::$data['bat']) {
|
||||
// User tried to log in after being kicked
|
||||
session::kill();
|
||||
$this->displayKicked();
|
||||
} elseif ($_SESSION['login'] != "") {
|
||||
$this->accountValidation();
|
||||
} elseif (input::$input['userlogin'] == 1) {
|
||||
if ($this->checkAccountAuth(input::$input['login'], input::$input['password'])) {
|
||||
input::$input = $_SESSION['original_request'];
|
||||
$this->accountValidation();
|
||||
} else {
|
||||
sleep(5);
|
||||
$this->displayLogin(input::$input['login'], true);
|
||||
}
|
||||
} else {
|
||||
$_SESSION['original_request'] = input::$input;
|
||||
$this->displayLogin();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function handleInput() {
|
||||
handler::$h->handle(input::$input);
|
||||
if (is_null(handler::$h->output)) {
|
||||
l::fatal(18, "In handler for ".input::$path." / " . input::$page);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initRPC() {
|
||||
ajax::init();
|
||||
}
|
||||
|
||||
|
||||
function outputData() {
|
||||
$this->outputPage(input::$game->version->id, handler::$h->output, handler::$h->data);
|
||||
}
|
||||
|
||||
function outputPage($version, $page, $args) {
|
||||
$lang = getLanguage();
|
||||
$f = $this->checkOutput($version, $page, $lang);
|
||||
|
||||
self::$version = $version;
|
||||
$layoutFile = config::$main['scriptdir'] . "/site/$version/page.inc";
|
||||
loader::load($layoutFile, "page_layout");
|
||||
$this->layout = new page_layout();
|
||||
|
||||
ob_start();
|
||||
$this->layout->header($page, $lang);
|
||||
$this->layout->includeFile($f, $args);
|
||||
$this->layout->footer($page, $lang);
|
||||
$data = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$this->sendHTTP($data);
|
||||
}
|
||||
|
||||
/** This function checks for the presence of a file containing the specified
|
||||
* page in the specified language. It dies with an internal error if the file
|
||||
* isn't found, and returns the file's path otherwise.
|
||||
*/
|
||||
function checkOutput($version, $page, $lg) {
|
||||
$file = config::$main['scriptdir']."/site/$version/output/$page.$lg.inc";
|
||||
if (file_exists($file) && is_readable($file) && is_file($file)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$file = config::$main['scriptdir']."/site/$version/output/$page.inc";
|
||||
if (!(file_exists($file) && is_readable($file) && is_file($file))) {
|
||||
l::fatal(16, "Page was '$page', language was '$lg'");
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
|
||||
function sendHTTP($data) {
|
||||
header("Content-Type: text/html; charset=utf-8");
|
||||
if (input::$IE || input::$safari) {
|
||||
// Don't even bother with the cache; in fact, make sure the cache doesn't interfere
|
||||
header("Expires: Thu, 19 Feb 1981 05:00:00 GMT");
|
||||
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
|
||||
header("Cache-Control: no-cache, must-revalidate");
|
||||
header("Pragma: no-cache");
|
||||
|
||||
// Send the data directly
|
||||
endRequest(false);
|
||||
echo $data;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Generate a HTML resource from the data
|
||||
addRawResource('html', $data);
|
||||
$resId = storeResource("html", 21600, false);
|
||||
$htmlId = md5($_SERVER['REQUEST_URI']) . ":$resId";
|
||||
|
||||
// Check the tracking cookie for the required contents
|
||||
if (!is_array(tracking::$data['htmlId'])) {
|
||||
tracking::$data['htmlId'] = array();
|
||||
} else {
|
||||
$now = time();
|
||||
foreach (tracking::$data['htmlId'] as $ck => $t) {
|
||||
if ($now - $t > 21600) {
|
||||
unset(tracking::$data['htmlId'][$ck]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset(tracking::$data['htmlId'][$htmlId])) {
|
||||
tracking::$data['htmlId'][$htmlId] = time();
|
||||
}
|
||||
|
||||
// Send the page's header
|
||||
$time = tracking::$data['htmlId'][$htmlId];
|
||||
$lastModified = substr(date('r', $time), 0, -5).'GMT';
|
||||
$etag = '"'.md5($lastModified).'"';
|
||||
header("Last-Modified: $lastModified GMT");
|
||||
header("ETag: $etag");
|
||||
header("Cache-Control: no-cache, must-revalidate");
|
||||
|
||||
// Sends either a 304 reply or the data depending on the browser's will
|
||||
$ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false;
|
||||
$ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false;
|
||||
if (($ifNoneMatch || $ifModifiedSince) && (!$ifNoneMatch || $ifNoneMatch == $etag) && (!$ifModifiedSince || $ifModifiedSince != $lastModified)) {
|
||||
header('HTTP/1.0 304 Not Modified');
|
||||
} else {
|
||||
endRequest(false);
|
||||
echo $data;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
34
scripts/lib/engines/redirect.inc
Normal file
34
scripts/lib/engines/redirect.inc
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
class display_engine {
|
||||
|
||||
function maintenance($maintenance) {
|
||||
$this->redirect = makeLink('main', 'index');
|
||||
$this->outputData();
|
||||
}
|
||||
|
||||
|
||||
function displayFatalError($code, $i, $i2) {
|
||||
header("Location: http://www.legacyworlds.com");
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
$this->redirect = handler::$h->redirect(input::$input);
|
||||
}
|
||||
|
||||
function outputData() {
|
||||
header("Location: {$this->redirect}");
|
||||
}
|
||||
|
||||
|
||||
function initSession() {
|
||||
session::handle(false);
|
||||
}
|
||||
|
||||
|
||||
function checkAuth() { }
|
||||
|
||||
function initRPC() { }
|
||||
}
|
||||
|
||||
|
167
scripts/lib/engines/rpc.inc
Normal file
167
scripts/lib/engines/rpc.inc
Normal file
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
class display_engine {
|
||||
|
||||
function rpcHeader() {
|
||||
header("Expires: Thu, 19 Feb 1981 05:00:00 GMT");
|
||||
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
|
||||
header("Cache-Control: no-cache, must-revalidate");
|
||||
header("Pragma: no-cache");
|
||||
header("Content-Type: text/plain; charset=utf-8");
|
||||
}
|
||||
|
||||
|
||||
function maintenance($maintenance) {
|
||||
$this->rpcHeader();
|
||||
echo "-:m";
|
||||
}
|
||||
|
||||
|
||||
function displayFatalError($code, $i, $i2) {
|
||||
$this->rpcHeader();
|
||||
echo "-:f:$code";
|
||||
}
|
||||
|
||||
|
||||
function initSession() {
|
||||
$create = handler::$h->needsAuth;
|
||||
$r = session::handle($create);
|
||||
if ($r == 1 && $create) {
|
||||
$this->needsLogin();
|
||||
} elseif ($r == 2 && $create) {
|
||||
l::debug("(RPC engine) tracknew? " . (tracking::$new ? "yes" : "no"));
|
||||
l::fatal(12);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function sendKick($reason) {
|
||||
$this->rpcHeader();
|
||||
echo "-:k:$reason";
|
||||
endRequest();
|
||||
}
|
||||
|
||||
|
||||
function needsLogin() {
|
||||
$this->rpcHeader();
|
||||
echo "-:a";
|
||||
endRequest();
|
||||
}
|
||||
|
||||
|
||||
function checkAuth() {
|
||||
if (!handler::$h->needsAuth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tracking::$data['bat']) {
|
||||
// User tried to log in after being kicked
|
||||
session::kill();
|
||||
|
||||
$q = dbQuery("SELECT reason FROM account WHERE id=" . tracking::$data['uid'] . " AND status='KICKED'");
|
||||
if ($q && dbCount($q) == 1) {
|
||||
list($reason) = dbFetchArray($q);
|
||||
} else {
|
||||
$reason = "";
|
||||
}
|
||||
$this->sendKick($reason);
|
||||
} elseif ($_SESSION['login'] != "") {
|
||||
$q = dbQuery("SELECT status,reason FROM account WHERE id={$_SESSION['userid']}");
|
||||
|
||||
if (!($q && dbCount($q) == 1)) {
|
||||
$this->needsLogin();
|
||||
}
|
||||
|
||||
list($status,$reason) = dbFetchArray($q);
|
||||
|
||||
if ($status == "STD" || $status == "VAC") {
|
||||
$this->checkGameRegistration();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($status == 'KICKED') {
|
||||
session::kill();
|
||||
$this->sendKick($reason);
|
||||
}
|
||||
|
||||
$this->rpcHeader();
|
||||
echo "-:";
|
||||
endRequest();
|
||||
} else {
|
||||
$this->needsLogin();
|
||||
}
|
||||
}
|
||||
|
||||
private function checkGameRegistration() {
|
||||
if (input::$path == "main") {
|
||||
return;
|
||||
}
|
||||
|
||||
$game = input::$game;
|
||||
if (!($game->status() == 'PRE' || $game->status() == 'FINISHED')) {
|
||||
$lib = $game->getLib();
|
||||
$pid = $lib->call("doesUserPlay", $_SESSION['userid']);
|
||||
if (!is_null($pid)) {
|
||||
if (!is_array($_SESSION["{$game->name}_data"])) {
|
||||
$_SESSION["{$game->name}_data"] = array("player" => $pid);
|
||||
}
|
||||
dbQuery("UPDATE main.credits SET resources_used = resources_used + 1 WHERE account = {$_SESSION['userid']}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->rpcHeader();
|
||||
echo "-:";
|
||||
endRequest();
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
// Get arguments and function name
|
||||
$this->func = preg_replace('/[^A-Za-z0-9_]/', '', input::$input['rs']);
|
||||
$this->args = empty(input::$input['rsargs']) ? array() : input::$input['rsargs'];
|
||||
}
|
||||
|
||||
|
||||
function initRPC() {
|
||||
ajax::init();
|
||||
}
|
||||
|
||||
|
||||
function outputData() {
|
||||
if (is_array($_SESSION) && $_SESSION['authok']) {
|
||||
if (is_null($_SESSION['last_page'])) {
|
||||
$_SESSION['last_page'] = time();
|
||||
} else if (time() - $_SESSION['last_page'] >= 7200) {
|
||||
l::notice("Player '{$_SESSION['login']}' (#{$_SESSION['userid']}) is idling - forcing log out");
|
||||
session::kill();
|
||||
$this->needsLogin();
|
||||
}
|
||||
}
|
||||
$this->rpcHeader();
|
||||
|
||||
$page = input::$page;
|
||||
if (ajax::canCall($this->func)) {
|
||||
$res = ajax::call($this->func, $this->args);
|
||||
|
||||
// Trace activity
|
||||
/* FIXME: Disabled for now, should be somewhere else.
|
||||
$spyOn = array();
|
||||
if (in_array($_SESSION['userid'], $spyOn)) {
|
||||
logText("*** TRACE ({$_SESSION['userid']}:{$_SESSION['login']}) RPC: {$page}::{$this->func} called, arguments:");
|
||||
foreach ($this->args as $arg) {
|
||||
logText("*** TRACE ({$_SESSION['userid']}:{$_SESSION['login']}) RPC: -> $arg");
|
||||
}
|
||||
logText("*** TRACE ({$_SESSION['userid']}:{$_SESSION['login']}) RPC: returning $res");
|
||||
}
|
||||
*/
|
||||
|
||||
echo "+:$res";
|
||||
} else {
|
||||
l::notice("RPC: unknown function call {$page}::{$this->func}");
|
||||
l::debug("Referer: " . $_SERVER['HTTP_REFERER']);
|
||||
echo "-:c:{$this->func}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
33
scripts/lib/engines/template.inc
Normal file
33
scripts/lib/engines/template.inc
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
class display_engine {
|
||||
|
||||
function maintenance($maintenance) {
|
||||
}
|
||||
|
||||
|
||||
function displayFatalError($code, $text, $info) {
|
||||
}
|
||||
|
||||
|
||||
function initSession() {
|
||||
}
|
||||
|
||||
|
||||
function checkAuth() {
|
||||
}
|
||||
|
||||
|
||||
function handleInput() {
|
||||
}
|
||||
|
||||
|
||||
function initRPC() {
|
||||
}
|
||||
|
||||
|
||||
function outputData() {
|
||||
}
|
||||
}
|
||||
|
||||
|
247
scripts/lib/engines/xml.inc
Normal file
247
scripts/lib/engines/xml.inc
Normal file
|
@ -0,0 +1,247 @@
|
|||
<?php
|
||||
|
||||
class display_engine {
|
||||
|
||||
private $document;
|
||||
private $outputMode;
|
||||
private $data;
|
||||
|
||||
function __construct() {
|
||||
$this->document = new DomDocument('1.0', 'utf-8');
|
||||
$this->outputMode = "xml";
|
||||
}
|
||||
|
||||
function maintenance($maintenance) {
|
||||
$this->data = new data_node('Maintenance');
|
||||
$this->data->addContents(new data_leaf('Until', strftime("%Y-%m-%d %H:%M:%S", $maintenance['until'])));
|
||||
$this->data->addContents(new data_leaf('Current', gmstrftime("%Y-%m-%d %H:%M:%S")));
|
||||
$this->data->addContents(new data_leaf('Reason', $maintenance['reason']));
|
||||
|
||||
$this->rawOutput($this->genOutput('xml'));
|
||||
}
|
||||
|
||||
|
||||
function displayFatalError($code, $i, $i2) {
|
||||
$this->data = new data_node('FatalError');
|
||||
$this->data->setAttribute('code', $code);
|
||||
$this->data->addContents(new data_leaf('Text', $i));
|
||||
|
||||
return $this->genOutput('xml');
|
||||
}
|
||||
|
||||
|
||||
private function loginFailed($code) {
|
||||
$this->data = new data_node('Failed');
|
||||
$this->data->setAttribute('code', $code);
|
||||
$this->rawOutput($this->genOutput('xml'));
|
||||
}
|
||||
|
||||
|
||||
private function kicked($reason) {
|
||||
$this->data = new data_node('Kicked');
|
||||
$this->data->addContents(new data_leaf('Reason', $reason));
|
||||
$this->rawOutput($this->genOutput('xml'));
|
||||
}
|
||||
|
||||
|
||||
function initSession() {
|
||||
// Check for "single-shot" request
|
||||
if (input::$input['__s'] == 1) {
|
||||
$_SESSION = array();
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: handle session, but why bother doing it when the whole thing will be rewritten?
|
||||
}
|
||||
|
||||
|
||||
function checkAuth() {
|
||||
// Check authentication directly in "single-shot" mode
|
||||
if (input::$input['__s'] == 1) {
|
||||
$this->singleShotAuth(input::$input['__l'], input::$input['__p']);
|
||||
$this->outputMode = "lw";
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: handle authentication, but why bother doing it when the whole thing will be rewritten?
|
||||
}
|
||||
|
||||
|
||||
private function singleShotAuth($login, $password) {
|
||||
// Check the account's login and password
|
||||
if (!$this->checkAccountAuth($login, $password)) {
|
||||
sleep(5);
|
||||
$this->loginFailed(0);
|
||||
}
|
||||
|
||||
// Make sure the account is in a valid state
|
||||
$q = dbQuery("SELECT status,reason FROM account WHERE id={$_SESSION['userid']}");
|
||||
if (!$q || dbCount($q) != 1) {
|
||||
$this->loginFailed(1);
|
||||
}
|
||||
|
||||
list($status,$reason) = dbFetchArray($q);
|
||||
if ($status == 'KICKED') {
|
||||
$this->kicked($reason);
|
||||
} elseif ($status == 'QUIT' || $status == 'INAC') {
|
||||
$this->loginFailed(2);
|
||||
} elseif ($status == 'NEW') {
|
||||
$this->loginFailed(3);
|
||||
}
|
||||
|
||||
$_SESSION['authok'] = true;
|
||||
if (!$this->checkVersionRegistration()) {
|
||||
$this->loginFailed(4);
|
||||
}
|
||||
|
||||
$this->singleShotLog();
|
||||
}
|
||||
|
||||
|
||||
private function checkAccountAuth($l, $p) {
|
||||
$l1 = addslashes($l); $p1 = addslashes($p);
|
||||
$q = "SELECT id,name,password FROM account WHERE lower(name)=lower('$l1')";
|
||||
$qr = dbQuery($q);
|
||||
|
||||
if (!$qr || dbCount($qr) != 1) {
|
||||
l::info("Single-shot account authentication failed: login '".urlencode($l)."' not found");
|
||||
if ($qr) {
|
||||
l::debug("Query returned " . dbCount($qr) . " row(s)", LOG_DEBUG);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
list($uid,$l2,$pw) = dbFetchArray($qr);
|
||||
if ($p != $pw) {
|
||||
l::info("Single-shot account authentication failed: login '"
|
||||
. urlencode($l) . "' used invalid password");
|
||||
l::debug("User ID: $uid ; real user name: $l2");
|
||||
return false;
|
||||
}
|
||||
|
||||
$_SESSION['login'] = $l2;
|
||||
$_SESSION['userid'] = $uid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private function checkVersionRegistration() {
|
||||
if (input::$path == "main") {
|
||||
return true;
|
||||
}
|
||||
|
||||
$game = config::getGame(input::$path);
|
||||
$lib = $game->getLib();
|
||||
$pid = $lib->call("doesUserPlay", $_SESSION['userid']);
|
||||
if (is_null($pid)) {
|
||||
return false;
|
||||
}
|
||||
if (!is_array($_SESSION["{$game->name}_data"])) {
|
||||
$_SESSION["{$game->name}_data"] = array("player" => $pid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private function singleShotLog() {
|
||||
$uid = $_SESSION['userid'];
|
||||
|
||||
$q = dbQuery("SELECT id FROM web_session WHERE account = $uid");
|
||||
if (!($q && dbCount($q))) {
|
||||
$in = time();
|
||||
$out = $in + 1;
|
||||
$q = dbQuery("UPDATE account SET last_login=$in,last_logout=$out WHERE id=$uid");
|
||||
}
|
||||
|
||||
dbQuery("INSERT INTO account_log(account,ip_addr,action,t) VALUES ($uid,'AUTO','IN',unix_timestamp(now()) - 1)");
|
||||
dbQuery("INSERT INTO account_log(account,ip_addr,action) VALUES ($uid,'AUTO','OUT')");
|
||||
}
|
||||
|
||||
|
||||
function handleInput() {
|
||||
$data = handler::$h->xml(input::$input);
|
||||
if (!($data instanceof data_gen)) {
|
||||
l::fatal(18, "In XML handler for " . input::$path . "/" . input::$page);
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
|
||||
function initRPC() { /*NO RPC*/ }
|
||||
|
||||
|
||||
function outputData() {
|
||||
$data = $this->genOutput($this->outputMode);
|
||||
|
||||
if (input::$IE || input::$safari || tracking::$disabled) {
|
||||
$this->rawOutput($data);
|
||||
} else {
|
||||
$this->cachedOutput($data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function cachedOutput($data) {
|
||||
// Generate an XML resource from the data
|
||||
addRawResource('xml', $data);
|
||||
$resId = storeResource("xml", 21600, false);
|
||||
$xmlId = md5($_SERVER['REQUEST_URI']) . ":$resId";
|
||||
|
||||
// Check the tracking cookie for the required contents
|
||||
if (!is_array(tracking::$data['xmlId'])) {
|
||||
tracking::$data['xmlId'] = array();
|
||||
}
|
||||
if (!isset(tracking::$data['xmlId'][$xmlId])) {
|
||||
tracking::$data['xmlId'][$xmlId] = time();
|
||||
}
|
||||
|
||||
// Send the page's header
|
||||
$time = tracking::$data['xmlId'][$xmlId];
|
||||
$lastModified = substr(date('r', $time), 0, -5).'GMT';
|
||||
$etag = '"'.md5($lastModified).'"';
|
||||
header("Content-Type: text/xml; charset=utf-8");
|
||||
header("Last-Modified: $lastModified GMT");
|
||||
header("ETag: $etag");
|
||||
header("Cache-Control: no-cache, must-revalidate");
|
||||
|
||||
// Sends either a 304 reply or the data depending on the browser's will
|
||||
$ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false;
|
||||
$ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false;
|
||||
if (($ifNoneMatch || $ifModifiedSince) && (!$ifNoneMatch || $ifNoneMatch == $etag) && (!$ifModifiedSince || $ifModifiedSince != $lastModified)) {
|
||||
header('HTTP/1.0 304 Not Modified');
|
||||
} else {
|
||||
endRequest(false);
|
||||
echo $data;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function rawOutput($data) {
|
||||
header("Content-Type: text/xml; charset=utf-8");
|
||||
header("Expires: Thu, 19 Feb 1981 05:00:00 GMT");
|
||||
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
|
||||
header("Cache-Control: no-cache, must-revalidate");
|
||||
header("Pragma: no-cache");
|
||||
|
||||
// Send the data directly
|
||||
endRequest(false);
|
||||
echo $data;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
private function genOutput($mode) {
|
||||
if ($mode == 'xml') {
|
||||
$data = $this->data->toXML($this->document);
|
||||
} else {
|
||||
$data = $this->data->toLWData($this->document);
|
||||
}
|
||||
$this->document->appendChild($data);
|
||||
return $this->document->saveXML();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
243
scripts/lib/game.inc
Normal file
243
scripts/lib/game.inc
Normal file
|
@ -0,0 +1,243 @@
|
|||
<?php
|
||||
|
||||
|
||||
class game {
|
||||
|
||||
private $__status = null;
|
||||
private $__firstTick = null;
|
||||
private $__lastTick = null;
|
||||
|
||||
function __construct($version, $name, $namespace, $public, $canJoin, $text) {
|
||||
$this->version = $version;
|
||||
$this->name = $name;
|
||||
$this->namespace = $namespace;
|
||||
$this->public = $public;
|
||||
$this->canJoin = $canJoin;
|
||||
$this->text = $text;
|
||||
$this->ticks = array();
|
||||
$this->params = array();
|
||||
$this->descriptions = array();
|
||||
$this->initExternal();
|
||||
}
|
||||
|
||||
|
||||
private function initExternal() {
|
||||
$this->dir = $this->version->getDirectory();
|
||||
$this->siteDir = $this->version->getSiteDirectory();
|
||||
}
|
||||
|
||||
|
||||
function getDBAccess() {
|
||||
if ($this->db) {
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
if (!db::$database) {
|
||||
return null;
|
||||
}
|
||||
return ($this->db = db::$database->getGameAccess($this->namespace));
|
||||
}
|
||||
|
||||
|
||||
function addTick($tick) {
|
||||
$td = $tick->definition->script;
|
||||
if (is_null($this->ticks[$td])) {
|
||||
$this->ticks[$td] = $tick;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function __loadActionObject() {
|
||||
if ($this->actionObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
$acn = $this->version->loadActionsClass();
|
||||
$this->actionObj = new $acn($this);
|
||||
|
||||
foreach (get_class_methods($acn) as $method) {
|
||||
$this->actions[strtolower($method)] = array(false, $method);
|
||||
}
|
||||
|
||||
if (is_array($this->actionObj->index)) {
|
||||
foreach ($this->actionObj->index as $action) {
|
||||
if (strtolower($action) == strtolower($acn)) {
|
||||
continue;
|
||||
}
|
||||
$this->actions[strtolower($action)] = array(true, $action, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function __doAction($action, $args, $log = false) {
|
||||
// Load actions object
|
||||
$this->__loadActionObject();
|
||||
|
||||
// Check action
|
||||
if (!is_array($this->actions[$action])) {
|
||||
l::fatal(23, "Unknown action was '$action' on game '{$this->name}'");
|
||||
}
|
||||
|
||||
if ($this->actions[$action][0]) {
|
||||
// Load separate action class
|
||||
if (!is_object($this->actions[$action][2])) {
|
||||
$acn = $this->version->loadAction($this->actions[$action][1]);
|
||||
$this->actions[$action][2] = new $acn($this);
|
||||
}
|
||||
$rv = call_user_func_array(array($this->actions[$action][2], 'run'), $args);
|
||||
} else {
|
||||
// Call the action instance's method
|
||||
$rv = call_user_func_array(array($this->actionObj, $this->actions[$action][1]), $args);
|
||||
}
|
||||
|
||||
return $rv;
|
||||
}
|
||||
|
||||
|
||||
public function action() {
|
||||
$n = func_num_args();
|
||||
if ($n == 0) {
|
||||
l::fatal(22, "Empty action call on game '{$this->name}'");
|
||||
}
|
||||
|
||||
// Get arguments
|
||||
$args = func_get_args();
|
||||
$action = strtolower(array_shift($args));
|
||||
|
||||
return $this->__doAction($action, $args);
|
||||
}
|
||||
|
||||
|
||||
public function deprecatedAction() {
|
||||
$n = func_num_args();
|
||||
if ($n == 0) {
|
||||
l::fatal(22, "Empty action call on game '{$this->name}'");
|
||||
}
|
||||
|
||||
// Get arguments
|
||||
$args = func_get_args();
|
||||
$action = strtolower(array_shift($args));
|
||||
|
||||
return $this->__doAction($action, $args);
|
||||
}
|
||||
|
||||
|
||||
function getLib($lib = null) {
|
||||
if (is_null($lib)) {
|
||||
$lib = $this->version->id;
|
||||
}
|
||||
|
||||
if (is_null($this->libraries[$lib])) {
|
||||
$this->libraries[$lib] = new library($lib, $this);
|
||||
}
|
||||
$this->getDBAccess();
|
||||
return $this->libraries[$lib];
|
||||
}
|
||||
|
||||
|
||||
function runTick($name, $okNotFound = false) {
|
||||
if (!is_null($this->ticks[$name])) {
|
||||
$this->ticks[$name]->run();
|
||||
} elseif ($okNotFound) {
|
||||
$tick = $this->getLib($this->version->id . "/ticks/$name");
|
||||
if (is_null($tick)) {
|
||||
l::error("TICK: tick library '$libName' not found");
|
||||
} else {
|
||||
$tick->call('runTick');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function earliestTick() {
|
||||
$then = time() + 10 * 365 * 24 * 60 * 60;
|
||||
foreach ($this->ticks as $td => $tick) {
|
||||
if ($tick->first < $then && $tick->definition->public) {
|
||||
$then = $tick->first;
|
||||
}
|
||||
}
|
||||
return $then;
|
||||
}
|
||||
|
||||
private function latestTick() {
|
||||
$then = 0;
|
||||
foreach ($this->ticks as $td => $tick) {
|
||||
if (is_null($tick->last)) {
|
||||
continue;
|
||||
}
|
||||
if ($tick->last > $then) {
|
||||
$then = $tick->last;
|
||||
}
|
||||
}
|
||||
return $then;
|
||||
}
|
||||
|
||||
private function computeStatus() {
|
||||
$visible = $this->public && $this->canJoin;
|
||||
$this->__firstTick = $this->earliestTick($this);
|
||||
$this->__lastTick = $this->latestTick($this);
|
||||
$now = time();
|
||||
|
||||
if ($this->__lastTick && $this->__lastTick < $now) {
|
||||
$this->__status = 'FINISHED';
|
||||
} elseif (!$visible) {
|
||||
$this->__status = 'PRE';
|
||||
} else {
|
||||
$running = ($this->__firstTick <= $now);
|
||||
if ($running) {
|
||||
if ($this->__lastTick) {
|
||||
$this->__status = "ENDING";
|
||||
} else {
|
||||
$gLib = $this->getLib();
|
||||
$this->__status = $gLib->call('isFinished') ? 'VICTORY' : 'RUNNING';
|
||||
}
|
||||
} else {
|
||||
$this->__status = 'READY';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function status() {
|
||||
if (is_null($this->__status)) {
|
||||
$this->computeStatus();
|
||||
}
|
||||
return $this->__status;
|
||||
}
|
||||
|
||||
function firstTick() {
|
||||
if (is_null($this->__firstTick)) {
|
||||
$this->computeStatus();
|
||||
}
|
||||
return $this->__firstTick;
|
||||
}
|
||||
|
||||
function lastTick() {
|
||||
if (is_null($this->__lastTick)) {
|
||||
$this->computeStatus();
|
||||
}
|
||||
return $this->__lastTick;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function __sleep() {
|
||||
return array('version', 'name', 'namespace', 'public', 'canJoin', 'text', 'descriptions', 'ticks', 'params');
|
||||
}
|
||||
|
||||
function __wakeup() {
|
||||
$this->initExternal();
|
||||
}
|
||||
|
||||
function sessName() {
|
||||
if (class_exists('input')) {
|
||||
return input::$game->name . "_data";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
77
scripts/lib/handler.inc
Normal file
77
scripts/lib/handler.inc
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* PAGE HANDLER FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
class handler {
|
||||
|
||||
static $h = null;
|
||||
|
||||
/** This function looks for a handler for the requested combination of game
|
||||
* version and page. If such a handler is found, the file is loaded and the
|
||||
* handler is initialized.
|
||||
*/
|
||||
function load() {
|
||||
$path = input::$path;
|
||||
$page = input::$page;
|
||||
$game = input::$game;
|
||||
|
||||
if (!file_exists("{$game->siteDir}/handlers") || !is_dir($game->siteDir."/handlers")) {
|
||||
l::fatal(8, array("Handlers directory for path '$path' not found (page '$page')"));
|
||||
}
|
||||
|
||||
$ifile = "{$game->siteDir}/handlers/$page.inc";
|
||||
if (!(file_exists($ifile) && is_readable($ifile) && is_file($ifile))) {
|
||||
l::notice("Requested path '" . input::$path . "' doesn't match any handler");
|
||||
l::debug("Referer was '{$_SERVER['HTTP_REFERER']}' ; requested page was '" . input::$page . "'");
|
||||
$path = input::$path = 'main';
|
||||
$page = input::$page = 'notfound';
|
||||
input::$eType = null;
|
||||
$game = input::$game = config::getGame('main');
|
||||
$ifile = "{$game->siteDir}/handlers/$page.inc";
|
||||
}
|
||||
|
||||
if (!include_once($ifile)) {
|
||||
l::fatal(10, "File inclusion failed: '$ifile'");
|
||||
}
|
||||
if (!class_exists('page_handler')) {
|
||||
l::fatal(11, "File '$ifile' did not define a page_handler class");
|
||||
}
|
||||
|
||||
handler::$h = $handler = new page_handler();
|
||||
$handler->game = $game;
|
||||
|
||||
/* The handler is loaded, check the engine type vs. the handler's
|
||||
* supported and default engines.
|
||||
*/
|
||||
|
||||
// Get the list of supported engines
|
||||
if (is_array($handler->engines)) {
|
||||
$engines = $handler->engines;
|
||||
} else {
|
||||
$engines = array('page', 'css', 'js', 'rpc');
|
||||
}
|
||||
// Get the default engine
|
||||
if (is_null($handler->defaultEngine)) {
|
||||
$dEngine = $engines[0];
|
||||
} else {
|
||||
$dEngine = $handler->defaultEngine;
|
||||
}
|
||||
|
||||
// Set the engine type to default if it isn't set
|
||||
if (is_null(input::$eType)) {
|
||||
input::$eType = $dEngine;
|
||||
}
|
||||
|
||||
// Check the engine's type against the list of supported engines
|
||||
if (!in_array(input::$eType, $engines)) {
|
||||
l::fatal(31, "$path/$page: trying to use unsupported engine type '" . input::$eType . "'");
|
||||
}
|
||||
|
||||
return $handler;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
120
scripts/lib/input.inc
Normal file
120
scripts/lib/input.inc
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* INPUT DATA GATHERING FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
class input {
|
||||
|
||||
static $input = array();
|
||||
|
||||
static $path = null;
|
||||
static $page = null;
|
||||
static $eType = null;
|
||||
static $game = null;
|
||||
|
||||
static $IE = null;
|
||||
static $safari = null;
|
||||
|
||||
/** This function analyzes the request to find out which "sub-version" is being
|
||||
* accessed, and which page should be displayed.
|
||||
*/
|
||||
static function identify() {
|
||||
$p = $_SERVER["PATH_INFO"];
|
||||
|
||||
if (preg_match('/\.[a-z0-9]{2,10}$/', $p)) {
|
||||
input::$eType = preg_replace('/^.*\.([a-z0-9]{2,10})$/', '\1', $p);
|
||||
$p = preg_replace('/\.[a-z0-9]{2,10}$/', '', $p);
|
||||
} else {
|
||||
input::$eType = null;
|
||||
}
|
||||
|
||||
if ($p == "") {
|
||||
input::$path = 'main';
|
||||
input::$page = 'index';
|
||||
} elseif (preg_match('/[^A-Za-z0-9\/]/', $p)) {
|
||||
l::notice("Invalid path requested: '$p'");
|
||||
l::debug("Referer was '{$_SERVER['HTTP_REFERER']}'");
|
||||
input::$path = 'main';
|
||||
input::$page = 'notfound';
|
||||
input::$eType = null;
|
||||
input::$game = config::getGame('main');
|
||||
} else {
|
||||
$p = preg_replace(
|
||||
array('/\/+/', '/\/$/', '/^\//'),
|
||||
array('/', '', ''),
|
||||
$p
|
||||
);
|
||||
$tmp = explode('/', $p);
|
||||
if (count($tmp) > 2) {
|
||||
l::notice("Invalid path requested: '$p'");
|
||||
l::debug("Referer was '{$_SERVER['HTTP_REFERER']}'");
|
||||
input::$path = 'main';
|
||||
input::$page = 'notfound';
|
||||
input::$eType = null;
|
||||
input::$game = config::getGame('main');
|
||||
} else {
|
||||
if (count($tmp) == 1) {
|
||||
input::$path = 'main';
|
||||
} else {
|
||||
input::$path = array_shift($tmp);
|
||||
}
|
||||
input::$page = $tmp[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Find the game instance for this game
|
||||
if (!config::hasGame(input::$path)) {
|
||||
l::notice("Requested path '" . input::$path . "' doesn't match any game");
|
||||
l::debug("Referer was '{$_SERVER['HTTP_REFERER']}' ; requested page was '" . input::$page . "'");
|
||||
input::$path = 'main';
|
||||
input::$page = 'notfound';
|
||||
input::$eType = null;
|
||||
input::$game = config::getGame('main');
|
||||
} else {
|
||||
input::$game = config::getGame(input::$path);
|
||||
}
|
||||
|
||||
l::setPrefix(input::$path . "/" . input::$page . (is_null(input::$eType) ? "" : ( "." . input::$eType)));
|
||||
|
||||
return array(input::$path, input::$page, input::$eType);
|
||||
}
|
||||
|
||||
|
||||
/** This function reads the arguments in the $_POST variable, then in the $_GET
|
||||
* variable, and stores them inside an hash table. If PHP's crappy "magic
|
||||
* quotes" are enabled, remove them.
|
||||
* NOTE: This behaviour should be reversed in a production version.
|
||||
*/
|
||||
function read() {
|
||||
input::$IE = preg_match('/MSIE/', $_SERVER['HTTP_USER_AGENT'])
|
||||
&& !preg_match('/Opera/', $_SERVER['HTTP_USER_AGENT']);
|
||||
input::$safari = preg_match('/AppleWebKit/', $_SERVER['HTTP_USER_AGENT']);
|
||||
|
||||
$p = array();
|
||||
foreach ($_POST as $k => $v) {
|
||||
$p[$k] = $v;
|
||||
}
|
||||
foreach ($_GET as $k => $v) {
|
||||
$p[$k] = $v;
|
||||
}
|
||||
if (get_magic_quotes_gpc()) {
|
||||
foreach ($p as $k => $v) {
|
||||
if (is_scalar($v)) {
|
||||
$p[$k] = stripslashes($v);
|
||||
} elseif (is_array($v)) {
|
||||
$p[$k] = array();
|
||||
foreach ($v as $ak => $av) {
|
||||
$p[$k][$ak] = stripslashes($av);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
input::$input = $p;
|
||||
return $p;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
79
scripts/lib/library.inc
Normal file
79
scripts/lib/library.inc
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
class library {
|
||||
var $name;
|
||||
var $game;
|
||||
var $mainClass = null;
|
||||
var $functions = array();
|
||||
|
||||
function library($name, $game) {
|
||||
$this->name = $name;
|
||||
$this->game = $game;
|
||||
}
|
||||
|
||||
function loadClass($name = null) {
|
||||
// Get the path to the class to be loaded
|
||||
$path = config::$main['scriptdir'] . "/game/{$this->name}/library";
|
||||
if (!is_null($name)) {
|
||||
$path .= "/$name";
|
||||
}
|
||||
$path .= ".inc";
|
||||
|
||||
// Get the class' name
|
||||
$cn = preg_replace('#/#', '_', strtolower($this->name));
|
||||
$cn .= is_null($name) ? "_library" : "_$name";
|
||||
|
||||
// Load it
|
||||
loader::load($path, $cn);
|
||||
return $cn;
|
||||
}
|
||||
|
||||
function call() {
|
||||
$n = func_num_args();
|
||||
if ($n == 0) {
|
||||
l::fatal(22, "Empty library call for library '{$this->name}' on game '{$this->game->game['site_path']}'");
|
||||
}
|
||||
|
||||
// Load the main class if that is needed
|
||||
if (!$this->mainClass) {
|
||||
$lcn = $this->loadClass();
|
||||
$this->mainClass = new $lcn($this);
|
||||
|
||||
foreach (get_class_methods($lcn) as $method) {
|
||||
$this->functions[strtolower($method)] = array(false, $method);
|
||||
}
|
||||
|
||||
if (is_array($this->mainClass->index)) {
|
||||
foreach ($this->mainClass->index as $function) {
|
||||
if (strtolower($function) == strtolower($lcn)) {
|
||||
continue;
|
||||
}
|
||||
$this->functions[strtolower($function)] = array(true, $function, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check function
|
||||
$args = func_get_args();
|
||||
$function = strtolower(array_shift($args));
|
||||
if (!is_array($this->functions[$function])) {
|
||||
l::fatal(23, "Unknown function call '$function' in library '{$this->name}' on game '{$this->game->game['site_path']}'");
|
||||
}
|
||||
|
||||
if ($this->functions[$function][0]) {
|
||||
// Load separate class
|
||||
if (!is_object($this->functions[$function][2])) {
|
||||
$fcn = $this->loadClass($this->functions[$function][1]);
|
||||
$this->functions[$function][2] = new $fcn($this);
|
||||
}
|
||||
$rv = call_user_func_array(array($this->functions[$function][2], 'run'), $args);
|
||||
} else {
|
||||
// Call the function instance's method
|
||||
$rv = call_user_func_array(array($this->mainClass, $this->functions[$function][1]), $args);
|
||||
}
|
||||
|
||||
return $rv;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
295
scripts/lib/log.inc
Normal file
295
scripts/lib/log.inc
Normal file
|
@ -0,0 +1,295 @@
|
|||
<?php
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* LOG AND FATAL ERRORS
|
||||
****************************************************************************/
|
||||
|
||||
class l {
|
||||
|
||||
/** This variable should contain the name of the function to call when
|
||||
* handling fatal errors.
|
||||
*/
|
||||
private static $fatalErrorCallback = "l::defaultFatalError";
|
||||
|
||||
/** This array lists the errors the engine can encounter and handle
|
||||
* through the fatalError() function.
|
||||
*/
|
||||
private static $engineErrors = array(
|
||||
0 => "Could not open configuration file",
|
||||
1 => "Could not connect to database",
|
||||
2 => "Failed to set up tracking data",
|
||||
3 => "Failed to set up tracking data",
|
||||
4 => "Failed to set up tracking data",
|
||||
5 => "Failed to set up tracking data",
|
||||
6 => 'Invalid request',
|
||||
7 => 'Invalid request',
|
||||
8 => 'Page not found',
|
||||
9 => 'Page not found',
|
||||
10 => 'Internal error',
|
||||
11 => 'Internal error',
|
||||
12 => "Failed to set up session data",
|
||||
13 => "Failed to set up session data",
|
||||
14 => "Failed to set up session data",
|
||||
15 => "Failed to set up session data",
|
||||
16 => 'Internal error',
|
||||
17 => 'Internal error',
|
||||
18 => "Internal error",
|
||||
19 => 'Internal error',
|
||||
20 => 'Internal error',
|
||||
21 => 'Internal error',
|
||||
22 => 'Internal error',
|
||||
23 => 'Internal error',
|
||||
24 => 'Internal error',
|
||||
25 => 'Internal error',
|
||||
26 => 'Invalid extension',
|
||||
27 => 'Unhandled extension',
|
||||
28 => 'Internal error',
|
||||
29 => 'Internal error',
|
||||
30 => 'Resource not found',
|
||||
31 => 'Unhandler extension',
|
||||
);
|
||||
|
||||
|
||||
/** This variable indicates whether the logging system has
|
||||
* been initialised.
|
||||
*/
|
||||
private static $initialised = false;
|
||||
|
||||
|
||||
/** This variable contains the prefix to use when writing
|
||||
* to syslog.
|
||||
*/
|
||||
private static $syslogPrefix = "lwEngine";
|
||||
|
||||
|
||||
/** This variable defines a text that is used as a prefix
|
||||
* when logging strings.
|
||||
*/
|
||||
private static $prefix = "";
|
||||
|
||||
|
||||
/** This variable prevents multiple "deprecated" entries from
|
||||
* being logged.
|
||||
*/
|
||||
private static $deprecatedLogged = false;
|
||||
|
||||
|
||||
/** This variable prevents multiple "FIXME" entries from
|
||||
* being logged.
|
||||
*/
|
||||
private static $fixmeLogged = false;
|
||||
|
||||
|
||||
/** This function is the default fatal error display function. */
|
||||
private function defaultFatalError($errno, $error, $info) {
|
||||
ob_start();
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<title>Legacy Worlds</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<b>The LegacyWorlds game engine encountered a fatal error:</b><br/>
|
||||
<?=$error?><br/>
|
||||
Please report this error by sending an e-mail to the staff at
|
||||
<a href='mailto:webmaster@legacyworlds.com'>webmaster@legacyworlds.com</a>
|
||||
explaining what happened.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
$html = ob_get_contents();
|
||||
ob_end_clean();
|
||||
die($html);
|
||||
}
|
||||
|
||||
|
||||
/** This method initialises communications with syslog.
|
||||
*/
|
||||
private static function init() {
|
||||
if (self::$initialised) {
|
||||
return;
|
||||
}
|
||||
|
||||
define_syslog_variables();
|
||||
openlog(self::$syslogPrefix, LOG_PID, LOG_USER);
|
||||
self::$initialised = true;
|
||||
}
|
||||
|
||||
|
||||
/** This method writes a string to the system log.
|
||||
*/
|
||||
private static function __write($txt, $level) {
|
||||
syslog($level, self::$prefix . $txt);
|
||||
}
|
||||
|
||||
|
||||
/** This method writes a string to the system log;
|
||||
* it is used by the external "logText" function and
|
||||
* should disappear in time.
|
||||
*/
|
||||
public static function write($txt, $level) {
|
||||
self::init();
|
||||
if (is_null($level)) {
|
||||
$level = LOG_INFO;
|
||||
}
|
||||
self::__write($txt, $level);
|
||||
}
|
||||
|
||||
|
||||
/** This method displays one of the game engine's
|
||||
* fatal errors and adds log entries accordingly.
|
||||
*/
|
||||
public function fatal($errno, $information = null) {
|
||||
// Log the error
|
||||
$errorText = self::$engineErrors[$errno] . " [" . sprintf("%03d", $errno) . "]";
|
||||
self::critical($errorText);
|
||||
|
||||
// Log additional information
|
||||
if (is_null($information)) {
|
||||
$information = array();
|
||||
} elseif (!is_array($information)) {
|
||||
$information = array($information);
|
||||
}
|
||||
|
||||
foreach ($information as $it) {
|
||||
self::info($it);
|
||||
}
|
||||
|
||||
// Log additional details
|
||||
if (class_exists('input') && !is_null(input::$page)) {
|
||||
self::debug("Request: " . input::$path . "/" . input::$page . "." . input::$eType);
|
||||
|
||||
if (tracking::$id != '') {
|
||||
$txt = "tracking: " . tracking::$id . " (" . tracking::$dbId . ")";
|
||||
if (session::$id != '') {
|
||||
$txt .= "; session: " . session::$id . " (" . session::$dbId . ")";
|
||||
if (!is_null($_SESSION['userid'])) {
|
||||
$txt .= "; user {$_SESSION['login']} ({$_SESSION['userid']})";
|
||||
}
|
||||
}
|
||||
$txt .= "; user agent: {$_SERVER['HTTP_USER_AGENT']}";
|
||||
self::debug($txt);
|
||||
}
|
||||
}
|
||||
self::backtrace();
|
||||
|
||||
eval(self::$fatalErrorCallback . '($errno, $errorText, $information);');
|
||||
}
|
||||
|
||||
|
||||
/** This method writes a backtrace to the log, removing all
|
||||
* entries from the logging class.
|
||||
*/
|
||||
public function backtrace() {
|
||||
$bt = debug_backtrace();
|
||||
while (is_array($bt[1]) && $bt[1]['class'] == 'l') {
|
||||
array_shift($bt);
|
||||
}
|
||||
|
||||
self::info("Backtrace to the error:");
|
||||
$base = dirname(config::$main['scriptdir']);
|
||||
foreach ($bt as $data) {
|
||||
$cnLength = strlen($data['class']);
|
||||
$str = "... " . str_repeat(' ', $cnLength > 30 ? 1 : (31 - $cnLength)) . $data['class']
|
||||
. " :: " . $data['function'];
|
||||
if (!is_null($data['file'])) {
|
||||
$cnLength = strlen($data['function']);
|
||||
$fn = preg_replace("#^$base/#", "", $data['file']);
|
||||
$str .= str_repeat(' ', $cnLength > 25 ? 1 : (26 - $cnLength))
|
||||
. " (line {$data['line']}, file '$fn')";
|
||||
}
|
||||
self::info($str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method changes the syslog prefix.
|
||||
*/
|
||||
public function setSyslogPrefix($prefix) {
|
||||
self::$syslogPrefix = $prefix;
|
||||
if (self::$initialised) {
|
||||
closelog();
|
||||
self::$initialised = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** This method changes the string prefix.
|
||||
*/
|
||||
public function setPrefix($prefix) {
|
||||
self::$prefix = "$prefix ";
|
||||
}
|
||||
|
||||
/** This method changes the function to call
|
||||
* for fatal errors.
|
||||
*/
|
||||
public function setFatalHandler($function) {
|
||||
self::$fatalErrorCallback = $function;
|
||||
}
|
||||
|
||||
/** This function logs the use of a deprecated
|
||||
* function call and prevents further logging of
|
||||
* similar occurences.
|
||||
*/
|
||||
public function deprecated($function) {
|
||||
if (config::$main['debug'] == 2 && !self::$deprecatedLogged) {
|
||||
l::trace("DEPRECATED: $function");
|
||||
l::backtrace();
|
||||
self::$deprecatedLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** This function logs FIXME's. */
|
||||
public function FIXME($text) {
|
||||
if (config::$main['debug'] >= 1 && !self::$fixmeLogged) {
|
||||
l::debug("FIXME: $text");
|
||||
if (config::$main['debug'] == 2) {
|
||||
l::backtrace();
|
||||
}
|
||||
self::$fixmeLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************* LOGGING METHODS ************************/
|
||||
/* These methods should replace logText() wherever possible */
|
||||
/************************************************************/
|
||||
public function crit($txt) { self::init(); self::__write($txt, LOG_CRIT); }
|
||||
public function critical($txt) { self::init(); self::__write($txt, LOG_CRIT); }
|
||||
public function error($txt) { self::init(); self::__write($txt, LOG_ERR); }
|
||||
public function warn($txt) { self::init(); self::__write($txt, LOG_WARNING); }
|
||||
public function warning($txt) { self::init(); self::__write($txt, LOG_WARNING); }
|
||||
public function notice($txt) { self::init(); self::__write($txt, LOG_NOTICE); }
|
||||
public function info($txt) { self::init(); self::__write($txt, LOG_INFO); }
|
||||
public function debug($txt) {
|
||||
if (config::$main['debug'] >= 1) {
|
||||
self::init(); self::__write($txt, LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
public function trace($txt) {
|
||||
if (config::$main['debug'] == 2) {
|
||||
self::init(); self::__write($txt, LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** This function writes an entry to the system log. */
|
||||
function logText($txt, $level = null) {
|
||||
l::deprecated("logText()");
|
||||
l::write($txt, $level);
|
||||
}
|
||||
|
||||
|
||||
/** This function displays one of the game engine's fatal errors and adds
|
||||
* log entries accordingly.
|
||||
*/
|
||||
function fatalError($errno, $information = null) {
|
||||
l::deprecated("fatalError($errno)");
|
||||
l::fatal($errno, $information);
|
||||
}
|
||||
|
||||
?>
|
141
scripts/lib/output.inc
Normal file
141
scripts/lib/output.inc
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* HELPER FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
function tooltip($n) {
|
||||
return " onmouseover=\"return escape('".utf8entities($n,ENT_QUOTES)."')\"";
|
||||
}
|
||||
|
||||
/** This function checks the static elements directory for a specified path.
|
||||
* If the path is found to exist, an URL to this path is returned, otherwise
|
||||
* a null value is returned.
|
||||
*/
|
||||
function getStatic($path) {
|
||||
$f = config::$main['staticdir'] . "/$path";
|
||||
if (!file_exists($f)) {
|
||||
return null;
|
||||
}
|
||||
return config::$main['staticurl'] . "/$path";
|
||||
}
|
||||
|
||||
|
||||
/** This function generates a link to a script page. It accepts either one or
|
||||
* two arguments: a page name and an optional version identifier. If the
|
||||
* version identifier isn't specified, the current version is used.
|
||||
*/
|
||||
function makeLink($p, $v = null, $e = null) {
|
||||
if (is_null($v)) {
|
||||
$v = display_engine::$version;
|
||||
}
|
||||
$es = is_null($e) ? "" : ".$e";
|
||||
return $_SERVER['SCRIPT_NAME'] . "/$v/$p$es";
|
||||
}
|
||||
|
||||
|
||||
/** This function terminates request processing by storing session and
|
||||
* tracking information. It then exits the script.
|
||||
*/
|
||||
function endRequest($quit = true) {
|
||||
session::store();
|
||||
tracking::store();
|
||||
dbEnd();
|
||||
if ($quit) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This function identifies the directory containing the lay-out scripts for
|
||||
* the current version+theme combination, checks for its existence (the script
|
||||
* bails out if it doesn't exist) and returns it.
|
||||
*/
|
||||
function getLayoutDirectory($version) {
|
||||
$d = config::$main['scriptdir'] . "/site/$version/layout";
|
||||
if ($version != 'main') {
|
||||
$t = prefs::get("$version/theme");
|
||||
if ($t == "") {
|
||||
$t = "default";
|
||||
}
|
||||
$d .= "/$t";
|
||||
}
|
||||
|
||||
if (!file_exists($d)||!is_dir($d)) {
|
||||
l::fatal(17, "Directory '$d' for version '$version'");
|
||||
}
|
||||
|
||||
return $d;
|
||||
}
|
||||
|
||||
|
||||
/** This function determines the current language from either:
|
||||
* 1) the user's preferences it they are available,
|
||||
* 2) tracking data if a user has already logged in,
|
||||
* 3) the default value, "en".
|
||||
* If user preferences are currently available, tracking data is modified
|
||||
* accordingly.
|
||||
*/
|
||||
function getLanguage() {
|
||||
$l = prefs::get('main/language');
|
||||
if ($l != "") {
|
||||
if ($_SESSION['authok']) {
|
||||
tracking::$data['language'] = $l;
|
||||
}
|
||||
return $l;
|
||||
} elseif ($_SESSION['authok']) {
|
||||
return "en";
|
||||
}
|
||||
|
||||
$l = tracking::$data['language'];
|
||||
if ($l != "") {
|
||||
return $l;
|
||||
}
|
||||
return "en";
|
||||
}
|
||||
|
||||
|
||||
/** This function acts like utf8entities, except for the fact that it uses
|
||||
* UTF-8 by default.
|
||||
*/
|
||||
function utf8entities($string, $mode = ENT_COMPAT) {
|
||||
return htmlentities($string, $mode, 'UTF-8');
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* PAGE OUTPUT FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
function handleInput() {
|
||||
engine::$e->initSession();
|
||||
engine::$e->checkAuth();
|
||||
|
||||
if (is_array($_SESSION) && is_array(config::$main['trace'])
|
||||
&& in_array($_SESSION['userid'], config::$main['trace'])) {
|
||||
|
||||
$path = input::$path;
|
||||
$page = input::$page;
|
||||
ob_start();
|
||||
print "REQUEST AT $path/$page." . input::$eType . "\n";
|
||||
print "==========================================\n";
|
||||
print "Time: " . gmstrftime("%Y-%m-%d %H:%M:%S", time()) . "\n\n";
|
||||
print "SESSION DATA\n";
|
||||
print_r($_SESSION);
|
||||
print "\n\nINPUT:";
|
||||
print_r(input::$input);
|
||||
print "\n\n";
|
||||
$log = ob_get_contents();
|
||||
$logFile = fopen("/tmp/trace-{$_SESSION['userid']}" . gmstrftime("-%Y-%m-%d", time()) . ".log", "a");
|
||||
fwrite($logFile, $log);
|
||||
fclose($logFile);
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
engine::$e->handleInput();
|
||||
engine::$e->initRPC();
|
||||
engine::$e->outputData();
|
||||
endRequest();
|
||||
}
|
||||
|
||||
?>
|
104
scripts/lib/pcheck.inc
Normal file
104
scripts/lib/pcheck.inc
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// LegacyWorlds Beta 5
|
||||
// Game libraries
|
||||
//
|
||||
// lib/pcheck.inc
|
||||
//
|
||||
// This library allows access to the proxy detector.
|
||||
//
|
||||
// Copyright(C) 2004-2008, DeepClone Development
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
class pcheck {
|
||||
|
||||
private static $queue = null;
|
||||
private static $pid = null;
|
||||
|
||||
public static function isRunning() {
|
||||
$fName = config::$main['cs_path'] . "/proxyDetector.pid";
|
||||
if (!file_exists($fName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = fopen($fName, "r");
|
||||
$line = rtrim(fgets($file));
|
||||
fclose($file);
|
||||
|
||||
list($pid, $time) = explode(" ", $line);
|
||||
if (time() - $time > 22) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $pid;
|
||||
}
|
||||
|
||||
private static function getQueue() {
|
||||
if (! is_null(self::$queue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$key = ftok(config::$main['scriptdir'] . "/lib/pcheck_manager.inc", "C");
|
||||
if ($key == -1) {
|
||||
throw new Exception("Could not create the control queue's key");
|
||||
}
|
||||
|
||||
self::$queue = @msg_get_queue($key);
|
||||
if (self::$queue === FALSE) {
|
||||
self::$queue = null;
|
||||
throw new Exception("Could not access the control queue (using key $key)");
|
||||
}
|
||||
|
||||
self::$pid = posix_getpid();
|
||||
}
|
||||
|
||||
private static function sendRequest($addresses) {
|
||||
array_unshift($addresses, self::$pid);
|
||||
if (!@msg_send(self::$queue, 1, $addresses, true, false)) {
|
||||
throw new Exception("Error while sending request");
|
||||
}
|
||||
}
|
||||
|
||||
private static function getResults() {
|
||||
$wait = 30;
|
||||
do {
|
||||
$success = @msg_receive(self::$queue, self::$pid, $type, 32768, $result,
|
||||
true, MSG_IPC_NOWAIT, $error);
|
||||
|
||||
if (!$success && $error != MSG_ENOMSG) {
|
||||
throw new Exception("Error while waiting for results");
|
||||
} elseif ($success) {
|
||||
if (is_array($result)) {
|
||||
return $result;
|
||||
} elseif ($result == 'PING') {
|
||||
$wait = 30;
|
||||
} else {
|
||||
throw new Exception("Invalid message received");
|
||||
}
|
||||
} else {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
$wait --;
|
||||
|
||||
} while ($wait);
|
||||
|
||||
throw new Exception("Timeout while waiting for results");
|
||||
}
|
||||
|
||||
public static function check($addresses, $force = false) {
|
||||
if (!($force || self::isRunning())) {
|
||||
throw new Exception("The detector doesn't seem to be running");
|
||||
}
|
||||
self::getQueue();
|
||||
self::sendRequest($addresses);
|
||||
return self::getResults();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
?>
|
629
scripts/lib/pcheck_manager.inc
Normal file
629
scripts/lib/pcheck_manager.inc
Normal file
|
@ -0,0 +1,629 @@
|
|||
<?php
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// LegacyWorlds Beta 5
|
||||
// Game libraries
|
||||
//
|
||||
// lib/pcheck_manager.inc
|
||||
//
|
||||
// This library contains the code of the open proxy detector's main
|
||||
// thread.
|
||||
//
|
||||
// Copyright(C) 2004-2008, DeepClone Development
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
|
||||
declare(ticks = 1); // PHP stupidity
|
||||
|
||||
|
||||
class pcheck_manager {
|
||||
|
||||
/** This property indicates how many of the threads are free.
|
||||
*/
|
||||
private $nFree;
|
||||
|
||||
/** These properties are accessed by the detection threads to
|
||||
* read their parameters.
|
||||
*/
|
||||
public static $timeout;
|
||||
public static $requests;
|
||||
|
||||
|
||||
/** This property points to the only current instance of the
|
||||
* proxy detector.
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/** This property indicates whether the manager is being run in
|
||||
* debugging mode.
|
||||
*/
|
||||
private $debug;
|
||||
|
||||
/** This property contains the objects corresponding to each thread.
|
||||
*/
|
||||
private $threads = array();
|
||||
|
||||
/** This property is set to true when the manager is shutting down.
|
||||
*/
|
||||
private $ending = false;
|
||||
|
||||
/** Ports to scan on.
|
||||
*/
|
||||
static private $ports = array(
|
||||
80, 81, 1075, 3128, 4480, 5490, 6588, 7033, 8000,
|
||||
8080, 8081, 8085, 8090, 8095, 8100, 8105, 8110
|
||||
);
|
||||
|
||||
|
||||
/** This method sends commands to the FIFO controller. If the manager
|
||||
* is being run in debugging mode, the commands are not sent.
|
||||
*/
|
||||
private static function sendToControl($command) {
|
||||
if (self::$instance->debug) {
|
||||
return;
|
||||
}
|
||||
$pipe = fopen(config::$main['cs_fifo'], "w");
|
||||
fwrite($pipe, "$command\n");
|
||||
fclose($pipe);
|
||||
}
|
||||
|
||||
|
||||
/** This method is called by the error manager in case a fatal error
|
||||
* prevents the manager from functionning properly.
|
||||
*/
|
||||
public static function fatalError($errno, $errorText, $information) {
|
||||
$instance->shutdown();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/** The constructor forks, initialises the message queues, starts
|
||||
* the detector threads then calls waitForInstructions(). It never
|
||||
* returns.
|
||||
*/
|
||||
public function __construct($debug) {
|
||||
self::$instance = $this;
|
||||
$this->debug = $debug;
|
||||
|
||||
$this->backgroundProcess();
|
||||
$this->initMessageQueues();
|
||||
$this->initSignals();
|
||||
$this->initData();
|
||||
$this->initThreads();
|
||||
$this->initCache();
|
||||
|
||||
// Sends our PID to the controller
|
||||
self::sendToControl("PCPID " . ($this->pid = posix_getpid()));
|
||||
|
||||
l::notice("Proxy detector initialised");
|
||||
l::debug("Timeout: " . self::$timeout . "s; threads: " . count($this->threads));
|
||||
l::debug("Using URL {$this->url}");
|
||||
|
||||
$this->mainLoop();
|
||||
|
||||
$this->shutdown();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
/** This method initialises the manager's signal handlers.
|
||||
*/
|
||||
private function initSignals() {
|
||||
pcntl_signal(SIGTERM, array($this, "terminateHandler"));
|
||||
pcntl_signal(SIGINT, array($this, "terminateHandler"));
|
||||
pcntl_signal(SIGCHLD, array($this, "threadEndHandler"));
|
||||
}
|
||||
|
||||
|
||||
/** This method handles the TERM and INT signals, which both cause
|
||||
* a clean shutdown.
|
||||
*/
|
||||
public function terminateHandler($signo) {
|
||||
if (! $this->mustEnd) {
|
||||
l::notice("Main thread terminating on SIG" . ($signo == SIGTERM ? "TERM" : "INT"));
|
||||
$this->mustEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method handles SIGCHLD and takes appropriate measures if
|
||||
* it has been caused by an error.
|
||||
*/
|
||||
public function threadEndHandler($signo) {
|
||||
// Wait for the child processes and stores their IDs
|
||||
$ended = array();
|
||||
do {
|
||||
$pid = pcntl_waitpid(-1, $status, WNOHANG);
|
||||
if ($pid > 0) {
|
||||
$ended[$pid] = $status;
|
||||
}
|
||||
} while ($pid > 0);
|
||||
|
||||
foreach ($this->threads as $thread) {
|
||||
if (array_key_exists($thread->pid, $ended)) {
|
||||
$thread->ended = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->ending) {
|
||||
l::trace("Threads have ended: " . join(', ', array_keys($ended)));
|
||||
} else {
|
||||
l::notice("Some children have met an untimely end! Terminating.");
|
||||
$this->mustEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method causes the proxy checked to be run in the background.
|
||||
*/
|
||||
private function backgroundProcess() {
|
||||
if ($this->debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fork to the background
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
l::crit("The open proxy detector failed to start.");
|
||||
exit(1);
|
||||
} elseif ($pid) {
|
||||
exit(0);
|
||||
}
|
||||
posix_setsid();
|
||||
}
|
||||
|
||||
|
||||
/** This method initialises the message queues: a first queue to be
|
||||
* used as a control channel and on which requests will be received,
|
||||
* and a second queue to communicate with the threads.
|
||||
*/
|
||||
private function initMessageQueues() {
|
||||
// Create the control queue's key
|
||||
$ctrlKey = ftok(config::$main['scriptdir'] . "/lib/pcheck_manager.inc", "C");
|
||||
if ($ctrlKey == -1) {
|
||||
l::crit("Could not create the control queue's key");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Create the thread queue's key
|
||||
$thrdKey = ftok(config::$main['scriptdir'] . "/lib/pcheck_manager.inc", "T");
|
||||
if ($ctrlKey == -1) {
|
||||
l::crit("Could not create the thread queue's key");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Create the control queue
|
||||
$ctrlQueue = msg_get_queue($ctrlKey, 0666 | IPC_CREAT);
|
||||
if ($ctrlQueue === FALSE) {
|
||||
l::crit("Could not create the control queue (using key $ctrlKey)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Create the thread queue
|
||||
$thrdQueue = msg_get_queue($thrdKey, 0600 | IPC_CREAT);
|
||||
if ($thrdQueue === FALSE) {
|
||||
l::crit("Could not create the thread queue (using key $thrdKey)");
|
||||
@msg_remove_queue($ctrlQueue);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$this->control = $ctrlQueue;
|
||||
$this->threadQueue = $thrdQueue;
|
||||
}
|
||||
|
||||
|
||||
/** This method destroys the queues.
|
||||
*/
|
||||
private function destroyMsgQueues() {
|
||||
@msg_remove_queue($this->control);
|
||||
@msg_remove_queue($this->threadQueue);
|
||||
}
|
||||
|
||||
|
||||
/** This method initialises the data used by the proxy detector
|
||||
* threads.
|
||||
*/
|
||||
private function initData() {
|
||||
$serv = config::getParam('pcheck_server');
|
||||
$this->url = "http://$serv" . config::getParam('pcheck_path');
|
||||
$timeout = (int) config::getParam('pcheck_timeout');
|
||||
|
||||
self::$requests = array(
|
||||
"GET" => "GET {$this->url}?k=__key__ HTTP/1.0\r\n"
|
||||
. "Host: $serv\r\n"
|
||||
. "Cache-Control: no-cache\r\n"
|
||||
. "Pragma: no-cache\r\n"
|
||||
. "User-Agent: OpenCheck 1.0\r\n"
|
||||
. "\r\n",
|
||||
"POST" => "POST {$this->url} HTTP/1.0\r\n"
|
||||
. "Host: $serv\r\n"
|
||||
. "Cache-Control: no-cache\r\n"
|
||||
. "Pragma: no-cache\r\n"
|
||||
. "User-Agent: OpenCheck 1.0\r\n"
|
||||
. "Content-Length: 34\r\n"
|
||||
. "\r\n"
|
||||
. "k=__key__\r\n"
|
||||
);
|
||||
self::$timeout = $timeout;
|
||||
}
|
||||
|
||||
|
||||
/** This method initialises all threads.
|
||||
*/
|
||||
private function initThreads() {
|
||||
$nThreads = (int) config::getParam('pcheck_threads');
|
||||
for ($i = 0; $i < $nThreads; $i ++) {
|
||||
try {
|
||||
$thread = new pcheck_thread($this->threadQueue);
|
||||
} catch (Exception $e) {
|
||||
l::crit("Thread " . ($i + 1) . " failed to initialise, exiting!");
|
||||
$this->shutdown();
|
||||
exit(1);
|
||||
}
|
||||
array_push($this->threads, $thread);
|
||||
}
|
||||
$this->nFree = $nThreads;
|
||||
}
|
||||
|
||||
/** This method shuts down the manager.
|
||||
*/
|
||||
private function shutdown() {
|
||||
$this->ending = true;
|
||||
|
||||
// Kill threads
|
||||
foreach ($this->threads as $thread) {
|
||||
$thread->send(array("type" => "QUIT"));
|
||||
}
|
||||
|
||||
// Wait until all threads have ended
|
||||
do {
|
||||
$endOk = true;
|
||||
foreach ($this->threads as $thread) {
|
||||
if (! $thread->ended) {
|
||||
$endOk = false;
|
||||
sleep(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!$endOk);
|
||||
|
||||
// Destroy message queues
|
||||
$this->destroyMsgQueues();
|
||||
}
|
||||
|
||||
|
||||
/** This method contains the manager's main loop, which handles everything
|
||||
* from receiving requests and sending results to managing the detection
|
||||
* threads.
|
||||
*/
|
||||
private function mainLoop() {
|
||||
$this->requests = array();
|
||||
$this->reqHostsFound = array();
|
||||
$this->jobsQueue = array();
|
||||
$this->jobsData = array();
|
||||
|
||||
$ticker = 0;
|
||||
while (!$this->mustEnd) {
|
||||
// Check for incoming requests
|
||||
$success = msg_receive($this->control, 1, $type, 32768,
|
||||
$message, true, MSG_IPC_NOWAIT, $error);
|
||||
if (!$success && $error != MSG_ENOMSG) {
|
||||
l::error("Manager failed to receive from control queue");
|
||||
break;
|
||||
} elseif ($success) {
|
||||
$this->requestReceived(array_shift($message), $message);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for incoming results from the detection threads
|
||||
$success = msg_receive($this->threadQueue, 1, $type, 32768,
|
||||
$message, true, MSG_IPC_NOWAIT, $error);
|
||||
if (!$success && $error != MSG_ENOMSG) {
|
||||
l::error("Manager failed to receive from thread queue");
|
||||
break;
|
||||
} elseif ($success) {
|
||||
// A result has been received
|
||||
$this->resultReceived(array_shift($message), $message);
|
||||
continue;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
$ticker ++;
|
||||
|
||||
// For each request in progress, send a message every 10 ticks
|
||||
// to signal the process we're not dead yet
|
||||
if ($ticker % 10 == 0 && count($this->requests)) {
|
||||
$this->sendPing();
|
||||
}
|
||||
|
||||
// Send PID to controller every 20 ticks
|
||||
if ($ticker == 20) {
|
||||
$ticker = 0;
|
||||
self::sendToControl("PCPID {$this->pid}");
|
||||
$this->flushCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method handles the reception of a new request.
|
||||
*/
|
||||
private function requestReceived($fromPID, $hosts) {
|
||||
l::debug("Request received from $fromPID");
|
||||
|
||||
$now = time();
|
||||
$this->requests[$fromPID] = array();
|
||||
$this->reqHostsFound[$fromPID] = 0;
|
||||
foreach ($hosts as $host) {
|
||||
if (is_array($this->cache[$host]) && $now - $this->cache[$host]['last'] < 86400) {
|
||||
// Cached entry found, store result
|
||||
$this->requests[$fromPID][$host] = $this->cache[$host]['status'];
|
||||
$this->reqHostsFound[$fromPID] ++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// No cached entry found
|
||||
$this->requests[$fromPID][$host] = -2;
|
||||
if (is_array($this->jobsData[$host])) {
|
||||
// We're already trying to detect this host
|
||||
continue;
|
||||
}
|
||||
|
||||
// This host needs to be detected
|
||||
$this->addToJobsQueue($host);
|
||||
}
|
||||
|
||||
if ($this->reqHostsFound[$fromPID] == count($this->requests[$fromPID])) {
|
||||
// The request could be satisfied directly from cached data
|
||||
$this->sendResponse($fromPID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method stores the results of a scan performed by one of the
|
||||
* detection threads.
|
||||
*/
|
||||
private function resultReceived($fromThread, $result) {
|
||||
list($host, $found) = $result;
|
||||
|
||||
// If a proxy was detected, log it
|
||||
if ($found) {
|
||||
l::info("Found open proxy at $host on port $port");
|
||||
}
|
||||
|
||||
// Store the results
|
||||
$this->jobsData[$host][1] |= $found;
|
||||
$this->jobsData[$host][0] --;
|
||||
if ($this->jobsData[$host][0] == 0) {
|
||||
$this->hostFinished($host);
|
||||
}
|
||||
|
||||
// Increase amount of free threads, set the thread as free
|
||||
$this->nFree ++;
|
||||
foreach ($this->threads as $thread) {
|
||||
if ($thread->pid == $fromThread) {
|
||||
$thread->free = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Shift the jobs queue
|
||||
$this->moveQueue();
|
||||
}
|
||||
|
||||
|
||||
/** This method adds the jobs required to scan a host to the queue.
|
||||
*/
|
||||
private function addToJobsQueue($host) {
|
||||
l::trace("Adding host $host to the queue...");
|
||||
|
||||
$this->jobsData[$host] = array(count(self::$ports), false);
|
||||
foreach (self::$ports as $port) {
|
||||
array_push($this->jobsQueue, array($host, $port));
|
||||
}
|
||||
|
||||
$this->moveQueue();
|
||||
}
|
||||
|
||||
|
||||
/** This method returns the response to a request through the queue.
|
||||
*/
|
||||
private function sendResponse($requestPID) {
|
||||
$request = $this->requests[$requestPID];
|
||||
|
||||
$nRequests = array();
|
||||
$nReqHF = array();
|
||||
foreach ($this->requests as $id => $data) {
|
||||
if ($id != $requestPID) {
|
||||
$nRequests[$id] = $data;
|
||||
$nReqHF[$id] = $this->reqHostsFound[$id];
|
||||
}
|
||||
}
|
||||
$this->reqHostsFound = $nReqHF;
|
||||
$this->requests = $nRequests;
|
||||
|
||||
l::debug("Sending response to process #$requestPID");
|
||||
msg_send($this->control, $requestPID, $request, true);
|
||||
}
|
||||
|
||||
|
||||
/** This method sends a "ping" packet to all waiting processes.
|
||||
*/
|
||||
private function sendPing() {
|
||||
l::trace("Pinging processes");
|
||||
foreach (array_keys($this->requests) as $id) {
|
||||
msg_send($this->control, $id, "PING", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method is called when the processing of a host
|
||||
* is complete.
|
||||
*/
|
||||
private function hostFinished($host) {
|
||||
l::trace("Host scanning finished for $host");
|
||||
$result = $this->jobsData[$host][1];
|
||||
|
||||
// Remove the entry from jobsData
|
||||
$nData = array();
|
||||
foreach ($this->jobsData as $h => $d) {
|
||||
if ($h != $host) {
|
||||
$nData[$h] = $d;
|
||||
}
|
||||
}
|
||||
$this->jobsData = $nData;
|
||||
|
||||
// Store result in cache
|
||||
$this->storeCache($host, $result);
|
||||
|
||||
// Check all requests that contained this host
|
||||
$checkRequests = array();
|
||||
foreach (array_keys($this->requests) as $request) {
|
||||
if (array_key_exists($host, $this->requests[$request])) {
|
||||
$this->reqHostsFound[$request] ++;
|
||||
$this->requests[$request][$host] = $result ? 1 : 0;
|
||||
array_push($checkRequests, $request);
|
||||
}
|
||||
}
|
||||
|
||||
// For each request that contained the host, check if it's completed
|
||||
$finished = array();
|
||||
foreach ($checkRequests as $request) {
|
||||
if ($this->reqHostsFound[$request] == count($this->requests[$request])) {
|
||||
array_push($finished, $request);
|
||||
}
|
||||
}
|
||||
|
||||
// Send responses to completed requests
|
||||
foreach ($finished as $request) {
|
||||
$this->sendResponse($request);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method sends free threads a scanning order if the
|
||||
* jobs queue isn't empty.
|
||||
*/
|
||||
private function moveQueue() {
|
||||
while ($this->nFree > 0 && count($this->jobsQueue)) {
|
||||
$job = array_shift($this->jobsQueue);
|
||||
foreach ($this->threads as $thread) {
|
||||
if ($thread->free) {
|
||||
l::trace("Assigning port {$job[1]} at {$job[0]} to thread {$thread->pid}");
|
||||
$this->nFree --;
|
||||
$thread->free = false;
|
||||
$thread->send(array(
|
||||
"type" => "SCAN",
|
||||
"scan" => $job
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method stores the result of a scan in the cache.
|
||||
*/
|
||||
private function storeCache($host, $result) {
|
||||
$this->cache[$host] = array(
|
||||
"status" => $result ? 1 : 0,
|
||||
"last" => time()
|
||||
);
|
||||
|
||||
$this->cacheModified = time();
|
||||
}
|
||||
|
||||
/** This method reads the cache from the database.
|
||||
*/
|
||||
private function initCache() {
|
||||
$this->cacheModified = 0;
|
||||
$this->cache = array();
|
||||
|
||||
$success = false;
|
||||
do {
|
||||
try {
|
||||
$db = db::connect();
|
||||
$db->enableExceptions();
|
||||
$db->query("LOCK TABLE proxy_detector IN ACCESS EXCLUSIVE MODE");
|
||||
$cacheRead = new db_copy("proxy_detector");
|
||||
$cacheRead->setAccessor($db);
|
||||
$cacheRead->execute();
|
||||
$db->end();
|
||||
$db->close();
|
||||
$db->disableExceptions();
|
||||
$success = true;
|
||||
} catch (Exception $e) {
|
||||
l::notice("Could not read cache from database. Will retry in 20 seconds.");
|
||||
l::info($e->getMessage());
|
||||
if (!is_null($db)) {
|
||||
l::trace("Closing database connection");
|
||||
$db->close();
|
||||
$db->disableExceptions();
|
||||
}
|
||||
sleep(20);
|
||||
}
|
||||
} while (! $success);
|
||||
|
||||
for ($i = 0; $i < $cacheRead->rows(); $i ++) {
|
||||
$row = $cacheRead->getRow($i);
|
||||
$this->cache[$row[0]] = array(
|
||||
"last" => $row[1],
|
||||
"status" => $row[2] == 't' ? 1 : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This method tries to store the cache's contents in the
|
||||
* database.
|
||||
*/
|
||||
private function flushCache() {
|
||||
if (! $this->cacheModified || time() - $this->cacheModified < 20) {
|
||||
return;
|
||||
}
|
||||
|
||||
l::debug("Flushing cache to database");
|
||||
$db = null;
|
||||
try {
|
||||
$db = db::connect();
|
||||
$db->enableExceptions();
|
||||
$db->query("LOCK TABLE proxy_detector IN ACCESS EXCLUSIVE MODE");
|
||||
$toWrite = $this->formatCache();
|
||||
$toWrite->setAccessor($db);
|
||||
$toWrite->execute();
|
||||
$db->end();
|
||||
$db->close();
|
||||
$db->disableExceptions();
|
||||
} catch (Exception $e) {
|
||||
l::notice("Could not write cache to database.");
|
||||
l::info($e->getMessage());
|
||||
if (!is_null($db)) {
|
||||
l::trace("Closing database connection");
|
||||
$db->close();
|
||||
$db->disableExceptions();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cacheModified = 0;
|
||||
}
|
||||
|
||||
|
||||
/** This method prepares the cache for copy into the database.
|
||||
*/
|
||||
private function formatCache() {
|
||||
$cache = new db_copy("proxy_detector", db_copy::copyToClean);
|
||||
$now = time();
|
||||
foreach ($this->cache as $host => $data) {
|
||||
if ($now - $data['last'] > 3 * 86400) {
|
||||
continue;
|
||||
}
|
||||
$cache->appendRow(array($host, $data['last'], $data['status'] ? 't' : 'f'));
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
144
scripts/lib/pcheck_thread.inc
Normal file
144
scripts/lib/pcheck_thread.inc
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// LegacyWorlds Beta 5
|
||||
// Game libraries
|
||||
//
|
||||
// lib/pcheck_thread.inc
|
||||
//
|
||||
// This library contains the code of the open proxy detector's scanning
|
||||
// threads.
|
||||
//
|
||||
// Copyright(C) 2004-2008, DeepClone Development
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
|
||||
class pcheck_thread {
|
||||
|
||||
/** The process ID of the thread.
|
||||
*/
|
||||
public $pid;
|
||||
|
||||
/** The message queue the thread is bound to. Undefined in the
|
||||
* parent process.
|
||||
*/
|
||||
private $queue;
|
||||
|
||||
/** This property indicates that the thread has ended.
|
||||
*/
|
||||
public $ended = false;
|
||||
|
||||
|
||||
/** The constructor forks (throwing an exception on failure), then
|
||||
* depending on whether it is in the parent process or in the child,
|
||||
* returns or enters the instruction loop.
|
||||
*/
|
||||
public function __construct($queue) {
|
||||
$pid = @pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
throw new Exception("Unable to fork");
|
||||
}
|
||||
|
||||
if ($pid != 0) {
|
||||
// In the parent, store the child's PID and get going.
|
||||
$this->pid = $pid;
|
||||
$this->free = true;
|
||||
$this->queue = $queue;
|
||||
return;
|
||||
}
|
||||
|
||||
// In the child, store our PID and the queue, then starts
|
||||
// waiting for instructions.
|
||||
$this->pid = posix_getpid();
|
||||
$this->queue = $queue;
|
||||
$this->waitLoop();
|
||||
}
|
||||
|
||||
|
||||
/** This method is called by the manager to send messages to the
|
||||
* threads.
|
||||
*/
|
||||
public function send($message) {
|
||||
return $this->ended ? false : msg_send($this->queue, $this->pid, $message, true);
|
||||
}
|
||||
|
||||
|
||||
/** This method implements the instruction loop.
|
||||
*/
|
||||
private function waitLoop() {
|
||||
$quit = false;
|
||||
$error = false;
|
||||
|
||||
do {
|
||||
$success = msg_receive($this->queue, $this->pid, $type, 32768, $message, true);
|
||||
if (! $success) {
|
||||
l::error("Child failed to receive a message");
|
||||
$quit = $error = true;
|
||||
} elseif ($message['type'] == 'QUIT') {
|
||||
$quit = true;
|
||||
} elseif ($message['type'] == 'SCAN') {
|
||||
list($host, $port) = $message['scan'];
|
||||
|
||||
$found = $this->executeCheck($host, $port, "GET")
|
||||
|| $this->executeCheck($host, $port, "POST");
|
||||
|
||||
if (!msg_send($this->queue, 1, array($this->pid, $host, $found), true)) {
|
||||
l::error("Child failed to send message");
|
||||
$quit = $error = true;
|
||||
}
|
||||
} else {
|
||||
l::notice("Unknown message {$message['type']} received");
|
||||
}
|
||||
} while (!$quit);
|
||||
|
||||
exit($error ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
/** This method will try to check one port for open proxy software, using
|
||||
* a specific method (POST or GET).
|
||||
*/
|
||||
private function executeCheck($ipAddress, $port, $method) {
|
||||
$key = md5(uniqid(rand()));
|
||||
|
||||
// Open the socket
|
||||
$socket = @fsockopen("tcp://$ipAddress", $port, $errno, $errstr, pcheck_manager::$timeout);
|
||||
if ($socket === FALSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure I/O doesn't timeout
|
||||
stream_set_timeout($socket, pcheck_manager::$timeout);
|
||||
|
||||
// Send the request
|
||||
$result = @fwrite($socket, preg_replace('/__key__/', $key, pcheck_manager::$requests[$method]));
|
||||
if ($result !== FALSE) {
|
||||
$info = stream_get_meta_data($socket);
|
||||
if ($info['timed_out']) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the page
|
||||
if ($result !== FALSE) {
|
||||
$result = @fread($socket, 4096);
|
||||
}
|
||||
if ($result !== FALSE) {
|
||||
$info = stream_get_meta_data($socket);
|
||||
if ($info['timed_out']) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the socket
|
||||
@fclose($socket);
|
||||
if ($result === FALSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return preg_match("/Key is \\<$key\\>/", $result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
112
scripts/lib/prefs.inc
Normal file
112
scripts/lib/prefs.inc
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* PREFERENCES MANAGEMENT FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
class prefs {
|
||||
private static $prefs = null;
|
||||
|
||||
/** This function transforms user_preferences database records into values
|
||||
* stored inside $prefs.
|
||||
*/
|
||||
private static function fromBase($qr, $toTrack = false) {
|
||||
while ($r = dbFetchHash($qr)) {
|
||||
$v = $r['version'];
|
||||
$i = $r['id'];
|
||||
if (!is_array(prefs::$prefs[$v])) {
|
||||
prefs::$prefs[$v] = array();
|
||||
}
|
||||
prefs::$prefs[$v][$i] = $r['value'];
|
||||
|
||||
if ($toTrack && $v == "main") {
|
||||
if (!is_array(tracking::$data['pref_override'])) {
|
||||
tracking::$data['pref_override'] = array();
|
||||
}
|
||||
tracking::$data['pref_override'][$i] = $r['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This function initializes $prefs to an empty array
|
||||
* then reads default preferences from the user_preferences table.
|
||||
*/
|
||||
private static function getDefaults() {
|
||||
prefs::$prefs = array();
|
||||
$qr = dbQuery("SELECT id,version,value FROM user_preferences WHERE account=0");
|
||||
if (!$qr || !count($qr)) {
|
||||
return;
|
||||
}
|
||||
prefs::fromBase($qr);
|
||||
}
|
||||
|
||||
|
||||
/** This function reads the current user's preferences from the base. */
|
||||
private static function loadUser() {
|
||||
$qr = dbQuery("SELECT id,version,value FROM user_preferences WHERE account={$_SESSION['userid']}");
|
||||
if (!$qr || !count($qr)) {
|
||||
return;
|
||||
}
|
||||
prefs::fromBase($qr, true);
|
||||
}
|
||||
|
||||
|
||||
/** This function loads preferences by loading default values and then, if
|
||||
* an user is authenticated, his own preferences.
|
||||
*/
|
||||
private static function load() {
|
||||
prefs::getDefaults();
|
||||
if ($_SESSION['authok']) {
|
||||
prefs::loadUser();
|
||||
} elseif (is_array(tracking::$data['pref_override'])) {
|
||||
foreach (tracking::$data['pref_override'] as $pref => $value) {
|
||||
prefs::$prefs['main'][$pref] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This function sets user preferences. */
|
||||
static function set($path, $val) {
|
||||
if (!$_SESSION['authok']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array(prefs::$prefs)) {
|
||||
prefs::load();
|
||||
}
|
||||
|
||||
list($ver, $pref) = explode('/', $path);
|
||||
$q = dbQuery("DELETE FROM user_preferences WHERE account={$_SESSION['userid']} AND id='$pref' AND version='$ver'");
|
||||
$q = dbQuery("INSERT INTO user_preferences VALUES('$pref','$ver',{$_SESSION['userid']},'".addslashes($val)."')");
|
||||
if ($q) {
|
||||
prefs::$prefs[$ver][$pref] = $val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** This function returns a value from the preferences using a version/pref_name path.
|
||||
*/
|
||||
static function get($path, $default = null) {
|
||||
if (!is_array(prefs::$prefs)) {
|
||||
prefs::load();
|
||||
}
|
||||
|
||||
list($version, $name) = explode('/', $path);
|
||||
if (is_array(prefs::$prefs[$version])) {
|
||||
$v = prefs::$prefs[$version][$name];
|
||||
} else {
|
||||
$v = null;
|
||||
}
|
||||
if (is_null($v) && !is_null($default)) {
|
||||
$v = $default;
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
149
scripts/lib/resource.inc
Normal file
149
scripts/lib/resource.inc
Normal file
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* STATIC RESOURCES MANAGEMENT
|
||||
****************************************************************************/
|
||||
|
||||
global $resources;
|
||||
|
||||
|
||||
/** This variable contains the list of resources to be loaded.
|
||||
*/
|
||||
$resources = array();
|
||||
|
||||
|
||||
/** This function appends a resource of some type to the list of resources
|
||||
* to be loaded.
|
||||
*/
|
||||
function addFileResource($type, $path) {
|
||||
if (!file_exists($path)) {
|
||||
//logText("Resource type '$type': '$path' not found", LOG_DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
global $resources;
|
||||
if (!is_array($resources[$type])) {
|
||||
$resources[$type] = array();
|
||||
}
|
||||
|
||||
$contents = @file_get_contents($path);
|
||||
if ($contents === FALSE) {
|
||||
l::notice("Resource type '$type': unable to read '$path'");
|
||||
return false;
|
||||
}
|
||||
$md5 = md5($contents);
|
||||
|
||||
array_push($resources[$type], array($path, $md5, $contents));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** This function appends a chunk of raw text to a resource output.
|
||||
*/
|
||||
function addRawResource($type, $text) {
|
||||
global $resources;
|
||||
if (!is_array($resources[$type])) {
|
||||
$resources[$type] = array();
|
||||
}
|
||||
|
||||
$md5 = md5($text);
|
||||
array_push($resources[$type], array(null, $md5, $text));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** This function generates the cached output for a set of resources.
|
||||
*/
|
||||
function generateResourceCache($type, $md5) {
|
||||
if (!is_dir(config::$main['cachedir']) && !@mkdir(config::$main['cachedir'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$f = @fopen(config::$main['cachedir'] . "/$md5.$type", "w");
|
||||
if (!$f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $resources;
|
||||
$c = count($resources[$type]);
|
||||
for ($i=0;$i<$c;$i++) {
|
||||
fwrite($f, preg_replace('/__STATICURL__/', config::$main['staticurl'], $resources[$type][$i][2]) . "\n");
|
||||
}
|
||||
|
||||
fclose($f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** This function stores a ressource into the database so that it may be used
|
||||
* from other scripts.
|
||||
*/
|
||||
function storeResource($type, $deleteOld = 0, $write = true) {
|
||||
global $resources;
|
||||
if (!is_array($resources[$type])) {
|
||||
// l::notice("No resource of type $type");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Delete old entries
|
||||
if ($deleteOld > 0) {
|
||||
if ($write) {
|
||||
$q = dbQuery("SELECT md5 FROM web_cache WHERE unix_timestamp(now())-last_used>$deleteOld AND rtype='$type'");
|
||||
while ($r = dbFetchArray($q)) {
|
||||
@unlink(config::$main['cachedir'] . "/{$r[0]}.$type");
|
||||
}
|
||||
}
|
||||
dbQuery("DELETE FROM web_cache WHERE unix_timestamp(now())-last_used>$deleteOld AND rtype='$type'");
|
||||
}
|
||||
|
||||
// Check for an existing entry
|
||||
$md5 = md5(serialize($resources[$type]));
|
||||
$q = dbQuery("SELECT id FROM web_cache WHERE rtype='$type' AND md5='$md5' FOR SHARE");
|
||||
if ($q && dbCount($q)) {
|
||||
list($id) = dbFetchArray($q);
|
||||
return $id;
|
||||
}
|
||||
|
||||
// Generate the output file
|
||||
if ($write && !generateResourceCache($type, $md5)) {
|
||||
l::warn("Resource file generation failed (type $type)");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add the database entry
|
||||
return dbQuery("INSERT INTO web_cache(rtype,md5,last_used) VALUES('$type','$md5'," . time() . ")");
|
||||
}
|
||||
|
||||
|
||||
/** This function reads a resource, identified by its DB identifier, from the
|
||||
* database. If the resource is found in the base, it then tries to send the
|
||||
* file's contents.
|
||||
*/
|
||||
function displayResource($id, $rtype) {
|
||||
$q = dbQuery("SELECT rtype,md5 FROM web_cache WHERE id=$id FOR UPDATE");
|
||||
if (!($q && dbCount($q) == 1)) {
|
||||
l::warn("Resource ID '$id' not in the database");
|
||||
return false;
|
||||
}
|
||||
|
||||
list($dbtype,$md5) = dbFetchArray($q);
|
||||
if ($rtype != $dbtype) {
|
||||
l::warn("Resource ID '$id' has wrong type $dbtype (expected $rtype)");
|
||||
return false;
|
||||
}
|
||||
|
||||
dbQuery("UPDATE web_cache SET last_used=" . time() . " WHERE id=$id");
|
||||
endRequest(false);
|
||||
|
||||
$path = config::$main['cachedir'] . "/$md5.$rtype";
|
||||
if (readfile($path) === FALSE) {
|
||||
l::warn("File not found for resource '$id': $path");
|
||||
return false;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
?>
|
185
scripts/lib/session.inc
Normal file
185
scripts/lib/session.inc
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* SESSION MANAGEMENT FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
class session {
|
||||
|
||||
static $cName = "";
|
||||
static $id = null;
|
||||
static $dbId = null;
|
||||
static $new = null;
|
||||
static $dataMD5 = null;
|
||||
|
||||
/** This function generates a new session identifier. */
|
||||
private static function generateId() {
|
||||
// Create a new tracking cookie if none exists
|
||||
do {
|
||||
$v = md5(uniqid(rand()));
|
||||
$q = "SELECT id FROM web_session WHERE cookie = '$v'";
|
||||
$qr = dbQuery($q);
|
||||
} while ($qr && dbCount($qr));
|
||||
|
||||
// If $qr is null, something went wrong
|
||||
if (!$qr) {
|
||||
l::warning('No query result in session::generateId()');
|
||||
return null;
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
|
||||
|
||||
/** This function reads the session cookie from the user's browser or generates
|
||||
* a value for the session cookie if none is found.
|
||||
*/
|
||||
private static function readId($create) {
|
||||
// Get session identifier from cookie if it exists
|
||||
if (isset($_COOKIE[self::$cName]) && ctype_alnum($_COOKIE[self::$cName]) && strlen($_COOKIE[self::$cName]) == 32) {
|
||||
$v = pg_escape_string($_COOKIE[self::$cName]);
|
||||
$q = "SELECT id FROM web_session WHERE cookie='$v' AND tracking=" . tracking::$dbId;
|
||||
$qr = dbQuery($q);
|
||||
if ($qr && dbCount($qr) == 1) {
|
||||
return array($v, false);
|
||||
} else {
|
||||
l::notice("Session '$v' not found");
|
||||
$create = true;
|
||||
}
|
||||
}
|
||||
return $create ? array(self::generateId(), true) : array(null, false);
|
||||
}
|
||||
|
||||
|
||||
/** This function inserts session data into the database's web_session table. */
|
||||
static private function createData() {
|
||||
$q = "INSERT INTO web_session(cookie,ip_addr,tracking,stored_data) VALUES ('"
|
||||
. self::$id . "','" . $_SERVER['REMOTE_ADDR'] . "'," . tracking::$dbId . ",'a:0:{}')";
|
||||
return dbQuery($q);
|
||||
}
|
||||
|
||||
|
||||
/** This function updates a session's last access timestamp. */
|
||||
static private function updateAccess() {
|
||||
$q = "UPDATE web_session SET last_used=unix_timestamp(now()) WHERE cookie='" . self::$id . "'";
|
||||
return dbQuery($q);
|
||||
}
|
||||
|
||||
|
||||
/** This function reads tracking data from the web_tracking table and stores it
|
||||
* in the $_SESSION autoglobal variable.
|
||||
*/
|
||||
static private function readData() {
|
||||
$q = "SELECT stored_data,id FROM web_session WHERE cookie = '" . self::$id . "'";
|
||||
$qr = dbQuery($q);
|
||||
if (!$qr || dbCount($qr) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tmp = dbFetchArray($qr);
|
||||
$_SESSION = unserialize($tmp[0]);
|
||||
self::$dataMD5 = md5($tmp[0]);
|
||||
self::$dbId = $tmp[1];
|
||||
return is_array($_SESSION);
|
||||
}
|
||||
|
||||
|
||||
/** This function updates the session record in the web_session table using
|
||||
* the serialized contents of $_SESSION.
|
||||
*/
|
||||
static function store() {
|
||||
if (is_null(self::$dbId)) {
|
||||
return true;
|
||||
}
|
||||
$ser = serialize($_SESSION);
|
||||
if ($ser != self::$dataMD5) {
|
||||
$txt = pg_escape_string($ser);
|
||||
$q = "UPDATE web_session SET stored_data='$txt' WHERE id=" . self::$dbId;
|
||||
return dbQuery($q);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** This function terminates the current session and removes the record from
|
||||
* the web_session table. If an user was authenticated, accountLog() is
|
||||
* called to account for the fact that the user has "logged out".
|
||||
*/
|
||||
static function kill() {
|
||||
if (is_null(self::$dbId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($_SESSION['userid'] != "") {
|
||||
dbQuery("UPDATE account SET last_logout=".time()." WHERE id={$_SESSION['userid']}");
|
||||
accountLog('o', $_SESSION['userid']);
|
||||
}
|
||||
$_SESSION = array();
|
||||
|
||||
setcookie(self::$cName, false, 0, dirname($_SERVER['SCRIPT_NAME']));
|
||||
dbQuery("DELETE FROM web_session WHERE id=".self::$dbId);
|
||||
self::$dbId = self::$id = null;
|
||||
}
|
||||
|
||||
|
||||
/** This method sets the account identifier associated with a session. */
|
||||
static function setAccount($account) {
|
||||
if (is_null($account)) {
|
||||
$account = "NULL";
|
||||
}
|
||||
dbQuery("UPDATE web_session SET account=$account WHERE id=" . self::$dbId);
|
||||
}
|
||||
|
||||
|
||||
/** This function manages the session, creating it if it is required or
|
||||
* reading it from the base.
|
||||
*/
|
||||
static function handle($create) {
|
||||
self::$cName = config::getParam('sessname');
|
||||
|
||||
// If tracking is disabled, return
|
||||
if (tracking::$disabled) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get or generate session identifier
|
||||
if (!tracking::$new) {
|
||||
list($sessId, $sessNew) = self::readId($create);
|
||||
} elseif ($create) {
|
||||
list($sessId, $sessNew) = array(self::generateId(), true);
|
||||
}
|
||||
|
||||
// If no session identifier has been returned, end.
|
||||
if (is_null($sessId)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
self::$id = $sessId;
|
||||
self::$new = $sessNew;
|
||||
|
||||
// Create or update session record
|
||||
if ($sessNew && !self::createData()) {
|
||||
l::fatal(13, "Session ID was $sessId");
|
||||
} elseif (!$sessNew && !self::updateAccess()) {
|
||||
l::fatal(14, "Session ID was $sessId");
|
||||
}
|
||||
|
||||
// Read session data
|
||||
if (!self::readData()) {
|
||||
l::fatal(15, "Session ID was $sessId");
|
||||
}
|
||||
|
||||
// If no user is currently authenticated and no
|
||||
// authentication is required, terminate the session
|
||||
if (!($create || $_SESSION['authok'])) {
|
||||
self::kill();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sets session cookie
|
||||
setcookie(self::$cName, $sessId, 0, dirname($_SERVER['SCRIPT_NAME']));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
94
scripts/lib/tick.inc
Normal file
94
scripts/lib/tick.inc
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* TICK MANAGEMENT CLASSES
|
||||
****************************************************************************/
|
||||
|
||||
class tick_definition {
|
||||
|
||||
function __construct($version, $script, $public) {
|
||||
$this->version = $version;
|
||||
$this->script = $script;
|
||||
$this->public = $public;
|
||||
$this->text = array();
|
||||
$this->version->addTickDefinition($this);
|
||||
}
|
||||
|
||||
function addText($lang, $name, $description) {
|
||||
$this->text[$lang] = array($name, $description);
|
||||
}
|
||||
|
||||
function getName($lang) {
|
||||
return ($this->text[$lang] ? $this->text[$lang][0] : "");
|
||||
}
|
||||
|
||||
function getDescription($lang) {
|
||||
return ($this->text[$lang] ? $this->text[$lang][1] : "");
|
||||
}
|
||||
|
||||
function getPath() {
|
||||
return "{$this->version->id}/ticks/{$this->script}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class tick_instance {
|
||||
|
||||
function __construct($game, $script, $first, $interval, $last) {
|
||||
$this->game = $game;
|
||||
$this->definition = $game->version->getTickDefinition($script);
|
||||
$this->first = $first;
|
||||
$this->interval = $interval;
|
||||
$this->last = $last;
|
||||
$this->game->addTick($this);
|
||||
$this->computeNext();
|
||||
}
|
||||
|
||||
function computeNext() {
|
||||
$now = time();
|
||||
|
||||
if (!is_null($this->last) && $now >= $this->last) {
|
||||
$this->next = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($now < $this->first) {
|
||||
$this->next = $this->first;
|
||||
return;
|
||||
}
|
||||
|
||||
$diff = $now - $this->first;
|
||||
$mod = $diff % $this->interval;
|
||||
$mul = ($diff - $mod) / $this->interval;
|
||||
$this->next = $this->first + ($mul + 1) * $this->interval;
|
||||
}
|
||||
|
||||
function run() {
|
||||
$libName = $this->definition->getPath();
|
||||
$tick = $this->game->getLib($libName);
|
||||
|
||||
l::setPrefix("{$this->game->name}::{$this->definition->script}");
|
||||
if (is_null($tick)) {
|
||||
l::error("tick library '$libName' not found");
|
||||
} else {
|
||||
try {
|
||||
$tick->call('runTick');
|
||||
} catch (Exception $e) {
|
||||
l::error("tick failed with exception " . get_class($e));
|
||||
l::info("exception message: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
l::setPrefix("");
|
||||
}
|
||||
|
||||
function __sleep() {
|
||||
return array('game', 'definition', 'first', 'interval', 'last');
|
||||
}
|
||||
|
||||
function __wakeup() {
|
||||
$this->computeNext();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
257
scripts/lib/tick_manager.inc
Normal file
257
scripts/lib/tick_manager.inc
Normal file
|
@ -0,0 +1,257 @@
|
|||
<?php
|
||||
|
||||
declare(ticks = 1); // PHP stupidity
|
||||
|
||||
|
||||
class tick_manager {
|
||||
var $threads = array();
|
||||
var $byId = array();
|
||||
|
||||
static function fatalError($errno, $errorText, $information) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
function tick_manager($debug) {
|
||||
$this->debug = $debug;
|
||||
if (!$this->debug) {
|
||||
// Forks to the background
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
die("The tick manager failed to launch: fork() failed.\n");
|
||||
} elseif ($pid) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// We're in the child, detach from parent as much as possible
|
||||
$this->pid = posix_getpid();
|
||||
posix_setsid();
|
||||
|
||||
// Tell the system control script that we are running
|
||||
if (file_exists(config::$main['cs_fifo'])) {
|
||||
$pipe = fopen(config::$main['cs_fifo'], "w");
|
||||
fwrite($pipe, "TMPID {$this->pid}\n");
|
||||
fclose($pipe);
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists(config::$main['cachedir'] . "/stop_ticks")) {
|
||||
unlink(config::$main['cachedir'] . "/stop_ticks");
|
||||
$this->ticksStopped = true;
|
||||
touch(config::$main['cachedir'] . "/ticks_stopped");
|
||||
} elseif (file_exists(config::$main['cachedir'] . "/ticks_stopped")) {
|
||||
$this->ticksStopped = true;
|
||||
} else {
|
||||
$this->ticksStopped = false;
|
||||
}
|
||||
|
||||
if ($this->ticksStopped) {
|
||||
l::warn("Ticks manager starting but ticks are stopped");
|
||||
} else {
|
||||
l::notice("Ticks manager started");
|
||||
}
|
||||
|
||||
l::setFatalHandler('tick_manager::fatalError');
|
||||
$this->run();
|
||||
}
|
||||
|
||||
|
||||
function run() {
|
||||
$this->setSignals(false);
|
||||
$this->setAlarm();
|
||||
while (1) {
|
||||
// Sleeps for a while
|
||||
sleep(10);
|
||||
|
||||
// Check for configuration update
|
||||
$this->update();
|
||||
|
||||
// Check last tick
|
||||
if (time() - $this->lastTick >= 6) {
|
||||
l::error("No ticks for 6+ seconds! Trying to re-schedule!");
|
||||
$this->setSignals(false);
|
||||
$this->setAlarm();
|
||||
$this->lastTick = time();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setSignals($mask) {
|
||||
$myHandler = array($this, "signalHandler");
|
||||
$sigs = array(SIGTERM, SIGINT, SIGCHLD);
|
||||
foreach ($sigs as $s) {
|
||||
if ($mask) {
|
||||
pcntl_signal($s, SIG_IGN);
|
||||
} else {
|
||||
pcntl_signal($s, $myHandler);
|
||||
}
|
||||
}
|
||||
|
||||
if ($mask) {
|
||||
pcntl_signal(SIGALRM, SIG_IGN);
|
||||
} else {
|
||||
pcntl_signal(SIGALRM, array($this, "alarm"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function signalHandler($signo) {
|
||||
switch ($signo) :
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
l::notice("Main thread terminating on SIG" . ($signo == SIGTERM ? "TERM" : "INT"));
|
||||
exit(0);
|
||||
case SIGCHLD:
|
||||
do {
|
||||
$wr = pcntl_waitpid(-1, $status, WNOHANG);
|
||||
} while ($wr > 0);
|
||||
break;
|
||||
endswitch;
|
||||
}
|
||||
|
||||
|
||||
function update() {
|
||||
// Mask signals
|
||||
$this->setSignals(true);
|
||||
|
||||
// Reload configuration
|
||||
if (config::reload()) {
|
||||
l::notice("Configuration changed, re-scheduling");
|
||||
$this->setAlarm();
|
||||
}
|
||||
|
||||
// Stop/start ticks
|
||||
if ($this->ticksStopped && file_exists(config::$main['cachedir'] . "/start_ticks")) {
|
||||
l::notice("Ticks restarted");
|
||||
$this->ticksStopped = false;
|
||||
unlink(config::$main['cachedir'] . "/ticks_stopped");
|
||||
} elseif (file_exists(config::$main['cachedir'] . "/stop_ticks")) {
|
||||
l::notice("Ticks stopped");
|
||||
$this->ticksStopped = true;
|
||||
touch(config::$main['cachedir'] . "/ticks_stopped");
|
||||
}
|
||||
@unlink(config::$main['cachedir'] . "/stop_ticks");
|
||||
@unlink(config::$main['cachedir'] . "/start_ticks");
|
||||
|
||||
// Tell the system control script that we are still running
|
||||
if (!$this->debug && file_exists(config::$main['cs_fifo'])) {
|
||||
$pipe = fopen(config::$main['cs_fifo'], "w");
|
||||
fwrite($pipe, "TMPID {$this->pid}\n");
|
||||
fclose($pipe);
|
||||
}
|
||||
|
||||
// Unmask child signals
|
||||
$this->setSignals(false);
|
||||
}
|
||||
|
||||
|
||||
function setAlarm() {
|
||||
$min = null;
|
||||
foreach (config::$config->games as $name => $game) {
|
||||
foreach ($game->ticks as $tName => $tick) {
|
||||
if (!is_null($tick->next) && (is_null($min) || $tick->next < $min)) {
|
||||
$min = $tick->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($min)) {
|
||||
l::warn("No ticks left to execute");
|
||||
return;
|
||||
}
|
||||
|
||||
$delay = $min - time();
|
||||
$rdelay = $delay <= 1 ? 2 : $delay;
|
||||
pcntl_alarm($rdelay);
|
||||
}
|
||||
|
||||
|
||||
function alarm($sig) {
|
||||
$this->setSignals(true);
|
||||
|
||||
$exec = array();
|
||||
$now = time();
|
||||
foreach (config::$config->games as $name => $game) {
|
||||
foreach ($game->ticks as $tName => $tick) {
|
||||
if (!is_null($tick->next) && $tick->next <= $now) {
|
||||
array_push($exec, $tick);
|
||||
$oNext = $tick->next;
|
||||
$tick->computeNext();
|
||||
if ($tick->next !== $oNext && !$this->ticksStopped) {
|
||||
if (is_null($tick->next)) {
|
||||
l::info("Not rescheduling {$name}::{$tName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!count($exec)) {
|
||||
l::warning("No ticks scheduled for execution!");
|
||||
}
|
||||
|
||||
$this->setAlarm();
|
||||
|
||||
foreach ($exec as $tick) {
|
||||
$this->startThread($tick);
|
||||
}
|
||||
|
||||
$this->setSignals(false);
|
||||
}
|
||||
|
||||
|
||||
function startThread($tick) {
|
||||
// Don't even try if ticks are stopped
|
||||
if ($this->ticksStopped) {
|
||||
$this->lastTick = time();
|
||||
return;
|
||||
}
|
||||
|
||||
// Fork
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
l::error("Fork failed for {$tick->game->name}::{$tick->definition->script}!");
|
||||
return false;
|
||||
} elseif ($pid) {
|
||||
$this->lastTick = time();
|
||||
return $pid;
|
||||
}
|
||||
|
||||
// We're in the child, detach from parent as much as possible
|
||||
if (!$this->debug) {
|
||||
posix_setsid();
|
||||
}
|
||||
|
||||
// Connect to the DB and check if the game's running
|
||||
if (!dbConnect(false)) {
|
||||
exit(0);
|
||||
}
|
||||
$tick->game->getDBAccess();
|
||||
if ($tick->game->version->id != 'main') {
|
||||
$status = $tick->game->status();
|
||||
if ($status == 'FINISHED'
|
||||
|| ($status == 'PRE' || $status == 'READY') && $tick->definition->public) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the tick
|
||||
l::info("TICK: {$tick->game->name}::{$tick->definition->script}");
|
||||
ob_start();
|
||||
$tick->run($t);
|
||||
$text = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
if ($text != '') {
|
||||
$text = explode("\n", $text);
|
||||
foreach ($text as $line) {
|
||||
l::notice("{$tick->game->name}::{$tick->definition->script} OUTPUT: $line");
|
||||
}
|
||||
}
|
||||
|
||||
dbClose();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
179
scripts/lib/tracking.inc
Normal file
179
scripts/lib/tracking.inc
Normal file
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* TRACKING FUNCTIONS
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
class tracking {
|
||||
|
||||
static $cName = "";
|
||||
static $disabled = false;
|
||||
static $id = null;
|
||||
static $dbId = null;
|
||||
static $data = array();
|
||||
static $new = false;
|
||||
static $dataMD5;
|
||||
|
||||
/** This function reads the tracking cookie from the user's browser or generates
|
||||
* a value for the tracking cookie if none is found.
|
||||
*/
|
||||
private static function readId() {
|
||||
// Get tracking identifier from cookie if it exists
|
||||
if (isset($_COOKIE[tracking::$cName]) && ctype_alnum($_COOKIE[tracking::$cName]) && strlen($_COOKIE[tracking::$cName]) == 32) {
|
||||
$v = $_COOKIE[tracking::$cName];
|
||||
$q = "SELECT id FROM web_tracking WHERE cookie = '$v'";
|
||||
$qr = dbQuery($q);
|
||||
if (!$qr) {
|
||||
return array(null, false);
|
||||
}
|
||||
if (dbCount($qr) == 1) {
|
||||
return array($v, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new tracking cookie if none exists
|
||||
do {
|
||||
$v = md5(uniqid(rand()));
|
||||
$q = "SELECT id FROM web_tracking WHERE cookie = '$v'";
|
||||
$qr = dbQuery($q);
|
||||
} while ($qr && dbCount($qr));
|
||||
|
||||
// If $qr is null, something went wrong
|
||||
if (!$qr) {
|
||||
l::debug("tracking: query failed for ID '$v'");
|
||||
return array(null, false);
|
||||
}
|
||||
|
||||
return array($v, true);
|
||||
}
|
||||
|
||||
|
||||
/** This function inserts tracking data into the database's web_tracking table. */
|
||||
private static function createData() {
|
||||
// Look for tracking records from the same IP/agent combination in the last
|
||||
// 10 minutes that have been used for less than 10 seconds
|
||||
$q = "SELECT COUNT(*) FROM web_tracking WHERE ip_addr = '" . $_SERVER['REMOTE_ADDR']
|
||||
. "' AND browser = '".addslashes($_SERVER['HTTP_USER_AGENT'])."'"
|
||||
. " AND UNIX_TIMESTAMP(now()) - created < 600"
|
||||
. " AND last_used - created < 10";
|
||||
$r = dbQuery($q);
|
||||
list($c) = dbFetchArray($r);
|
||||
|
||||
// If more than 5 recent unused records are found, tracking is disabled.
|
||||
if ($c > 5) {
|
||||
tracking::$disabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
$q = "INSERT INTO web_tracking(cookie,created,last_used,ip_addr,"
|
||||
. "browser,stored_data) VALUES ('" . tracking::$id
|
||||
. "',unix_timestamp(now()),unix_timestamp(now()),'".$_SERVER['REMOTE_ADDR']."','"
|
||||
. addslashes($_SERVER['HTTP_USER_AGENT']) . "',"
|
||||
. "'a:0:{}')";
|
||||
return dbQuery($q);
|
||||
}
|
||||
|
||||
|
||||
/** This function updates a tracking entry's last access timestamp. */
|
||||
private static function updateAccess() {
|
||||
$q = "UPDATE web_tracking SET last_used=unix_timestamp(now()) WHERE cookie='" . tracking::$id . "'";
|
||||
return dbQuery($q);
|
||||
}
|
||||
|
||||
|
||||
/** This function reads tracking data from the web_tracking table and stores it
|
||||
* in the tracking::$data variable.
|
||||
*/
|
||||
private static function readData() {
|
||||
$trackId = tracking::$id;
|
||||
|
||||
$q = "SELECT stored_data,id FROM web_tracking WHERE cookie = '$trackId' FOR UPDATE";
|
||||
$qr = dbQuery($q);
|
||||
if (!$qr || dbCount($qr) != 1) {
|
||||
l::notice("Tracking data not found for cookie '$trackId'");
|
||||
return false;
|
||||
}
|
||||
|
||||
$tmp = dbFetchArray($qr);
|
||||
$trackData = unserialize($tmp[0]);
|
||||
$trackDBId = $tmp[1];
|
||||
if (!is_array($trackData)) {
|
||||
// Make sure we delete the tracking data that caused the problem
|
||||
l::notice("Invalid tracking data for '$trackId'");
|
||||
l::debug("DB id= $trackDBId, data type= '" . gettype($trackData) . "'");
|
||||
l::info("Moving entry out of the way");
|
||||
//dbQuery("DELETE FROM web_tracking WHERE id=$trackDBId");
|
||||
dbQuery("UPDATE web_tracking SET cookie='DISABLED $trackDBId' WHERE id=$trackDBId");
|
||||
dbEnd();
|
||||
} else {
|
||||
tracking::$dbId = $trackDBId;
|
||||
tracking::$data = $trackData;
|
||||
tracking::$dataMD5 = md5($tmp[0]);
|
||||
}
|
||||
|
||||
return is_array($trackData);
|
||||
}
|
||||
|
||||
|
||||
/** This function initializes the tracking system */
|
||||
static function init() {
|
||||
tracking::$cName = config::getParam('trackname');
|
||||
|
||||
list($trackId,$trackNew) = tracking::readId();
|
||||
if (handler::$h->noTracking && (is_null($trackId) || $trackNew)) {
|
||||
tracking::$disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($trackId)) {
|
||||
l::fatal(2);
|
||||
}
|
||||
|
||||
tracking::$id = $trackId;
|
||||
tracking::$new = $trackNew;
|
||||
|
||||
if ($trackNew && !tracking::createData()) {
|
||||
l::fatal(3);
|
||||
} elseif (!$trackNew && !tracking::updateAccess()) {
|
||||
l::fatal(4);
|
||||
}
|
||||
|
||||
if (tracking::$disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tracking::readData()) {
|
||||
setcookie(tracking::$cName, $trackId, time() + 31536000, dirname($_SERVER['SCRIPT_NAME']));
|
||||
} else {
|
||||
$trackDBId = tracking::$dbId;
|
||||
l::fatal(5, "Tracking data: ID='$trackId',DB ID=$trackDBId" . ($trackNew ? ",new" : ""));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This function updates the web_tracking table using the serialized contents
|
||||
* of tracking::$data
|
||||
*/
|
||||
static function store() {
|
||||
if (is_null(tracking::$dbId)) {
|
||||
if (tracking::$disabled) {
|
||||
return 1;
|
||||
}
|
||||
l::warn("storeTrackingData: database identifier is null");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$serialized = serialize(tracking::$data);
|
||||
if (self::$dataMD5 != md5($serialized)) {
|
||||
$txt = pg_escape_string(serialize(tracking::$data));
|
||||
$q = "UPDATE web_tracking SET last_used=unix_timestamp(now()),stored_data='$txt' WHERE id='" . tracking::$dbId . "'";
|
||||
} else {
|
||||
$q = "UPDATE web_tracking SET last_used=unix_timestamp(now()) WHERE id='" . tracking::$dbId . "'";
|
||||
}
|
||||
return dbQuery($q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
48
scripts/lib/version.inc
Normal file
48
scripts/lib/version.inc
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* VERSION MANAGEMENT
|
||||
****************************************************************************/
|
||||
|
||||
class version {
|
||||
|
||||
function __construct($id, $playable, $text) {
|
||||
$this->id = $id;
|
||||
$this->playable = $playable;
|
||||
$this->text = $text;
|
||||
$this->tickDefs = array();
|
||||
}
|
||||
|
||||
function addTickDefinition($def) {
|
||||
if (is_null($this->tickDefs[$def->script])) {
|
||||
$this->tickDefs[$def->script] = $def;
|
||||
}
|
||||
}
|
||||
|
||||
function getTickDefinition($id) {
|
||||
return $this->tickDefs[$id];
|
||||
}
|
||||
|
||||
function getDirectory() {
|
||||
return config::$main['scriptdir'] . "/game/{$this->id}";
|
||||
}
|
||||
|
||||
function getSiteDirectory() {
|
||||
return config::$main['scriptdir'] . "/site/{$this->id}";
|
||||
}
|
||||
|
||||
function loadActionsClass() {
|
||||
$cn = "actions_{$this->id}";
|
||||
loader::load($this->getDirectory() . "/actions.inc", $cn);
|
||||
return $cn;
|
||||
}
|
||||
|
||||
function loadAction($action) {
|
||||
$cn = "{$this->id}_{$action}";
|
||||
loader::load($this->getDirectory() . "/actions/$action.inc", $cn);
|
||||
|
||||
return $cn;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
376
scripts/lib/xml_config.inc
Normal file
376
scripts/lib/xml_config.inc
Normal file
|
@ -0,0 +1,376 @@
|
|||
<?php
|
||||
|
||||
/****************************************************************************
|
||||
* XML CONFIGURATION PARSER
|
||||
****************************************************************************/
|
||||
|
||||
class xml_config {
|
||||
|
||||
private static $mVersion = null;
|
||||
private static $mGame = null;
|
||||
private static $versions = null;
|
||||
private static $games = null;
|
||||
private static $defGame = null;
|
||||
|
||||
private function parseMainParams($root) {
|
||||
$node = $root->firstChild;
|
||||
|
||||
while ($node) {
|
||||
if ($node->nodeType == XML_ELEMENT_NODE && $node->nodeName == 'Param') {
|
||||
$aName = $node->getAttribute('name');
|
||||
$aValue = $node->getAttribute('value');
|
||||
if ($aName == "") {
|
||||
l::warn("CONFIG: a main parameter is missing a 'name' attribute");
|
||||
} elseif (is_null(xml_config::$mGame->params[$aName])) {
|
||||
xml_config::$mGame->params[$aName] = $aValue;
|
||||
} else {
|
||||
l::notice("CONFIG: duplicate main parameter '$aName'");
|
||||
}
|
||||
} elseif ($node->nodeType == XML_ELEMENT_NODE) {
|
||||
l::notice("CONFIG: found unexpected tag '{$node->nodeName}' in MainParams section");
|
||||
}
|
||||
$node = $node->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function parseMainTicks($root) {
|
||||
$node = $root->firstChild;
|
||||
|
||||
while ($node) {
|
||||
if ($node->nodeType == XML_ELEMENT_NODE && $node->nodeName == 'Tick') {
|
||||
$tScript = $node->getAttribute('script');
|
||||
$tFirst = $node->getAttribute('first');
|
||||
$tInterval = $node->getAttribute('interval');
|
||||
$tLast = $node->getAttribute('last');
|
||||
if ($tScript == "" || $tFirst == "" || $tInterval == "") {
|
||||
l::warn("CONFIG: a main tick declaration is missing a mandatory attribute");
|
||||
} elseif ((int)$tInterval == 0) {
|
||||
l::warn("CONFIG: invalid interval for main tick '$tScript'");
|
||||
} elseif (is_null(xml_config::$mVersion->getTickDefinition($tScript))) {
|
||||
new tick_definition(xml_config::$mVersion, $tScript, false);
|
||||
new tick_instance(xml_config::$mGame, $tScript, (int)$tFirst,
|
||||
(int)$tInterval, $tLast ? (int)$tLast : null);
|
||||
} else {
|
||||
l::notice("CONFIG: duplicate main tick '$tScript'");
|
||||
}
|
||||
} elseif ($node->nodeType == XML_ELEMENT_NODE) {
|
||||
l::notice("CONFIG: found unexpected tag '{$node->nodeName}' in MainTicks section");
|
||||
}
|
||||
$node = $node->nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function parseTickDefinition($version, $root) {
|
||||
$script = $root->getAttribute('script');
|
||||
$public = ($root->getAttribute('public') == 1);
|
||||
|
||||
if ($script == "") {
|
||||
l::warn("CONFIG: invalid tick definition for version {$version->id}");
|
||||
return;
|
||||
}
|
||||
if (!is_null($version->getTickDefinition($script))) {
|
||||
l::notice("CONFIG: duplicate tick definition '$script' for version {$version->id}");
|
||||
return;
|
||||
}
|
||||
|
||||
$def = new tick_definition($version, $script, $public);
|
||||
if (!$public) {
|
||||
return;
|
||||
}
|
||||
|
||||
$names = array();
|
||||
$descs = array();
|
||||
$node = $root->firstChild;
|
||||
while ($node) {
|
||||
if ($node->nodeType != XML_ELEMENT_NODE) {
|
||||
$node = $node->nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node->nodeName != "Name" && $node->nodeName != 'Description') {
|
||||
l::warn("CONFIG: unexpected tag '{$node->nodeName}' found in tick definition "
|
||||
. "'{$script}' for version '{$version->id}'");
|
||||
$node = $node->nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
$lang = $node->getAttribute('lang');
|
||||
if ($lang == '') {
|
||||
l::warn("CONFIG: missing language for {$node->nodeName} in tick definition "
|
||||
. "'{$script}' for version '{$version->id}'");
|
||||
$node = $node->nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
$contents = trim($node->textContent);
|
||||
if ($contents == '') {
|
||||
l::warn("CONFIG: missing contents for {$node->nodeName} in tick definition "
|
||||
. "'{$script}' for version '{$version->id}'");
|
||||
$node = $node->nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node->nodeName == 'Name') {
|
||||
$names[$lang] = $contents;
|
||||
} else {
|
||||
$descs[$lang] = $contents;
|
||||
}
|
||||
|
||||
$node = $node->nextSibling;
|
||||
}
|
||||
|
||||
foreach ($names as $lang => $name) {
|
||||
$def->addText($lang, $name, $descs[$lang]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function parseVersion($root) {
|
||||
$id = $root->getAttribute("id");
|
||||
$cp = ($root->getAttribute("playable") == 1);
|
||||
$tx = $root->getAttribute("text");
|
||||
|
||||
if ($id == "" || $tx == "") {
|
||||
l::warn("CONFIG: invalid version definition (missing identifier or text)");
|
||||
return null;
|
||||
} elseif ($id == 'main') {
|
||||
l::warn("CONFIG: invalid version definition (using 'main' identifier)");
|
||||
return null;
|
||||
}
|
||||
|
||||
$version = new version($id, $cp, $tx);
|
||||
|
||||
$node = $root->firstChild;
|
||||
while ($node) {
|
||||
if ($node->nodeType == XML_ELEMENT_NODE && $node->nodeName == 'Tick') {
|
||||
xml_config::parseTickDefinition($version, $node);
|
||||
} elseif ($node->nodeType == XML_ELEMENT_NODE) {
|
||||
l::warn("CONFIG: found unexpected tag '{$node->nodeName}' in MainTicks section");
|
||||
}
|
||||
$node = $node->nextSibling;
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
|
||||
private function parseVersions($root) {
|
||||
$node = $root->firstChild;
|
||||
|
||||
$versions = array();
|
||||
while ($node) {
|
||||
if ($node->nodeType == XML_ELEMENT_NODE && $node->nodeName == 'Version') {
|
||||
$v = xml_config::parseVersion($node);
|
||||
if (!is_null($v)) {
|
||||
if (is_null($versions[$v->id])) {
|
||||
$versions[$v->id] = $v;
|
||||
} else {
|
||||
l::notice("CONFIG: found duplicate definition for version '{$v->id}'");
|
||||
}
|
||||
}
|
||||
} elseif ($node->nodeType == XML_ELEMENT_NODE) {
|
||||
l::notice("CONFIG: found unexpected tag '{$node->nodeName}' in Versions section");
|
||||
}
|
||||
$node = $node->nextSibling;
|
||||
}
|
||||
|
||||
return $versions;
|
||||
}
|
||||
|
||||
|
||||
private function parseGame($root) {
|
||||
$id = $root->getAttribute('id');
|
||||
if ($id == '') {
|
||||
l::warn("CONFIG: game definition is missing an identifier");
|
||||
return null;
|
||||
}
|
||||
if ($id == 'main') {
|
||||
l::warn("CONFIG: game definition with 'main' identifier ignored");
|
||||
return null;
|
||||
}
|
||||
|
||||
$version = $root->getAttribute('version');
|
||||
if (is_null(xml_config::$versions[$version])) {
|
||||
l::warn("CONFIG: game '$id' has unknown version '$version'");
|
||||
return null;
|
||||
}
|
||||
|
||||
$namespace = $root->getAttribute('namespace');
|
||||
if ($namespace == '') {
|
||||
l::warn("CONFIG: no namespace defined for game '$id'");
|
||||
return null;
|
||||
}
|
||||
$text = $root->getAttribute('text');
|
||||
if ($namespace == '') {
|
||||
l::warn("CONFIG: no text for game '$id'");
|
||||
return null;
|
||||
}
|
||||
|
||||
$public = ($root->getAttribute('public') == 1);
|
||||
$canJoin = ($root->getAttribute('canjoin') == 1);
|
||||
|
||||
$game = new game(xml_config::$versions[$version], $id, $namespace, $public, $canJoin, $text);
|
||||
|
||||
$node = $root->firstChild;
|
||||
while ($node) {
|
||||
if ($node->nodeType != XML_ELEMENT_NODE) {
|
||||
$node = $node->nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node->nodeName == 'Param') {
|
||||
$aName = $node->getAttribute('name');
|
||||
$aValue = $node->getAttribute('value');
|
||||
if ($aName == "") {
|
||||
l::warn("CONFIG: a parameter is missing a 'name' attribute for game '$id'");
|
||||
} elseif (is_null($game->params[$aName])) {
|
||||
$game->params[$aName] = $aValue;
|
||||
} else {
|
||||
l::notice("CONFIG: duplicate parameter '$aName'");
|
||||
}
|
||||
} elseif ($node->nodeName == 'Description') {
|
||||
$lang = $node->getAttribute('lang');
|
||||
$contents = trim($node->textContent);
|
||||
if ($lang == "") {
|
||||
l::warn("CONFIG: a description is missing the 'lang' attribute for game '$id'");
|
||||
} elseif ($contents == "") {
|
||||
l::warn("CONFIG: description in language '$lang' has no contents for game '$id'");
|
||||
} elseif (!is_null($game->descriptions[$lang])) {
|
||||
l::notice("CONFIG: description in language '$lang' appears twice for game '$id'");
|
||||
} else {
|
||||
$game->descriptions[$lang] = $contents;
|
||||
}
|
||||
} elseif ($node->nodeName == 'Tick') {
|
||||
$tScript = $node->getAttribute('script');
|
||||
$tFirst = $node->getAttribute('first');
|
||||
$tInterval = $node->getAttribute('interval');
|
||||
$tLast = $node->getAttribute('last');
|
||||
if ($tScript == "" || $tFirst == "" || $tInterval == "") {
|
||||
l::warn("CONFIG: a tick declaration is missing a mandatory attribute "
|
||||
. "for game '$id'");
|
||||
} elseif ((int)$tInterval == 0) {
|
||||
l::warn("CONFIG: invalid interval for tick '$tScript' in game '$id'");
|
||||
} elseif (is_null($game->version->getTickDefinition($tScript))) {
|
||||
l::warn("CONFIG: no definition for tick '$tScript' in game '$id' "
|
||||
. "(version '$version')");
|
||||
} elseif (is_null($game->ticks[$tScript])) {
|
||||
new tick_instance($game, $tScript, (int)$tFirst, (int)$tInterval,
|
||||
$tLast ? (int)$tLast : null);
|
||||
} else {
|
||||
l::notice("CONFIG: duplicate tick initialiser for tick '$tScript' in game '$id'");
|
||||
}
|
||||
} else {
|
||||
l::notice("CONFIG: found unexpected tag '{$node->nodeName}' in game '$id' definition");
|
||||
}
|
||||
|
||||
$node = $node->nextSibling;
|
||||
}
|
||||
|
||||
return $game;
|
||||
}
|
||||
|
||||
|
||||
private function parseGames($root) {
|
||||
$defaultId = $root->getAttribute('default');
|
||||
$games = array();
|
||||
|
||||
$node = $root->firstChild;
|
||||
while ($node) {
|
||||
if ($node->nodeType == XML_ELEMENT_NODE && $node->nodeName == 'Game') {
|
||||
$g = xml_config::parseGame($node);
|
||||
if (!is_null($g)) {
|
||||
if (is_null($games[$g->name])) {
|
||||
$games[$g->name] = $g;
|
||||
} else {
|
||||
l::notice("CONFIG: found duplicate definition for game '{$v->name}'");
|
||||
}
|
||||
}
|
||||
} elseif ($node->nodeType == XML_ELEMENT_NODE) {
|
||||
l::notice("CONFIG: found unexpected tag '{$node->nodeName}' in Games section");
|
||||
}
|
||||
$node = $node->nextSibling;
|
||||
}
|
||||
|
||||
if (count($games) && ($defaultId == '' || is_null($games[$defaultId]))) {
|
||||
$defaultId = $games[0]->name;
|
||||
l::notice("CONFIG: no default game, using '$defaultId'");
|
||||
}
|
||||
xml_config::$defGame = $defaultId;
|
||||
|
||||
return $games;
|
||||
}
|
||||
|
||||
|
||||
function parse($xmlData) {
|
||||
$doc = new DOMDocument();
|
||||
if (!$doc->loadXML($xmlData)) {
|
||||
l::error("CONFIG: error while parsing XML configuration");
|
||||
return false;
|
||||
}
|
||||
|
||||
xml_config::$mVersion = new version('main', false, 'Legacy Worlds');
|
||||
xml_config::$mGame = new game(xml_config::$mVersion, 'main', '', false, false, 'Legacy Worlds');
|
||||
xml_config::$versions = null;
|
||||
xml_config::$games = null;
|
||||
xml_config::$defGame = null;
|
||||
|
||||
$root = $doc->documentElement;
|
||||
$node = $root->firstChild;
|
||||
while ($node) {
|
||||
if ($node->nodeType == XML_ELEMENT_NODE) {
|
||||
switch ($node->nodeName) :
|
||||
case 'MainParams':
|
||||
xml_config::parseMainParams($node);
|
||||
break;
|
||||
|
||||
case 'MainTicks':
|
||||
xml_config::parseMainTicks($node);
|
||||
break;
|
||||
|
||||
case 'Versions':
|
||||
if (is_array(xml_config::$versions)) {
|
||||
l::notice("CONFIG: found duplicate set of version definitions");
|
||||
} else {
|
||||
xml_config::$versions = xml_config::parseVersions($node);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Games':
|
||||
if (is_array(xml_config::$games)) {
|
||||
l::notice("CONFIG: found duplicate set of game definitions");
|
||||
} elseif (is_array(xml_config::$versions)) {
|
||||
xml_config::$games = xml_config::parseGames($node);
|
||||
} else {
|
||||
l::notice("CONFIG: game definitions found before version "
|
||||
. "definitions, ignored");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
l::notice("CONFIG: found unknown tag '{$node->nodeName}' at toplevel");
|
||||
break;
|
||||
endswitch;
|
||||
}
|
||||
$node = $node->nextSibling;
|
||||
}
|
||||
|
||||
if (!is_array(xml_config::$versions) || count(xml_config::$versions) == 0) {
|
||||
l::error("CONFIG: no versions have been defined!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array(xml_config::$games) || count(xml_config::$games) == 0) {
|
||||
l::error("CONFIG: no games have been defined!");
|
||||
return false;
|
||||
}
|
||||
|
||||
xml_config::$versions['main'] = xml_config::$mVersion;
|
||||
xml_config::$games['main'] = xml_config::$mGame;
|
||||
return new config(xml_config::$versions, xml_config::$games, xml_config::$defGame);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
Loading…
Add table
Add a link
Reference in a new issue