diff --git a/.htaccess b/.htaccess index a7528a3..3db7ac6 100644 --- a/.htaccess +++ b/.htaccess @@ -1,8 +1,5 @@ php_flag display_errors on Options -Indexes -ErrorDocument 404 /s/404 - -RewriteEngine on -RewriteRule ^s/(.*)?$ index.php?s=$1&$2 [L,QSA] - +RewriteEngine On +RewriteRule ^api/(.*)?$ index.php?api=$1&$2 [L,QSA] diff --git a/core/Api/ExecuteSelect.class.php b/core/Api/ExecuteSelect.class.php index 99d468b..b54f4fc 100644 --- a/core/Api/ExecuteSelect.class.php +++ b/core/Api/ExecuteSelect.class.php @@ -16,21 +16,19 @@ class ExecuteSelect extends Request { $this->variableParamCount = true; } - public function getDescription() { return 'Führt ein SELECT Statement aus.'; } - public function getSection() { return "Internal"; } - - public function execute($aValues = array()) { - if(!parent::execute($aValues)) { + public function execute($values = array()) { + if(!parent::execute($values)) { return false; } + $sql = $this->user->getSQL(); $this->success = false; $this->result['rows'] = array(); if(count($this->params) === 1) { - $res = $this->user->getSQL()->query($this->getParam('query')); + $res = $sql->query($this->getParam('query')); if(!$res) { - $this->lastError = 'Database Error: query() failed with ' . $this->user->getSQL()->getLastError(); + $this->lastError = 'Database Error: query() failed with ' . $sql->getLastError(); return false; } @@ -77,7 +75,7 @@ class ExecuteSelect extends Request { $tmp = array(); foreach($aSqlParams as $key => $value) $tmp[$key] = &$aSqlParams[$key]; - if($stmt = $this->user->getSQL()->connection->prepare($this->getParam('query'))) { + if($stmt = $sql->connection->prepare($this->getParam('query'))) { if(call_user_func_array(array($stmt, "bind_param"), $tmp)) { if($stmt->execute()) { @@ -89,18 +87,18 @@ class ExecuteSelect extends Request { $res->close(); $this->success = true; } else { - $this->lastError = 'Database Error: execute() failed with ' . $this->user->getSQL()->getLastError(); + $this->lastError = 'Database Error: execute() failed with ' . $sql->getLastError(); } } else { - $this->lastError = 'Database Error: get_result() failed with ' . $this->user->getSQL()->getLastError(); + $this->lastError = 'Database Error: get_result() failed with ' . $sql->getLastError(); } } else { - $this->lastError = 'Database Error: bind_param() failed with ' . $this->user->getSQL()->getLastError(); + $this->lastError = 'Database Error: bind_param() failed with ' . $sql->getLastError(); } $stmt->close(); } else { - $this->lastError = 'Database Error: prepare failed with() ' . $this->user->getSQL()->getLastError(); + $this->lastError = 'Database Error: prepare failed with() ' . $sql->getLastError(); } } diff --git a/core/Api/ExecuteStatement.class.php b/core/Api/ExecuteStatement.class.php index 309af75..7162fbc 100644 --- a/core/Api/ExecuteStatement.class.php +++ b/core/Api/ExecuteStatement.class.php @@ -16,8 +16,8 @@ class ExecuteStatement extends Request { $this->variableParamCount = true; } - public function execute($aValues = array()) { - if(!parent::execute($aValues)) { + public function execute($values = array()) { + if(!parent::execute($values)) { return false; } diff --git a/core/Api/GetLanguages.class.php b/core/Api/GetLanguages.class.php new file mode 100644 index 0000000..aec1f86 --- /dev/null +++ b/core/Api/GetLanguages.class.php @@ -0,0 +1,36 @@ +user); + $this->success = $request->execute(array('query' => $query)); + $this->lastError = $request->getLastError(); + + if($this->success) { + $this->result['languages'] = array(); + if(count($request->getResult()['rows']) === 0) { + $this->lastError = L("No languages found"); + } else { + foreach($request->getResult()['rows'] as $row) { + $this->result['languages'][$row['uid']] = $row; + } + } + } + + return $this->success; + } +}; + +?> diff --git a/core/Api/Login.class.php b/core/Api/Login.class.php new file mode 100644 index 0000000..281f5cf --- /dev/null +++ b/core/Api/Login.class.php @@ -0,0 +1,75 @@ + new StringType('username', 32), + 'password' => new StringType('password'), + )); + $this->forbidMethod("GET"); + } + + private function wrongCredentials() { + $runtime = microtime(true) - $this->startedAt; + $sleepTime = round(3e6 - $runtime); + if($sleepTime > 0) usleep($sleepTime); + return $this->createError(L('Wrong username or password')); + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + if($this->user->isLoggedIn()) { + $this->lastError = L('You are already logged in'); + $this->success = true; + return true; + } + + $this->startedAt = microtime(true); + $this->success = false; + $username = $this->getParam('username'); + $password = $this->getParam('password'); + + $query = 'SELECT User.uid, User.password, User.salt FROM User WHERE User.name=?'; + $request = new ExecuteSelect($this->user); + $this->success = $request->execute(array('query' => $query, $username)); + $this->lastError = $request->getLastError(); + + if($this->success) { + $this->success = false; + if(count($request->getResult()['rows']) === 0) { + return $this->wrongCredentials(); + $this->lastError = L('Wrong username or password'); + } else { + $row = $request->getResult()['rows'][0]; + $salt = $row['salt']; + $uid = $row['uid']; + $hash = hash('sha256', $password . $salt); + if($hash === $row['password']) { + if(!($this->success = $this->user->createSession($uid))) { + return $this->createError("Error creating Session"); + } else { + $this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds(); + } + } + else { + return $this->wrongCredentials(); + } + } + } + + return $this->success; + } +}; + +?> diff --git a/core/Api/Logout.class.php b/core/Api/Logout.class.php new file mode 100644 index 0000000..4fb6149 --- /dev/null +++ b/core/Api/Logout.class.php @@ -0,0 +1,24 @@ +loginRequired = true; + $this->apiKeyAllowed = false; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + $this->success = true; + $this->user->logout(); + return true; + } +}; + +?> diff --git a/core/Api/Request.class.php b/core/Api/Request.class.php index dd54166..cb82284 100644 --- a/core/Api/Request.class.php +++ b/core/Api/Request.class.php @@ -53,9 +53,9 @@ class Request { return "($str)"; } - public function parseParams($aValues) { + public function parseParams($values) { foreach($this->params as $name => $param) { - $value = (isset($aValues[$name]) ? $aValues[$name] : NULL); + $value = (isset($values[$name]) ? $values[$name] : NULL); if(!$param->optional && is_null($value)) { $this->lastError = 'Missing parameter: ' . $name; @@ -73,8 +73,8 @@ class Request { return true; } - public function parseVariableParams($aValues) { - foreach($aValues as $name => $value) { + public function parseVariableParams($values) { + foreach($values as $name => $value) { if(isset($this->params[$name])) continue; $type = Parameter\Parameter::parseType($value); $param = new Parameter\Parameter($name, $type, true); @@ -83,7 +83,7 @@ class Request { } } - public function execute($aValues = array()) { + public function execute($values = array()) { $this->params = $this->aDefaultParams; $this->success = false; $this->result = array(); @@ -94,10 +94,10 @@ class Request { } if($this->externCall) { - $aValues = $_REQUEST; + $values = $_REQUEST; if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER["CONTENT_TYPE"]) && in_array("application/json", explode(";", $_SERVER["CONTENT_TYPE"]))) { $jsonData = json_decode(file_get_contents('php://input'), true); - $aValues = array_merge($aValues, $jsonData); + $values = array_merge($values, $jsonData); } } @@ -121,8 +121,8 @@ class Request { if($this->loginRequired) { $authorized = false; - if(isset($aValues['api_key']) && $this->apiKeyAllowed) { - $apiKey = $aValues['api_key']; + if(isset($values['api_key']) && $this->apiKeyAllowed) { + $apiKey = $values['api_key']; $authorized = $this->user->authorize($apiKey); } @@ -133,11 +133,11 @@ class Request { } } - if(!$this->parseParams($aValues)) + if(!$this->parseParams($values)) return false; if($this->variableParamCount) - $this->parseVariableParams($aValues); + $this->parseVariableParams($values); if(!$this->user->getSQL()->isConnected()) { $this->lastError = $this->user->getSQL()->getLastError(); diff --git a/core/Api/SetLanguage.php b/core/Api/SetLanguage.php new file mode 100644 index 0000000..4c59911 --- /dev/null +++ b/core/Api/SetLanguage.php @@ -0,0 +1,75 @@ + new Parameter('langId', Parameter::TYPE_INT, true, NULL), + 'langCode' => new StringType('langCode', 5, true, NULL), + )); + } + + private function checkLanguage() { + $langId = $this->getParam("langId"); + $langCode = $this->getParam("langCode"); + + if(is_null($langId) && is_null($langCode)) { + return $this->createError(L("Either langId or langCode must be given")); + } + + $query = "SELECT uid, code, name FROM Language WHERE uid=? OR code=?"; + $request = new ExecuteSelect($this->user); + $this->success = $request->execute(array("query" => $query, $langId, $langCode)); + $this->lastError = $request->getLastError(); + + if($this->success) { + if(count($request->getResult()['rows']) == 0) { + return $this->createError(L("This Language does not exist")); + } else { + $row = $request->getResult()['rows'][0]; + $this->language = \Objects\Language::newInstance($row['uid'], $row['code'], $row['name']); + if(!$this->language) { + return $this->createError(L("Error while loading language")); + } + } + } + + return $this->success; + } + + private function updateLanguage() { + $languageId = $this->language->getId(); + $userId = $this->user->getId(); + + $query = "UPDATE User SET uidLanguage = ? WHERE uid = ?"; + $request = new ExecuteStatement($this->user); + $this->success = $request->execute(array("query" => $query, $languageId, $userId)); + $this->lastError = $request->getLastError(); + return $this->success; + } + + public function execute($values = array()) { + if(!parent::execute($values)) { + return false; + } + + if(!$this->checkLanguage()) + return false; + + if($this->user->isLoggedIn()) { + $this->updateLanguage(); + } + + $this->user->setLangauge($this->language); + return $this->success; + } +}; + +?> diff --git a/core/Configuration/database.sql b/core/Configuration/database.sql index 7833a24..3b4ee9d 100644 --- a/core/Configuration/database.sql +++ b/core/Configuration/database.sql @@ -30,7 +30,7 @@ CREATE TABLE IF NOT EXISTS UserGroup ( UNIQUE(`uid`, `gid`) ); -CREATE TABLE Session IF NOT EXISTS ( +CREATE TABLE IF NOT EXISTS Session ( `uid` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, `expires` timestamp NOT NULL, `uidUser` int(11) NOT NULL, @@ -52,8 +52,13 @@ CREATE TABLE IF NOT EXISTS Language ( `name` VARCHAR(32) UNIQUE NOT NULL ); +INSERT INTO Language (`uid`, `code`, `name`) VALUES + (1, 'en_US', 'American English'), + (2, 'de_DE', 'Deutsch Standard') + ON DUPLICATE KEY UPDATE name=name; + CREATE TABLE IF NOT EXISTS ExternalSiteCache ( `url` VARCHAR(256) PRIMARY KEY, `data` TEXT NOT NULL, - `expires` TIMESTAMP DEFAULT NULL + `expires` DATETIME DEFAULT NULL ); diff --git a/core/Documents/Admin.class.php b/core/Documents/Admin.class.php new file mode 100644 index 0000000..14b0425 --- /dev/null +++ b/core/Documents/Admin.class.php @@ -0,0 +1,70 @@ +databseRequired = false; + } + } +} + +namespace Documents\Admin { + + class Head extends \Elements\Head { + + public function __construct($document) { + parent::__construct($document); + } + + protected function initSources() { + $this->loadJQuery(); + $this->loadBootstrap(); + $this->loadFontawesome(); + $this->addJS(\Elements\Script::CORE); + $this->addCSS(\Elements\Link::CORE); + $this->addJS(\Elements\Script::ADMIN); + $this->addCSS(\Elements\Link::ADMIN); + } + + protected function initMetas() { + return array( + array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'), + array('name' => 'format-detection', 'content' => 'telephone=yes'), + array('charset' => 'utf-8'), + array("http-equiv" => 'expires', 'content' => '0'), + array("name" => 'robots', 'content' => 'noarchive'), + ); + } + + protected function initRawFields() { + return array(); + } + + protected function initTitle() { + return "WebBase - Administration"; + } + } + + class Body extends \Elements\Body { + + public function __construct($document) { + parent::__construct($document); + } + + public function getCode() { + $html = parent::getCode(); + + $document = $this->getDocument(); + if(!$document->getUser()->isLoggedIn()) { + $html .= new \Views\Login($document); + } else { + $html .= "You are logged in :]"; + } + + return $html; + } + } +} + +?> diff --git a/core/Documents/Install.class.php b/core/Documents/Install.class.php index 7aa122a..77a2f3e 100644 --- a/core/Documents/Install.class.php +++ b/core/Documents/Install.class.php @@ -97,24 +97,23 @@ namespace Documents\Install { return self::DATABASE_CONFIGURATION; } - $query = "SELECT * FROM User"; - $sql = $user->getSQL(); - if(!is_null($sql) && $sql->isConnected()) { - $res = $sql->query($query); - if($res) { - if($res->num_rows === 0) { - $step = self::CREATE_USER; - } else { - $step = self::ADD_MAIL_SERVICE; - } + $request = new \Api\ExecuteSelect($user); + $success = $request->execute(array("query" => "SELECT COUNT(*) AS count FROM User")); + $this->errorString = $request->getLastError(); + + if($success) { + if($request->getResult()['rows'][0]["count"] > 0) { + $step = self::ADD_MAIL_SERVICE; + } else { + return self::CREATE_USER; } } else { - $step = self::DATABASE_CONFIGURATION; + return self::DATABASE_CONFIGURATION; } - if($step == self::ADD_MAIL_SERVICE && $config->isFilePresent("Mail")) { + if($step === self::ADD_MAIL_SERVICE && $config->isFilePresent("Mail")) { $step = self::FINISH_INSTALLATION; - if(!$config->isFilePresent("JWT") && $config->create("JWT", generateRandomString(32))) { + if(!$config->isFilePresent("JWT") && !$config->create("JWT", generateRandomString(32))) { $this->errorString = "Unable to create jwt file"; } } @@ -532,7 +531,8 @@ namespace Documents\Install { array("title" => "Username", "name" => "username", "type" => "text", "required" => true), array("title" => "Password", "name" => "password", "type" => "password", "required" => true), array("title" => "Confirm Password", "name" => "confirmPassword", "type" => "password", "required" => true), - ) + ), + "previousButton" => true ), self::ADD_MAIL_SERVICE => array( "title" => "Optional: Add Mail Service", @@ -550,7 +550,8 @@ namespace Documents\Install { ) )), ), - "skip" => true + "skip" => true, + "previousButton" => true ), self::FINISH_INSTALLATION => array( "title" => "Finish Installation", @@ -562,8 +563,8 @@ namespace Documents\Install { return ""; } - $prevDisabled = ($this->currentStep <= self::DATABASE_CONFIGURATION); $currentView = $views[$this->currentStep]; + $prevDisabled = !isset($currentView["previousButton"]) || !$currentView["previousButton"]; $spinnerIcon = $this->createIcon("spinner"); $title = $currentView["title"]; @@ -695,6 +696,7 @@ namespace Documents\Install { $progressSidebar = $this->createProgressSidebar(); $progressMainview = $this->createProgessMainview(); $errorStyle = ($this->errorString ? '' : ' style="display:none"'); + $errorClass = ($this->errorString ? ' alert-danger' : ''); $html .= " @@ -718,7 +720,7 @@ namespace Documents\Install {
$progressMainview -
$this->errorString
+
$this->errorString
diff --git a/core/Driver/SQL.class.php b/core/Driver/SQL.class.php index d5c3e89..80a1bf1 100644 --- a/core/Driver/SQL.class.php +++ b/core/Driver/SQL.class.php @@ -97,6 +97,15 @@ class SQL { return false; } + while (($success = $this->connection->next_result())) { + if (!$this->connection->more_results()) break; + } + + if(!$success) { + $this->lastError = mysqli_error($this->connection); + return false; + } + return true; } diff --git a/core/Elements/Link.class.php b/core/Elements/Link.class.php index 18e9176..5424d05 100644 --- a/core/Elements/Link.class.php +++ b/core/Elements/Link.class.php @@ -17,7 +17,7 @@ class Link extends Source { // const HIGHLIGHT = '/css/highlight.css'; // const HIGHLIGHT_THEME = '/css/theme.css'; const CORE = "/css/style.css"; - // const ADMIN = "/css/admin.css"; + const ADMIN = "/css/admin.css"; // const HOME = "/css/home.css"; // const REVEALJS = "/css/reveal.css"; // const REVEALJS_THEME_MOON = "/css/reveal_moon.css"; diff --git a/core/Elements/Script.class.php b/core/Elements/Script.class.php index 022351c..f8a2592 100644 --- a/core/Elements/Script.class.php +++ b/core/Elements/Script.class.php @@ -8,7 +8,7 @@ class Script extends Source { const CORE = "/js/script.js"; // const HOME = "/js/home.js"; - // const ADMIN = "/js/admin.js"; + const ADMIN = "/js/admin.js"; // const SORTTABLE = "/js/sorttable.js"; const JQUERY = "/js/jquery.min.js"; // const JQUERY_UI = "/js/jquery-ui.js"; diff --git a/core/Objects/Language.class.php b/core/Objects/Language.class.php index c20e380..928f3b5 100644 --- a/core/Objects/Language.class.php +++ b/core/Objects/Language.class.php @@ -1,133 +1,135 @@ languageId = $languageId; - $this->langCode = $langCode; - $this->langName = $langName; - $this->entries = array(); - $this->modules = array(); - } - - public function getId() { return $this->languageId; } - public function getCode() { return $this->langCode; } - public function getShortCode() { return substr($this->langCode, 0, 2); } - public function getName() { return $this->langName; } - public function getIconPath() { return "/img/icons/lang/$this->langCode.gif"; } - public function getEntries() { return $this->entries; } - public function getModules() { return $this->modules; } - - public function loadModule($module) { - if(!is_object($module)) - $module = new $module; - - $aModuleEntries = $module->getEntries($this->langCode); - $this->entries = array_merge($this->entries, $aModuleEntries); - $this->modules[] = $module; - } - - public function translate($key) { - if(isset($this->entries[$key])) - return $this->entries[$key]; - - return $key; - } - - public function sendCookie() { - setcookie('lang', $this->langCode, 0, "/", ""); - } - - public function jsonSerialize() { - return array( - 'uid' => $this->languageId, - 'code' => $this->langCode, - 'name' => $this->langName, - ); - } - - public static function newInstance($languageId, $langCode, $langName) { - - if(!preg_match(Language::LANG_CODE_PATTERN, $langCode)) { - return false; + public function __construct($languageId, $langCode, $langName) { + $this->languageId = $languageId; + $this->langCode = $langCode; + $this->langName = $langName; + $this->entries = array(); + $this->modules = array(); } - // TODO: include dynamically wanted Language - return new Language($languageId, $langCode, $langName); + public function getId() { return $this->languageId; } + public function getCode() { return $this->langCode; } + public function getShortCode() { return substr($this->langCode, 0, 2); } + public function getName() { return $this->langName; } + public function getIconPath() { return "/img/icons/lang/$this->langCode.gif"; } + public function getEntries() { return $this->entries; } + public function getModules() { return $this->modules; } - // $className = $langCode - // return new $className($languageId, $langCode); - } + public function loadModule($module) { + if(!is_object($module)) + $module = new $module; - public function load() { - global $LANGUAGE; - $LANGUAGE = $this; - } + $aModuleEntries = $module->getEntries($this->langCode); + $this->entries = array_merge($this->entries, $aModuleEntries); + $this->modules[] = $module; + } - public static function DEFAULT_LANGUAGE() { - if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - $acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE']; - $aSplit = explode(',',$acceptLanguage); - foreach($aSplit as $code) { - if(strlen($code) == 2) { - $code = $code . '_' . strtoupper($code); - } + public function translate($key) { + if(isset($this->entries[$key])) + return $this->entries[$key]; - $code = str_replace("-", "_", $code); - if(strlen($code) != 5) - continue; + return $key; + } - $lang = Language::newInstance(0, $code, ""); - if($lang) - return $lang; + public function sendCookie() { + setcookie('lang', $this->langCode, 0, "/", ""); + } + + public function jsonSerialize() { + return array( + 'uid' => $this->languageId, + 'code' => $this->langCode, + 'name' => $this->langName, + ); + } + + public static function newInstance($languageId, $langCode, $langName) { + + if(!preg_match(Language::LANG_CODE_PATTERN, $langCode)) { + return false; } + + // TODO: include dynamically wanted Language + return new Language($languageId, $langCode, $langName); + + // $className = $langCode + // return new $className($languageId, $langCode); } - return Language::newInstance(1, "en_US", "American English"); + public function load() { + global $LANGUAGE; + $LANGUAGE = $this; + } + + public static function DEFAULT_LANGUAGE() { + if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + $acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE']; + $aSplit = explode(',',$acceptLanguage); + foreach($aSplit as $code) { + if(strlen($code) == 2) { + $code = $code . '_' . strtoupper($code); + } + + $code = str_replace("-", "_", $code); + if(strlen($code) != 5) + continue; + + $lang = Language::newInstance(0, $code, ""); + if($lang) + return $lang; + } + } + + return Language::newInstance(1, "en_US", "American English"); + } + }; +} + +namespace { + function L($key) { + if(!array_key_exists('LANGUAGE', $GLOBALS)) + return $key; + + global $LANGUAGE; + return $LANGUAGE->translate($key); } -}; -function L($key) { - if(!array_key_exists('LANGUAGE', $GLOBALS)) - return $key; + function LANG_NAME() { + if(!array_key_exists('LANGUAGE', $GLOBALS)) + return "LANG_NAME"; - global $LANGUAGE; - return $LANGUAGE->translate($key); + global $LANGUAGE; + return $LANGUAGE->getName(); + } + + function LANG_CODE() { + if(!array_key_exists('LANGUAGE', $GLOBALS)) + return "LANG_CODE"; + + global $LANGUAGE; + return $LANGUAGE->getCode(); + } + + function SHORT_LANG_CODE() { + if(!array_key_exists('LANGUAGE', $GLOBALS)) + return "SHORT_LANG_CODE"; + + global $LANGUAGE; + return $LANGUAGE->getShortCode(); + } } - -function LANG_NAME() { - if(!array_key_exists('LANGUAGE', $GLOBALS)) - return "LANG_NAME"; - - global $LANGUAGE; - return $LANGUAGE->getName(); -} - -function LANG_CODE() { - if(!array_key_exists('LANGUAGE', $GLOBALS)) - return "LANG_CODE"; - - global $LANGUAGE; - return $LANGUAGE->getCode(); -} - -function SHORT_LANG_CODE() { - if(!array_key_exists('LANGUAGE', $GLOBALS)) - return "SHORT_LANG_CODE"; - - global $LANGUAGE; - return $LANGUAGE->getShortCode(); -} - ?> diff --git a/core/Objects/Session.class.php b/core/Objects/Session.class.php index 2441146..2de2af8 100644 --- a/core/Objects/Session.class.php +++ b/core/Objects/Session.class.php @@ -19,18 +19,27 @@ class Session extends ApiObject { } private function updateMetaData() { - $userAgent = get_browser($_SERVER['HTTP_USER_AGENT'], true); $this->expires = time() + Session::DURATION * 60; $this->ipAddress = $_SERVER['REMOTE_ADDR']; - $this->os = $userAgent['platform']; - $this->browser = $userAgent['parent']; + try { + $userAgent = @get_browser($_SERVER['HTTP_USER_AGENT'], true); + $this->os = $userAgent['platform'] ?? "Unknown"; + $this->browser = $userAgent['parent'] ?? "Unknown"; + } catch(\Exception $ex) { + $this->os = "Unknown"; + $this->browser = "Unknown"; + } } public function sendCookie() { $this->updateMetaData(); - $token = array('userId' => $this->user->getId(), 'sessionId' => $this->sessionId); - $sessionCookie = JWT::encode($token, getJwtKey()); - setcookie('session', $sessionCookie, $this->expires, "/", "", true); + $jwt = $this->user->getConfiguration()->getJwt(); + if($jwt) { + $token = array('userId' => $this->user->getId(), 'sessionId' => $this->sessionId); + $sessionCookie = \External\JWT::encode($token, $jwt->getKey()); + $secure = strcmp(getProtocol(), "https") === 0; + setcookie('session', $sessionCookie, $this->expires, "/", "", $secure); + } } public function getExpiresTime() { @@ -56,7 +65,7 @@ class Session extends ApiObject { $this->updateMetaData(); $query = 'INSERT INTO Session (expires, uidUser, ipAddress, os, browser) VALUES (DATE_ADD(NOW(), INTERVAL ? MINUTE),?,?,?,?)'; - $request = new CExecuteStatement($this->user); + $request = new \Api\ExecuteStatement($this->user); $success = $request->execute(array( 'query' => $query, @@ -77,7 +86,7 @@ class Session extends ApiObject { public function destroy() { $query = 'DELETE FROM Session WHERE Session.uid=? OR Session.expires<=NOW()'; - $request = new CExecuteStatement($this->user); + $request = new \Api\ExecuteStatement($this->user); $success = $request->execute(array('query' => $query, $this->sessionId)); return $success; } @@ -88,7 +97,7 @@ class Session extends ApiObject { SET Session.expires=DATE_ADD(NOW(), INTERVAL ? MINUTE), Session.ipAddress=?, Session.os=?, Session.browser=? WHERE Session.uid=?'; - $request = new CExecuteStatement($this->user); + $request = new \Api\ExecuteStatement($this->user); $success = $request->execute(array( 'query' => $query, Session::DURATION, diff --git a/core/Objects/User.class.php b/core/Objects/User.class.php index ea577b6..e3ddc79 100644 --- a/core/Objects/User.class.php +++ b/core/Objects/User.class.php @@ -80,8 +80,10 @@ class User extends ApiObject { } public function updateLanguage($lang) { - $request = new CSetLanguage($this); - return $request->execute(array("langCode" => $lang)); + if($this->sql) { + $request = new \Api\SetLanguage($this); + return $request->execute(array("langCode" => $lang)); + } } public function sendCookies() { @@ -93,12 +95,12 @@ class User extends ApiObject { } public function readData($userId, $sessionId, $sessionUpdate = true) { - $query = 'SELECT User.name as userName, Language.uid as langId, Language.code as langCode + $query = 'SELECT User.name as userName, Language.uid as langId, Language.code as langCode, Language.name as langName FROM User INNER JOIN Session ON User.uid=Session.uidUser LEFT JOIN Language ON User.uidLanguage=Language.uid WHERE User.uid=? AND Session.uid=? AND Session.expires>now()'; - $request = new CExecuteSelect($this); + $request = new \Api\ExecuteSelect($this); $success = $request->execute(array('query' => $query, $userId, $sessionId)); if($success) { @@ -108,12 +110,12 @@ class User extends ApiObject { $row = $request->getResult()['rows'][0]; $this->username = $row['userName']; $this->uid = $userId; - $this->session = new CSession($this, $sessionId); + $this->session = new Session($this, $sessionId); if($sessionUpdate) $this->session->update(); $this->loggedIn = true; if(!is_null($row['langId'])) { - $this->setLangauge(CLanguage::newInstance($row['langId'], $row['langCode'])); + $this->setLangauge(Language::newInstance($row['langId'], $row['langCode'], $row['langName'])); } } } @@ -128,7 +130,7 @@ class User extends ApiObject { && ($jwt = $this->configuration->getJWT())) { try { $token = $_COOKIE['session']; - $decoded = (array)External\JWT::decode($token, $jwt->getKey()); + $decoded = (array)\External\JWT::decode($token, $jwt->getKey()); if(!is_null($decoded)) { $userId = (isset($decoded['userId']) ? $decoded['userId'] : NULL); $sessionId = (isset($decoded['sessionId']) ? $decoded['sessionId'] : NULL); @@ -143,9 +145,9 @@ class User extends ApiObject { if(isset($_GET['lang']) && is_string($_GET["lang"]) && !empty($_GET["lang"])) { $this->updateLanguage($_GET['lang']); - } else if(isset($_COOKIE['lang']) && is_string($_COOKIE["lang"]) && !empty($_COOKIE["lang"])) { + }/* else if(isset($_COOKIE['lang']) && is_string($_COOKIE["lang"]) && !empty($_COOKIE["lang"])) { $this->updateLanguage($_COOKIE['lang']); - } + }*/ } public function createSession($userId) { @@ -164,7 +166,7 @@ class User extends ApiObject { LEFT JOIN Language ON User.uidLanguage=Language.uid WHERE api_key=? AND valid_until > now() AND User.uid = ApiKey.uidUser'; - $request = new CExecuteSelect($this); + $request = new \Api\ExecuteSelect($this); $success = $request->execute(array('query' => $query, $apiKey)); if($success) { diff --git a/core/View.class.php b/core/View.class.php index c6293f9..664a887 100644 --- a/core/View.class.php +++ b/core/View.class.php @@ -226,4 +226,5 @@ abstract class View { } }; + ?> diff --git a/core/Views/LanguageFlags.class.php b/core/Views/LanguageFlags.class.php new file mode 100644 index 0000000..72916b7 --- /dev/null +++ b/core/Views/LanguageFlags.class.php @@ -0,0 +1,54 @@ +getDocument()->getUser()); + $params = explode("&", $queryString); + $query = array(); + foreach($params as $param) { + $aParam = explode("=", $param); + $key = $aParam[0]; + + if($key == "s" && startsWith($requestUri, "/s/")) + continue; + + $val = (isset($aParam[1]) ? $aParam[1] : ""); + if(!empty($key)) { + $query[$key] = $val; + } + } + + $url = parse_url($requestUri, PHP_URL_PATH) . "?"; + if($request->execute()) { + foreach($request->getResult()['languages'] as $lang) { + $langCode = $lang['code']; + $langName = $lang['name']; + $query['lang'] = $langCode; + $queryString = http_build_query($query); + + $flags[] = $this->createLink( + "$url$queryString", + "\"$langName\"" + ); + } + } else { + $flags[] = $this->createErrorText($request->getLastError()); + } + + return implode('', $flags); + } +} + +?> diff --git a/core/Views/Login.class.php b/core/Views/Login.class.php new file mode 100644 index 0000000..46ae1c8 --- /dev/null +++ b/core/Views/Login.class.php @@ -0,0 +1,57 @@ +getDocument()); + $iconBack = $this->createIcon("arrow-circle-left", "right"); + $domain = $_SERVER['HTTP_HOST']; + $protocol = getProtocol(); + + $accountCreated = ""; + if(isset($_GET["accountCreated"])) { + $accountCreated .= ' +
+ Your account was successfully created, you may now login with your credentials +
'; + } + + $html = " +
+
+

Admin Control Panel

+
+
+
+ + + + + +
+
+ $flags + $iconBack$backToStartPage + $accountCreated +
+
"; + + return $html; + + return $html; + } +} + +?> diff --git a/core/core.php b/core/core.php index 082eb5c..4eb87e5 100644 --- a/core/core.php +++ b/core/core.php @@ -24,6 +24,10 @@ return $result; } + function getProtocol() { + return stripos($_SERVER['SERVER_PROTOCOL'],'https') === 0 ? 'https://' : 'http://'; + } + function includeDir($dir, $aIgnore = array(), $recursive = false) { $aIgnore[] = '.'; $aIgnore[] = '..'; diff --git a/css/admin.css b/css/admin.css new file mode 100644 index 0000000..08c2c09 --- /dev/null +++ b/css/admin.css @@ -0,0 +1,229 @@ +.loginContainer { + border-radius: 5px; + width: 600px; + position: relative; +} + +.loginForm { + padding: 25px; + border: 1px solid #bbb; + border-radius: 5px; + background-color: #bbb; + color: black; +} + +.loginForm input { + margin-bottom: 10px; +} + +.loginForm button, .loginForm div { + margin-top: 15px; + margin-bottom: 0; +} + +.loginForm input[type="checkbox"] { + padding: 0; + margin-bottom: 0; + vertical-align: bottom; +} + +.device-table > tbody > tr:hover { + cursor: pointer; + background-color: grey; +} + +.device-table > tbody > tr > td:nth-child(3) { + text-align: center; +} + +.apikey-table > tbody > tr > td:last-child { + float: right; +} + +.apikey-table > tbody > tr > td:first-child { + word-break: break-all; +} + +.apikey-table > tbody > tr:hover { + background-color: grey; + cursor: pointer; +} + +.sidebar { + margin: 0; + padding: 0; + color: white; + text-align: center; +} + +.sidebar a { + color: white; +} + +.status { font-size: 12px; } +.status-ok { color: #38e40d; } +.status-error { color: red; } +.status-offline { color: gray; } + +.sidebar .nav-item { + line-height: 30px; + border-bottom: 1px solid gray; +} + +.nav-link { + grid-template-columns: 20px auto 20px; +} + +.nav-link-center { + grid-column-start: 2; + grid-column-end: 2; +} + +.sidebar-title { + font-size: 18px; + margin-bottom: 0.5em; + font-weight: bold; +} + +.sidebar-top { + height: 100px; + padding: 10px; + border-bottom: 5px solid gray; + background-color: #555; +} + +.sidebar-bottom { + height: 100px; + padding: 10px; + position: absolute; + bottom: 0; + border-top: 5px solid gray; + width: 100%; + grid-template-rows: 50% 50%; +} + +.grid { + display: grid; +} + +.grid > span { + align-self: center; +} + +.sidebar-bottom > a { + grid-template-columns: 20px auto; + align-self: center; +} + +.sidebar-bottom > a:hover { + text-decoration: none; + font-weight: bold; +} + +.sidebar .active .nav-link-center { + text-decoration: underline; + font-weight: bold; +} + +.sidebar .nav-item:hover { + background-color: gray; +} + +.nav-device { + background-color: #5a5a5a; +} + +.service { + margin: 10px; +} + +.service > .card-header { + color: black; + cursor: pointer; + display: grid; + grid-template-columns: 20px auto; +} + +.service-icon { + width: 32px; + height: 32px; +} + +.fs-listview > tbody > tr:hover { + cursor: pointer; +} + +.fs-listview > tbody > tr.downloading > td, .fs-gridview > div.downloading > span { + font-style: italic; + color: gray; +} + +.fs-toolbar { + display: grid; + grid-template-columns: 1px 40px 40px 1px auto 1px 40px 1px 40px 40px 1px; +} + +.fs-toolbar > i { + align-self: center; + cursor: pointer; +} + +.fs-toolbar > span { + text-align: left; + align-self: center; +} + +.fs-gridview { + display: grid; + grid-template-columns: repeat(4, auto); +} + +.fs-gridview div { + align-self: center; + text-align: center; +} + +.fs-gridview > div { + padding: 5px; + display: grid; + grid-template-rows: 48px auto; +} + +.fs-gridview > div:hover { + background: #ddd; + cursor: pointer; +} + +.vr { + border-left: 1px solid #dee2e6; + height: 100%; +} + +.camera-stream { + cursor: pointer; +} + +.temperature-controls { + display: grid; + grid-template-columns: auto 30px; +} + +.temperature-controls > div { + align-self: center; + text-align: left; +} + +.shell-tabs > li > a { + color: black; + line-height: inherit; + outline: none; +} + +.shell { + text-align: left +} + +.speaker-controls { + display:grid; + grid-template-columns: auto auto auto; +} diff --git a/index.php b/index.php index 1ffdfd5..5d2dbeb 100644 --- a/index.php +++ b/index.php @@ -10,6 +10,10 @@ function getWebRoot() { return dirname(__FILE__); } +function createError($msg) { + return json_encode(array("success" => false, "msg" => $msg)); +} + spl_autoload_extensions(".php"); spl_autoload_register(function($class) { $full_path = getClassPath($class); @@ -27,11 +31,42 @@ $config = new Configuration\Configuration(); $installation = (!$config->load()); $user = new Objects\User($config); -if ($installation) { - $document = new Documents\Install($user); +if(isset($_GET["api"]) && is_string($_GET["api"])) { + header("Content-Type: application/json"); + if($installation) { + $response = createError("Not installed"); + } else { + $apiFunction = $_GET["api"]; + if(empty($apiFunction)) { + header("403 Forbidden"); + $response = ""; + } else if(!preg_match("/[a-zA-Z]+(\/[a-zA-Z]+)*/", $apiFunction)) { + $response = createError("Invalid Method"); + } else { + $apiFunction = strtoupper($apiFunction[0]) . substr($apiFunction, 1); + $class = "\\Api\\$apiFunction"; + $file = getClassPath($class); + if(!file_exists($file)) { + header("404 Not Found"); + $response = createError("Not found"); + } else { + $request = new $class($user, true); + $success = $request->execute(); + $msg = $request->getLastError(); + $response = $request->getJsonResult(); + } + } + } } else { - print("DON'T INSTALL"); + if ($installation) { + $document = new Documents\Install($user); + } else { + $document = new Documents\Admin($user); + } + + $response = $document->getCode(); } -die($document->getCode()); +$user->sendCookies(); +die($response); ?> diff --git a/js/admin.js b/js/admin.js new file mode 100644 index 0000000..4cb2ff0 --- /dev/null +++ b/js/admin.js @@ -0,0 +1,25 @@ +$(document).ready(function() { + $("#username").keypress(function(e) { if(e.which == 13) $("#password").focus(); }); + $("#password").keypress(function(e) { if(e.which == 13) $("#btnLogin").click(); }); + $("#btnLogin").click(function() { + var username = $("#username").val(); + var password = $("#password").val(); + var errorDiv = $("#loginError"); + var createdDiv = $("#accountCreated"); + var btn = $(this); + + errorDiv.hide(); + btn.prop("disabled", true); + btn.html("Logging in… "); + jsCore.apiCall("login", {"username": username, "password": password}, function(data) { + window.location.reload(); + }, function(err) { + btn.html("Login"); + btn.prop("disabled", false); + $("#password").val(""); + createdDiv.hide(); + errorDiv.html(err); + errorDiv.show(); + }); + }); +}); diff --git a/js/script.js b/js/script.js index e8b29e1..2c31c99 100644 --- a/js/script.js +++ b/js/script.js @@ -11,7 +11,7 @@ var Core = function() { callback = typeof callback !== 'undefined' ? callback : function(data) { }; onerror = typeof onerror !== 'undefined' ? onerror : function(msg) { bootbox.alert("Ein Fehler ist aufgetreten: " + msg); }; - $.post('/php/api/' + func + '.php', aParams, function(data) { + $.post('/api/' + func, aParams, function(data) { console.log(func + "(): success=" + data.success + " msg=" + data.msg); if(data.hasOwnProperty('logoutIn') && $("#logoutTimer").length > 0) { $("#logoutTimer").attr("data-time", data.logoutIn); @@ -24,7 +24,7 @@ var Core = function() { } }, "json").fail(function(jqXHR, textStatus, errorThrown) { console.log("API-Function Error: " + func + " Status: " + textStatus + " error thrown: " + errorThrown); - onerror.call(this, "Ein Fehler ist aufgetreten. API-Funktion: " + func + " Status: " + textStatus + " - " + errorThrown); + onerror.call(this, "An error occurred. API-Function: " + func + " Status: " + textStatus + " - " + errorThrown); }); }; @@ -32,7 +32,7 @@ var Core = function() { var name = cname + "="; var decodedCookie = decodeURIComponent(document.cookie); var ca = decodedCookie.split(";"); - for(var i = 0; i