diff --git a/.editorconfig b/.editorconfig index 23cc4257..2ec19b78 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,5 +12,8 @@ insert_final_newline = true [*.md] trim_trailing_whitespace = false -[composer.json] -indent_style = space \ No newline at end of file +[{composer.json,package.json}] +indent_style = space + +[package.json] +indent_size = 2 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 032840e4..3ce9fb84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,19 @@ Thumbs.db .DS_Store .idea -tmp # composer composer.lock vendor +# npm +node_modules + +# created by release.sh releases +tmp + config.local.php -PERSONAL_NOTES # all custom templates templates/* @@ -35,6 +39,10 @@ system/logs/* system/data/* !system/data/index.html +# php sessions +system/php_sessions/* +!system/php_sessions/index.html + # plugins plugins/* !plugins/.htaccess @@ -42,7 +50,6 @@ plugins/* !plugins/account-create-hint.json !plugins/account-create-hint landing -/login.php # system system/functions_custom.php diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 00000000..dd7aafdd --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,14 @@ +# automatically exported using this script: +# git log --all --format='%cN <%cE>' | sort -u > contributors +# in no particular order +# cleaned for readability + +Evil Puncker +Fernando Matos +Lee <42119604+Leesneaks@users.noreply.github.com> +caio +slawkens +tobi132 <52947952+tobi132@users.noreply.github.com> +vankk +whiteblXK +xitobuh diff --git a/CREDITS b/CREDITS index 50461327..f727b661 100644 --- a/CREDITS +++ b/CREDITS @@ -1,2 +1,3 @@ * Gesior.pl (2007 - 2008) -* Slawkens (2009 - 2020) +* Slawkens (2009 - 2021) +* Contributors listed in CONTRIBUTORS.txt diff --git a/admin/template/template.php b/admin/template/template.php index ff47b671..407fd8ac 100644 --- a/admin/template/template.php +++ b/admin/template/template.php @@ -175,6 +175,16 @@ echo $content; } ?> +display('admin-bar.html.twig', [ + 'username' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId() + ]); +} +?> diff --git a/common.php b/common.php index ea0b2bc9..2f14939e 100644 --- a/common.php +++ b/common.php @@ -24,68 +24,70 @@ * @link https://my-aac.org */ if (version_compare(phpversion(), '7.1', '<')) die('PHP version 7.1 or higher is required.'); -session_start(); -define('MYAAC', true); -define('MYAAC_VERSION', '0.9.0-dev'); -define('DATABASE_VERSION', 32); -define('TABLE_PREFIX', 'myaac_'); +const MYAAC = true; +const MYAAC_VERSION = '0.9.0-dev'; +const DATABASE_VERSION = 32; +const TABLE_PREFIX = 'myaac_'; define('START_TIME', microtime(true)); define('MYAAC_OS', stripos(PHP_OS, 'WIN') === 0 ? 'WINDOWS' : (strtoupper(PHP_OS) === 'DARWIN' ? 'MAC' : 'LINUX')); define('IS_CLI', in_array(php_sapi_name(), ['cli', 'phpdb'])); // account flags -define('FLAG_ADMIN', 1); -define('FLAG_SUPER_ADMIN', 2); -define('FLAG_CONTENT_PAGES', 4); -define('FLAG_CONTENT_MAILER', 8); -define('FLAG_CONTENT_NEWS', 16); -define('FLAG_CONTENT_FORUM', 32); -define('FLAG_CONTENT_COMMANDS', 64); -define('FLAG_CONTENT_SPELLS', 128); -define('FLAG_CONTENT_MONSTERS', 256); -define('FLAG_CONTENT_GALLERY', 512); -define('FLAG_CONTENT_VIDEOS', 1024); -define('FLAG_CONTENT_FAQ', 2048); -define('FLAG_CONTENT_MENUS', 4096); -define('FLAG_CONTENT_PLAYERS', 8192); +const FLAG_ADMIN = 1; +const FLAG_SUPER_ADMIN = 2; +const FLAG_CONTENT_PAGES = 4; +const FLAG_CONTENT_MAILER = 8; +const FLAG_CONTENT_NEWS = 16; +const FLAG_CONTENT_FORUM = 32; +const FLAG_CONTENT_COMMANDS = 64; +const FLAG_CONTENT_SPELLS = 128; +const FLAG_CONTENT_MONSTERS = 256; +const FLAG_CONTENT_GALLERY = 512; +const FLAG_CONTENT_VIDEOS = 1024; +const FLAG_CONTENT_FAQ = 2048; +const FLAG_CONTENT_MENUS = 4096; +const FLAG_CONTENT_PLAYERS = 8192; // news -define('NEWS', 1); -define('TICKER', 2); -define('ARTICLE', 3); +const NEWS = 1; +const TICKER = 2; +const ARTICLE = 3; // directories -define('BASE', __DIR__ . '/'); -define('ADMIN', BASE . 'admin/'); -define('SYSTEM', BASE . 'system/'); -define('CACHE', SYSTEM . 'cache/'); -define('LOCALE', SYSTEM . 'locale/'); -define('LIBS', SYSTEM . 'libs/'); -define('LOGS', SYSTEM . 'logs/'); -define('PAGES', SYSTEM . 'pages/'); -define('PLUGINS', BASE . 'plugins/'); -define('TEMPLATES', BASE . 'templates/'); -define('TOOLS', BASE . 'tools/'); -define('VENDOR', BASE . 'vendor/'); +const BASE = __DIR__ . '/'; +const ADMIN = BASE . 'admin/'; +const SYSTEM = BASE . 'system/'; +const CACHE = SYSTEM . 'cache/'; +const LOCALE = SYSTEM . 'locale/'; +const LIBS = SYSTEM . 'libs/'; +const LOGS = SYSTEM . 'logs/'; +const PAGES = SYSTEM . 'pages/'; +const PLUGINS = BASE . 'plugins/'; +const TEMPLATES = BASE . 'templates/'; +const TOOLS = BASE . 'tools/'; +const VENDOR = BASE . 'vendor/'; // menu categories -define('MENU_CATEGORY_NEWS', 1); -define('MENU_CATEGORY_ACCOUNT', 2); -define('MENU_CATEGORY_COMMUNITY', 3); -define('MENU_CATEGORY_FORUM', 4); -define('MENU_CATEGORY_LIBRARY', 5); -define('MENU_CATEGORY_SHOP', 6); +const MENU_CATEGORY_NEWS = 1; +const MENU_CATEGORY_ACCOUNT = 2; +const MENU_CATEGORY_COMMUNITY = 3; +const MENU_CATEGORY_FORUM = 4; +const MENU_CATEGORY_LIBRARY = 5; +const MENU_CATEGORY_SHOP = 6; // otserv versions -define('OTSERV', 1); -define('OTSERV_06', 2); -define('OTSERV_FIRST', OTSERV); -define('OTSERV_LAST', OTSERV_06); -define('TFS_02', 3); -define('TFS_03', 4); -define('TFS_FIRST', TFS_02); -define('TFS_LAST', TFS_03); +const OTSERV = 1; +const OTSERV_06 = 2; +const OTSERV_FIRST = OTSERV; +const OTSERV_LAST = OTSERV_06; +const TFS_02 = 3; +const TFS_03 = 4; +const TFS_FIRST = TFS_02; +const TFS_LAST = TFS_03; + +session_save_path(SYSTEM . 'php_sessions'); +session_start(); // basedir $basedir = ''; diff --git a/composer.json b/composer.json index f5c67e7d..0999b420 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,14 @@ { "require": { - "php": ">=7.1", - "ext-dom": "*", - "ext-json": "*", - "ext-gd": "*", + "php": "^7.2.5 || ^8.0", "ext-pdo": "*", "ext-pdo_mysql": "*", + "ext-json": "*", "ext-xml": "*", - "ext-zip": "*", + "ext-dom": "*", "phpmailer/phpmailer": "^6.1", "composer/semver": "^3.2", - "twig/twig": "~1.42.5", + "twig/twig": "^1.0", "erusev/parsedown": "^1.7" } } diff --git a/index.php b/index.php index 3bb96435..3ce40bb5 100644 --- a/index.php +++ b/index.php @@ -369,6 +369,14 @@ if($config['backward_support']) { $topic = $title; } +/** + * @var OTS_Account $account_logged + */ +if ($logged && admin()) { + $content .= $twig->render('admin-bar.html.twig', [ + 'username' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId() + ]); +} $title_full = (isset($title) ? $title . ' - ' : '') . $config['lua']['serverName']; require $template_path . '/' . $template_index; diff --git a/install/includes/twig_error.html b/install/includes/twig_error.html index 933a36c1..40daedec 100644 --- a/install/includes/twig_error.html +++ b/install/includes/twig_error.html @@ -2,10 +2,10 @@ We have detected that you don't have access to write to the system/cache directo \ No newline at end of file + diff --git a/install/steps/3-requirements.php b/install/steps/3-requirements.php index 65fa0c18..cd727f0d 100644 --- a/install/steps/3-requirements.php +++ b/install/steps/3-requirements.php @@ -12,10 +12,11 @@ $dirs_optional = [ ]; $extensions_required = [ - 'json', 'pdo', 'pdo_mysql', 'xml', 'zip' + 'pdo', 'pdo_mysql', 'json', 'xml' ]; $extensions_optional = [ 'gd' => $locale['step_requirements_warning_player_signatures'], + 'zip' => $locale['step_requirements_warning_install_plugins'], ]; /* * diff --git a/login.php b/login.php new file mode 100644 index 00000000..96acd48d --- /dev/null +++ b/login.php @@ -0,0 +1,285 @@ +getAttribute('startdate'); + return date_create("{$date}")->format('U'); + } else { + $date = $table1->getAttribute('enddate'); + return date_create("{$date}")->format('U'); + } + } else { + foreach($table1 as $attr) { + if ($attr) { + return $attr->getAttribute($table2); + } + } + } + } + return 'error'; +} + +$request = json_decode(file_get_contents('php://input')); +$action = $request->type ?? ''; + +/** @var OTS_Base_DB $db */ +/** @var array $config */ + +switch ($action) { + case 'cacheinfo': + $playersonline = $db->query("select count(*) from `players_online`")->fetchAll(); + die(json_encode([ + 'playersonline' => (intval($playersonline[0][0])), + 'twitchstreams' => 0, + 'twitchviewer' => 0, + 'gamingyoutubestreams' => 0, + 'gamingyoutubeviewer' => 0 + ])); + + case 'eventschedule': + $eventlist = []; + $file_path = config('server_path') . 'data/XML/events.xml'; + if (!file_exists($file_path)) { + die(json_encode([])); + } + $xml = new DOMDocument; + $xml->load($file_path); + $tmplist = []; + $tableevent = $xml->getElementsByTagName('event'); + + foreach ($tableevent as $event) { + if ($event) { $tmplist = [ + 'colorlight' => parseEvent($event->getElementsByTagName('colors'), false, 'colorlight'), + 'colordark' => parseEvent($event->getElementsByTagName('colors'), false, 'colordark'), + 'description' => parseEvent($event->getElementsByTagName('description'), false, 'description'), + 'displaypriority' => intval(parseEvent($event->getElementsByTagName('details'), false, 'displaypriority')), + 'enddate' => intval(parseEvent($event, true, false)), + 'isseasonal' => getBoolean(intval(parseEvent($event->getElementsByTagName('details'), false, 'isseasonal'))), + 'name' => $event->getAttribute('name'), + 'startdate' => intval(parseEvent($event, true, true)), + 'specialevent' => intval(parseEvent($event->getElementsByTagName('details'), false, 'specialevent')) + ]; + $eventlist[] = $tmplist; } } + die(json_encode(['eventlist' => $eventlist, 'lastupdatetimestamp' => time()])); + + case 'boostedcreature': + $boostDB = $db->query("select * from " . $db->tableName('boosted_creature'))->fetchAll(); + foreach ($boostDB as $Tableboost) { + die(json_encode([ + 'boostedcreature' => true, + 'raceid' => intval($Tableboost['raceid']) + ])); + } + break; + + case 'login': + + $port = $config['lua']['gameProtocolPort']; + + // default world info + $world = [ + 'id' => 0, + 'name' => $config['lua']['serverName'], + 'externaladdress' => $config['lua']['ip'], + 'externalport' => $port, + 'externaladdressprotected' => $config['lua']['ip'], + 'externalportprotected' => $port, + 'externaladdressunprotected' => $config['lua']['ip'], + 'externalportunprotected' => $port, + 'previewstate' => 0, + 'location' => 'BRA', // BRA, EUR, USA + 'anticheatprotection' => false, + 'pvptype' => array_search($config['lua']['worldType'], ['pvp', 'no-pvp', 'pvp-enforced']), + 'istournamentworld' => false, + 'restrictedstore' => false, + 'currenttournamentphase' => 2 + ]; + + $characters = []; + $account = new OTS_Account(); + + $inputEmail = $request->email ?? false; + $inputAccountName = $request->accountname ?? false; + $inputToken = $request->token ?? false; + + if ($inputEmail != false) { // login by email + $account->findByEmail($request->email); + } + else if($inputAccountName != false) { // login by account name + $account->find($inputAccountName); + } + + $config_salt_enabled = fieldExist('salt', 'accounts'); + $current_password = encrypt(($config_salt_enabled ? $account->getCustomField('salt') : '') . $request->password); + + if (!$account->isLoaded() || $account->getPassword() != $current_password) { + sendError(($inputEmail != false ? 'Email' : 'Account name') . ' or password is not correct.'); + } + + //log_append('test.log', var_export($account->getCustomField('secret'), true)); + $accountHasSecret = false; + if (fieldExist('secret', 'accounts')) { + $accountSecret = $account->getCustomField('secret'); + if ($accountSecret != null && $accountSecret != '') { + $accountHasSecret = true; + if ($inputToken === false) { + sendError('Submit a valid two-factor authentication token.', 6); + } else { + require_once LIBS . 'rfc6238.php'; + if (TokenAuth6238::verify($accountSecret, $inputToken) !== true) { + sendError('Two-factor authentication failed, token is wrong.', 6); + } + } + } + } + + // common columns + $columns = 'id, name, level, sex, vocation, looktype, lookhead, lookbody, looklegs, lookfeet, lookaddons'; + + if (fieldExist('isreward', 'accounts')) { + $columns .= ', isreward'; + } + + if (fieldExist('istutorial', 'accounts')) { + $columns .= ', istutorial'; + } + + $players = $db->query("select {$columns} from players where account_id = " . $account->getId() . " AND deletion = 0"); + if($players && $players->rowCount() > 0) { + $players = $players->fetchAll(); + + $highestLevelId = 0; + $highestLevel = 0; + foreach ($players as $player) { + if ($player['level'] >= $highestLevel) { + $highestLevel = $player['level']; + $highestLevelId = $player['id']; + } + } + + foreach ($players as $player) { + $characters[] = create_char($player, $highestLevelId); + } + } + + if (fieldExist('premdays', 'accounts') && fieldExist('lastday', 'accounts')) { + $save = false; + $timeNow = time(); + $query = $db->query("select `premdays`, `lastday` from `accounts` where `id` = " . $account->getId()); + if ($query->rowCount() > 0) { + $query = $query->fetch(); + $premDays = (int)$query['premdays']; + $lastDay = (int)$query['lastday']; + $lastLogin = $lastDay; + } else { + sendError("Error while fetching your account data. Please contact admin."); + } + if ($premDays != 0 && $premDays != PHP_INT_MAX) { + if ($lastDay == 0) { + $lastDay = $timeNow; + $save = true; + } else { + $days = (int)(($timeNow - $lastDay) / 86400); + if ($days > 0) { + if ($days >= $premDays) { + $premDays = 0; + $lastDay = 0; + } else { + $premDays -= $days; + $reminder = ($timeNow - $lastDay) % 86400; + $lastDay = $timeNow - $reminder; + } + + $save = true; + } + } + } else if ($lastDay != 0) { + $lastDay = 0; + $save = true; + } + if ($save) { + $db->query("update `accounts` set `premdays` = " . $premDays . ", `lastday` = " . $lastDay . " where `id` = " . $account->getId()); + } + } + + $worlds = [$world]; + $playdata = compact('worlds', 'characters'); + + $sessionKey = ($inputEmail !== false) ? $inputEmail : $inputAccountName; // email or account name + $sessionKey .= "\n" . $request->password; // password + if (!fieldExist('istutorial', 'players')) { + $sessionKey .= "\n"; + } + $sessionKey .= ($accountHasSecret && strlen($accountSecret) > 5) ? $inputToken : ''; + + // this is workaround to distinguish between TFS 1.x and otservbr + // TFS 1.x requires the number in session key + // otservbr requires just login and password + // so we check for istutorial field which is present in otservbr, and not in TFS + if (!fieldExist('istutorial', 'players')) { + $sessionKey .= "\n".floor(time() / 30); + } + + //log_append('slaw.log', $sessionKey); + + $session = [ + 'sessionkey' => $sessionKey, + 'lastlogintime' => 0, + 'ispremium' => $config['lua']['freePremium'] || $account->isPremium(), + 'premiumuntil' => ($account->getPremDays()) > 0 ? (time() + ($account->getPremDays() * 86400)) : 0, + 'status' => 'active', // active, frozen or suspended + 'returnernotification' => false, + 'showrewardnews' => true, + 'isreturner' => true, + 'fpstracking' => false, + 'optiontracking' => false, + 'tournamentticketpurchasestate' => 0, + 'emailcoderequest' => false + ]; + die(json_encode(compact('session', 'playdata'))); + + default: + sendError("Unrecognized event {$action}."); + break; +} + +function create_char($player, $highestLevelId) { + global $config; + return [ + 'worldid' => 0, + 'name' => $player['name'], + 'ismale' => intval($player['sex']) === 1, + 'tutorial' => isset($player['istutorial']) && $player['istutorial'], + 'level' => intval($player['level']), + 'vocation' => $config['vocations'][$player['vocation']], + 'outfitid' => intval($player['looktype']), + 'headcolor' => intval($player['lookhead']), + 'torsocolor' => intval($player['lookbody']), + 'legscolor' => intval($player['looklegs']), + 'detailcolor' => intval($player['lookfeet']), + 'addonsflags' => intval($player['lookaddons']), + 'ishidden' => isset($player['deletion']) && (int)$player['deletion'] === 1, + 'istournamentparticipant' => false, + 'ismaincharacter' => $highestLevelId == $player['id'], + 'dailyrewardstate' => isset($player['isreward']) ? intval($player['isreward']) : 0, + 'remainingdailytournamentplaytime' => 0 + ]; +} diff --git a/system/functions.php b/system/functions.php index b1f4e77f..fbed8384 100644 --- a/system/functions.php +++ b/system/functions.php @@ -462,7 +462,7 @@ function tickers() */ function template_place_holder($type) { - global $template_place_holders; + global $twig, $template_place_holders; $ret = ''; if(array_key_exists($type, $template_place_holders) && is_array($template_place_holders[$type])) @@ -471,6 +471,9 @@ function template_place_holder($type) if($type === 'head_start') { $ret .= template_header(); } + elseif ($type === 'body_start') { + $ret .= $twig->render('browsehappy.html.twig'); + } elseif($type === 'body_end') { $ret .= template_ga_code(); } diff --git a/system/libs/CreateCharacter.php b/system/libs/CreateCharacter.php index 5aa87ad4..6eb4771b 100644 --- a/system/libs/CreateCharacter.php +++ b/system/libs/CreateCharacter.php @@ -12,27 +12,44 @@ class CreateCharacter { /** - * @param string $name - * @param int $sex - * @param int $vocation - * @param int $town - * @param array $errors + * @param $name + * @param $errors * @return bool */ - public function check($name, $sex, &$vocation, &$town, &$errors) { + public function checkName($name, &$errors) + { $minLength = config('character_name_min_length'); $maxLength = config('character_name_max_length'); - if(empty($name)) + if(empty($name)) { $errors['name'] = 'Please enter a name for your character!'; - else if(strlen($name) > $maxLength) - $errors['name'] = 'Name is too long. Max. length '.$maxLength.' letters.'; - else if(strlen($name) < $minLength) - $errors['name'] = 'Name is too short. Min. length '.$minLength.' letters.'; - else { - if(!admin() && !Validator::newCharacterName($name)) { - $errors['name'] = Validator::getLastError(); - } + return false; + } + + if(strlen($name) > $maxLength) { + $errors['name'] = 'Name is too long. Max. length ' . $maxLength . ' letters.'; + return false; + } + + if(strlen($name) < $minLength) { + $errors['name'] = 'Name is too short. Min. length ' . $minLength . ' letters.'; + return false; + } + + $name_length = strlen($name); + if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- '") != $name_length) { + $errors['name'] = 'This name contains invalid letters, words or format. Please use only a-Z, - , \' and space.'; + return false; + } + + if(!preg_match("/[A-z ']/", $name)) { + $errors['name'] = 'Your name contains illegal characters.'; + return false; + } + + if(!admin() && !Validator::newCharacterName($name)) { + $errors['name'] = Validator::getLastError(); + return false; } $player = new OTS_Player(); @@ -42,20 +59,38 @@ class CreateCharacter return false; } - if(empty($sex) && $sex != "0") + return empty($errors); + } + + /** + * @param string $name + * @param int $sex + * @param int $vocation + * @param int $town + * @param array $errors + * @return bool + */ + public function check($name, $sex, &$vocation, &$town, &$errors) + { + $this->checkName($name, $errors); + + if(empty($sex) && $sex != "0") { $errors['sex'] = 'Please select the sex for your character!'; + } if(count(config('character_samples')) > 1) { if(!isset($vocation)) $errors['vocation'] = 'Please select a vocation for your character.'; } - else + else { $vocation = config('character_samples')[0]; + } if(count(config('character_towns')) > 1) { - if(!isset($town)) + if(!isset($town)) { $errors['town'] = 'Please select a town for your character.'; + } } else { $town = config('character_towns')[0]; diff --git a/system/libs/pot/OTS_Base_DB.php b/system/libs/pot/OTS_Base_DB.php index e6ffa9be..21902b9c 100644 --- a/system/libs/pot/OTS_Base_DB.php +++ b/system/libs/pot/OTS_Base_DB.php @@ -83,10 +83,10 @@ abstract class OTS_Base_DB extends PDO implements IOTS_DB $startTime = microtime(true); } - $ret = parent::query(...$args);; + $ret = parent::query(...$args); if($this->logged) { $totalTime = microtime(true) - $startTime; - $this->log .= round($totalTime, 4) . ' ms - ' . $query . PHP_EOL; + $this->log .= round($totalTime, 4) . ' ms - ' . $args[0] . PHP_EOL; } return $ret; diff --git a/system/libs/rfc6238.php b/system/libs/rfc6238.php new file mode 100644 index 00000000..579f08f2 --- /dev/null +++ b/system/libs/rfc6238.php @@ -0,0 +1,285 @@ +'0', 'B'=>'1', 'C'=>'2', 'D'=>'3', 'E'=>'4', 'F'=>'5', 'G'=>'6', 'H'=>'7', + 'I'=>'8', 'J'=>'9', 'K'=>'10', 'L'=>'11', 'M'=>'12', 'N'=>'13', 'O'=>'14', 'P'=>'15', + 'Q'=>'16', 'R'=>'17', 'S'=>'18', 'T'=>'19', 'U'=>'20', 'V'=>'21', 'W'=>'22', 'X'=>'23', + 'Y'=>'24', 'Z'=>'25', '2'=>'26', '3'=>'27', '4'=>'28', '5'=>'29', '6'=>'30', '7'=>'31' + ); + + /** + * Use padding false when encoding for urls + * + * @return base32 encoded string + * @author Bryan Ruiz + **/ + public static function encode($input, $padding = true) { + if(empty($input)) return ""; + + $input = str_split($input); + $binaryString = ""; + + for($i = 0; $i < count($input); $i++) { + $binaryString .= str_pad(base_convert(ord($input[$i]), 10, 2), 8, '0', STR_PAD_LEFT); + } + + $fiveBitBinaryArray = str_split($binaryString, 5); + $base32 = ""; + $i=0; + + while($i < count($fiveBitBinaryArray)) { + $base32 .= self::$map[base_convert(str_pad($fiveBitBinaryArray[$i], 5,'0'), 2, 10)]; + $i++; + } + + if($padding && ($x = strlen($binaryString) % 40) != 0) { + if($x == 8) $base32 .= str_repeat(self::$map[32], 6); + else if($x == 16) $base32 .= str_repeat(self::$map[32], 4); + else if($x == 24) $base32 .= str_repeat(self::$map[32], 3); + else if($x == 32) $base32 .= self::$map[32]; + } + + return $base32; + } + + public static function decode($input) { + if(empty($input)) return; + + $paddingCharCount = substr_count($input, self::$map[32]); + $allowedValues = array(6,4,3,1,0); + + if(!in_array($paddingCharCount, $allowedValues)) return false; + + for($i=0; $i<4; $i++){ + if($paddingCharCount == $allowedValues[$i] && + substr($input, -($allowedValues[$i])) != str_repeat(self::$map[32], $allowedValues[$i])) return false; + } + + $input = str_replace('=','', $input); + $input = str_split($input); + $binaryString = ""; + + for($i=0; $i < count($input); $i = $i+8) { + $x = ""; + + if(!in_array($input[$i], self::$map)) return false; + + for($j=0; $j < 8; $j++) { + $x .= str_pad(base_convert(@self::$flippedMap[@$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT); + } + + $eightBits = str_split($x, 8); + + for($z = 0; $z < count($eightBits); $z++) { + $binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:""; + } + } + + return $binaryString; + } +} + +// http://www.faqs.org/rfcs/rfc6238.html +// https://github.com/Voronenko/PHPOTP/blob/08cda9cb9c30b7242cf0b3a9100a6244a2874927/code/rfc6238.php +// Local changes: http -> https, consistent indentation, 200x200 -> 300x300 QR image size, PHP end tag +class TokenAuth6238 { + + /** + * verify + * + * @param string $secretkey Secret clue (base 32). + * @return bool True if success, false if failure + */ + public static function verify($secretkey, $code, $rangein30s = 3) { + $key = base32static::decode($secretkey); + $unixtimestamp = time()/30; + + for($i=-($rangein30s); $i<=$rangein30s; $i++) { + $checktime = (int)($unixtimestamp+$i); + $thiskey = self::oath_hotp($key, $checktime); + + if ((int)$code == self::oath_truncate($thiskey,6)) { + return true; + } + + } + return false; + } + + + public static function getTokenCode($secretkey,$rangein30s = 3) { + $result = ""; + $key = base32static::decode($secretkey); + $unixtimestamp = time()/30; + + for($i=-($rangein30s); $i<=$rangein30s; $i++) { + $checktime = (int)($unixtimestamp+$i); + $thiskey = self::oath_hotp($key, $checktime); + $result = $result." # ".self::oath_truncate($thiskey,6); + } + + return $result; + } + + public static function getTokenCodeDebug($secretkey,$rangein30s = 3) { + $result = ""; + print "
SecretKey: $secretkey
"; + + $key = base32static::decode($secretkey); + print "Key(base 32 decode): $key
"; + + $unixtimestamp = time()/30; + print "UnixTimeStamp (time()/30): $unixtimestamp
"; + + for($i=-($rangein30s); $i<=$rangein30s; $i++) { + $checktime = (int)($unixtimestamp+$i); + print "Calculating oath_hotp from (int)(unixtimestamp +- 30sec offset): $checktime basing on secret key
"; + + $thiskey = self::oath_hotp($key, $checktime, true); + print "======================================================
"; + print "CheckTime: $checktime oath_hotp:".$thiskey."
"; + + $result = $result." # ".self::oath_truncate($thiskey,6,true); + } + + return $result; + } + + public static function getBarCodeUrl($username, $domain, $secretkey, $issuer) { + $url = "https://chart.apis.google.com/chart"; + $url = $url."?chs=300x300&chld=M|0&cht=qr&chl=otpauth://totp/"; + $url = $url.$username . "@" . $domain . "%3Fsecret%3D" . $secretkey . '%26issuer%3D' . rawurlencode($issuer); + return $url; + } + + public static function generateRandomClue($length = 16) { + $b32 = "234567QWERTYUIOPASDFGHJKLZXCVBNM"; + $s = ""; + + for ($i = 0; $i < $length; $i++) + $s .= $b32[rand(0,31)]; + + return $s; + } + + private static function hotp_tobytestream($key) { + $result = array(); + $last = strlen($key); + for ($i = 0; $i < $last; $i = $i + 2) { + $x = $key[$i] + $key[$i + 1]; + $x = strtoupper($x); + $x = hexdec($x); + $result = $result.chr($x); + } + + return $result; + } + + private static function oath_hotp ($key, $counter, $debug=false) { + $result = ""; + $orgcounter = $counter; + $cur_counter = array(0,0,0,0,0,0,0,0); + + if ($debug) { + print "Packing counter $counter (".dechex($counter).")into binary string - pay attention to hex representation of key and binary representation
"; + } + + for($i=7;$i>=0;$i--) { // C for unsigned char, * for repeating to the end of the input data + $cur_counter[$i] = pack ('C*', $counter); + + if ($debug) { + print $cur_counter[$i]."(".dechex(ord($cur_counter[$i])).")"." from $counter
"; + } + + $counter = $counter >> 8; + } + + if ($debug) { + foreach ($cur_counter as $char) { + print ord($char) . " "; + } + + print "
"; + } + + $binary = implode($cur_counter); + + // Pad to 8 characters + str_pad($binary, 8, chr(0), STR_PAD_LEFT); + + if ($debug) { + print "Prior to HMAC calculation pad with zero on the left until 8 characters.
"; + print "Calculate sha1 HMAC(Hash-based Message Authentication Code http://en.wikipedia.org/wiki/HMAC).
"; + print "hash_hmac ('sha1', $binary, $key)
"; + } + + $result = hash_hmac ('sha1', $binary, $key); + + if ($debug) { + print "Result: $result
"; + } + + return $result; + } + + private static function oath_truncate($hash, $length = 6, $debug=false) { + $result=""; + + // Convert to dec + if($debug) { + print "converting hex hash into characters
"; + } + + $hashcharacters = str_split($hash,2); + + if($debug) { + print_r($hashcharacters); + print "
and convert to decimals:
"; + } + + for ($j=0; $j"; + print "offset:".$offset; + } + + $result = ( + (($hmac_result[$offset+0] & 0x7f) << 24 ) | + (($hmac_result[$offset+1] & 0xff) << 16 ) | + (($hmac_result[$offset+2] & 0xff) << 8 ) | + ($hmac_result[$offset+3] & 0xff) + ) % pow(10,$length); + + return $result; + } +} +?> diff --git a/system/libs/validator.php b/system/libs/validator.php index c69f0039..38db7068 100644 --- a/system/libs/validator.php +++ b/system/libs/validator.php @@ -354,16 +354,6 @@ class Validator } } - if(strspn($name, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- '") != $name_length) { - self::$lastError = 'This name contains invalid letters, words or format. Please use only a-Z, - , \' and space.'; - return false; - } - - if(!preg_match("/[A-z ']/", $name)) { - self::$lastError = 'Your name containst illegal characters.'; - return false; - } - return true; } diff --git a/system/locale/en/install.php b/system/locale/en/install.php index f9e75756..e0cbfbff 100644 --- a/system/locale/en/install.php +++ b/system/locale/en/install.php @@ -41,6 +41,7 @@ $locale['step_requirements_extension'] = '$EXTENSION$ PHP extension'; $locale['step_requirements_warning_images_guilds'] = 'Guild logo upload will not work'; $locale['step_requirements_warning_images_gallery'] = 'Gallery image upload will not work'; $locale['step_requirements_warning_player_signatures'] = 'Player Signatures will not work'; +$locale['step_requirements_warning_install_plugins'] = 'It will be not possible to install plugins'; // config $locale['step_config'] = 'Configuration'; diff --git a/system/locale/pl/install.php b/system/locale/pl/install.php index c26a5fc9..5822a4d2 100644 --- a/system/locale/pl/install.php +++ b/system/locale/pl/install.php @@ -41,6 +41,7 @@ $locale['step_requirements_extension'] = 'Rozszerzenie PHP - $EXTENSION$'; $locale['step_requirements_warning_images_guilds'] = 'Nie będzie możliwości uploadu obrazków gildii'; $locale['step_requirements_warning_images_gallery'] = 'Nie będzie możliwości uploadu obrazków do galerii'; $locale['step_requirements_warning_player_signatures'] = 'Sygnatury graczy nie będą działać'; +$locale['step_requirements_warning_install_plugins'] = 'Nie będzie można instalować rozszerzeń'; // config $locale['step_config'] = 'Konfiguracja'; diff --git a/system/pages/account/change_name.php b/system/pages/account/change_name.php index 6771760b..077b2060 100644 --- a/system/pages/account/change_name.php +++ b/system/pages/account/change_name.php @@ -52,6 +52,29 @@ else $old_name = $player->getName(); $player->setName($name); $player->save(); + + if ($db->hasTable('player_deaths') && + $db->hasColumn('player_deaths', 'mostdamage_is_player') && + $db->hasColumn('player_deaths', 'killed_by')) { + + $namesToChange = $db->query('SELECT `player_id`, `time`, `is_player`, `killed_by`, `mostdamage_is_player`, `mostdamage_by` FROM `player_deaths` WHERE (`is_player` = 1 AND `killed_by` = ' . $db->quote($old_name) . ') OR (`mostdamage_is_player` = 1 AND `mostdamage_by` = ' . $db->quote($old_name) . ');'); + + if ($namesToChange->rowCount() > 0) { + foreach ($namesToChange->fetchAll(PDO::FETCH_ASSOC) as $row) { + $changeKey = ''; + if ($row['is_player'] == '1' && $row['killed_by'] == $old_name) { + $changeKey = 'killed_by'; + } else if ($row['mostdamage_is_player'] == '1' && $row['mostdamage_by'] == $old_name) { + $changeKey = 'mostdamage_by'; + } + + if (!empty($changeKey)) { + $db->update('player_deaths', [$changeKey => $name], ['player_id' => $row['player_id'], 'time' => $row['time']]); + } + } + } + } + $account_logged->setCustomField("premium_points", $points - $config['account_change_character_name_points']); $account_logged->logAction('Changed name from ' . $old_name . ' to ' . $player->getName() . '.'); $twig->display('success.html.twig', array( @@ -83,4 +106,4 @@ else } } -?> \ No newline at end of file +?> diff --git a/system/pages/guilds/change_rank.php b/system/pages/guilds/change_rank.php index 7e4fe92c..a4fb7b17 100644 --- a/system/pages/guilds/change_rank.php +++ b/system/pages/guilds/change_rank.php @@ -86,7 +86,7 @@ if($guild_vice) else { $player_in_guild = false; - if($guild->getName() === $player_to_change->getRank()->getGuild()->getName() || $guild_leader) + if($guild->getName() === $player_to_change->getRank()->getGuild()->getName()) { $player_in_guild = true; $player_has_lower_rank = false; diff --git a/system/php_sessions/index.html b/system/php_sessions/index.html new file mode 100644 index 00000000..e69de29b diff --git a/system/templates/admin-bar.html.twig b/system/templates/admin-bar.html.twig new file mode 100644 index 00000000..0825dd27 --- /dev/null +++ b/system/templates/admin-bar.html.twig @@ -0,0 +1,123 @@ + + + diff --git a/system/templates/admin.news.form.html.twig b/system/templates/admin.news.form.html.twig index db98e522..2427429f 100644 --- a/system/templates/admin.news.form.html.twig +++ b/system/templates/admin.news.form.html.twig @@ -104,7 +104,7 @@ $('#select-type').change(function () { var value = $('#select-type').val(); - if (value == {{ constant('ARTICLE') }}) { + if (value === {{ constant('ARTICLE') }}) { $('#article-text').show(); $('#article-image').show(); } else { @@ -118,8 +118,8 @@