diff --git a/common.php b/common.php index 2f14939e..f0927d6b 100644 --- a/common.php +++ b/common.php @@ -86,6 +86,9 @@ const TFS_03 = 4; const TFS_FIRST = TFS_02; const TFS_LAST = TFS_03; +// other definitions +const ACCOUNT_NUMBER_LENGTH = 10; + session_save_path(SYSTEM . 'php_sessions'); session_start(); diff --git a/config.php b/config.php index 58ef4b46..a4fa1ac3 100644 --- a/config.php +++ b/config.php @@ -99,6 +99,8 @@ $config = array( // account 'account_management' => true, // disable if you're using other method to manage users (fe. tfs account manager) + 'account_login_by_email' => false, // use email instead of Account Name like in latest Tibia + 'account_login_by_email_fallback' => false, // allow also additionally login by Account Name/Number (for users that might forget their email) 'account_create_auto_login' => false, // auto login after creating account? 'account_create_character_create' => true, // allow directly to create character on create account page? 'account_mail_verify' => false, // force users to confirm their email addresses when registering account diff --git a/index.php b/index.php index 2117cadc..3ce40bb5 100644 --- a/index.php +++ b/index.php @@ -178,6 +178,11 @@ $template_place_holders = array(); require_once SYSTEM . 'init.php'; +// verify myaac tables exists in database +if(!$db->hasTable('myaac_account_actions')) { + throw new RuntimeException('Seems that the table myaac_account_actions of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting this url.'); +} + // event system require_once SYSTEM . 'hooks.php'; $hooks = new Hooks(); @@ -189,11 +194,6 @@ require_once SYSTEM . 'status.php'; $twig->addGlobal('config', $config); $twig->addGlobal('status', $status); -// verify myaac tables exists in database -if(!$db->hasTable('myaac_account_actions')) { - throw new RuntimeException('Seems that the table myaac_account_actions of MyAAC doesn\'t exist in the database. This is a fatal error. You can try to reinstall MyAAC by visiting this url.'); -} - require SYSTEM . 'migrate.php'; $hooks->trigger(HOOK_STARTUP); diff --git a/system/exception.php b/system/exception.php index 5c76d66b..3ad1fefe 100644 --- a/system/exception.php +++ b/system/exception.php @@ -39,7 +39,7 @@ function exception_handler($exception) { // we just replace some values manually // cause in case Twig throws exception, we can show it too $content = file_get_contents($template_file); - $content = str_replace(array('{{ BASE_URL }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content); + $content = str_replace(array('{{ BASE_URL }}', '{{ exceptionClass }}', '{{ message }}', '{{ backtrace }}', '{{ powered_by }}'), array(BASE_URL, get_class($exception), $message, $backtrace_formatted, base64_decode('UG93ZXJlZCBieSA8YSBocmVmPSJodHRwOi8vbXktYWFjLm9yZyIgdGFyZ2V0PSJfYmxhbmsiPk15QUFDLjwvYT4=')), $content); echo $content; } diff --git a/system/functions.php b/system/functions.php index b73868f6..fbed8384 100644 --- a/system/functions.php +++ b/system/functions.php @@ -1467,6 +1467,23 @@ function truncate($string, $length) return $string; } +function getAccountLoginByLabel() +{ + $ret = ''; + if (config('account_login_by_email')) { + $ret = 'Email Address'; + if (config('account_login_by_email_fallback')) { + $ret .= ' or '; + } + } + + if (!config('account_login_by_email') || config('account_login_by_email_fallback')) { + $ret .= 'Account ' . (USE_ACCOUNT_NAME ? 'Name' : 'Number'); + } + + return $ret; +} + // validator functions require_once LIBS . 'validator.php'; require_once SYSTEM . 'compat.php'; diff --git a/system/init.php b/system/init.php index cc42180c..d4c623bf 100644 --- a/system/init.php +++ b/system/init.php @@ -127,6 +127,8 @@ $ots = POT::getInstance(); require_once SYSTEM . 'database.php'; define('USE_ACCOUNT_NAME', $db->hasColumn('accounts', 'name')); +define('USE_ACCOUNT_NUMBER', $db->hasColumn('accounts', 'number')); + // load vocation names $tmp = ''; if($cache->enabled() && $cache->fetch('vocations', $tmp)) { @@ -158,4 +160,4 @@ else { unset($tmp, $id, $vocation); require LIBS . 'Towns.php'; -Towns::load(); \ No newline at end of file +Towns::load(); diff --git a/system/libs/pot/OTS_Account.php b/system/libs/pot/OTS_Account.php index beed47dd..8a9e63ac 100644 --- a/system/libs/pot/OTS_Account.php +++ b/system/libs/pot/OTS_Account.php @@ -101,6 +101,37 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable return $name; } + /** + * @param $email + * @return mixed + * @throws Exception + */ + public function createWithEmail($email = null) + { + // if name is not passed then it will be generated randomly + if( !isset($email) ) + { + throw new Exception(__CLASS__ . ':' . __METHOD__ . ' createWithEmail called without e-mail.'); + } + + // repeats until name is unique + do + { + $name = uniqid(); + + $query = $this->db->query('SELECT `id` FROM `accounts` WHERE `name` = ' . $this->db->quote($name)); + } while($query->rowCount() >= 1); + + // saves blank account info + $this->db->exec('INSERT INTO `accounts` (`name`, `password`, `email`, `created`) VALUES (' . $this->db->quote($name) . ', ' . '\'\', ' . $this->db->quote($email) . ', ' . time() . ')'); + + // reads created account's ID + $this->data['id'] = $this->db->lastInsertId(); + $this->data['name'] = $name; + + // return name of newly created account + return $name; + } /** * Creates new account. * @@ -138,11 +169,32 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable */ public function create($name = NULL, $id = NULL) { - // saves blank account info - $this->db->exec('INSERT INTO `accounts` (' . (isset($id) ? '`id`,' : '') . (isset($name) ? '`name`,' : '') . '`password`, `email`, `created`) VALUES (' . (isset($id) ? $id . ',' : '') . (isset($name) ? $this->db->quote($name) . ',' : '') . ' \'\', \'\',' . time() . ')'); + if(isset($name)) { + $nameOrNumber = 'name'; + $nameOrNumberValue = $name; + } + else { + if (USE_ACCOUNT_NUMBER) { + $nameOrNumber = 'number'; + $nameOrNumberValue = $id; + $id = null; + } + else { + $nameOrNumber = null; + } + } - if(isset($name)) + // saves blank account info + $this->db->exec('INSERT INTO `accounts` (' . (isset($id) ? '`id`,' : '') . (isset($nameOrNumber) ? '`' . $nameOrNumber . '`,' : '') . '`password`, `email`, `created`) VALUES (' . (isset($id) ? $id . ',' : '') . (isset($nameOrNumber) ? $this->db->quote($nameOrNumberValue) . ',' : '') . ' \'\', \'\',' . time() . ')'); + + if(isset($name)) { $this->data['name'] = $name; + } + else { + if (USE_ACCOUNT_NUMBER) { + $this->data['number'] = $name; + } + } $lastInsertId = $this->db->lastInsertId(); if($lastInsertId != 0) { @@ -179,15 +231,26 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable * @param int $id Account number. * @throws PDOException On PDO operation error. */ - public function load($id, $fresh = false) + public function load($id, $fresh = false, $searchOnlyById = false) { if(!$fresh && isset(self::$cache[$id])) { $this->data = self::$cache[$id]; return; } + $numberColumn = 'id'; + $nameOrNumber = ''; + if (!$searchOnlyById) { + if (USE_ACCOUNT_NAME) { + $nameOrNumber = '`name`,'; + } else if (USE_ACCOUNT_NUMBER) { + $nameOrNumber = '`number`,'; + $numberColumn = 'number'; + } + } + // SELECT query on database - $this->data = $this->db->query('SELECT `id`, ' . ($this->db->hasColumn('accounts', 'name') ? '`name`,' : '') . '`password`, `email`, `blocked`, `rlname`, `location`, `country`, `web_flags`, ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays`, ' : '') . ($this->db->hasColumn('accounts', 'lastday') ? '`lastday`, ' : ($this->db->hasColumn('accounts', 'premend') ? '`premend`,' : ($this->db->hasColumn('accounts', 'premium_ends_at') ? '`premium_ends_at`,' : ''))) . '`created` FROM `accounts` WHERE `id` = ' . (int) $id)->fetch(); + $this->data = $this->db->query('SELECT `id`, ' . $nameOrNumber . '`password`, `email`, `blocked`, `rlname`, `location`, `country`, `web_flags`, ' . ($this->db->hasColumn('accounts', 'premdays') ? '`premdays`, ' : '') . ($this->db->hasColumn('accounts', 'lastday') ? '`lastday`, ' : ($this->db->hasColumn('accounts', 'premend') ? '`premend`,' : ($this->db->hasColumn('accounts', 'premium_ends_at') ? '`premium_ends_at`,' : ''))) . '`created` FROM `accounts` WHERE `' . $numberColumn . '` = ' . (int) $id)->fetch(); self::$cache[$id] = $this->data; } @@ -306,6 +369,15 @@ class OTS_Account extends OTS_Row_DAO implements IteratorAggregate, Countable return $this->data['id']; } + public function getNumber() + { + if (isset($this->data['number'])) { + return $this->data['number']; + } + + return $this->data['id']; + } + public function getRLName() { if( !isset($this->data['rlname']) ) diff --git a/system/libs/pot/OTS_Player.php b/system/libs/pot/OTS_Player.php index 1f6a54c9..a32bed8a 100644 --- a/system/libs/pot/OTS_Player.php +++ b/system/libs/pot/OTS_Player.php @@ -602,7 +602,7 @@ class OTS_Player extends OTS_Row_DAO } $account = new OTS_Account(); - $account->load($this->data['account_id']); + $account->load($this->data['account_id'], false, true); return $account; } diff --git a/system/login.php b/system/login.php index 330efd13..cf33d04b 100644 --- a/system/login.php +++ b/system/login.php @@ -85,17 +85,24 @@ else } $account_logged = new OTS_Account(); - if(USE_ACCOUNT_NAME) - $account_logged->find($login_account); - else - $account_logged->load($login_account, true); + if (config('account_login_by_email')) { + $account_logged->findByEMail($login_account); + } + + if (!config('account_login_by_email') || config('account_login_by_email_fallback')) { + if(USE_ACCOUNT_NAME) { + $account_logged->find($login_account); + } else { + $account_logged->load($login_account, true); + } + } $config_salt_enabled = $db->hasColumn('accounts', 'salt'); if($account_logged->isLoaded() && encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $login_password) == $account_logged->getPassword() && (!isset($t) || $t['attempts'] < 5) ) { - setSession('account', $account_logged->getId()); + setSession('account', $account_logged->getNumber()); setSession('password', encrypt(($config_salt_enabled ? $account_logged->getCustomField('salt') : '') . $login_password)); if($remember_me) { setSession('remember_me', true); @@ -121,6 +128,8 @@ else { $hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me)); + $errorMessage = getAccountLoginByLabel() . ' or password is not correct.'; + // temporary solution for blocking failed login attempts if($cache->enabled()) { @@ -132,24 +141,24 @@ else if($t['attempts'] >= 5) $errors[] = 'A wrong password has been entered 5 times in a row. You are unable to log into your account for the next 5 minutes. Please wait.'; else - $errors[] = 'Account name or password is not correct.'; + $errors[] = $errorMessage; } else { $t = array('attempts' => 1, 'last' => time()); - $errors[] = 'Account name or password is not correct.'; + $errors[] = $errorMessage; } $tmp[$ip] = $t; $cache->set('failed_logins', serialize($tmp), 60 * 60); // save for 1 hour } else { - $errors[] = 'Account name or password is not correct.'; + $errors[] = $errorMessage; } } } else { - $errors[] = 'Please enter your account ' . (USE_ACCOUNT_NAME ? 'name' : 'number') . ' and password.'; + $errors[] = 'Please enter your ' . getAccountLoginByLabel() . ' and password.'; $hooks->trigger(HOOK_LOGIN_ATTEMPT, array('account' => $login_account, 'password' => $login_password, 'remember_me' => $remember_me)); } diff --git a/system/pages/accountmanagement.php b/system/pages/accountmanagement.php index e3444a73..53c65c0c 100644 --- a/system/pages/accountmanagement.php +++ b/system/pages/accountmanagement.php @@ -40,6 +40,7 @@ if(!$logged) $twig->display('account.login.html.twig', array( 'redirect' => isset($_REQUEST['redirect']) ? $_REQUEST['redirect'] : null, 'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', + 'account_login_by' => getAccountLoginByLabel(), 'error' => isset($errors[0]) ? $errors[0] : null )); @@ -126,7 +127,7 @@ $errors = array(); 'email_request' => $email_request, 'email_new_time' => $email_new_time, 'email_new' => isset($email_new) ? $email_new : '', - 'account' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getId(), + 'account' => USE_ACCOUNT_NAME ? $account_logged->getName() : $account_logged->getNumber(), 'account_email' => $account_email, 'account_created' => $account_created, 'account_status' => $account_status, diff --git a/system/pages/admin/login.php b/system/pages/admin/login.php index 2c6d4e9f..24c1e9b5 100644 --- a/system/pages/admin/login.php +++ b/system/pages/admin/login.php @@ -13,5 +13,6 @@ $title = 'Login'; $twig->display('admin.login.html.twig', array( 'logout' => ($action == 'logout' ? 'You have been logged out!' : ''), 'account' => USE_ACCOUNT_NAME ? 'Name' : 'Number', + 'account_login_by' => getAccountLoginByLabel(), 'errors' => isset($errors)? $errors : '' )); diff --git a/system/pages/createaccount.php b/system/pages/createaccount.php index 8af31629..06f5f414 100644 --- a/system/pages/createaccount.php +++ b/system/pages/createaccount.php @@ -34,11 +34,13 @@ $errors = array(); $save = isset($_POST['save']) && $_POST['save'] == 1; if($save) { - if(USE_ACCOUNT_NAME) { - $account_name = $_POST['account']; - } - else { - $account_id = $_POST['account']; + if(!config('account_login_by_email')) { + if(USE_ACCOUNT_NAME) { + $account_name = $_POST['account']; + } + else { + $account_id = $_POST['account']; + } } $email = $_POST['email']; @@ -46,12 +48,14 @@ if($save) $password2 = $_POST['password2']; // account - if(isset($account_id)) { - if(!Validator::accountId($account_id)) + if(!config('account_login_by_email')) { + if (isset($account_id)) { + if (!Validator::accountId($account_id)) { + $errors['account'] = Validator::getLastError(); + } + } else if (!Validator::accountName($account_name)) $errors['account'] = Validator::getLastError(); } - else if(!Validator::accountName($account_name)) - $errors['account'] = Validator::getLastError(); // email if(!Validator::email($email)) @@ -93,7 +97,7 @@ if($save) } // check if account name is not equal to password - if(USE_ACCOUNT_NAME && strtoupper($account_name) == strtoupper($password)) { + if(!config('account_login_by_email') && USE_ACCOUNT_NAME && strtoupper($account_name) == strtoupper($password)) { $errors['password'] = 'Password may not be the same as account name.'; } @@ -106,16 +110,28 @@ if($save) } $account_db = new OTS_Account(); - if(USE_ACCOUNT_NAME) - $account_db->find($account_name); - else - $account_db->load($account_id); + if (config('account_login_by_email')) { + $account_db->findByEMail($email); + } + else { + if(USE_ACCOUNT_NAME) { + $account_db->find($account_name); + } + else { + $account_db->load($account_id); + } + } if($account_db->isLoaded()) { - if(USE_ACCOUNT_NAME) - $errors['account'] = 'Account with this name already exist.'; - else - $errors['account'] = 'Account with this id already exist.'; + if (config('account_login_by_email') && !config('account_mail_unique')) { + $errors['account'] = 'Account with this email already exist.'; + } + else if (!config('account_login_by_email')) { + if (USE_ACCOUNT_NAME) + $errors['account'] = 'Account with this name already exist.'; + else + $errors['account'] = 'Account with this id already exist.'; + } } if(!isset($_POST['accept_rules']) || $_POST['accept_rules'] !== 'true') @@ -130,11 +146,12 @@ if($save) 'accept_rules' => isset($_POST['accept_rules']) ? $_POST['accept_rules'] === 'true' : false, ); - if(USE_ACCOUNT_NAME) { - $params['account_name'] = $_POST['account']; - } - else { - $params['account_id'] = $_POST['account']; + if (!config('account_login_by_email')) { + if (USE_ACCOUNT_NAME) { + $params['account_name'] = $_POST['account']; + } else { + $params['account_id'] = $_POST['account']; + } } $hooks->trigger(HOOK_ACCOUNT_CREATE_AFTER_SUBMIT, $params); @@ -151,10 +168,15 @@ if($save) if(empty($errors)) { $new_account = new OTS_Account(); - if(USE_ACCOUNT_NAME) - $new_account->create($account_name); - else - $new_account->create(NULL, $account_id); + if (config('account_login_by_email')) { + $new_account->createWithEmail($email); + } + else { + if(USE_ACCOUNT_NAME) + $new_account->create($account_name); + else + $new_account->create(NULL, $account_id); + } $config_salt_enabled = $db->hasColumn('accounts', 'salt'); if($config_salt_enabled) @@ -192,7 +214,11 @@ if($save) $new_account->setCustomField('premium_points', $config['account_premium_points']); } - $tmp_account = (USE_ACCOUNT_NAME ? $account_name : $account_id); + $tmp_account = $email; + if (!config('account_login_by_email')) { + $tmp_account = (USE_ACCOUNT_NAME ? $account_name : $account_id); + } + if($config['mail_enabled'] && $config['account_mail_verify']) { $hash = md5(generateRandomString(16, true, true) . $email); diff --git a/system/templates/account.create.html.twig b/system/templates/account.create.html.twig index 8d9d57f9..cb89d4b3 100644 --- a/system/templates/account.create.html.twig +++ b/system/templates/account.create.html.twig @@ -30,15 +30,20 @@ {{ hook('HOOK_ACCOUNT_CREATE_BEFORE_ACCOUNT') }} + {% if not config.account_login_by_email %} Account {% if constant('USE_ACCOUNT_NAME') %}Name{% else %}Number{% endif %}: + {% if not constant('USE_ACCOUNT_NAME') %} +
[suggest number]
+ {% endif %} + {% endif %} {% if errors.account is defined %}{{ errors.account }}{% endif %} {{ hook('HOOK_ACCOUNT_CREATE_AFTER_ACCOUNT') }} @@ -334,3 +339,8 @@ {{ hook('HOOK_ACCOUNT_CREATE_AFTER_FORM') }} + diff --git a/system/templates/account.create.js.html.twig b/system/templates/account.create.js.html.twig index 5dfb4b4b..74bfb8bf 100644 --- a/system/templates/account.create.js.html.twig +++ b/system/templates/account.create.js.html.twig @@ -20,6 +20,9 @@ $('#password2').blur(function() { checkPassword(); }); + $('#SuggestAccountNumber a').click(function (event) { + generateAccountNumber(event); + }); }); function updateFlag() @@ -192,4 +195,18 @@ lastSend = timeNow; } + + function generateAccountNumber(event) + { + event.preventDefault(); + $.getJSON("tools/generate_account_number.php", { uid: Math.random() }, + function(data){ + if(data.hasOwnProperty('success')) { + $('#account_input').val(data.success); + } + } + ); + + setTimeout(checkAccount, 1000); + } diff --git a/system/templates/account.login.html.twig b/system/templates/account.login.html.twig index 2ac4f3f6..eb566b22 100644 --- a/system/templates/account.login.html.twig +++ b/system/templates/account.login.html.twig @@ -24,7 +24,7 @@ Please enter your account {{ account|lower }} and your password.
- Account {{ account }}: + {{ account_login_by }}: @@ -73,4 +73,4 @@ Please enter your account {{ account|lower }} and your password.
- +
diff --git a/system/templates/exception.html.twig b/system/templates/exception.html.twig index 5a259079..6f505245 100644 --- a/system/templates/exception.html.twig +++ b/system/templates/exception.html.twig @@ -64,6 +64,8 @@

Whoops something went wrong...

+ Exception class: {{ exceptionClass }}() +

{{ message }}


Backtrace:

@@ -74,4 +76,4 @@

{{ powered_by }}

- \ No newline at end of file + diff --git a/templates/tibiacom/account.login.html.twig b/templates/tibiacom/account.login.html.twig index 65e189e1..fd701ad1 100644 --- a/templates/tibiacom/account.login.html.twig +++ b/templates/tibiacom/account.login.html.twig @@ -33,7 +33,9 @@ - + diff --git a/tools/generate_account_number.php b/tools/generate_account_number.php new file mode 100644 index 00000000..a53ba9a3 --- /dev/null +++ b/tools/generate_account_number.php @@ -0,0 +1,54 @@ + + * @copyright 2021 MyAAC + * @link https://my-aac.org + */ + +// we need some functions +require '../common.php'; +require SYSTEM . 'functions.php'; +require SYSTEM . 'init.php'; + +if (config('account_login_by_email') || USE_ACCOUNT_NAME) { + return; +} + +$hasNumberColumn = $db->hasColumn('accounts', 'number'); +do { + $length = ACCOUNT_NUMBER_LENGTH; + $min = 10 ** ($length - 1); + $max = 10 ** $length - 1; + + try { + $number = random_int($min, $max); + } catch (Exception $e) { + error_(''); + } + + $query = $db->query('SELECT `id` FROM `accounts` WHERE `' . ($hasNumberColumn ? 'number' : 'id') . '` = ' . $db->quote($number)); +} while($query->rowCount() >= 1); + +success_($number); + +/** + * Output message & exit. + * + * @param string $desc Description + */ +function success_($desc) { + echo json_encode([ + 'success' => $desc + ]); + exit(); +} +function error_($desc) { + echo json_encode([ + 'error' => $desc + ]); + exit(); +}
Account {{ account }}: + {{ account_login_by }}: +