SHARE
TWEET

Untitled

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