Advertisement
Guest User

Untitled

a guest
Apr 11th, 2018
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.29 KB | None | 0 0
  1. <?php
  2.  
  3. $config = [
  4. "Hostname" => "penguinremix.tk", // external hostname
  5. "External" => "http://play.penguinremix.tk/", // external path to play
  6. "SecretKey" => "6LfVyVEUAAAAAOEyiGKCQBKSd_YdIqWuh9f3Aad5", // reCaptcha secret key, leave blank to disable
  7. "Cloudflare" => true, // is host behind cloudflare?
  8. "Activate" => true, // activate user by default or send verification email
  9. "Clean" => true, // delete old inactive accounts?
  10. "CleanDays" => 365, // number of days before inactive accounts expire
  11. "ForceCase" => true, // force CamelCase on usernames
  12. "AllowedChars" => "A-Za-z0-9)(*&^$!`\_+={};:@~#>.<", // Allowed characters in usernames
  13. "Database" => [
  14. "Host" => // mysql host
  15. // mysql password
  16. "Name" => "Houdini" // database name
  17. ]
  18. ];
  19.  
  20. final class Database extends PDO {
  21.  
  22. private $connection = null;
  23.  
  24. public function __construct($host, $user, $password, $database) {
  25. $connectionString = sprintf("mysql:dbname=%s;host=%s", $database, $host);
  26.  
  27. parent::__construct($connectionString, $user, $password);
  28. }
  29.  
  30. public function encryptPassword($password, $md5 = true) {
  31. if($md5 !== false) {
  32. $password = md5($password);
  33. }
  34. $hash = substr($password, 16, 16) . substr($password, 0, 16);
  35. return $hash;
  36. }
  37.  
  38. public function getLoginHash($password, $staticKey) {
  39. $hash = $this->encryptPassword($password, false);
  40. $hash .= $staticKey;
  41. $hash .= 'Y(02.>\'H}t":E1';
  42. $hash = $this->encryptPassword($hash);
  43. return $hash;
  44. }
  45.  
  46. public function addUser($username, $password, $color, $email, $isActive = 0) {
  47. $hashedPassword = strtoupper(md5($password));
  48. $staticKey = 'houdini';
  49. $flashClientHash = $this->getLoginHash($hashedPassword, $staticKey);
  50. $bcryptPassword = password_hash($flashClientHash, PASSWORD_DEFAULT, [ 'cost' => 12 ]);
  51. $insertPenguin = "INSERT INTO `penguin` (`ID`, `Username`, `Nickname`, `Approval`, `Password`, `Email`, `Active`, `Color`) VALUES ";
  52. $insertPenguin .= "(NULL, :Username, :Username, 1, :Password, :Email, :Active, :Color);";
  53.  
  54. $insertStatement = $this->prepare($insertPenguin);
  55. $insertStatement->bindValue(":Username", $username);
  56. $insertStatement->bindValue(":Password", $bcryptPassword);
  57. $insertStatement->bindValue(":Email", $email);
  58. $insertStatement->bindValue(":Active", $isActive);
  59. $insertStatement->bindValue(":Color", $color);
  60.  
  61. $insertStatement->execute();
  62. $insertStatement->closeCursor();
  63.  
  64. $penguinId = $this->lastInsertId();
  65.  
  66. $this->insertInventory($penguinId, $color);
  67. $this->addActiveIgloo($penguinId);
  68. $this->sendMail($penguinId, null, 125);
  69.  
  70. return $penguinId;
  71. }
  72.  
  73. public function insertInventory($penguinId, $itemId) {
  74. $insertInventory = $this->prepare("INSERT INTO `inventory` (`PenguinID`, `ItemID`) VALUES (:PenguinID, :ItemID);");
  75. $insertInventory->bindValue(":PenguinID", $penguinId);
  76. $insertInventory->bindValue(":ItemID", $itemId);
  77. $insertInventory->execute();
  78. $insertInventory->closeCursor();
  79. }
  80.  
  81. public function sendMail($recipientId, $senderId, $postcardType) {
  82. $sendMail = $this->prepare("INSERT INTO `postcard` (`ID`, `SenderID`, `RecipientID`, `Type`) VALUES (NULL, :SenderID, :RecipientID, :Type);");
  83. $sendMail->bindValue(":RecipientID", $recipientId);
  84. $sendMail->bindValue(":SenderID", $senderId);
  85. $sendMail->bindValue(":Type", $postcardType);
  86. $sendMail->execute();
  87. $sendMail->closeCursor();
  88.  
  89. $postcardId = $this->lastInsertId();
  90.  
  91. return $postcardId;
  92. }
  93.  
  94. private function addActiveIgloo($penguinId) {
  95. $insertStatement = $this->prepare("INSERT INTO `igloo` (`ID`, `PenguinID`) VALUES (NULL, :PenguinID);");
  96. $insertStatement->bindValue(":PenguinID", $penguinId);
  97. $insertStatement->execute();
  98. $insertStatement->closeCursor();
  99.  
  100. $iglooId = $this->lastInsertId();
  101. return $iglooId;
  102. }
  103.  
  104. public function usernameTaken($username) {
  105. $usernameTaken = "SELECT Username FROM `penguins` WHERE Username = :Username;";
  106.  
  107. $takenQuery = $this->prepare($usernameTaken);
  108. $takenQuery->bindValue(":Username", $username);
  109. $takenQuery->execute();
  110.  
  111. $rowCount = $takenQuery->rowCount();
  112. $takenQuery->closeCursor();
  113.  
  114. return $rowCount > 0;
  115. }
  116.  
  117. public function createActivationKey($penguinId, $key) {
  118. $insertStatement = $this->prepare("INSERT INTO `activation_key` (`PenguinID`, `ActivationKey`) VALUES (:PenguinID, :Key);");
  119. $insertStatement->bindValue(":PenguinID", $penguinId);
  120. $insertStatement->bindValue(":Key", $key);
  121. $insertStatement->execute();
  122. $insertStatement->closeCursor();
  123. }
  124.  
  125. public function activateUser($key) {
  126. $setActive = $this->prepare("UPDATE `penguin` INNER JOIN activation on penguins.ID = activation.PenguinID " .
  127. "SET penguins.Active = 1 WHERE activation.Key = :Key;");
  128. $setActive->bindValue(":Key", $key);
  129. $setActive->execute();
  130. $deleteActivation = $this->prepare("DELETE FROM `activation` WHERE `Key` = :Key;");
  131. $deleteActivation->bindValue(":Key", $key);
  132. $deleteActivation->execute();
  133. }
  134.  
  135. public function takenUsernames($username) {
  136. $usernamesTaken = "SELECT Username FROM `penguin` WHERE Username LIKE :Username;";
  137.  
  138. $usernamesQuery = $this->prepare($usernamesTaken);
  139. $usernamesQuery->bindValue(":Username", $username . "%");
  140. $usernamesQuery->execute();
  141.  
  142. $usernames = $usernamesQuery->fetchAll(self::FETCH_COLUMN);
  143. return $usernames;
  144. }
  145.  
  146. public function cleanInactive($expiry = 10) {
  147. $deleteInactive = "DELETE FROM `penguin` WHERE Active = 0 AND RegistrationDate < :Expiry;";
  148.  
  149. $deleteQuery = $this->prepare($deleteInactive);
  150. $deleteQuery->bindValue(":Expiry", time() - strtotime("$expiry days", 0));
  151. $deleteQuery->execute();
  152. }
  153.  
  154. }
  155.  
  156. $localization = [
  157. "en" => [
  158. "terms" => "You must agree to the Rules and Terms of Use.",
  159. "name_missing" => "You need to name your penguin.",
  160. "name_short" => "Penguin name is too short.",
  161. "name_number" => "Penguin names can only contain 5 numbers.",
  162. "penguin_letter" => "Penguin names must contain at least 1 letter.",
  163. "name_not_allowed" => "That penguin name is not allowed.",
  164. "name_taken" => "That penguin name is already taken.",
  165. "name_suggest" => "That penguin name is already taken. Try {suggestion}.",
  166. "passwords_match" => "Passwords do not match.",
  167. "password_short" => "Password is too short.",
  168. "email_invalid" => "Invalid email address."
  169. ],
  170. "fr" => [
  171. "terms" => "Tu dois accepter les conditions d'utilisation.",
  172. "name_missing" => "Tu dois donner un nom à ton pingouin.",
  173. "name_short" => "Le nom de pingouin est trop court.",
  174. "name_number" => "Un nom de pingouin ne peut contenir plus de 5 nombres.",
  175. "penguin_letter" => "Un nom de pingouin doit contenir au moins une lettre.",
  176. "name_not_allowed" => "Ce nom de pingouing n'est pas autorisé.",
  177. "name_taken" => "Ce nom de pingouin est pris.",
  178. "name_suggest" => "Ce nom de pingouin est pris. Essaye {suggestion}.",
  179. "passwords_match" => "Les mots de passes ne correspondent pas.",
  180. "password_short" => "Le mot de passe est trop court.",
  181. "email_invalid" => "Adresse email invalide."
  182. ],
  183. "es" => [
  184. "terms" => "Debes seguir las reglas y los términos de uso.",
  185. "name_missing" => "Debes escoger un nombre para tu pingüino.",
  186. "name_short" => "El nombre de tu pingüino es muy corto.",
  187. "name_number" => "Los nombres de usuario sólo pueden tener 5 números.",
  188. "penguin_letter" => "Los nombres de usuario deben tener por lo menos 1 letra.",
  189. "name_not_allowed" => "Ese nombre de usuario no está permitido.",
  190. "name_taken" => "Ese nombre de usuario ya ha sido escogido.",
  191. "name_suggest" => "Ese nombre de usuario ya ha sido escogido. Intenta éste {suggestion}.",
  192. "passwords_match" => "Las contraseñas no coinciden.",
  193. "password_short" => "La contraseña es muy corta.",
  194. "email_invalid" => "El correo eléctronico es incorrecto."
  195. ],
  196. "pt" => [
  197. "terms" => "Você precisa concordar com as Regras e com os Termos de Uso.",
  198. "name_missing" => "Voce precisa nomear seu pinguim.",
  199. "name_short" => "O nome do pinguim é muito curto.",
  200. "name_number" => "O nome do pinguim só pode conter 5 números",
  201. "penguin_letter" => "O nome do seu pinguim tem de conter pelomenos uma letra.",
  202. "name_not_allowed" => "Esse nome de pinguim não é permitido.",
  203. "name_taken" => "Esse nome de pinguim já foi escolhido.",
  204. "name_suggest" => "Esse nome de pinguim já foi escolhido. Tente {suggestion}.",
  205. "passwords_match" => "As senhas não estão iguais.",
  206. "password_short" => "A senha é muito curta.",
  207. "email_invalid" => "Esse endereço de E-Mail é invalido."
  208. ]
  209. ];
  210.  
  211. session_start();
  212.  
  213. function response($data) {
  214. die(http_build_query($data));
  215. }
  216.  
  217. function attemptDataRetrieval($key, $session = false) {
  218. if(!$session && array_key_exists($key, $_POST)) {
  219. return $_POST[$key];
  220. }
  221.  
  222. if($session && array_key_exists($key, $_SESSION)) {
  223. return $_SESSION[$key];
  224. }
  225.  
  226. response([
  227. "error" => ""
  228. ]);
  229. }
  230.  
  231. function generateActivationKey($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
  232. {
  233. $str = '';
  234. $max = mb_strlen($keyspace, '8bit') - 1;
  235. for ($i = 0; $i < $length; ++$i) {
  236. $str .= $keyspace[random_int(0, $max)];
  237. }
  238. return $str;
  239. }
  240.  
  241. $action = attemptDataRetrieval("action");
  242. $lang = attemptDataRetrieval("lang");
  243.  
  244. if(!in_array($lang, array_keys($localization))) {
  245. response([
  246. "error" => ""
  247. ]);
  248. }
  249.  
  250. if($action == "validate_agreement") {
  251. $agreeTerms = attemptDataRetrieval("agree_to_terms");
  252. $agreeRules = attemptDataRetrieval("agree_to_rules");
  253. if(!$agreeTerms || !$agreeRules) {
  254. response([
  255. "error" => $localization[$lang]["terms"]
  256. ]);
  257. }
  258.  
  259. response([
  260. "success" => 1
  261. ]);
  262. } elseif($action == "validate_username") {
  263. $username = attemptDataRetrieval("username");
  264. $color = attemptDataRetrieval("colour");
  265. $colors = range(1, 15);
  266.  
  267. if(strlen($username) == 0) {
  268. response([
  269. "error" => $localization[$lang]["name_missing"]
  270. ]);
  271. } elseif(strlen($username) < 4 || strlen($username) > 12) {
  272. response([
  273. "error" => $localization[$lang]["name_short"]
  274. ]);
  275. } elseif(preg_match_all("/[0-9]/", $username) > 5) {
  276. response([
  277. "error" => $localization[$lang]["name_number"]
  278. ]);
  279. } elseif(!preg_match("/[A-z]/i", $username)) {
  280. response([
  281. "error" => $localization[$lang]["penguin_letter"]
  282. ]);
  283. } elseif(preg_match("/[^" . $config["AllowedChars"] . "]/", $username)) {
  284. response([
  285. "error" => $localization[$lang]["name_not_allowed"]
  286. ]);
  287. } elseif(!is_numeric($color) || !in_array($color, $colors)) {
  288. response([
  289. "error" => ""
  290. ]);
  291. }
  292.  
  293. $db = new Database($config["Database"]["Host"], $config["Database"]["User"],
  294. $config["Database"]["Pass"], $config["Database"]["Name"]);
  295.  
  296. if($db->usernameTaken($username)) {
  297. $username = preg_replace("/\d+$/", "", $username);
  298. $takenUsernames = $db->takenUsernames($username);
  299. $i = 1;
  300. while(true) {
  301. $suggestion = $username . $i++;
  302. if(preg_match_all("/[0-9]/", $username) > 1) {
  303. response([
  304. "error" => $localization[$lang]["name_taken"]
  305. ]);
  306. }
  307. if(!in_array(strtolower($suggestion), $takenUsernames)) {
  308. break;
  309. }
  310. }
  311. response([
  312. "error" => str_replace("{suggestion}", $suggestion, $localization[$lang]["name_suggest"])
  313. ]);
  314. }
  315.  
  316. $_SESSION['sid'] = session_id();
  317. $_SESSION['username'] = ($config["ForceCase"] ? ucfirst(strtolower($username)) : $username);
  318. $_SESSION['colour'] = $color;
  319.  
  320. response([
  321. "success" => 1,
  322. "sid" => session_id()
  323. ]);
  324. } elseif($action == "validate_password_email") {
  325. $sessionId = attemptDataRetrieval("sid", true);
  326. $username = attemptDataRetrieval("username", true);
  327. $color = attemptDataRetrieval("colour", true);
  328. $password = attemptDataRetrieval("password");
  329. $passwordConfirm = attemptDataRetrieval("password_confirm");
  330. $email = attemptDataRetrieval("email");
  331. $gtoken = attemptDataRetrieval("gtoken");
  332.  
  333. if(!empty($config["SecretKey"])) {
  334. $post_data = http_build_query(
  335. [
  336. 'secret' => $config["SecretKey"],
  337. 'response' => $gtoken,
  338. 'remoteip' => ($config["Cloudflare"] ? $_SERVER["HTTP_CF_CONNECTING_IP"] : $_SERVER['REMOTE_ADDR'])
  339. ]
  340. );
  341. $opts = ['http' => [
  342. 'method' => 'POST',
  343. 'header' => 'Content-type: application/x-www-form-urlencoded',
  344. 'content' => $post_data
  345. ]];
  346.  
  347. $context = stream_context_create($opts);
  348. $response = file_get_contents('https://www.google.com/recaptcha/api/siteverify', false, $context);
  349. $result = json_decode($response);
  350. }
  351.  
  352. if($sessionId !== session_id()) {
  353. response([
  354. "error" => ""
  355. ]);
  356. } elseif(empty($result->success) && !empty($config["SecretKey"])) {
  357. response([
  358. "error" => ""
  359. ]);
  360. } elseif($password !== $passwordConfirm) {
  361. response([
  362. "error" => $localization[$lang]["passwords_match"]
  363. ]);
  364. } elseif(strlen($password) < 4) {
  365. response([
  366. "error" => $localization[$lang]["password_short"]
  367. ]);
  368. } elseif(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
  369. response([
  370. "error" => $localization[$lang]["email_invalid"]
  371. ]);
  372. }
  373.  
  374. $db = new Database($config["Database"]["Host"], $config["Database"]["User"],
  375. $config["Database"]["Pass"], $config["Database"]["Name"]);
  376. $penguinId = $db->addUser($username, $password, $color, $email, ($config["Activate"] ? 1 : 0));
  377.  
  378. if(!$config["Activate"]) {
  379. $db->createActivationKey($penguinId, generateActivationKey(60));
  380.  
  381. $headers = "From: noreply@{$config['Hostname']}\r\n";
  382. $headers .= "Reply-To: noreply@{$config['Hostname']}\r\n";
  383. $headers .= "Return-Path: noreply@{$config['Hostname']}\r\n";
  384.  
  385. ob_start();
  386. ?>
  387.  
  388. <!doctype html>
  389. <html>
  390. <head>
  391. <title>Activate your penguin!</title>
  392. </head>
  393. <body>
  394. <p>Hello,</p>
  395. <p>Thank you for creating a penguin on <?php print($activationLink); ?>. Please click below to activate your penguin account.</p>
  396. <a href="<?php print($activationLink); ?>">Activate</a>
  397. </body>
  398. </html>
  399.  
  400. <?php
  401. $emailContent = ob_get_clean();
  402.  
  403. mail($email, "Activate your penguin!", $emailContent, $headers);
  404. }
  405.  
  406. if($config["Clean"] == true) {
  407. $db->cleanInactive($config["CleanDays"]);
  408. }
  409.  
  410. session_destroy();
  411.  
  412. response([
  413. "success" => 1
  414. ]);
  415. }
  416.  
  417. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement