Upgrade your PHP Version to at least PHP 5.4!'); // Configuration Management and local configuration Configuration::init($config); // Database Database::init(); // "Dynamic" Configuration Configuration::initDB(); // Create new session Session::init(); // Check if management mode was requested self::$_MANAGE_MODE = defined('SAKURA_MANAGE'); // Templating engine Templates::init(self::$_MANAGE_MODE ? Configuration::getConfig('manage_style') : Configuration::getConfig('site_style')); // Assign servers file to whois class Whois::setServers(Configuration::getLocalConfig('etc', 'whoisservers')); // Markdown Parser self::initMD(); } // Initialise Parsedown private static function initMD() { self::$_MD = new \Parsedown(); } // Parse markdown public static function mdParse($text) { return self::$_MD->text($text); } // Verify ReCAPTCHA public static function verifyCaptcha($response) { // Attempt to get the response $resp = @file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='. Configuration::getConfig('recaptcha_private') .'&response='. $response); // In the highly unlikely case that it failed to get anything forge a false if(!$resp) return array('success' => false, 'error-codes' => array('Could not connect to the ReCAPTCHA server.')); // Decode the response JSON from the servers $resp = json_decode($resp, true); // Return shit return $resp; } // Error Handler public static function errorHandler($errno, $errstr, $errfile, $errline) { // Set some variables to work with including A HUGE fallback hackjob for the templates folder $errstr = str_replace(ROOT, '', $errstr); $errfile = str_replace(ROOT, '', $errfile); $templates = ROOT .'_sakura/templates/'; switch ($errno) { case E_ERROR: case E_USER_ERROR: $error = 'FATAL ERROR: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile; break; case E_WARNING: case E_USER_WARNING: $error = 'WARNING: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile; break; case E_NOTICE: case E_USER_NOTICE: $error = 'NOTICE: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile; break; default: $error = 'Unknown error type [' . $errno . ']: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile; } // Use file_get_contents instead of Twig in case the problem is related to twig $errorPage = file_get_contents($templates. 'errorPage.tpl'); // str_replace {{ error }} on the error page with the error data $error = str_replace('{{ error }}', $error, $errorPage); // Truncate all previous outputs ob_clean(); // Die and display error message die($error); } // Send emails public static function sendMail($to, $subject, $body) { // Initialise PHPMailer $mail = new \PHPMailer(); // Set to SMTP $mail->IsSMTP(); // Set the SMTP server host $mail->Host = Configuration::getConfig('smtp_server'); // Do we require authentication? $mail->SMTPAuth = Configuration::getConfig('smtp_auth'); // Do we encrypt as well? $mail->SMTPSecure = Configuration::getConfig('smtp_secure'); // Set the port to the SMTP server $mail->Port = Configuration::getConfig('smtp_port'); // If authentication is required log in as well if(Configuration::getConfig('smtp_auth')) { $mail->Username = Configuration::getConfig('smtp_username'); $mail->Password = base64_decode(Configuration::getConfig('smtp_password')); } // Add a reply-to header $mail->AddReplyTo(Configuration::getConfig('smtp_replyto_mail'), Configuration::getConfig('smtp_replyto_name')); // Set a from address as well $mail->SetFrom(Configuration::getConfig('smtp_from_email'), Configuration::getConfig('smtp_from_name')); // Set the addressee foreach($to as $email => $name) $mail->AddBCC($email, $name); // Subject line $mail->Subject = $subject; // Set the mail type to HTML $mail->isHTML(true); // Set email contents $htmlMail = file_get_contents(ROOT .'_sakura/templates/htmlEmail.tpl'); // Replace template tags $htmlMail = str_replace('{{ sitename }}', Configuration::getConfig('sitename'), $htmlMail); $htmlMail = str_replace('{{ siteurl }}', '//'. Configuration::getLocalConfig('urls', 'main'), $htmlMail); $htmlMail = str_replace('{{ contents }}', self::mdParse($body), $htmlMail); // Set HTML body $mail->Body = $htmlMail; // Set fallback body $mail->AltBody = $body; // Send the message $send = $mail->Send(); // Clear the addressee list $mail->ClearAddresses(); // If we got an error return the error if(!$send) return $mail->ErrorInfo; // Else just return whatever return $send; } // Legacy password hashing to be able to validate passwords from users on the old backend. public static function legacyPasswordHash($data) { return hash('sha512', strrev(hash('sha512', $data))); } // Cleaning strings public static function cleanString($string, $lower = false, $nospecial = false) { // Run common sanitisation function over string $string = htmlentities($string, ENT_QUOTES | ENT_IGNORE, Configuration::getConfig('charset')); $string = stripslashes($string); $string = strip_tags($string); // If set also make the string lowercase if($lower) $string = strtolower($string); // If set remove all characters that aren't a-z or 0-9 if($nospecial) $string = preg_replace('/[^a-z0-9]/', '', $string); // Return clean string return $string; } // Getting news posts public static function getNewsPosts($limit = null, $pid = false) { // Get news posts $newsPosts = Database::fetch('news', true, ($pid ? ['id' => [$limit, '=']] : null), ['id', true], ($limit && !$pid ? [$limit] : null)); // Get user data foreach($newsPosts as $newsId => $newsPost) { $newsPosts[$newsId]['parsed'] = self::mdParse($newsPost['content']); $newsPosts[$newsId]['udata'] = Users::getUser($newsPost['uid']); $newsPosts[$newsId]['rdata'] = Users::getRank($newsPosts[$newsId]['udata']['rank_main']); // Check if a custom name colour is set and if so overwrite the rank colour if($newsPosts[$newsId]['udata']['name_colour'] != null) $newsPosts[$newsId]['rdata']['colour'] = $newsPosts[$newsId]['udata']['name_colour']; } // Return posts return $newsPosts; } // Loading info pages public static function loadInfoPage($id) { // Get contents from the database $infopage = Database::fetch('infopages', false, ['shorthand' => [$id, '=']]); // Return the data if there is any else just return false return count($infopage) ? $infopage : false; } // Validate MX records public static function checkMXRecord($email) { // Get the domain from the e-mail address $domain = substr(strstr($email, '@'), 1); // Check the MX record $record = checkdnsrr($domain, 'MX'); // Return the record data return $record; } // Check IP version public static function ipVersion($ip) { // Check if var is IP if(filter_var($ip, FILTER_VALIDATE_IP)) { // IPv4 if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) return 4; // IPv6 if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return 6; } // Not an IP or unknown type return 0; } // Convert inet_pton to string with bits public static function inetToBits($inet) { // Unpack string $unpacked = unpack('A16', $inet); // Split the string $unpacked = str_split($unpacked[1]); // Define variable $binaryIP = null; // "Build" binary IP foreach($unpacked as $char) $binaryIP .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT); // Return IP return $binaryIP; } // Match IP subnets public static function matchSubnet($ip, $range) { // Use the proper IP type switch(self::ipVersion($ip)) { case 4: // Break the range up in parts list($subnet, $bits) = explode('/', $range); // Convert IP and Subnet to long $ip = ip2long($ip); $subnet = ip2long($subnet); $mask = -1 << (32 - $bits); // In case the supplied subnet wasn't correctly aligned $subnet &= $mask; // Return true if IP is in subnet return ($ip & $mask) == $subnet; case 6: // Break the range up in parts list($subnet, $bits) = explode('/', $range); // Convert subnet to packed address and convert it to binary $subnet = inet_pton($subnet); $binarySubnet = self::inetToBits($subnet); // Convert IPv6 to packed address and convert it to binary as well $ip = inet_pton($ip); $binaryIP = self::inetToBits($ip); // Return bits of the strings according to the bits $ipBits = substr($binaryIP, 0, $bits); $subnetBits = substr($binarySubnet, 0, $bits); return ($ipBits === $subnetBits); default: return 0; } } // Check if IP is a CloudFlare IP public static function checkCFIP($ip) { // Get CloudFlare Subnet list $cfhosts = file_get_contents(Configuration::getLocalConfig('etc', 'cfhosts')); // Replace \r\n with \n $cfhosts = str_replace("\r\n", "\n", $cfhosts); // Explode the file into an array $cfhosts = explode("\n", $cfhosts); // Check if IP is in a CloudFlare subnet foreach($cfhosts as $subnet) { // Return true if found if(self::matchSubnet($ip, $subnet)) return true; } // Return false if fails return false; } // Gets IP of current visitor public static function getRemoteIP() { // Assign REMOTE_ADDR to a variables $ip = $_SERVER['REMOTE_ADDR']; // Check if the IP is a CloudFlare IP if(self::checkCFIP($ip)) { // If it is check if the CloudFlare IP header is set and if it is assign it to the ip variable if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) $ip = $_SERVER['HTTP_CF_CONNECTING_IP']; } // Return the correct IP return $ip; } // Get country code from CloudFlare header (which just returns EU if not found) public static function getCountryCode() { // Check if the required header is set and return it if(isset($_SERVER['HTTP_CF_IPCOUNTRY'])) return $_SERVER['HTTP_CF_IPCOUNTRY']; // Return EU as a fallback return 'EU'; } // Create a new action code public static function newActionCode($action, $userid, $instruct) { // Make sure the user we're working with exists if(Users::getUser($userid)['id'] == 0) return false; // Convert the instruction array to a JSON $instruct = json_encode($instruct); // Generate a key $key = sha1(date("r") . time() . $userid . $action . rand(0, 9999)); // Insert the key into the database Database::insert('actioncodes', [ 'action' => $action, 'userid' => $userid, 'actkey' => $key, 'instruction' => $instruct ]); // Return the key return $key; } // Use an action code public static function useActionCode($action, $key, $uid = 0) { // Retrieve the row from the database $keyRow = Database::fetch('actioncodes', false, [ 'actkey' => [$key, '='], 'action' => [$action, '='] ]); // Check if the code exists if(count($keyRow) <= 1) return [0, 'INVALID_CODE']; // Check if the code was intended for the user that's using this code if($keyRow['userid'] != 0) { if($keyRow['userid'] != $uid) return [0, 'INVALID_USER']; } // Remove the key from the database Database::delete('actioncodes', [ 'id' => [$keyRow['id'], '='] ]); // Return success return [1, 'SUCCESS', $keyRow['instruction']]; } // Calculate password entropy public static function pwdEntropy($pw) { // Decode utf-8 chars $pw = utf8_decode($pw); // Count the amount of unique characters in the password string and calculate the entropy return count(count_chars($pw, 1)) * log(256, 2); } // Get country name from ISO 3166 code public static function getCountryName($code) { // Parse JSON file $iso3166 = json_decode(file_get_contents(Configuration::getLocalConfig('etc', 'iso3166')), true); // Check if key exists if(array_key_exists($code, $iso3166)) return $iso3166[$code]; // If entry found return the full name else return 'Unknown'; // Else return unknown } // Get FAQ data public static function getFaqData() { // Do database call $faq = Database::fetch('faq', true, null, ['id']); // Return FAQ data return $faq; } // Get log type string public static function getLogStringFromType($type) { // Query the database $return = Database::fetch('logtypes', false, ['id' => [$type, '=']]); // Check if type exists and else return a unformattable string if(count($return) < 2) return 'Unknown action.'; // Return the string return $return['string']; } // Get formatted logs public static function getUserLogs($uid = 0) { // Check if a user is specified $conditions = ($uid ? ['uid' => [$uid, '=']] : null); // Get data from database $logsDB = Database::fetch('logs', true, $conditions, ['id', true]); // Storage array $logs = array(); // Iterate over entries foreach($logsDB as $log) { // Store usable data $logs[$log['id']] = [ 'user' => $_USER = Users::getUser($log['uid']), 'rank' => Users::getRank($_USER['rank_main']), 'string' => vsprintf(self::getLogStringFromType($log['action']), json_decode($log['attribs'], true)) ]; } // Return new logs return $logs; } // Convert a number to a hexadecimal value public static function toHex($num) { // Convert $num to an int if not yet $num = intval($num); // Check if it's within the proper range if($num < 0 || $num > 255) return 00; } }