PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Flux/LoginServer.php

https://github.com/chokoleytdesignoper/fluxcp_choko
PHP | 547 lines | 386 code | 76 blank | 85 comment | 63 complexity | aa21a4641ddec487b269a76a4600241a MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, BSD-3-Clause
  1. <?php
  2. require_once 'Flux/BaseServer.php';
  3. require_once 'Flux/RegisterError.php';
  4. /**
  5. * Represents an eAthena Login Server.
  6. */
  7. class Flux_LoginServer extends Flux_BaseServer {
  8. /**
  9. * Connection to the MySQL server.
  10. *
  11. * @access public
  12. * @var Flux_Connection
  13. */
  14. public $connection;
  15. /**
  16. * Login server database.
  17. *
  18. * @access public
  19. * @var string
  20. */
  21. public $loginDatabase;
  22. /**
  23. * Logs database. (is not set until setConnection() is called.)
  24. *
  25. * @access public
  26. * @var string
  27. */
  28. public $logsDatabase;
  29. /**
  30. * Overridden to add custom properties.
  31. *
  32. * @access public
  33. */
  34. public function __construct(Flux_Config $config)
  35. {
  36. parent::__construct($config);
  37. $this->loginDatabase = $config->getDatabase();
  38. }
  39. /**
  40. * Set the connection object to be used for this LoginServer instance.
  41. *
  42. * @param Flux_Connection $connection
  43. * @return Flux_Connection
  44. * @access public
  45. */
  46. public function setConnection(Flux_Connection $connection)
  47. {
  48. $this->connection = $connection;
  49. $this->logsDatabase = $connection->logsDbConfig->getDatabase();
  50. return $connection;
  51. }
  52. /**
  53. * Validate credentials against the login server's database information.
  54. *
  55. * @param string $username Ragnarok account username.
  56. * @param string $password Ragnarok account password.
  57. * @return bool True/false if valid or invalid.
  58. * @access public
  59. */
  60. public function isAuth($username, $password)
  61. {
  62. if ($this->config->get('UseMD5')) {
  63. $password = Flux::hashPassword($password);
  64. }
  65. if (trim($username) == '' || trim($password) == '') {
  66. return false;
  67. }
  68. $sql = "SELECT userid FROM {$this->loginDatabase}.login WHERE sex != 'S' AND level >= 0 ";
  69. if ($this->config->getNoCase()) {
  70. $sql .= 'AND LOWER(userid) = LOWER(?) ';
  71. }
  72. else {
  73. $sql .= 'AND CAST(userid AS BINARY) = ? ';
  74. }
  75. $sql .= "AND user_pass = ? LIMIT 1";
  76. $sth = $this->connection->getStatement($sql);
  77. $sth->execute(array($username, $password));
  78. $res = $sth->fetch();
  79. if ($res) {
  80. return true;
  81. }
  82. else {
  83. return false;
  84. }
  85. }
  86. /**
  87. *
  88. */
  89. public function register($username, $password, $confirmPassword, $email, $gender, $birthDate, $securityCode)
  90. {
  91. if (strlen($username) < Flux::config('MinUsernameLength')) {
  92. throw new Flux_RegisterError('Username is too short', Flux_RegisterError::USERNAME_TOO_SHORT);
  93. }
  94. elseif (strlen($username) > Flux::config('MaxUsernameLength')) {
  95. throw new Flux_RegisterError('Username is too long', Flux_RegisterError::USERNAME_TOO_LONG);
  96. }
  97. elseif (strlen($password) < Flux::config('MinPasswordLength')) {
  98. throw new Flux_RegisterError('Password is too short', Flux_RegisterError::PASSWORD_TOO_SHORT);
  99. }
  100. elseif (strlen($password) > Flux::config('MaxPasswordLength')) {
  101. throw new Flux_RegisterError('Password is too long', Flux_RegisterError::PASSWORD_TOO_LONG);
  102. }
  103. elseif ($password !== $confirmPassword) {
  104. throw new Flux_RegisterError('Passwords do not match', Flux_RegisterError::PASSWORD_MISMATCH);
  105. }
  106. elseif (!preg_match('/(.+?)@(.+?)/', $email)) {
  107. throw new Flux_RegisterError('Invalid e-mail address', Flux_RegisterError::INVALID_EMAIL_ADDRESS);
  108. }
  109. elseif (!in_array(strtoupper($gender), array('M', 'F'))) {
  110. throw new Flux_RegisterError('Invalid gender', Flux_RegisterError::INVALID_GENDER);
  111. }
  112. elseif ($birthDate > date('Y-m-d')) {
  113. throw new Flux_RegisterError('Invalid Birth date', Flux_RegisterError::INVALID_BIRTH_DATE);
  114. }
  115. elseif (Flux::config('UseCaptcha')) {
  116. if (Flux::config('EnableReCaptcha')) {
  117. require_once 'recaptcha/recaptchalib.php';
  118. $resp = recaptcha_check_answer(
  119. Flux::config('ReCaptchaPrivateKey'),
  120. $_SERVER['REMOTE_ADDR'],
  121. // Checks POST fields.
  122. $_POST['recaptcha_challenge_field'],
  123. $_POST['recaptcha_response_field']);
  124. if (!$resp->is_valid) {
  125. throw new Flux_RegisterError('Invalid security code', Flux_RegisterError::INVALID_SECURITY_CODE);
  126. }
  127. }
  128. elseif (strtolower($securityCode) !== strtolower(Flux::$sessionData->securityCode)) {
  129. throw new Flux_RegisterError('Invalid security code', Flux_RegisterError::INVALID_SECURITY_CODE);
  130. }
  131. }
  132. $sql = "SELECT userid FROM {$this->loginDatabase}.login WHERE ";
  133. if ($this->config->getNoCase()) {
  134. $sql .= 'LOWER(userid) = LOWER(?) ';
  135. }
  136. else {
  137. $sql .= 'BINARY userid = ? ';
  138. }
  139. $sql .= 'LIMIT 1';
  140. $sth = $this->connection->getStatement($sql);
  141. $sth->execute(array($username));
  142. $res = $sth->fetch();
  143. if ($res) {
  144. throw new Flux_RegisterError('Username is already taken', Flux_RegisterError::USERNAME_ALREADY_TAKEN);
  145. }
  146. if (!Flux::config('AllowDuplicateEmails')) {
  147. $sql = "SELECT email FROM {$this->loginDatabase}.login WHERE email = ? LIMIT 1";
  148. $sth = $this->connection->getStatement($sql);
  149. $sth->execute(array($email));
  150. $res = $sth->fetch();
  151. if ($res) {
  152. throw new Flux_RegisterError('E-mail address is already in use', Flux_RegisterError::EMAIL_ADDRESS_IN_USE);
  153. }
  154. }
  155. if ($this->config->getUseMD5()) {
  156. $password = Flux::hashPassword($password);
  157. }
  158. $sql = "INSERT INTO {$this->loginDatabase}.login (userid, user_pass, email, sex, birthdate) VALUES (?, ?, ?, ?, ?)";
  159. $sth = $this->connection->getStatement($sql);
  160. $res = $sth->execute(array($username, $password, $email, $gender, $birthDate));
  161. if ($res) {
  162. $idsth = $this->connection->getStatement("SELECT LAST_INSERT_ID() AS account_id");
  163. $idsth->execute();
  164. $idres = $idsth->fetch();
  165. $createTable = Flux::config('FluxTables.AccountCreateTable');
  166. $sql = "INSERT INTO {$this->loginDatabase}.{$createTable} (account_id, userid, user_pass, sex, email, reg_date, reg_ip, confirmed) ";
  167. $sql .= "VALUES (?, ?, ?, ?, ?, NOW(), ?, 1)";
  168. $sth = $this->connection->getStatement($sql);
  169. $sth->execute(array($idres->account_id, $username, $password, $gender, $email, $_SERVER['REMOTE_ADDR']));
  170. return $idres->account_id;
  171. }
  172. else {
  173. return false;
  174. }
  175. }
  176. /**
  177. *
  178. */
  179. public function temporarilyBan($bannedBy, $banReason, $accountID, $until)
  180. {
  181. $info = $this->getBanInfo($accountID);
  182. $table = Flux::config('FluxTables.AccountBanTable');
  183. if (!$info || $info->ban_type !== '1') {
  184. $sql = "INSERT INTO {$this->loginDatabase}.$table (account_id, banned_by, ban_type, ban_until, ban_date, ban_reason) ";
  185. $sql .= "VALUES (?, ?, 1, ?, NOW(), ?)";
  186. $sth = $this->connection->getStatement($sql);
  187. $res = $sth->execute(array($accountID, $bannedBy, $until, $banReason));
  188. $ts = strtotime($until);
  189. $sql = "UPDATE {$this->loginDatabase}.login SET state = 0, unban_time = '$ts' WHERE account_id = ?";
  190. $sth = $this->connection->getStatement($sql);
  191. return $sth->execute(array($accountID));
  192. }
  193. else {
  194. return false;
  195. }
  196. }
  197. /**
  198. *
  199. */
  200. public function permanentlyBan($bannedBy, $banReason, $accountID)
  201. {
  202. $info = $this->getBanInfo($accountID);
  203. $table = Flux::config('FluxTables.AccountBanTable');
  204. if (!$info || $info->ban_type !== '2') {
  205. $sql = "INSERT INTO {$this->loginDatabase}.$table (account_id, banned_by, ban_type, ban_until, ban_date, ban_reason) ";
  206. $sql .= "VALUES (?, ?, 2, '0000-00-00 00:00:00', NOW(), ?)";
  207. $sth = $this->connection->getStatement($sql);
  208. $res = $sth->execute(array($accountID, $bannedBy, $banReason));
  209. if ($res) {
  210. $sql = "UPDATE {$this->loginDatabase}.login SET state = 5, unban_time = 0 WHERE account_id = ?";
  211. $sth = $this->connection->getStatement($sql);
  212. return $sth->execute(array($accountID));
  213. }
  214. else {
  215. return false;
  216. }
  217. }
  218. else {
  219. return false;
  220. }
  221. }
  222. /**
  223. *
  224. */
  225. public function unban($unbannedBy, $unbanReason, $accountID)
  226. {
  227. //$info = $this->getBanInfo($accountID);
  228. $table = Flux::config('FluxTables.AccountBanTable');
  229. $createTable = Flux::config('FluxTables.AccountCreateTable');
  230. //if (!$info || !$info->ban_type) {
  231. $sql = "INSERT INTO {$this->loginDatabase}.$table (account_id, banned_by, ban_type, ban_until, ban_date, ban_reason) ";
  232. $sql .= "VALUES (?, ?, 0, '0000-00-00 00:00:00', NOW(), ?)";
  233. $sth = $this->connection->getStatement($sql);
  234. $res = $sth->execute(array($accountID, $unbannedBy, $unbanReason));
  235. if ($res) {
  236. $sql = "UPDATE {$this->loginDatabase}.$createTable SET confirmed = 1, confirm_expire = NULL WHERE account_id = ?";
  237. $sth = $this->connection->getStatement($sql);
  238. $sth->execute(array($accountID));
  239. $sql = "UPDATE {$this->loginDatabase}.login SET state = 0, unban_time = 0 WHERE account_id = ?";
  240. $sth = $this->connection->getStatement($sql);
  241. return $sth->execute(array($accountID));
  242. }
  243. else {
  244. return false;
  245. }
  246. //}
  247. //else {
  248. //return false;
  249. //}
  250. }
  251. /**
  252. *
  253. */
  254. public function getBanInfo($accountID)
  255. {
  256. $table = Flux::config('FluxTables.AccountBanTable');
  257. $col = "$table.id, $table.account_id, $table.banned_by, $table.ban_type, ";
  258. $col .= "$table.ban_until, $table.ban_date, $table.ban_reason, login.userid";
  259. $sql = "SELECT $col FROM {$this->loginDatabase}.$table ";
  260. $sql .= "LEFT OUTER JOIN {$this->loginDatabase}.login ON login.account_id = $table.banned_by ";
  261. $sql .= "WHERE $table.account_id = ? ORDER BY $table.ban_date DESC ";
  262. $sth = $this->connection->getStatement($sql);
  263. $res = $sth->execute(array($accountID));
  264. if ($res) {
  265. $ban = $sth->fetchAll();
  266. return $ban;
  267. }
  268. else {
  269. return false;
  270. }
  271. }
  272. /**
  273. *
  274. */
  275. public function hasCreditsRecord($accountID)
  276. {
  277. $creditsTable = Flux::config('FluxTables.CreditsTable');
  278. $sql = "SELECT COUNT(account_id) AS hasRecord FROM {$this->loginDatabase}.$creditsTable WHERE account_id = ?";
  279. $sth = $this->connection->getStatement($sql);
  280. $sth->execute(array($accountID));
  281. if ($sth->fetch()->hasRecord) {
  282. return true;
  283. }
  284. else {
  285. return false;
  286. }
  287. }
  288. /**
  289. *
  290. */
  291. public function depositCredits($targetAccountID, $credits, $donationAmount = null)
  292. {
  293. $sql = "SELECT COUNT(account_id) AS accountExists FROM {$this->loginDatabase}.login WHERE account_id = ?";
  294. $sth = $this->connection->getStatement($sql);
  295. if (!$sth->execute(array($targetAccountID)) || !$sth->fetch()->accountExists) {
  296. return false; // Account doesn't exist.
  297. }
  298. $creditsTable = Flux::config('FluxTables.CreditsTable');
  299. if (!$this->hasCreditsRecord($targetAccountID)) {
  300. $fields = 'account_id, balance';
  301. $values = '?, ?';
  302. if (!is_null($donationAmount)) {
  303. $fields .= ', last_donation_date, last_donation_amount';
  304. $values .= ', NOW(), ?';
  305. }
  306. $sql = "INSERT INTO {$this->loginDatabase}.$creditsTable ($fields) VALUES ($values)";
  307. $sth = $this->connection->getStatement($sql);
  308. $vals = array($targetAccountID, $credits);
  309. if (!is_null($donationAmount)) {
  310. $vals[] = $donationAmount;
  311. }
  312. return $sth->execute($vals);
  313. }
  314. else {
  315. $vals = array();
  316. $sql = "UPDATE {$this->loginDatabase}.$creditsTable SET balance = balance + ? ";
  317. if (!is_null($donationAmount)) {
  318. $sql .= ", last_donation_date = NOW(), last_donation_amount = ? ";
  319. }
  320. $vals[] = $credits;
  321. if (!is_null($donationAmount)) {
  322. $vals[] = $donationAmount;
  323. }
  324. $vals[] = $targetAccountID;
  325. $sql .= "WHERE account_id = ?";
  326. $sth = $this->connection->getStatement($sql);
  327. return $sth->execute($vals);
  328. }
  329. }
  330. /**
  331. *
  332. */
  333. public function getPrefs($accountID, array $prefs = array())
  334. {
  335. $sql = "SELECT account_id FROM {$this->loginDatabase}.`login` WHERE account_id = ? LIMIT 1";
  336. $sth = $this->connection->getStatement($sql);
  337. if ($sth->execute(array($accountID)) && ($char=$sth->fetch())) {
  338. $accountPrefsTable = Flux::config('FluxTables.AccountPrefsTable');
  339. $pref = array();
  340. $bind = array($accountID);
  341. $sql = "SELECT name, value FROM {$this->loginDatabase}.$accountPrefsTable ";
  342. $sql .= "WHERE account_id = ?";
  343. if ($prefs) {
  344. foreach ($prefs as $p) {
  345. $pref[] = "name = ?";
  346. $bind[] = $p;
  347. }
  348. $sql .= sprintf(' AND (%s)', implode(' OR ', $pref));
  349. }
  350. $sth = $this->connection->getStatement($sql);
  351. if ($sth->execute($bind)) {
  352. $prefsArray = array();
  353. foreach ($sth->fetchAll() as $p) {
  354. $prefsArray[$p->name] = $p->value;
  355. }
  356. return new Flux_Config($prefsArray);
  357. }
  358. else {
  359. return false;
  360. }
  361. }
  362. else {
  363. return false;
  364. }
  365. }
  366. /**
  367. *
  368. */
  369. public function setPrefs($accountID, array $prefsArray)
  370. {
  371. $sql = "SELECT account_id FROM {$this->loginDatabase}.`login` WHERE account_id = ? LIMIT 1";
  372. $sth = $this->connection->getStatement($sql);
  373. if ($sth->execute(array($accountID)) && ($char=$sth->fetch())) {
  374. $accountPrefsTable = Flux::config('FluxTables.AccountPrefsTable');
  375. $pref = array();
  376. $bind = array($accountID);
  377. $sql = "SELECT id, name, value FROM {$this->loginDatabase}.$accountPrefsTable ";
  378. $sql .= "WHERE account_id = ?";
  379. if ($prefsArray) {
  380. foreach ($prefsArray as $prefName => $prefValue) {
  381. $pref[] = "name = ?";
  382. $bind[] = $prefName;
  383. }
  384. $sql .= sprintf(' AND (%s)', implode(' OR ', $pref));
  385. }
  386. $sth = $this->connection->getStatement($sql);
  387. if ($sth->execute($bind)) {
  388. $prefs = $sth->fetchAll();
  389. $update = array();
  390. $usql = "UPDATE {$this->loginDatabase}.$accountPrefsTable ";
  391. $usql .= "SET value = ? WHERE id = ?";
  392. $usth = $this->connection->getStatement($usql);
  393. $isql = "INSERT INTO {$this->loginDatabase}.$accountPrefsTable ";
  394. $isql .= "(account_id, name, value, create_date) ";
  395. $isql .= "VALUES (?, ?, ?, NOW())";
  396. $isth = $this->connection->getStatement($isql);
  397. foreach ($prefs as $p) {
  398. $update[$p->name] = $p->id;
  399. }
  400. foreach ($prefsArray as $pref => $value) {
  401. if (array_key_exists($pref, $update)) {
  402. $id = $update[$pref];
  403. $usth->execute(array($value, $id));
  404. }
  405. else {
  406. $isth->execute(array($accountID, $pref, $value));
  407. }
  408. }
  409. return true;
  410. }
  411. else {
  412. return false;
  413. }
  414. }
  415. else {
  416. return false;
  417. }
  418. }
  419. /**
  420. *
  421. */
  422. public function getPref($accountID, $pref)
  423. {
  424. $prefs = $this->getPrefs($accountID, array($pref));
  425. if ($prefs instanceOf Flux_Config) {
  426. return $prefs->get($pref);
  427. }
  428. else {
  429. return false;
  430. }
  431. }
  432. /**
  433. *
  434. */
  435. public function setPref($accountID, $pref, $value)
  436. {
  437. return $this->setPrefs($accountID, array($pref => $value));
  438. }
  439. /**
  440. *
  441. */
  442. public function isIpBanned($ip = null)
  443. {
  444. if (is_null($ip)) {
  445. $ip = $_SERVER['REMOTE_ADDR'];
  446. }
  447. $ip = trim($ip);
  448. if (!preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip, $m)) {
  449. // Invalid IP.
  450. return false;
  451. }
  452. $sql = "SELECT list FROM {$this->loginDatabase}.ipbanlist WHERE ";
  453. $sql .= "rtime > NOW() AND (list = ? OR list = ? OR list = ? OR list = ?) LIMIT 1";
  454. $sth = $this->connection->getStatement($sql);
  455. $list = array(
  456. sprintf('%u.*.*.*', $m[1]),
  457. sprintf('%u.%u.*.*', $m[1], $m[2]),
  458. sprintf('%u.%u.%u.*', $m[1], $m[2], $m[3]),
  459. sprintf('%u.%u.%u.%u', $m[1], $m[2], $m[3], $m[4])
  460. );
  461. $sth->execute($list);
  462. $ipban = $sth->fetch();
  463. if ($ipban) {
  464. return true;
  465. }
  466. else {
  467. return false;
  468. }
  469. }
  470. }
  471. ?>