Skip to content

Commit

Permalink
feature: api client account creator (opentibiabr#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
elsongabriel authored Jun 4, 2024
1 parent da912ab commit 04720d0
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 8 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ node_modules

# Files
login.php
clientcreateaccount.php

# Styles
admin/bootstrap/pace/templates/pace-theme-loading-bar.tmpl.css
Expand Down
279 changes: 279 additions & 0 deletions clientcreateaccount.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
<?php
/**
* Create account by client
*
* @package MyAAC
* @author Elson <[email protected]>
* @author OpenTibiaBR
* @copyright 2023 MyAAC
* @link https://github.com/opentibiabr/myaac
*/

global $config, $db;
require_once 'common.php';
require_once 'config.php';
require_once 'config.local.php';
require_once SYSTEM . 'functions.php';
require_once SYSTEM . 'init.php';

$request = file_get_contents('php://input');
$result = json_decode($request);
//log_append('create_account_service.log', "req: $request");

if ($result === null || (isset($result->Type) && isset($result->type))) {
log_append('create_account_service.log', 'Request error!');
}

$action = mb_strtolower($result->type ?? ($result->Type ?? ''));

$response = null;

switch ($action) {
case 'getaccountcreationstatus':
$response = json_encode([
"Worlds" => [getServerStatus()],
"RecommendedWorld" => configLua('serverName'),
"IsCaptchaDeactivated" => true
]);
break;

case 'generatecharactername':
// todo generate suggested names.
$response = json_encode([
"GeneratedName" => ""
]);
break;

case 'checkcharactername':
$name = $result->CharacterName;
$check = $db->query("SELECT `name` FROM `players` WHERE `name` = {$db->quote($name)}")->rowCount() == 0;
$response = json_encode([
"CharacterName" => $name,
"IsAvailable" => $check
]);
break;

case 'checkemail':
$email = mb_strtolower($result->Email);
$check = filter_var($email, FILTER_VALIDATE_EMAIL) && $db->query("SELECT `email` FROM `accounts` WHERE `email` = {$db->quote($email)}")->rowCount() == 0;
$response = json_encode($check ? [
"IsValid" => true,
"EMail" => $email
] : [
"IsValid" => false,
"errorCode" => 59,
"errorMessage" => "This email address has an invalid format. Please enter a correct email address!",
"EMail" => $email
]);
break;

case 'checkpassword':
$errors = validatePassword($pass = $result->Password1);
$response = json_encode([
"PasswordRequirements" => [
"PasswordLength" => !in_array(0, $errors),
"InvalidCharacters" => !in_array(1, $errors),
"HasLowerCase" => !in_array(2, $errors),
"HasUpperCase" => !in_array(3, $errors),
"HasNumber" => !in_array(4, $errors)
],
"Password1" => $pass,
"PasswordStrength" => count($errors) == 0 ? 4 : abs(count($errors) - 4),
"PasswordStrengthColor" => count($errors) == 0 ? "#20a000" : '#ec644b',
"PasswordValid" => count($errors) == 0,
]);
break;

case 'createaccountandcharacter':
$email = mb_strtolower($result->EMail);
try {
if (filter_var($email, FILTER_VALIDATE_EMAIL) && $db->query("SELECT `email` FROM `accounts` WHERE `email` = {$db->quote($email)}")->rowCount() == 0) {
$response = json_encode([
"Success" => true,
"AccountID" => createAccount([
"email" => $email,
"password" => $result->Password,
"characterName" => stripslashes(ucwords(strtolower($result->CharacterName))),
"characterSex" => $result->CharacterSex == 'male' ? 1 : 0,
//"clientType" => "Client",
//"recaptcha2token" => "",
//"recaptcha3token" => "",
//"selectedWorld" => configLua('serverName'),
]),
// "Password" => $result->Password
]);
} else {
throw new Exception('Email invalid!');
}
} catch (Exception $e) {
log_append('create_account_service.log', "Error caught: {$e->getMessage()}");
$response = json_encode([
"errorCode" => 101,
"errorMessage" => $e->getMessage(), //"An internal error has occurred. Please try again later!",
"Success" => false,
"IsRecaptcha2Requested" => false,
]);
}
break;
}
log_append('create_account_service.log', "response: $response");
die($response);

/**
* Function to return server status as array to worlds list
*
* @return array
*/
function getServerStatus(): array
{
global $db;
$playersOnline = $db->query("SELECT COUNT(*) FROM `players_online`")->fetchAll()[0][0] ?? 0;
$date = $db->query("SELECT `date` FROM `myaac_changelog`")->fetch()['date'] ?? 0;
switch (configLua('worldType')) {
default:
case 'pvp':
$pvpType = "Open PVP";
break;
case 'no-pvp':
$pvpType = 'Optional PVP';
break;
case 'pvp-enforced':
$pvpType = 'PVP';
break;
case 'retro-pvp':
$pvpType = 'Retro PvP';
break;
}
return [
"Name" => configLua('serverName'),
"PlayersOnline" => intval($playersOnline),
"CreationDate" => intval($date),
"Region" => "America",
"PvPType" => $pvpType,
"PremiumOnly" => configLua('onlyPremiumAccount'),
"TransferType" => "Blocked",
"BattlEyeActivationTimestamp" => intval($date),
"BattlEyeInitiallyActive" => 0
];

}

/**
* Function to check strength of password
*
* @param $password
* @return array|int[]
*/
function validatePassword($password): array
{
$checks = [];
$minLength = 10;
$invalids = [' ', '\'', '"', '\\', '/'];

if (strlen($password) < $minLength) $checks = [0];
foreach ($invalids as $char) {
if (strpos($password, $char) !== false) $checks = array_merge($checks, [1]);
}
if (!preg_match('/[a-z]/', $password)) $checks = array_merge($checks, [2]);
if (!preg_match('/[A-Z]/', $password)) $checks = array_merge($checks, [3]);
if (!preg_match('/[0-9]/', $password)) $checks = array_merge($checks, [4]);
return $checks;
}

/**
* Function to create account
*
* @param array $data
* @return false|string
* @throws Exception
*/
function createAccount(array $data)
{
global $db, $config, $twig;
try {
$account_name = USE_ACCOUNT_NAME ?
@(explode('@', $data['email']))[0] . date('isH')
: date('HisdmyHi');

if (USE_ACCOUNT_NAME && mb_strtolower($account_name) == mb_strtolower($data['password'])) {
throw new Exception('Password may not be the same as account name.');
}

$account_db = new OTS_Account();
$account_db->find($account_name);
if ($account_db->isLoaded()) {
throw new Exception('Account with this name already exist.');
}

$info = json_decode(@file_get_contents('https://ipinfo.io/' . $_SERVER['REMOTE_ADDR'] . '/geo'), true);
$country = strtolower($info['country'] ?? 'br');

$new_account = new OTS_Account();
$new_account->create($account_name);

$config_salt_enabled = $db->hasColumn('accounts', 'salt');
if ($config_salt_enabled) {
$salt = generateRandomString(10, false, true, true);
$data['password'] = $salt . $data['password'];
$new_account->setCustomField('salt', $salt);
}

$new_account->setPassword(encrypt($data['password']));
$new_account->setEMail($data['email']);
$new_account->setCustomField('created', time());
$new_account->setCustomField('country', $country);
$new_account->save();

$new_account->logAction('Account created.');

if (($premdays = ($config['account_premium_days'] ?? 0)) > 0) {
if ($db->hasColumn('accounts', 'premend')) { // othire
$new_account->setCustomField('premend', time() + $premdays * 86400);
} else {
$new_account->setCustomField('premdays', $premdays);
$lastDay = ($premdays < OTS_Account::GRATIS_PREMIUM_DAYS) ? time() + ($premdays * 86400) : 0;
$new_account->setCustomField('lastday', $lastDay);
}
}

if (($welcomeCoins = ($config['account_welcome_coins'] ?? 0)) > 0) {
$coinType = $config['account_coin_type_usage'] ?? 'coins_transferable';
$new_account->setCustomField($coinType, $welcomeCoins);
}

if ($config['mail_enabled'] && $config['account_mail_verify']) {
$hash = md5(generateRandomString(16, true, true) . $data['email']);
$new_account->setCustomField('email_hash', $hash);

$verify_url = getLink('account/confirm_email/' . $hash);
$body_html = $twig->render('mail.account.verify.html.twig', array(
'account' => $account_name,
'verify_url' => generateLink($verify_url, $verify_url, true)
));

if (!_mail($data['email'], 'New account on ' . configLua('serverName'), $body_html)) {
$new_account->delete();
throw new Exception('An error occurred while sending email! Account not created. Try again. For Admin: More info can be found in system/logs/mailer-error.log');
}
} else if ($config['mail_enabled'] && $config['account_welcome_mail']) {
$mailBody = $twig->render('account.welcome_mail.html.twig', ['account' => $account_name]);
if (!_mail($data['email'], 'Your account on ' . configLua('serverName'), $mailBody)) {
throw new Exception('An error occurred while sending email. For Admin: More info can be found in system/logs/mailer-error.log');
}
}

// character creation only if start in dawnport
if (count(config('character_samples')) == 1 && !empty($data['characterName'])) {
require_once LIBS . 'CreateCharacter.php';
$createCharacter = new CreateCharacter();
$errors = [];
if (!$createCharacter->doCreate($data['characterName'], $data['characterSex'], 0, 0, $new_account, $errors)) {
throw new Exception('There was an error creating your character. Please create your character later in account management page.');
}
}

return $account_name;
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
4 changes: 2 additions & 2 deletions config.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@
],
'account_mail_unique' => true, // email addresses cannot be duplicated? (one account = one email)
'account_premium_days' => 0, // default premium days on new account
'account_premium_coins' => 0, // default coins on new account
'account_welcome_coins' => 0, // default coins on new account
'account_welcome_mail' => false, // send welcome email when user registers
'account_welcome_mail_show_pass' => false, // send password in welcome email
'account_mail_change' => 2, // how many days user need to change email to account - block hackers
'account_country' => true, // user will be able to set country of origin when registering account, this information will be viewable in others places aswell
'account_country_recognize' => true, // should country of user be automatically recognized by his IP? This makes an external API call to http://ipinfo.io

'account_change_coin_type' => 'coins', // which coin you want to use, coins or coins_transferable to buy changes at site
'account_coin_type_usage' => 'coins_transferable', // which coin you want to use, coins or coins_transferable to buy changes at site
'account_change_character_name' => false, // can user change their character name for coins?
'account_change_character_name_coins' => 250, // cost of name change
'account_change_character_sex' => false, // can user change their character sex for coins?
Expand Down
2 changes: 1 addition & 1 deletion system/pages/account/change_main.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
echo 'Changing main character for coins is disabled on this server.';
return;
}
$coinType = $config['account_change_coin_type'] ?? 'coins';
$coinType = $config['account_coin_type_usage'] ?? 'coins_transferable';
$coinName = $coinType == 'coins' ? $coinType : 'transferable coins';
$needCoins = $config['account_change_character_main_coins'];

Expand Down
2 changes: 1 addition & 1 deletion system/pages/account/change_name.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

$player_id = isset($_POST['player_id']) ? (int)$_POST['player_id'] : NULL;
$name = isset($_POST['name']) ? stripslashes(ucwords(strtolower($_POST['name']))) : NULL;
$coinType = $config['account_change_coin_type'] ?? 'coins';
$coinType = $config['account_coin_type_usage'] ?? 'coins_transferable';
$coinName = $coinType == 'coins' ? $coinType : 'transferable coins';
$needCoins = $config['account_change_character_name_coins'];

Expand Down
2 changes: 1 addition & 1 deletion system/pages/account/change_sex.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
$sex_changed = false;
$player_id = isset($_POST['player_id']) ? (int)$_POST['player_id'] : NULL;
$new_sex = isset($_POST['new_sex']) ? (int)$_POST['new_sex'] : NULL;
$coinType = $config['account_change_coin_type'] ?? 'coins';
$coinType = $config['account_coin_type_usage'] ?? 'coins_transferable';
$coinName = $coinType == 'coins' ? $coinType : 'transferable coins';
$needCoins = $config['account_change_character_sex_coins'];

Expand Down
2 changes: 1 addition & 1 deletion system/pages/account/register_new.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
if ((!$config['generate_new_reckey'] || !$config['mail_enabled']) || empty($reckey))
echo "You can't get new recovery key";
else {
$coinType = $config['account_change_coin_type'] ?? 'coins';
$coinType = $config['account_coin_type_usage'] ?? 'coins_transferable';
$coinName = $coinType == 'coins' ? $coinType : 'transferable coins';
$needCoins = $config['generate_new_reckey_price'];

Expand Down
5 changes: 3 additions & 2 deletions system/pages/createaccount.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,9 @@
}
}

if ($config['account_premium_coins']) {
$new_account->setCustomField('coins', $config['account_premium_coins']);
if (($welcomeCoins = ($config['account_welcome_coins'] ?? 0)) > 0) {
$coinType = $config['account_coin_type_usage'] ?? 'coins_transferable';
$new_account->setCustomField($coinType, $welcomeCoins);
}

$tmp_account = (USE_ACCOUNT_NAME ? $account_name : $account_id);
Expand Down

0 comments on commit 04720d0

Please sign in to comment.