diff --git a/_sakura/components/Configuration.php b/_sakura/components/Configuration.php
index 3430917..aa3ff73 100644
--- a/_sakura/components/Configuration.php
+++ b/_sakura/components/Configuration.php
@@ -6,39 +6,39 @@
namespace Flashii;
class Configuration {
-
+
public static $_LCNF;
public static $_DCNF;
-
+
// Constructor
public static function init($local) {
-
+
// Store $local in $_LCNF
if(is_array($local))
self::$_LCNF = $local;
else
die('
Failed to initialise configuration.
');
-
+
}
-
+
// Initialise Database configuration values.
- // Different from __construct as that is called before the database connection is initially
+ // Different from init as that is called before the database connection is initially
// established
public static function initDB() {
-
+
$_DATA = Database::fetch('config', true);
$_DBCN = array();
-
+
foreach($_DATA as $_CONF)
$_DBCN[$_CONF[0]] = $_CONF[1];
-
+
self::$_DCNF = $_DBCN;
-
+
}
-
+
// Get values from the configuration on the file system
public static function getLocalConfig($key, $subkey = null) {
-
+
if(array_key_exists($key, self::$_LCNF)) {
if($subkey)
return self::$_LCNF[$key][$subkey];
@@ -46,31 +46,30 @@ class Configuration {
return self::$_LCNF[$key];
} else
return null;
-
+
}
// Dynamically set local configuration values, does not update the configuration file
public static function setLocalConfig($key, $subkey, $value) {
-
+
if($subkey) {
- if(!isset(self::$_LCNF[$key])) {
+ if(!isset(self::$_LCNF[$key]))
self::$_LCNF[$key] = array();
- }
self::$_LCNF[$key][$subkey] = $value;
} else {
self::$_LCNF[$key] = $value;
}
-
+
}
-
+
// Get values from the configuration in the database
public static function getConfig($key) {
-
+
if(array_key_exists($key, self::$_DCNF))
return self::$_DCNF[$key];
else
return null;
-
+
}
-
+
}
diff --git a/_sakura/components/Hashing.php b/_sakura/components/Hashing.php
index 37cce25..076e635 100644
--- a/_sakura/components/Hashing.php
+++ b/_sakura/components/Hashing.php
@@ -32,13 +32,13 @@
namespace Flashii;
class Hashing {
-
+
// These variables can be changed without break the existing hashes
private static $_PBKDF2_HASH_ALGORITHM = 'sha256';
private static $_PBKDF2_ITERATIONS = 1000;
private static $_PBKDF2_SALT_BYTES = 24;
private static $_PBKDF2_HASH_BYTES = 24;
-
+
// Changing these will break it though
private static $_HASH_ALGORITHM_INDEX = 0;
private static $_HASH_ITERATION_INDEX = 1;
@@ -48,13 +48,14 @@ class Hashing {
// Returns an array formatted like: [algorithm, iterations, salt, hash]
public static function create_hash($pass) {
+
$salt = base64_encode(
\mcrypt_create_iv(
self::$_PBKDF2_SALT_BYTES,
MCRYPT_DEV_URANDOM
)
);
-
+
$hash = base64_encode(
self::pbkdf2(
self::$_PBKDF2_HASH_ALGORITHM,
@@ -65,24 +66,26 @@ class Hashing {
true
)
);
-
+
$passwordData = array(
self::$_PBKDF2_HASH_ALGORITHM,
self::$_PBKDF2_ITERATIONS,
$salt,
$hash
);
-
+
return $passwordData;
+
}
-
+
// Validates hashed password
public static function validate_password($password, $params) {
+
if(count($params) < self::$_HASH_SECTIONS);
return false;
-
+
$pbkdf2 = base64_decode($params[self::$_HASH_PBKDF2_INDEX]);
-
+
$validate = self::slow_equals(
$pbkdf2,
self::pbkdf2(
@@ -94,21 +97,23 @@ class Hashing {
true
)
);
-
+
return $validate;
+
}
-
+
// Compares two strings $a and $b in length-constant time.
public static function slow_equals($a, $b) {
+
$diff = strlen($a) ^ strlen($b);
-
- for($i = 0; $i < strlen($a) && $i < strlen($b); $i++) {
+
+ for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
$diff |= ord($a[$i]) ^ ord($b[$i]);
- }
-
+
return $diff === 0;
+
}
-
+
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
@@ -124,16 +129,17 @@ class Hashing {
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
-
+
private static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) {
+
$algorithm = strtolower($algorithm);
-
+
if(!in_array($algorithm, hash_algos(), true))
trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
-
+
if($count <= 0 || $key_length <= 0)
trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
-
+
if(function_exists('hash_pbkdf2')) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if($raw_output)
@@ -141,31 +147,32 @@ class Hashing {
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
-
+
$hash_length = strlen(hash($algorithm, '', true));
$block_count = ceil($key_length / $hash_length);
-
+
$output = '';
-
+
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack('N', $i);
-
+
// First iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
-
+
// Perform the other $count - 1 interations
- for($j = 1; $j < $count; $j++) {
+ for($j = 1; $j < $count; $j++)
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
- }
-
+
$output .= $xorsum;
-
+
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
+
}
+
}
-
+
}
diff --git a/_sakura/components/Main.php b/_sakura/components/Main.php
index e4d1a95..5ae12ec 100644
--- a/_sakura/components/Main.php
+++ b/_sakura/components/Main.php
@@ -5,121 +5,138 @@
namespace Flashii;
-class Flashii {
+class Main {
public $_TPL;
public $_MD;
-
+
// Constructor
- function __construct($config) {
-
+ public static function init($config) {
+
// Stop the execution if the PHP Version is older than 5.4.0
if(version_compare(phpversion(), '5.4.0', '<'))
die('Upgrade your PHP Version to at least PHP 5.4!
');
-
+
// Start session
if(session_status() != PHP_SESSION_ACTIVE)
session_start();
-
+
// Configuration Management and local configuration
Configuration::init($config);
-
+
// Database
Database::init();
-
+
// "Dynamic" Configuration
Configuration::initDB();
-
+
// Templating engine
- $this->initTwig();
-
+ self::initTwig();
+
// Markdown Parser
- $this->initParsedown();
-
+ self::initParsedown();
+
}
-
+
// Alias for Configuration::getConfig(), only exists because I'm lazy
public static function getConfig($key) {
-
+
return Configuration::getConfig($key);
-
+
}
-
+
// Initialise Twig
- private function initTwig($templateName = null, $templatesFolder = null) {
-
- // Assign default values set in the configuration if $templateName and $templatesFolder are null
- $templateName = is_null($templateName) ? Configuration::getLocalConfig('etc', 'design') : $templateName;
- $templatesFolder = is_null($templatesFolder) ? Configuration::getLocalConfig('etc', 'templatesPath') : $templatesFolder;
-
+ private static function initTwig() {
+
// Initialise Twig Filesystem Loader
- $twigLoader = new \Twig_Loader_Filesystem($templatesFolder . $templateName);
+ $twigLoader = new \Twig_Loader_Filesystem(Configuration::getLocalConfig('etc', 'templatesPath') .'/'. Configuration::getLocalConfig('etc', 'design'));
// And now actually initialise the templating engine
- $this->_TPL = new \Twig_Environment($twigLoader, array(
- // 'cache' => ROOT_DIRECTORY. $satoko['cacheFolder']
+ self::$_TPL = new \Twig_Environment($twigLoader, array(
+
+ // 'cache' => SATOKO_ROOT_DIRECTORY. self::getConfig('path', 'cache') // Set cache directory
+
));
-
+
// Load String template loader
- $this->_TPL->addExtension(new \Twig_Extension_StringLoader());
-
+ self::$_TPL->addExtension(new \Twig_Extension_StringLoader());
+
}
-
+
// Initialise Parsedown
private function initParsedown() {
-
- $this->_MD = new \Parsedown();
-
+
+ self::$_MD = new \Parsedown();
+
}
-
+
+ // Verify ReCAPTCHA
+ public static function verifyCaptcha($response) {
+
+ // Attempt to get the response
+ $resp = @file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='. self::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(self::getConfig('etc', 'localPath'), '', $errstr);
$errfile = str_replace(self::getConfig('etc', 'localPath'), '', $errfile);
$templates = (self::getConfig('etc', 'templatesPath') !== null && !empty(self::getConfig('etc', 'templatesPath'))) ? self::getConfig('etc', 'templatesPath') : '/var/www/flashii.net/_sakuya/templates/';
-
+
switch ($errno) {
+
case E_ERROR:
case E_USER_ERROR:
$error = 'FATAL ERROR: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
- break;
+ break;
case E_WARNING:
case E_USER_WARNING:
$error = 'WARNING: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
- break;
+ break;
case E_NOTICE:
case E_USER_NOTICE:
$error = 'NOTICE: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
- break;
+ break;
default:
$error = 'Unknown error type [' . $errno . ']: ' . $errstr . ' on line ' . $errline . ' in ' . $errfile;
- break;
+
}
-
+
// 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);
-
+
}
-
+
// 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)));
-
+
}
-
-}
\ No newline at end of file
+
+}
diff --git a/_sakura/components/database/mysql.php b/_sakura/components/database/mysql.php
index e8d2eef..f1d7110 100644
--- a/_sakura/components/database/mysql.php
+++ b/_sakura/components/database/mysql.php
@@ -21,29 +21,29 @@ class Database {
if(!extension_loaded('PDO')) {
// Return error and die
- die('PDO extension not loaded.
');
+ trigger_error('SQL Driver: PDO extension not loaded.', E_USER_ERROR);
}
// Initialise connection
self::initConnect(
(
- Configuration::getLocalConfig('db', 'unixsocket') ?
+ Board::getConfig('db', 'unixsocket') ?
self::prepareSock(
- Configuration::getLocalConfig('db', 'host'),
- Configuration::getLocalConfig('db', 'database')
+ Board::getConfig('db', 'host'),
+ Board::getConfig('db', 'database')
) :
self::prepareHost(
- Configuration::getLocalConfig('db', 'host'),
- Configuration::getLocalConfig('db', 'database'),
+ Board::getConfig('db', 'host'),
+ Board::getConfig('db', 'database'),
(
- Configuration::getLocalConfig('db', 'port') !== null ?
- Configuration::getLocalConfig('db', 'port') :
+ Board::getConfig('db', 'port') !== null ?
+ Board::getConfig('db', 'port') :
3306
)
)
),
- Configuration::getLocalConfig('db', 'username'),
- Configuration::getLocalConfig('db', 'password')
+ Board::getConfig('db', 'username'),
+ Board::getConfig('db', 'password')
);
}
@@ -74,7 +74,7 @@ class Database {
self::$sql = new PDO($DSN, $dbUname, $dbPword);
} catch(PDOException $e) {
// Catch connection errors
- die("" . $e->getMessage() . "
");
+ trigger_error('SQL Driver: '. $e->getMessage(), E_USER_ERROR);
}
return true;
@@ -82,30 +82,74 @@ class Database {
}
// Fetch array from database
- public static function fetch($table, $fetchAll = true, $data = null) {
-
+ public static function fetch($table, $fetchAll = true, $data = null, $order = null, $limit = null, $group = null, $distinct = false, $column = '*') {
+
// Begin preparation of the statement
- $prepare = 'SELECT * FROM `' . Configuration::getLocalConfig('db', 'prefix') . $table . '`';
-
+ $prepare = 'SELECT '. ($distinct ? 'DISTINCT ' : '') . $column .' FROM `' . Board::getConfig('db', 'prefix') . $table . '`';
+
// If $data is set and is an array continue
if(is_array($data)) {
-
+
$prepare .= ' WHERE';
-
+
foreach($data as $key => $value) {
- $prepare .= ' `'. $key .'` '. $value[1] .' :'. $key .($key == key(array_slice($data, -1, 1, true)) ? ';' : ' AND');
+ $prepare .= ' `'. $key .'` '. $value[1] .' :'. $key . ($key == key(array_slice($data, -1, 1, true)) ? '' : ' AND');
+
+ // Unset variables to be safe
+ unset($key);
+ unset($value);
}
-
+
+ }
+
+ // If $order is set and is an array continue
+ if(is_array($order)) {
+
+ $prepare .= ' ORDER BY `'. $order[0] .'`'. (!empty($order[1]) && $order[1] ? ' DESC' : '');
+
+ }
+
+ // If $group is set and is an array continue
+ if(is_array($group)) {
+
+ $prepare .= ' GROUP BY';
+
+ foreach($group as $key => $value) {
+ $prepare .= ' `'. $value .'`'. ($key == key(array_slice($group, -1, 1, true)) ? '' : ',');
+
+ // Unset variables to be safe
+ unset($key);
+ unset($value);
+ }
+
}
+ // If $limit is set and is an array continue
+ if(is_array($limit)) {
+
+ $prepare .= ' LIMIT';
+
+ foreach($limit as $key => $value) {
+ $prepare .= ' '. $value . ($key == key(array_slice($limit, -1, 1, true)) ? '' : ',');
+
+ // Unset variables to be safe
+ unset($key);
+ unset($value);
+ }
+
+ }
+
+ // Add the finishing semicolon
+ $prepare .= ';';
+
// Actually prepare the preration
$query = self::$sql->prepare($prepare);
-
+
// Bind those parameters if $data is an array that is
if(is_array($data)) {
foreach($data as $key => $value) {
$query->bindParam(':'. $key, $value[0]);
-
+
// Unset variables to be safe
unset($key);
unset($value);
@@ -114,23 +158,24 @@ class Database {
// Execute the prepared statements with parameters bound
$query->execute();
-
+
// Do fetch or fetchAll
if($fetchAll)
$result = $query->fetchAll(PDO::FETCH_BOTH);
else
$result = $query->fetch(PDO::FETCH_BOTH);
-
+
+
// And return the output
return $result;
-
+
}
// Insert data to database
public static function insert($table, $data) {
// Begin preparation of the statement
- $prepare = 'INSERT INTO `' . Configuration::getLocalConfig('db', 'prefix') . $table . '` ';
+ $prepare = 'INSERT INTO `' . Board::getConfig('db', 'prefix') . $table . '` ';
// Run the foreach statement twice for (`stuff`) VALUES (:stuff)
for($i = 0; $i < 2; $i++) {
@@ -170,7 +215,7 @@ class Database {
public static function update($table, $data) {
// Begin preparation of the statement
- $prepare = 'UPDATE `' . Configuration::getLocalConfig('db', 'prefix') . $table . '`';
+ $prepare = 'UPDATE `' . Board::getConfig('db', 'prefix') . $table . '`';
// Run a foreach on $data and complete the statement
foreach($data as $key => $values) {
diff --git a/_sakura/sakura.php b/_sakura/sakura.php
index f3a8dcd..1244ebb 100644
--- a/_sakura/sakura.php
+++ b/_sakura/sakura.php
@@ -4,11 +4,14 @@
* (c)Flashwave/Flashii Media 2013-2015
*/
+// Declare Namespace
+namespace Flashii;
+
// Start output buffering
ob_start();
// Define Sakura version
-define('SAKURA_VERSION', '20150221');
+define('SAKURA_VERSION', '20150321');
// Define Sakura Path
define('ROOT_DIRECTORY', str_replace('_sakura', '', dirname(__FILE__)));
@@ -39,7 +42,7 @@ else
set_error_handler(array('Flashii\Flashii', 'ErrorHandler'));
// Initialise Flashii Class
-$flashii = new Flashii\Flashii($fiiConf);
+Main::init($fiiConf);
// Set base page rendering data
$renderData = array(
diff --git a/main/404.php b/main/404.php
index e58cfb7..1d0e5d6 100644
--- a/main/404.php
+++ b/main/404.php
@@ -3,8 +3,11 @@
* Flashii.net Main Index
*/
+// Declare Namespace
+namespace Flashii;
+
// Include components
require_once('/var/www/flashii.net/_sakura/sakura.php');
// Print page contents
-print $flashii->_TPL->render('errors/http404.tpl', $renderData);
+print Main::$_TPL->render('errors/http404.tpl', $renderData);
diff --git a/main/index.php b/main/index.php
index 8e6e6a6..b403e5c 100644
--- a/main/index.php
+++ b/main/index.php
@@ -3,6 +3,9 @@
* Flashii.net Main Index
*/
+// Declare Namespace
+namespace Flashii;
+
// Include components
require_once('/var/www/flashii.net/_sakura/sakura.php');
@@ -12,4 +15,4 @@ $renderData['page'] = [
];
// Print page contents
-print $flashii->_TPL->render('main/index.tpl', $renderData);
+print Main::$_TPL->render('main/index.tpl', $renderData);