Initial Commit

This commit is contained in:
2020-02-09 23:02:19 +01:00
commit 62f67967ba
61 changed files with 25369 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
<?php
namespace Api;
use Api\Parameter\Parameter;
use Api\Parameter\StringType;
class ExecuteSelect extends Request {
public function __construct($user, $externCall = false) {
parent::__construct($user, $externCall, array(
'query' => new StringType('query')
));
$this->isPublic = false;
$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)) {
return false;
}
$this->success = false;
$this->result['rows'] = array();
if(count($this->params) === 1) {
$res = $this->user->getSQL()->query($this->getParam('query'));
if(!$res) {
$this->lastError = 'Database Error: query() failed with ' . $this->user->getSQL()->getLastError();
return false;
}
while($row = $res->fetch_assoc()) {
array_push($this->result['rows'], $row);
}
$this->success = true;
$res->close();
} else {
$aSqlParams = array('');
foreach($this->params as $param) {
if($param->name === 'query') continue;
$value = $param->value;
switch($param->type) {
case Parameter::TYPE_BOOLEAN:
$value = $param->value ? 1 : 0;
case Parameter::TYPE_INT:
$aSqlParams[0] .= 'i';
break;
case Parameter::TYPE_FLOAT:
$aSqlParams[0] .= 'd';
break;
case Parameter::TYPE_DATE:
$value = $value->format('Y-m-d');
$aSqlParams[0] .= 's';
break;
case Parameter::TYPE_TIME:
$value = $value->format('H:i:s');
$aSqlParams[0] .= 's';
break;
case Parameter::TYPE_DATE_TIME:
$value = $value->format('Y-m-d H:i:s');
$aSqlParams[0] .= 's';
break;
case Parameter::TYPE_EMAIL:
default:
$aSqlParams[0] .= 's';
}
$aSqlParams[] = $value;
}
$tmp = array();
foreach($aSqlParams as $key => $value) $tmp[$key] = &$aSqlParams[$key];
if($stmt = $this->user->getSQL()->connection->prepare($this->getParam('query'))) {
if(call_user_func_array(array($stmt, "bind_param"), $tmp))
{
if($stmt->execute()) {
$res = $stmt->get_result();
if($res) {
while($row = $res->fetch_assoc()) {
array_push($this->result['rows'], $row);
}
$res->close();
$this->success = true;
} else {
$this->lastError = 'Database Error: execute() failed with ' . $this->user->getSQL()->getLastError();
}
} else {
$this->lastError = 'Database Error: get_result() failed with ' . $this->user->getSQL()->getLastError();
}
} else {
$this->lastError = 'Database Error: bind_param() failed with ' . $this->user->getSQL()->getLastError();
}
$stmt->close();
} else {
$this->lastError = 'Database Error: prepare failed with() ' . $this->user->getSQL()->getLastError();
}
}
return $this->success;
}
};
?>

View File

@@ -0,0 +1,97 @@
<?php
namespace Api;
use Api\Parameter\Parameter;
use Api\Parameter\StringType;
class ExecuteStatement extends Request {
public function __construct($user, $externCall = false) {
parent::__construct($user, $externCall, array(
'query' => new StringType('query')
));
$this->isPublic = false;
$this->variableParamCount = true;
}
public function execute($aValues = array()) {
if(!parent::execute($aValues)) {
return false;
}
$this->success = false;
$this->result['rows'] = array();
if(count($this->params) == 1) {
$this->success = $this->user->getSQL()->execute($this->getParam('query'));
if(!$this->success) {
$this->lastError = $this->user->getSQL()->getLastError();
}
} else {
$aSqlParams = array('');
foreach($this->params as $param) {
if($param->name === 'query') continue;
$value = $param->value;
if(is_null($value)) {
$aSqlParams[0] .= 's';
} else {
switch($param->type) {
case Parameter::TYPE_BOOLEAN:
$value = $param->value ? 1 : 0;
$aSqlParams[0] .= 'i';
break;
case Parameter::TYPE_INT:
$aSqlParams[0] .= 'i';
break;
case Parameter::TYPE_FLOAT:
$aSqlParams[0] .= 'd';
break;
case Parameter::TYPE_DATE:
$value = $value->format('Y-m-d');
$aSqlParams[0] .= 's';
break;
case Parameter::TYPE_TIME:
$value = $value->format('H:i:s');
$aSqlParams[0] .= 's';
break;
case Parameter::TYPE_DATE_TIME:
$value = $value->format('Y-m-d H:i:s');
$aSqlParams[0] .= 's';
break;
case Parameter::TYPE_EMAIL:
default:
$aSqlParams[0] .= 's';
}
}
$aSqlParams[] = $value;
}
$tmp = array();
foreach($aSqlParams as $key => $value) $tmp[$key] = &$aSqlParams[$key];
if($stmt = $this->user->getSQL()->connection->prepare($this->getParam('query'))) {
if(call_user_func_array(array($stmt, "bind_param"), $tmp)) {
if($stmt->execute()) {
$this->result['rows'] = $stmt->affected_rows;
$this->success = true;
} else {
$this->lastError = 'Database Error: execute() failed with ' . $this->user->getSQL()->getLastError();
}
} else {
$this->lastError = 'Database Error: bind_param() failed with ' . $this->user->getSQL()->getLastError();
}
$stmt->close();
} else {
$this->lastError = 'Database Error: prepare() failed with ' . $this->user->getSQL()->getLastError();
}
}
return $this->success;
}
};
?>

View File

@@ -0,0 +1,161 @@
<?php
namespace Api\Parameter;
class Parameter {
const TYPE_INT = 0;
const TYPE_FLOAT = 1;
const TYPE_BOOLEAN = 2;
const TYPE_STRING = 3;
const TYPE_DATE = 4;
const TYPE_TIME = 5;
const TYPE_DATE_TIME = 6;
const TYPE_EMAIL = 7;
// only internal access
const TYPE_RAW = 8;
const TYPE_ARRAY = 9;
const names = array('Integer', 'Float', 'Boolean', 'String', 'Date', 'Time', 'DateTime', 'E-Mail', 'Raw', 'Array');
public $name;
public $value;
public $optional;
public $type;
public $typeName;
public function __construct($name, $type, $optional = FALSE, $defaultValue = NULL) {
$this->name = $name;
$this->optional = $optional;
$this->value = $defaultValue;
$this->type = $type;
$this->typeName = $this->getTypeName();
}
public function getTypeName() {
return ($this->type >= 0 && $this->type < count(Parameter::names)) ? Parameter::names[$this->type] : "INVALID";
}
public function toString() {
$typeName = Parameter::names[$this->type];
$str = "$typeName $this->name";
$defaultValue = (is_null($this->value) ? 'NULL' : $this->value);
if($this->optional) {
$str = "[$str = $defaultValue]";
}
return $str;
}
public static function parseType($value) {
if(is_array($value))
return Parameter::TYPE_ARRAY;
else if(is_numeric($value) && intval($value) == $value)
return Parameter::TYPE_INT;
else if(is_float($value) || (is_numeric($value) && floatval($value) == $value))
return Parameter::TYPE_FLOAT;
else if(is_bool($value) || $value == "true" || $value == "false")
return Parameter::TYPE_BOOLEAN;
else if(is_a($value, 'DateTime'))
return Parameter::TYPE_DATE_TIME;
else if(($d = \DateTime::createFromFormat('Y-m-d', $value)) && $d->format('Y-m-d') === $value)
return Parameter::TYPE_DATE;
else if(($d = \DateTime::createFromFormat('H:i:s', $value)) && $d->format('H:i:s') === $value)
return Parameter::TYPE_TIME;
else if(($d = \DateTime::createFromFormat('Y-m-d H:i:s', $value)) && $d->format('Y-m-d H:i:s') === $value)
return Parameter::TYPE_DATE_TIME;
else if (filter_var($value, FILTER_VALIDATE_EMAIL))
return Parameter::TYPE_EMAIL;
else
return Parameter::TYPE_STRING;
}
public function parseParam($value) {
switch($this->type) {
case Parameter::TYPE_INT:
if(is_numeric($value) && intval($value) == $value) {
$this->value = intval($value);
return true;
}
return false;
case Parameter::TYPE_FLOAT:
if(is_numeric($value) && (floatval($value) == $value || intval($value) == $value)) {
$this->value = floatval($value);
return true;
}
return false;
case Parameter::TYPE_BOOLEAN:
if(strcasecmp($value, 'true') === 0)
$this->value = true;
else if(strcasecmp($value, 'false') === 0)
$this->value = false;
else if(is_bool($value))
$this->value = (bool)$value;
else
return false;
return true;
case Parameter::TYPE_DATE:
if(is_a($value, "DateTime")) {
$this->value = $value;
return true;
}
$d = DateTime::createFromFormat('Y-m-d', $value);
if($d && $d->format('Y-m-d') === $value) {
$this->value = $d;
return true;
}
return false;
case Parameter::TYPE_TIME:
if(is_a($value, "DateTime")) {
$this->value = $value;
return true;
}
$d = DateTime::createFromFormat('H:i:s', $value);
if($d && $d->format('H:i:s') === $value) {
$this->value = $d;
return true;
}
return false;
case Parameter::TYPE_DATE_TIME:
if(is_a($value, 'DateTime')) {
$this->value = $value;
return true;
} else {
$d = DateTime::createFromFormat('Y-m-d H:i:s', $value);
if($d && $d->format('Y-m-d H:i:s') === $value) {
$this->value = $d;
return true;
}
}
return false;
case Parameter::TYPE_EMAIL:
if (filter_var($value, FILTER_VALIDATE_EMAIL)) {
$this->value = $value;
return true;
}
return false;
case Parameter::TYPE_ARRAY:
if(is_array($value)) {
$this->value = $value;
return true;
}
return false;
default:
$this->value = $value;
return true;
}
}
}
?>

View File

@@ -0,0 +1,43 @@
<?php
namespace Api\Parameter;
class StringType extends Parameter {
public $maxLength;
public function __construct($name, $maxLength = -1, $optional = FALSE, $defaultValue = NULL) {
parent::__construct($name, Parameter::TYPE_STRING, $optional, $defaultValue);
$this->maxLength = $maxLength;
}
public function parseParam($value) {
if(!is_string($value)) {
return false;
}
if($this->maxLength > 0 && strlen($value) > $this->maxLength) {
return false;
}
$this->value = $value;
return true;
}
public function getTypeName() {
$maxLength = ($this->maxLength > 0 ? "($this->maxLength)" : "");
return parent::getTypeName() . $maxLength;
}
public function toString() {
$typeName = $this->getTypeName();
$str = "$typeName $this->name";
$defaultValue = (is_null($this->value) ? 'NULL' : $this->value);
if($this->optional) {
$str = "[$str = $defaultValue]";
}
return $str;
}
}
?>

195
core/Api/Request.class.php Normal file
View File

@@ -0,0 +1,195 @@
<?php
namespace Api;
class Request {
protected $user;
protected $params;
protected $lastError;
protected $result;
protected $success;
protected $isPublic;
protected $loginRequired;
protected $variableParamCount;
protected $isDisabled;
protected $apiKeyAllowed;
private $aDefaultParams;
private $allowedMethods;
private $externCall;
public function __construct($user, $externCall = false, $params = array()) {
$this->user = $user;
$this->aDefaultParams = $params;
$this->lastError = '';
$this->success = false;
$this->result = array();
$this->externCall = $externCall;
$this->isPublic = true;
$this->isDisabled = false;
$this->loginRequired = false;
$this->variableParamCount = false;
$this->apiKeyAllowed = true;
$this->allowedMethods = array("GET", "POST");
}
protected function forbidMethod($method) {
if (($key = array_search($method, $this->allowedMethods)) !== false) {
unset($this->allowedMethods[$key]);
}
}
public function getParamsString() {
$str = "";
$count = count($this->params);
$i = 0;
foreach($this->params as $param) {
$str .= $param->toString();
if($i < $count - 1) $str .= ", ";
$i++;
}
return "($str)";
}
public function parseParams($aValues) {
foreach($this->params as $name => $param) {
$value = (isset($aValues[$name]) ? $aValues[$name] : NULL);
if(!$param->optional && is_null($value)) {
$this->lastError = 'Missing parameter: ' . $name;
return false;
}
if(!is_null($value)) {
if(!$param->parseParam($value)) {
$value = print_r($value, true);
$this->lastError = "Invalid Type for parameter: $name '$value' (Required: " . $param->getTypeName() . ")";
return false;
}
}
}
return true;
}
public function parseVariableParams($aValues) {
foreach($aValues as $name => $value) {
if(isset($this->params[$name])) continue;
$type = Parameter\Parameter::parseType($value);
$param = new Parameter\Parameter($name, $type, true);
$param->parseParam($value);
$this->params[$name] = $param;
}
}
public function execute($aValues = array()) {
$this->params = $this->aDefaultParams;
$this->success = false;
$this->result = array();
$this->lastError = '';
if($this->user->isLoggedIn()) {
$this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds();
}
if($this->externCall) {
$aValues = $_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);
}
}
if($this->isDisabled) {
$this->lastError = "This function is currently disabled.";
return false;
}
if($this->externCall && !$this->isPublic) {
$this->lastError = 'This function is private.';
header('HTTP 1.1 403 Forbidden');
return false;
}
if(!in_array($_SERVER['REQUEST_METHOD'], $this->allowedMethods)) {
$this->lastError = 'This method is not allowed';
header('HTTP 1.1 405 Method Not Allowed');
return false;
}
if($this->loginRequired) {
$authorized = false;
if(isset($aValues['api_key']) && $this->apiKeyAllowed) {
$apiKey = $aValues['api_key'];
$authorized = $this->user->authorize($apiKey);
}
if(!$this->user->isLoggedIn() && !$authorized) {
$this->lastError = 'You are not logged in.';
header('HTTP 1.1 401 Unauthorized');
return false;
}
}
if(!$this->parseParams($aValues))
return false;
if($this->variableParamCount)
$this->parseVariableParams($aValues);
if(!$this->user->getSQL()->isConnected()) {
$this->lastError = $this->user->getSQL()->getLastError();
return false;
}
$this->user->getSQL()->setLastError('');
$this->success = true;
return true;
}
protected function isValidString($str, $regex) {
return preg_replace($regex, "", $str) === $str;
}
protected function createError($err) {
$this->success = false;
$this->lastError = $err;
return false;
}
//
// public static function callDirectly($class, $db) {
// header('Content-Type: application/json');
// require_once realpath($_SERVER['DOCUMENT_ROOT']) . '/php/api/objects/User.php';
// require_once realpath($_SERVER['DOCUMENT_ROOT']) . '/php/sql.php';
// require_once realpath($_SERVER['DOCUMENT_ROOT']) . '/php/conf/sql.php';
//
// $sql = connectSQL(getSqlData($db));
// $user = new CUser($sql);
// $request = new $class($user, true);
// $request->execute();
// $sql->close();
// $user->sendCookies();
// return $request->getJsonResult();
// }
protected function getParam($name) { return isset($this->params[$name]) ? $this->params[$name]->value : NULL; }
public function isPublic() { return $this->isPublic; }
public function getDescription() { return ''; }
public function getSection() { return 'Default'; }
public function getLastError() { return $this->lastError; }
public function getResult() { return $this->result; }
public function success() { return $this->success; }
public function loginRequired() { return $this->loginRequired; }
public function isExternCall() { return $this->externCall; }
public function getJsonResult() {
$this->result['success'] = $this->success;
$this->result['msg'] = $this->lastError;
return json_encode($this->result);
}
};
?>

4
core/Configuration/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
Mail\.class\.php
JWT\.class\.php
Database\.class\.php
Google\.class\.php

View File

@@ -0,0 +1 @@
DENY FROM ALL

View File

@@ -0,0 +1,106 @@
<?php
namespace Configuration;
class Configuration {
private $database;
private $mail;
private $jwt;
private $google;
function __construct() {
}
public function load() {
try {
$classes = array(
\Configuration\Database::class => &$this->database,
\Configuration\Mail::class => &$this->mail,
\Configuration\JWT::class => &$this->jwt,
\Configuration\Google::class => &$this->google,
);
$success = true;
foreach($classes as $class => &$ref) {
$path = getClassPath($class);
if(!file_exists($path)) {
$success = false;
} else {
include_once $path;
if(class_exists($class)) {
$ref = new $class();
}
}
}
return $success;
} catch(\Error $e) {
die($e);
}
}
public function getDatabase() { return $this->database; }
public function getJWT() { return $this->jwt; }
public function getMail() { return $this->mail; }
public function isFilePresent($className) {
$path = getClassPath("\\Configuration\\$className");
return file_exists($path);
}
public function create($className, $data) {
$path = getClassPath("\\Configuration\\$className");
if($data) {
// TODO: Generalize this...
$superClass = get_class($data);
$host = addslashes($data->getHost());
$port = intval($data->getPort());
$login = addslashes($data->getLogin());
$password = addslashes($data->getPassword());
$properties = "";
foreach($data->getProperties() as $key => $val) {
$key = addslashes($key);
$val = is_string($val) ? "'" . addslashes($val) . "'" : $val;
$properties .= "\n\$this->setProperty('$key', $val);";
}
$code = intendCode(
"<?php
namespace Configuration;
class $className extends \\$superClass {
public function __construct() {
parent::__construct('$host', $port, '$login', '$password');$properties
}
}
?>", false
);
} else {
$code = intendCode(
"<?php
?>", false);
}
return file_put_contents($path, $code);
}
public function delete($className) {
$path = getClassPath("\\Configuration\\$className");
if(file_exists($path)) {
return unlink($path);
}
return true;
}
};
?>

View File

@@ -0,0 +1,59 @@
--
-- API
--
CREATE TABLE IF NOT EXISTS User (
`uid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
`email` VARCHAR(256) UNIQUE DEFAULT NULL,
`name` VARCHAR(32) UNIQUE NOT NULL,
`salt` varchar(16) NOT NULL,
`password` varchar(64) NOT NULL,
`uidLanguage` int(11) DEFAULT 1
);
CREATE TABLE IF NOT EXISTS UserInvitation (
`email` VARCHAR(256) NOT NULL,
`token` VARCHAR(36) UNIQUE NOT NULL,
`valid_until` DATETIME NOT NULL
);
CREATE TABLE IF NOT EXISTS `Group` (
`gid` INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL
);
INSERT INTO `Group` (gid, name) VALUES (1, "Default"), (2, "Administrator")
ON DUPLICATE KEY UPDATE name=name;
CREATE TABLE IF NOT EXISTS UserGroup (
`uid` INTEGER NOT NULL,
`gid` INTEGER NOT NULL,
UNIQUE(`uid`, `gid`)
);
CREATE TABLE Session IF NOT EXISTS (
`uid` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`expires` timestamp NOT NULL,
`uidUser` int(11) NOT NULL,
`ipAddress` varchar(45) NOT NULL,
`os` varchar(64) NOT NULL,
`browser` varchar(64) NOT NULL
);
CREATE TABLE IF NOT EXISTS ApiKey (
`uid` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`uidUser` int(11) NOT NULL,
`api_key` VARCHAR(64) NOT NULL,
`valid_until` DATETIME NOT NULL
);
CREATE TABLE IF NOT EXISTS Language (
`uid` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`code` VARCHAR(5) UNIQUE NOT NULL,
`name` VARCHAR(32) UNIQUE NOT NULL
);
CREATE TABLE IF NOT EXISTS ExternalSiteCache (
`url` VARCHAR(256) PRIMARY KEY,
`data` TEXT NOT NULL,
`expires` TIMESTAMP DEFAULT NULL
);

View File

@@ -0,0 +1,721 @@
<?php
namespace Documents {
class Install extends \Elements\Document {
public function __construct($user) {
parent::__construct($user, Install\Head::class, Install\Body::class);
$this->databseRequired = false;
}
}
}
namespace Documents\Install {
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::INSTALL);
}
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 - Installation";
}
}
class Body extends \Elements\Body {
// Status enum
const NOT_STARTED = 0;
const PENDING = 1;
const SUCCESFULL = 2;
const ERROR = 3;
// Step enum
const CHECKING_REQUIRMENTS = 1;
const DATABASE_CONFIGURATION = 2;
const CREATE_USER = 3;
const ADD_MAIL_SERVICE = 4;
const ADD_GOOGLE_SERVICE = 5;
//
private $configDirectory;
private $databaseScript;
function __construct($document) {
parent::__construct($document);
// TODO: make better
$this->configDirectory = getWebRoot() . '/core/Configuration';
$this->databaseScript = getWebRoot() . '/core/Configuration/database.sql';
}
private function getParameter($name) {
if(isset($_REQUEST[$name]) && is_string($_REQUEST[$name])) {
return trim($_REQUEST[$name]);
}
return NULL;
}
private function getCurrentStep() {
if(!$this->checkRequirements()) {
return self::CHECKING_REQUIRMENTS;
}
$user = $this->getDocument()->getUser();
$config = $user->getConfiguration();
// Check if database configuration exists
if(!$config->getDatabase()) {
return self::DATABASE_CONFIGURATION;
}
$query = "SELECT * FROM User";
$sql = $user->getSQL();
if(!is_null($sql) && $sql->isConnected()) {
$this->getDocument()->getUser()->setSql($sql);
$res = $sql->query($query);
if($res) {
if($res->num_rows === 0) {
$step = self::CREATE_USER;
} else {
$step = self::ADD_MAIL_SERVICE;
}
}
} else {
$step = self::DATABASE_CONFIGURATION;
}
if($step == self::ADD_MAIL_SERVICE && $config->isFilePresent("Mail")) {
$step = self::ADD_GOOGLE_SERVICE;
}
return $step;
}
private function checkRequirements() {
$msg = "";
$success = true;
$failedRequirements = array();
if(!is_writeable($this->configDirectory)) {
$failedRequirements[] = "<b>$this->configDirectory</b> is not writeable. Try running <b>chmod 600</b>";
$success = false;
}
if(!is_readable($this->databaseScript)) {
$failedRequirements[] = "<b>$this->databaseScript</b> is not readable.";
$success = false;
}
if(version_compare(PHP_VERSION, '7.1', '<')) {
$failedRequirements[] = "PHP Version <b>>= 7.1</b> is required. Got: <b>" . PHP_VERSION . "</b>";
$success = false;
}
if(!function_exists('mysqli_connect')) {
$link = $this->createExternalLink("https://secure.php.net/manual/en/mysqli.setup.php");
$failedRequirements[] = "mysqli is not enabled yet. See: $link";
$success = false;
}
if(!$success) {
$msg = "The following requirements failed the check:<br>" .
$this->createUnorderedList($failedRequirements);
}
return array("success" => $success, "msg" => $msg);
}
private function databaseConfiguration() {
$host = $this->getParameter("host");
$port = $this->getParameter("port");
$username = $this->getParameter("username");
$password = $this->getParameter("password");
$database = $this->getParameter("database");
$success = true;
$missingInputs = array();
if(is_null($host) || empty($host)) {
$success = false;
$missingInputs[] = "Host";
}
if(is_null($port) || empty($port)) {
$success = false;
$missingInputs[] = "Port";
}
if(is_null($username) || empty($username)) {
$success = false;
$missingInputs[] = "Username";
}
if(is_null($password)) {
$success = false;
$missingInputs[] = "Password";
}
if(is_null($database) || empty($database)) {
$success = false;
$missingInputs[] = "Database";
}
if(!$success) {
$msg = "Please fill out the following inputs:<br>" .
$this->createUnorderedList($missingInputs);
} else if(!is_numeric($port) || ($port = intval($port)) < 1 || $port > 65535) {
$msg = "Port must be in range of 1-65535.";
$success = false;
} else {
$connectionData = new \Objects\ConnectionData($host, $port, $username, $password);
$connectionData->setProperty('database', $database);
$connectionData->setProperty('encoding', 'utf8');
$sql = new \Driver\SQL($connectionData);
$success = $sql->connect();
if(!$success) {
$msg = "Error connecting to database:<br>" . $sql->getLastError();
} else {
try {
$msg = "Error loading database script $this->databaseScript";
$commands = file_get_contents($this->databaseScript);
$success = $sql->executeMulti($commands);
if(!$success) {
$msg = $sql->getLastError();
} else if(!$this->getDocument()->getUser()->getConfiguration()->create("Database", $connectionData)) {
$success = false;
$msg = "Unable to write file";
} else {
$msg = "";
}
} catch(Exception $e) {
$success = false;
$msg .= ": " . $e->getMessage();
}
if($sql) {
$sql->close();
}
}
}
return array("success" => $success, "msg" => $msg);
}
private function createUser() {
$user = $this->getDocument()->getUser();
if($this->getParameter("prev") === "true") {
$success = $user->getConfiguration()->delete("Database");
$msg = $success ? "" : error_get_last();
return array("success" => $success, "msg" => $msg);
}
$username = $this->getParameter("username");
$password = $this->getParameter("password");
$confirmPassword = $this->getParameter("confirmPassword");
$msg = "";
$success = true;
$missingInputs = array();
if(is_null($username) || empty($username)) {
$success = false;
$missingInputs[] = "Username";
}
if(is_null($password) || empty($password)) {
$success = false;
$missingInputs[] = "Password";
}
if(is_null($confirmPassword) || empty($confirmPassword)) {
$success = false;
$missingInputs[] = "Confirm Password";
}
if(!$success) {
$msg = "Please fill out the following inputs:<br>" .
$this->createUnorderedList($missingInputs);
} else if(strlen($username) < 5 || strlen($username) > 32) {
$msg = "The username should be between 5 and 32 characters long";
$success = false;
} else if(strcmp($password, $confirmPassword) !== 0) {
$msg = "The given passwords do not match";
$success = false;
} else if(strlen($password) < 6) {
$msg = "The password should be at least 6 characters long";
$success = false;
} else {
$salt = generateRandomString(16);
$hash = hash('sha256', $password . $salt);
$query = "INSERT INTO User (name, salt, password) VALUES (?,?,?)";
$req = new \Api\ExecuteStatement($user);
$success = $req->execute(array("query" => $query, $username, $salt, $hash));
$nsg = $req->getLastError();
}
return array("msg" => $msg, "success" => $success);
}
private function addMailService() {
$user = $this->getDocument()->getUser();
if($this->getParameter("prev") === "true") {
$req = new \Api\ExecuteStatement($user);
$success = $req->execute(array("query" => "TRUNCATE User"));
$msg = $req->getLastError();
return array("success" => $success, "msg" => $msg);
}
$success = true;
$msg = "";
if($this->getParameter("skip") === "true") {
if(!$user->getConfiguration()->create("Mail", null)) {
$success = false;
$msg = "Unable to create file";
}
} else {
$address = $this->getParameter("address");
$port = $this->getParameter("port");
$username = $this->getParameter("username");
$password = $this->getParameter("password");
$success = true;
$missingInputs = array();
if(is_null($address) || empty($address)) {
$success = false;
$missingInputs[] = "SMTP Address";
}
if(is_null($port) || empty($port)) {
$success = false;
$missingInputs[] = "Port";
}
if(is_null($username) || empty($username)) {
$success = false;
$missingInputs[] = "Username";
}
if(is_null($password)) {
$success = false;
$missingInputs[] = "Password";
}
if(!$success) {
$msg = "Please fill out the following inputs:<br>" .
$this->createUnorderedList($missingInputs);
} else if(!is_numeric($port) || ($port = intval($port)) < 1 || $port > 65535) {
$msg = "Port must be in range of 1-65535.";
$success = false;
} else {
$success = false;
$mail = new \External\PHPMailer\PHPMailer(true);
$mail->IsSMTP();
$mail->SMTPAuth = true;
$mail->Username = $username;
$mail->Password = $password;
$mail->Host = $address;
$mail->Port = $port;
$mail->SMTPSecure = 'tls';
$mail->Timeout = 10;
try {
$success = $mail->SmtpConnect();
if(!$success) {
$error = empty($mail->ErrorInfo) ? "Unknown Error" : $mail->ErrorInfo;
$msg = "Could not connect to SMTP Server: $error";
} else {
$success = true;
$msg = "";
$mail->smtpClose();
}
} catch(\External\PHPMailer\Exception $error) {
$msg = "Could not connect to SMTP Server: " . $error->errorMessage();
}
if($success) {
$connectionData = new \Objects\ConnectionData($address, $port, $username, $password);
if(!$user->getConfiguration()->create("Mail", $connectionData)) {
$success = false;
$msg = "Unable to create file";
}
}
}
}
return array("success" => $success, "msg" => $msg);
}
private function addGoogleService() {
// return array("success" => $success, "msg" => $msg);
}
private function performStep() {
switch($this->currentStep) {
case self::CHECKING_REQUIRMENTS:
return $this->checkRequirements();
case self::DATABASE_CONFIGURATION:
return $this->databaseConfiguration();
case self::CREATE_USER:
return $this->createUser();
case self::ADD_MAIL_SERVICE:
return $this->addMailService();
case self::ADD_GOOGLE_SERVICE:
return $this->addGoogleService();
default:
return array(
"success" => false,
"msg" => "Invalid step number"
);
}
}
private function createProgressSidebar() {
$items = array();
foreach($this->steps as $num => $step) {
$title = $step["title"];
$status = $step["status"];
$currentStep = ($num == $this->currentStep) ? " id=\"currentStep\"" : "";
switch($status) {
case self::PENDING:
$statusIcon = '<i class="fas fa-spin fa-spinner"></i>';
$statusText = "Loading…";
$statusColor = "muted";
break;
case self::SUCCESFULL:
$statusIcon = '<i class="fas fa-check-circle"></i>';
$statusText = "Successfull";
$statusColor = "success";
break;
case self::ERROR:
$statusIcon = '<i class="fas fa-times-circle"></i>';
$statusText = "Failed";
$statusColor = "danger";
break;
case self::NOT_STARTED:
default:
$statusIcon = '<i class="far fa-circle"></i>';
$statusText = "Pending";
$statusColor = "muted";
break;
}
$items[] = "
<li class=\"list-group-item d-flex justify-content-between lh-condensed\"$currentStep>
<div>
<h6 class=\"my-0\">$title</h6>
<small class=\"text-$statusColor\">$statusText</small>
</div>
<span class=\"text-$statusColor\">$statusIcon</span>
</li>";
}
return implode("", $items);
}
private function createFormItem($formItem, $inline=false) {
$title = $formItem["title"];
$name = $formItem["name"];
$type = $formItem["type"];
$attributes = array(
"name" => $name,
"id" => $name,
"class" => "form-control",
"type" => $type,
);
if(isset($formItem["required"]) && $formItem["required"]) {
$attributes["required"] = "";
}
if(isset($formItem["value"]) && $formItem["value"]) {
$attributes["value"] = $formItem["value"];
}
if($type === "number") {
if(isset($formItem["min"]) && is_numeric($formItem["min"]))
$attributes["min"] = $formItem["min"];
if(isset($formItem["max"]) && is_numeric($formItem["max"]))
$attributes["max"] = $formItem["max"];
if(isset($formItem["step"]) && is_numeric($formItem["step"]))
$attributes["step"] = $formItem["step"];
}
$attributes = str_replace("+", " ", str_replace("&", "\" ", str_replace("=", "=\"", http_build_query($attributes)))) . "\"";
if(!$inline) {
return
"<div class=\"d-block my-3\">
<label for=\"$name\">$title</label>
<input $attributes>
</div>";
} else {
return
"<div class=\"col-md-6 mb-3\">
<label for=\"$name\">$title</label>
<input $attributes>
</div>";
}
}
private function createProgessMainview() {
$views = array(
self::CHECKING_REQUIRMENTS => array(
"title" => "Application Requirements",
"progressText" => "Checking requirements, please wait a moment…"
),
self::DATABASE_CONFIGURATION => array(
"title" => "Database configuration",
"form" => array(
array("title" => "Username", "name" => "username", "type" => "text", "required" => true),
array("title" => "Password", "name" => "password", "type" => "password"),
array("title" => "Database", "name" => "database", "type" => "text", "required" => true),
array("type" => "row", "items" => array(
array(
"title" => "Address", "name" => "host", "type" => "text", "required" => true,
"value" => "localhost", "row" => true
),
array(
"title" => "Port", "name" => "port", "type" => "number", "required" => true,
"value" => "3306", "min" => "1", "max" => "65535", "row" => true
)
)),
)
),
self::CREATE_USER => array(
"title" => "Create a User",
"form" => array(
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),
)
),
self::ADD_MAIL_SERVICE => array(
"title" => "Optional: Add Mail Service",
"form" => array(
array("title" => "Username", "name" => "username", "type" => "text", "required" => true),
array("title" => "Password", "name" => "password", "type" => "password"),
array("type" => "row", "items" => array(
array(
"title" => "SMTP Address", "name" => "address", "type" => "text", "required" => true,
"value" => "localhost", "row" => true
),
array(
"title" => "Port", "name" => "port", "type" => "number", "required" => true,
"value" => "587", "min" => "1", "max" => "65535", "row" => true
)
)),
),
"skip" => true
),
self::ADD_GOOGLE_SERVICE => array(
"title" => "Optional: Add Google Services",
)
);
if(!isset($views[$this->currentStep])) {
return "";
}
$prevDisabled = ($this->currentStep <= self::DATABASE_CONFIGURATION);
$currentView = $views[$this->currentStep];
$spinnerIcon = $this->createIcon("spinner");
$title = $currentView["title"];
$html = "<h4 class=\"mb-3\">$title</h4><hr class=\"mb-4\">";
if(isset($currentView["progressText"])) {
$progressText = $currentView["progressText"];
$html .= "<div id=\"progressText\" class=\"my-3\">$progressText$spinnerIcon</i></div>";
}
if(isset($currentView["form"])) {
$html .= "<form id=\"installForm\">";
foreach($currentView["form"] as $formItem) {
if($formItem["type"] === "row") {
$html .= "<div class=\"row\">";
foreach($formItem["items"] as $item) {
$html .= $this->createFormItem($item, true);
}
$html .= "</div>";
} else {
$html .= $this->createFormItem($formItem);
}
}
$html .= "
</form>";
}
$buttons = array(
array("title" => "Go Back", "type" => "info", "id" => "btnPrev", "float" => "left", "disabled" => $prevDisabled),
array("title" => "Submit", "type" => "success", "id" => "btnSubmit", "float" => "right")
);
if(isset($currentView["skip"])) {
$buttons[] = array("title" => "Skip", "type" => "secondary", "id" => "btnSkip", "float" => "right");
}
$buttonsLeft = "";
$buttonsRight = "";
foreach($buttons as $button) {
$title = $button["title"];
$type = $button["type"];
$id = $button["id"];
$float = $button["float"];
$disabled = (isset($button["disabled"]) && $button["disabled"]) ? " disabled" : "";
$button = "<button type=\"button\" id=\"$id\" class=\"btn btn-$type margin-xs\"$disabled>$title</button>";
if($float === "left") {
$buttonsLeft .= $button;
} else {
$buttonsRight .= $button;
}
}
$html .=
"<div class=\"row\">
<div class=\"col-6 float-left text-left\">$buttonsLeft</div>
<div class=\"col-6 float-right text-right\">$buttonsRight</div>
</div>";
return $html;
}
function getCode() {
$html = parent::getCode();
$this->steps = array(
self::CHECKING_REQUIRMENTS => array(
"title" => "Checking requirements",
"status" => self::NOT_STARTED
),
self::DATABASE_CONFIGURATION => array(
"title" => "Database configuration",
"status" => self::NOT_STARTED
),
self::CREATE_USER => array(
"title" => "Create User",
"status" => self::NOT_STARTED
),
self::ADD_MAIL_SERVICE => array(
"title" => "Add Mail Service",
"status" => self::NOT_STARTED
),
self::ADD_GOOGLE_SERVICE => array(
"title" => "Add Google Services",
"status" => self::NOT_STARTED
),
);
$this->currentStep = $this->getCurrentStep();
// set status
for($step = self::CHECKING_REQUIRMENTS; $step < $this->currentStep; $step++) {
$this->steps[$step]["status"] = self::SUCCESFULL;
}
// POST
if($_SERVER['REQUEST_METHOD'] == 'POST') {
$response = $this->performStep();
die(json_encode($response));
}
if($this->currentStep == self::CHECKING_REQUIRMENTS) {
$this->getDocument()->getHead()->addJSCode("
$(document).ready(function() {
retry();
});
");
}
$progressSidebar = $this->createProgressSidebar();
$progressMainview = $this->createProgessMainview();
$html .= "
<body class=\"bg-light\">
<div class=\"container\">
<div class=\"py-5 text-center\">
<h2>WebBase - Installation</h2>
<p class=\"lead\">
Process the following steps and fill out the required forms to install your WebBase-Installation.
</p>
</div>
<div class=\"row\">
<div class=\"col-md-4 order-md-2 mb-4\">
<h4 class=\"d-flex justify-content-between align-items-center mb-3\">
<span class=\"text-muted\">Progress</span>
</h4>
<ul class=\"list-group mb-3\">
$progressSidebar
</ul>
</div>
<div class=\"col-md-8 order-md-1\">
$progressMainview
<div class=\"alert margin-top-m\" id=\"status\" style=\"display:none\"></div>
</div>
</div>
</div>
</body>";
return $html;
}
}
}
?>

124
core/Driver/SQL.class.php Normal file
View File

@@ -0,0 +1,124 @@
<?php
namespace Driver;
class SQL {
public $connection;
public $lastError;
private $connectionData;
public function __construct($connectionData) {
$this->connection = NULL;
$this->lastError = 'Not connected';
$this->connectionData = $connectionData;
}
public function connect() {
if(!is_null($this->connection))
return true;
@$this->connection = mysqli_connect(
$this->connectionData->getHost(),
$this->connectionData->getLogin(),
$this->connectionData->getPassword(),
$this->connectionData->getProperty('database'),
$this->connectionData->getPort()
);
if (mysqli_connect_errno($this->connection)) {
$this->lastError = "Failed to connect to MySQL: " . mysqli_connect_error();
$this->connection = NULL;
return false;
}
mysqli_set_charset($this->connection, $this->connectionData->getProperty('encoding'));
return true;
}
public function disconnect() {
if(is_null($this->connection))
return;
mysqli_close($this->connection);
$this->connection = NULL;
}
public function isConnected() {
return !is_null($this->connection);
}
public function getLastError() {
return empty(trim($this->lastError)) ? mysqli_error($this->connection) . " " . $this->getLastErrorNumber() : trim($this->lastError);
}
public function setLastError($str) {
$this->lastError = $str;
}
public function getLastErrorNumber() {
return mysqli_errno($this->connection);
}
public function getLastInsertId() {
return $this->connection->insert_id;
}
public function close() {
if(!is_null($this->connection)) {
$this->connection->close();
}
}
public function getAffectedRows() {
return $this->connection->affected_rows;
}
public function execute($query) {
if(!$this->isConnected()) {
return false;
}
if(!mysqli_query($this->connection, $query)) {
$this->lastError = mysqli_error($this->connection);
return false;
}
return true;
}
public function executeMulti($queries) {
if(!$this->isConnected()) {
return false;
}
if(!$this->connection->multi_query($queries)) {
$this->lastError = mysqli_error($this->connection);
return false;
}
return true;
}
public function query($query) {
if(!$this->isConnected()) {
return false;
}
$res = mysqli_query($this->connection, $query);
if(!$res) {
$this->lastError = mysqli_error($this->connection);
return false;
}
return $res;
}
public static function createConnection($connectionData) {
$sql = new SQL($connectionData);
$sql->connect();
return $sql;
}
}
?>

View File

@@ -0,0 +1,10 @@
<?php
namespace Elements;
abstract class Body extends \View {
public function __construct($document) {
parent::__construct($document);
}
};
?>

View File

@@ -0,0 +1,80 @@
<?php
namespace Elements;
abstract class Document {
protected $head;
protected $body;
protected $user;
protected $databseRequired;
public function __construct($user, $headClass, $bodyClass) {
$this->head = new $headClass($this);
$this->body = new $bodyClass($this);
$this->user = $user;
$this->databseRequired = true;
}
public function getHead() { return $this->head; }
public function getBody() { return $this->body; }
public function getSQL() { return $this->user->getSQL(); }
public function getUser() { return $this->user; }
protected function sendHeaders() {
header("X-Frame-Options: DENY");
}
public static function createSearchableDocument($documentClass, $user) {
return new $documentClass($user);
}
public static function createDocument($class) {
// TODO: check instance, configuration, ..
require_once realpath($_SERVER['DOCUMENT_ROOT']) . '/php/sql.php';
// require_once realpath($_SERVER['DOCUMENT_ROOT']) . '/php/conf/config.php';
// require_once realpath($_SERVER['DOCUMENT_ROOT']) . "/php/pages/$file.php";
require_once realpath($_SERVER['DOCUMENT_ROOT']) . '/php/api/objects/User.php';
$connectionData = getSqlData($database);
$sql = connectSQL($connectionData);
if(!$sql->isConnected()) {
http_response_code(500);
die('Internal Database error');
}
$user = new CUser($sql);
$document = new $class($user);
$code = $document->getCode();
$document->sendHeaders();
$user->sendCookies();
die($code);
}
function getCode() {
if ($this->databseRequired) {
$sql = $this->user->getSQL();
if (is_null($sql)) {
die("Database is not configured yet.");
} else {
die("Database is not connected: " . $sql->getLastError());
}
}
$body = $this->body->getCode();
$head = $this->head->getCode();
$html = "<!DOCTYPE html>";
$html .= "<html>";
$html .= $head;
$html .= $body;
$html .= "</html>";
return $html;
}
};
?>

View File

@@ -0,0 +1,126 @@
<?php
namespace Elements;
abstract class Head extends \View {
protected $sources;
protected $title;
protected $metas;
protected $rawFields;
protected $keywords;
protected $description;
protected $baseUrl;
function __construct($document) {
parent::__construct($document);
$this->sources = array();
$this->metas = $this->initMetas();
$this->rawFields = $this->initRawFields();
$this->title = $this->initTitle();
$this->initSources();
$this->init();
}
protected abstract function initSources();
protected abstract function initMetas();
protected abstract function initRawFields();
protected abstract function initTitle();
protected function init() {
$this->keywords = array();
$this->description = "";
$this->baseUrl = "";
}
public function setBaseUrl($baseUrl) { $this->baseUrl = $baseUrl; }
public function setDescription($description) { $this->description = $description; }
public function setKeywords($keywords) { $this->keywords = $keywords; }
public function setTitle($title) { $this->title = $title; }
public function getSources() { return $this->sources; }
public function addScript($type, $url, $js = '') { $this->sources[] = new Script($type, $url, $js); }
public function addRawField($rawField) { $this->rawFields[] = $rawField; }
public function addMeta($aMeta) { $this->metas[] = $aMeta; }
public function addLink($rel, $href, $type = "") { $this->sources[] = new Link($rel, $href, $type); }
public function addKeywords($keywords) { array_merge($this->keywords, $keywords); }
public function getTitle() { return $this->title; }
public function addCSS($href, $type = Link::MIME_TEXT_CSS) { $this->sources[] = new Link("stylesheet", $href, $type); }
public function addStyle($style) { $this->sources[] = new Style($style); }
public function addJS($url) { $this->sources[] = new Script(Script::MIME_TEXT_JAVASCRIPT, $url, ""); }
public function addJSCode($code) { $this->sources[] = new Script(Script::MIME_TEXT_JAVASCRIPT, "", $code); }
public function loadFontawesome() {
$this->addCSS(Link::FONTAWESOME);
}
public function loadSyntaxHighlighting() {
$this->addJS(Script::HIGHLIGHT);
$this->addJSCode(Script::HIGHLIGHT_JS_LOADER);
$this->addCSS(Link::HIGHLIGHT);
$this->addCSS(Link::HIGHLIGHT_THEME);
}
public function loadJQueryTerminal($unixFormatting = true) {
$this->addJS(Script::JQUERY_TERMINAL);
if($unixFormatting) $this->addJS(Script::JQUERY_TERMINAL_UNIX);
$this->addCSS(Link::JQUERY_TERMINAL);
}
public function loadGoogleRecaptcha($siteKey) {
$this->addJS("https://www.google.com/recaptcha/api.js?render=$siteKey");
}
public function loadJQuery() {
$this->addJS(Script::JQUERY);
}
public function loadBootstrap() {
$this->addCSS(Link::BOOTSTRAP);
$this->addJS(Script::BOOTSTRAP);
}
public function loadChartJS() {
$this->addJS(Script::MOMENT);
$this->addJS(Script::CHART);
}
public function getCode() {
$header = "<head>";
foreach($this->metas as $aMeta) {
$header .= '<meta';
foreach($aMeta as $key => $val) {
$header .= " $key=\"$val\"";
}
$header .= ' />';
}
if(!empty($this->description)) {
$header .= "<meta name=\"description\" content=\"$this->description\" />";
}
if(!empty($this->keywords)) {
$keywords = implode(", ", $this->keywords);
$header .= "<meta name=\"keywords\" content=\"$keywords\" />";
}
if(!empty($this->baseUrl)) {
$header .= "<base href=\"$this->baseUrl\">";
}
$header .= "<title>$this->title</title>";
foreach($this->sources as $src) {
$header .= $src->getCode();
}
foreach($this->rawFields as $raw) {
$header .= $raw;
}
$header .= "</head>";
return $header;
}
}
?>

View File

@@ -0,0 +1,42 @@
<?php
namespace Elements;
class Link extends Source {
const STYLESHEET = "stylesheet";
const MIME_TEXT_CSS = "text/css";
const FONTAWESOME = '/css/fontawesome.min.css';
// const JQUERY_UI = '/css/jquery-ui.css';
// const JQUERY_TERMINAL = '/css/jquery.terminal.min.css';
const BOOTSTRAP = '/css/bootstrap.min.css';
// const BOOTSTRAP_THEME = '/css/bootstrap-theme.min.css';
// const BOOTSTRAP_DATEPICKER_CSS = '/css/bootstrap-datepicker.standalone.min.css';
// const BOOTSTRAP_DATEPICKER3_CSS = '/css/bootstrap-datepicker.standalone.min.css';
// const HIGHLIGHT = '/css/highlight.css';
// const HIGHLIGHT_THEME = '/css/theme.css';
const CORE = "/css/style.css";
// const ADMIN = "/css/admin.css";
// const HOME = "/css/home.css";
// const REVEALJS = "/css/reveal.css";
// const REVEALJS_THEME_MOON = "/css/reveal_moon.css";
// const REVEALJS_THEME_BLACK = "/css/reveal_black.css";
private $type;
private $rel;
function __construct($rel, $href, $type = "") {
parent::__construct('link', $href);
$this->type = $type;
$this->rel = $rel;
}
function getCode() {
$type = (empty($this->type) ? "" : " type=\"$this->type\"");
$link = "<link rel=\"$this->rel\" href=\"$this->url\" $type/>";
return $link;
}
}
?>

View File

@@ -0,0 +1,53 @@
<?php
namespace Elements;
class Script extends Source {
const MIME_TEXT_JAVASCRIPT = "text/javascript";
const CORE = "/js/script.js";
// const HOME = "/js/home.js";
// const ADMIN = "/js/admin.js";
// const SORTTABLE = "/js/sorttable.js";
const JQUERY = "/js/jquery.min.js";
// const JQUERY_UI = "/js/jquery-ui.js";
// const JQUERY_MASKED_INPUT = "/js/jquery.maskedinput.min.js";
// const JQUERY_CONTEXT_MENU = "/js/jquery.contextmenu.min.js";
// const JQUERY_TERMINAL = "/js/jquery.terminal.min.js";
// const JQUERY_TERMINAL_UNIX = "/js/unix_formatting.js";
// const JSCOLOR = "/js/jscolor.min.js";
// const SYNTAX_HIGHLIGHTER = "/js/syntaxhighlighter.js";
// const HIGHLIGHT = "/js/highlight.pack.js";
// const GOOGLE_CHARTS = "/js/loader.js";
const BOOTSTRAP = "/js/bootstrap.min.js";
// const BOOTSTRAP_DATEPICKER_JS = "/js/bootstrap-datepicker.min.js";
// const POPPER = "/js/popper.min.js";
// const JSMPEG = "/js/jsmpeg.min.js";
// const MOMENT = "/js/moment.min.js";
// const CHART = "/js/chart.js";
// const REVEALJS = "/js/reveal.js";
// const REVEALJS_PLUGIN_NOTES = "/js/reveal_notes.js";
const INSTALL = "/js/install.js";
const HIGHLIGHT_JS_LOADER = "\$(document).ready(function(){\$('code').each(function(i, block) { hljs.highlightBlock(block); }); })";
private $type;
private $content;
function __construct($type, $src, $content = "") {
parent::__construct('script', $src);
$this->type = $type;
$this->content = $content;
}
function getCode() {
$src = (empty($this->url) ? "" : " src=\"$this->url\"");
$script = "<script type=\"$this->type\"$src>";
$script .= $this->content;
$script .= '</script>';
return $script;
}
}
?>

View File

@@ -0,0 +1,22 @@
<?php
namespace Elements;
class Source extends \View {
protected $sourceType;
protected $url;
public function __construct($sourceType, $url) {
$this->sourceType = $sourceType;
$this->url = $url;
}
public function getCode() {
return "<$sourceType />";
}
public function getUrl() { return $this->url; }
}
?>

View File

@@ -0,0 +1,19 @@
<?php
namespace Elements;
class Style extends Source {
private $style;
function __construct($style) {
parent::__construct('style', '');
$this->style = $style;
}
function getCode() {
return "<style>$this->style</style>";
}
}
?>

39
core/External/PHPMailer/Exception.php vendored Normal file
View File

@@ -0,0 +1,39 @@
<?php
/**
* PHPMailer Exception class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2017 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace External\PHPMailer;
/**
* PHPMailer exception handler.
*
* @author Marcus Bointon <phpmailer@synchromedia.co.uk>
*/
class Exception extends \Exception
{
/**
* Prettify error message output.
*
* @return string
*/
public function errorMessage()
{
return '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n";
}
}

138
core/External/PHPMailer/OAuth.php vendored Normal file
View File

@@ -0,0 +1,138 @@
<?php
/**
* PHPMailer - PHP email creation and transport class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2015 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace External\PHPMailer;
use League\OAuth2\Client\Grant\RefreshToken;
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Token\AccessToken;
/**
* OAuth - OAuth2 authentication wrapper class.
* Uses the oauth2-client package from the League of Extraordinary Packages.
*
* @see http://oauth2-client.thephpleague.com
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
*/
class OAuth
{
/**
* An instance of the League OAuth Client Provider.
*
* @var AbstractProvider
*/
protected $provider;
/**
* The current OAuth access token.
*
* @var AccessToken
*/
protected $oauthToken;
/**
* The user's email address, usually used as the login ID
* and also the from address when sending email.
*
* @var string
*/
protected $oauthUserEmail = '';
/**
* The client secret, generated in the app definition of the service you're connecting to.
*
* @var string
*/
protected $oauthClientSecret = '';
/**
* The client ID, generated in the app definition of the service you're connecting to.
*
* @var string
*/
protected $oauthClientId = '';
/**
* The refresh token, used to obtain new AccessTokens.
*
* @var string
*/
protected $oauthRefreshToken = '';
/**
* OAuth constructor.
*
* @param array $options Associative array containing
* `provider`, `userName`, `clientSecret`, `clientId` and `refreshToken` elements
*/
public function __construct($options)
{
$this->provider = $options['provider'];
$this->oauthUserEmail = $options['userName'];
$this->oauthClientSecret = $options['clientSecret'];
$this->oauthClientId = $options['clientId'];
$this->oauthRefreshToken = $options['refreshToken'];
}
/**
* Get a new RefreshToken.
*
* @return RefreshToken
*/
protected function getGrant()
{
return new RefreshToken();
}
/**
* Get a new AccessToken.
*
* @return AccessToken
*/
protected function getToken()
{
return $this->provider->getAccessToken(
$this->getGrant(),
['refresh_token' => $this->oauthRefreshToken]
);
}
/**
* Generate a base64-encoded OAuth token.
*
* @return string
*/
public function getOauth64()
{
// Get a new token if it's not available or has expired
if (null === $this->oauthToken || $this->oauthToken->hasExpired()) {
$this->oauthToken = $this->getToken();
}
return base64_encode(
'user=' .
$this->oauthUserEmail .
"\001auth=Bearer " .
$this->oauthToken .
"\001\001"
);
}
}

4778
core/External/PHPMailer/PHPMailer.php vendored Normal file
View File

File diff suppressed because it is too large Load Diff

419
core/External/PHPMailer/POP3.php vendored Normal file
View File

@@ -0,0 +1,419 @@
<?php
/**
* PHPMailer POP-Before-SMTP Authentication Class.
* PHP Version 5.5.
*
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
*
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
* @author Brent R. Matzelle (original founder)
* @copyright 2012 - 2019 Marcus Bointon
* @copyright 2010 - 2012 Jim Jagielski
* @copyright 2004 - 2009 Andy Prevost
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
* @note This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
namespace External\PHPMailer;
/**
* PHPMailer POP-Before-SMTP Authentication Class.
* Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
* 1) This class does not support APOP authentication.
* 2) Opening and closing lots of POP3 connections can be quite slow. If you need
* to send a batch of emails then just perform the authentication once at the start,
* and then loop through your mail sending script. Providing this process doesn't
* take longer than the verification period lasts on your POP3 server, you should be fine.
* 3) This is really ancient technology; you should only need to use it to talk to very old systems.
* 4) This POP3 class is deliberately lightweight and incomplete, implementing just
* enough to do authentication.
* If you want a more complete class there are other POP3 classes for PHP available.
*
* @author Richard Davey (original author) <rich@corephp.co.uk>
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
*/
class POP3
{
/**
* The POP3 PHPMailer Version number.
*
* @var string
*/
const VERSION = '6.1.4';
/**
* Default POP3 port number.
*
* @var int
*/
const DEFAULT_PORT = 110;
/**
* Default timeout in seconds.
*
* @var int
*/
const DEFAULT_TIMEOUT = 30;
/**
* Debug display level.
* Options: 0 = no, 1+ = yes.
*
* @var int
*/
public $do_debug = 0;
/**
* POP3 mail server hostname.
*
* @var string
*/
public $host;
/**
* POP3 port number.
*
* @var int
*/
public $port;
/**
* POP3 Timeout Value in seconds.
*
* @var int
*/
public $tval;
/**
* POP3 username.
*
* @var string
*/
public $username;
/**
* POP3 password.
*
* @var string
*/
public $password;
/**
* Resource handle for the POP3 connection socket.
*
* @var resource
*/
protected $pop_conn;
/**
* Are we connected?
*
* @var bool
*/
protected $connected = false;
/**
* Error container.
*
* @var array
*/
protected $errors = [];
/**
* Line break constant.
*/
const LE = "\r\n";
/**
* Simple static wrapper for all-in-one POP before SMTP.
*
* @param string $host The hostname to connect to
* @param int|bool $port The port number to connect to
* @param int|bool $timeout The timeout value
* @param string $username
* @param string $password
* @param int $debug_level
*
* @return bool
*/
public static function popBeforeSmtp(
$host,
$port = false,
$timeout = false,
$username = '',
$password = '',
$debug_level = 0
) {
$pop = new self();
return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
}
/**
* Authenticate with a POP3 server.
* A connect, login, disconnect sequence
* appropriate for POP-before SMTP authorisation.
*
* @param string $host The hostname to connect to
* @param int|bool $port The port number to connect to
* @param int|bool $timeout The timeout value
* @param string $username
* @param string $password
* @param int $debug_level
*
* @return bool
*/
public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
{
$this->host = $host;
// If no port value provided, use default
if (false === $port) {
$this->port = static::DEFAULT_PORT;
} else {
$this->port = (int) $port;
}
// If no timeout value provided, use default
if (false === $timeout) {
$this->tval = static::DEFAULT_TIMEOUT;
} else {
$this->tval = (int) $timeout;
}
$this->do_debug = $debug_level;
$this->username = $username;
$this->password = $password;
// Reset the error log
$this->errors = [];
// connect
$result = $this->connect($this->host, $this->port, $this->tval);
if ($result) {
$login_result = $this->login($this->username, $this->password);
if ($login_result) {
$this->disconnect();
return true;
}
}
// We need to disconnect regardless of whether the login succeeded
$this->disconnect();
return false;
}
/**
* Connect to a POP3 server.
*
* @param string $host
* @param int|bool $port
* @param int $tval
*
* @return bool
*/
public function connect($host, $port = false, $tval = 30)
{
// Are we already connected?
if ($this->connected) {
return true;
}
//On Windows this will raise a PHP Warning error if the hostname doesn't exist.
//Rather than suppress it with @fsockopen, capture it cleanly instead
set_error_handler([$this, 'catchWarning']);
if (false === $port) {
$port = static::DEFAULT_PORT;
}
// connect to the POP3 server
$this->pop_conn = fsockopen(
$host, // POP3 Host
$port, // Port #
$errno, // Error Number
$errstr, // Error Message
$tval
); // Timeout (seconds)
// Restore the error handler
restore_error_handler();
// Did we connect?
if (false === $this->pop_conn) {
// It would appear not...
$this->setError(
"Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr"
);
return false;
}
// Increase the stream time-out
stream_set_timeout($this->pop_conn, $tval, 0);
// Get the POP3 server response
$pop3_response = $this->getResponse();
// Check for the +OK
if ($this->checkResponse($pop3_response)) {
// The connection is established and the POP3 server is talking
$this->connected = true;
return true;
}
return false;
}
/**
* Log in to the POP3 server.
* Does not support APOP (RFC 2828, 4949).
*
* @param string $username
* @param string $password
*
* @return bool
*/
public function login($username = '', $password = '')
{
if (!$this->connected) {
$this->setError('Not connected to POP3 server');
}
if (empty($username)) {
$username = $this->username;
}
if (empty($password)) {
$password = $this->password;
}
// Send the Username
$this->sendString("USER $username" . static::LE);
$pop3_response = $this->getResponse();
if ($this->checkResponse($pop3_response)) {
// Send the Password
$this->sendString("PASS $password" . static::LE);
$pop3_response = $this->getResponse();
if ($this->checkResponse($pop3_response)) {
return true;
}
}
return false;
}
/**
* Disconnect from the POP3 server.
*/
public function disconnect()
{
$this->sendString('QUIT');
//The QUIT command may cause the daemon to exit, which will kill our connection
//So ignore errors here
try {
@fclose($this->pop_conn);
} catch (Exception $e) {
//Do nothing
}
}
/**
* Get a response from the POP3 server.
*
* @param int $size The maximum number of bytes to retrieve
*
* @return string
*/
protected function getResponse($size = 128)
{
$response = fgets($this->pop_conn, $size);
if ($this->do_debug >= 1) {
echo 'Server -> Client: ', $response;
}
return $response;
}
/**
* Send raw data to the POP3 server.
*
* @param string $string
*
* @return int
*/
protected function sendString($string)
{
if ($this->pop_conn) {
if ($this->do_debug >= 2) { //Show client messages when debug >= 2
echo 'Client -> Server: ', $string;
}
return fwrite($this->pop_conn, $string, strlen($string));
}
return 0;
}
/**
* Checks the POP3 server response.
* Looks for for +OK or -ERR.
*
* @param string $string
*
* @return bool
*/
protected function checkResponse($string)
{
if (strpos($string, '+OK') !== 0) {
$this->setError("Server reported an error: $string");
return false;
}
return true;
}
/**
* Add an error to the internal error store.
* Also display debug output if it's enabled.
*
* @param string $error
*/
protected function setError($error)
{
$this->errors[] = $error;
if ($this->do_debug >= 1) {
echo '<pre>';
foreach ($this->errors as $e) {
print_r($e);
}
echo '</pre>';
}
}
/**
* Get an array of error messages, if any.
*
* @return array
*/
public function getErrors()
{
return $this->errors;
}
/**
* POP3 connection error handler.
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
*/
protected function catchWarning($errno, $errstr, $errfile, $errline)
{
$this->setError(
'Connecting to the POP3 server raised a PHP warning:' .
"errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline"
);
}
}

1370
core/External/PHPMailer/SMTP.php vendored Normal file
View File

File diff suppressed because it is too large Load Diff

199
core/External/jwt.php vendored Normal file
View File

@@ -0,0 +1,199 @@
<?php
/**
* JSON Web Token implementation, based on this spec:
* http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06
*
* PHP version 5
*
* @category Authentication
* @package Authentication_JWT
* @author Neuman Vong <neuman@twilio.com>
* @author Anant Narayanan <anant@php.net>
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
* @link https://github.com/firebase/php-jwt
*/
namespace External;
class JWT
{
/**
* Decodes a JWT string into a PHP object.
*
* @param string $jwt The JWT
* @param string|null $key The secret key
* @param bool $verify Don't skip verification process
*
* @return object The JWT's payload as a PHP object
* @throws UnexpectedValueException Provided JWT was invalid
* @throws DomainException Algorithm was not provided
*
* @uses jsonDecode
* @uses urlsafeB64Decode
*/
public static function decode($jwt, $key = null, $verify = true)
{
$tks = explode('.', $jwt);
if (count($tks) != 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $bodyb64, $cryptob64) = $tks;
if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))) {
throw new UnexpectedValueException('Invalid segment encoding');
}
if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64))) {
throw new UnexpectedValueException('Invalid segment encoding');
}
$sig = JWT::urlsafeB64Decode($cryptob64);
if ($verify) {
if (empty($header->alg)) {
throw new DomainException('Empty algorithm');
}
if ($sig != JWT::sign("$headb64.$bodyb64", $key, $header->alg)) {
throw new UnexpectedValueException('Signature verification failed');
}
}
return $payload;
}
/**
* Converts and signs a PHP object or array into a JWT string.
*
* @param object|array $payload PHP object or array
* @param string $key The secret key
* @param string $algo The signing algorithm. Supported
* algorithms are 'HS256', 'HS384' and 'HS512'
*
* @return string A signed JWT
* @uses jsonEncode
* @uses urlsafeB64Encode
*/
public static function encode($payload, $key, $algo = 'HS256')
{
$header = array('typ' => 'JWT', 'alg' => $algo);
$segments = array();
$segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header));
$segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload));
$signing_input = implode('.', $segments);
$signature = JWT::sign($signing_input, $key, $algo);
$segments[] = JWT::urlsafeB64Encode($signature);
return implode('.', $segments);
}
/**
* Sign a string with a given key and algorithm.
*
* @param string $msg The message to sign
* @param string $key The secret key
* @param string $method The signing algorithm. Supported
* algorithms are 'HS256', 'HS384' and 'HS512'
*
* @return string An encrypted message
* @throws DomainException Unsupported algorithm was specified
*/
public static function sign($msg, $key, $method = 'HS256')
{
$methods = array(
'HS256' => 'sha256',
'HS384' => 'sha384',
'HS512' => 'sha512',
);
if (empty($methods[$method])) {
throw new DomainException('Algorithm not supported');
}
return hash_hmac($methods[$method], $msg, $key, true);
}
/**
* Decode a JSON string into a PHP object.
*
* @param string $input JSON string
*
* @return object Object representation of JSON string
* @throws DomainException Provided string was invalid JSON
*/
public static function jsonDecode($input)
{
$obj = json_decode($input);
if (function_exists('json_last_error') && $errno = json_last_error()) {
JWT::_handleJsonError($errno);
} else if ($obj === null && $input !== 'null') {
throw new DomainException('Null result with non-null input');
}
return $obj;
}
/**
* Encode a PHP object into a JSON string.
*
* @param object|array $input A PHP object or array
*
* @return string JSON representation of the PHP object or array
* @throws DomainException Provided object could not be encoded to valid JSON
*/
public static function jsonEncode($input)
{
$json = json_encode($input);
if (function_exists('json_last_error') && $errno = json_last_error()) {
JWT::_handleJsonError($errno);
} else if ($json === 'null' && $input !== null) {
throw new DomainException('Null result with non-null input');
}
return $json;
}
/**
* Decode a string with URL-safe Base64.
*
* @param string $input A Base64 encoded string
*
* @return string A decoded string
*/
public static function urlsafeB64Decode($input)
{
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
/**
* Encode a string with URL-safe Base64.
*
* @param string $input The string you want encoded
*
* @return string The base64 encode of what you passed in
*/
public static function urlsafeB64Encode($input)
{
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}
/**
* Helper method to create a JSON error.
*
* @param int $errno An error number from json_last_error()
*
* @return void
*/
private static function _handleJsonError($errno)
{
$messages = array(
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
);
throw new DomainException(
isset($messages[$errno])
? $messages[$errno]
: 'Unknown JSON error: ' . $errno
);
}
}

5702
core/External/phpQuery.php vendored Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
<?php
namespace Objects;
abstract class ApiObject implements \JsonSerializable {
public abstract function jsonSerialize();
public function __construct() { }
public function __toString() { return json_encode($this); }
}
?>

View File

@@ -0,0 +1,46 @@
<?php
namespace Objects;
class ConnectionData {
private $host;
private $port;
private $login;
private $password;
private $properties;
public function __construct($host, $port, $login, $password) {
$this->host = $host;
$this->port = $port;
$this->login = $login;
$this->password = $password;
$this->properties = array();
}
public function getProperties() {
return $this->properties;
}
public function getProperty($key) {
if(isset($this->properties[$key]))
return $this->properties[$key];
else
return '';
}
public function setProperty($key, $val) {
if(!is_string($val)) {
return false;
}
$this->properties[$key] = $val;
}
public function getHost() { return $this->host; }
public function getPort() { return $this->port; }
public function getLogin() { return $this->login; }
public function getPassword() { return $this->password; }
}
?>

View File

@@ -0,0 +1,133 @@
<?php
namespace Objects;
class Language extends ApiObject {
const LANG_CODE_PATTERN = "/^[a-zA-Z]+_[a-zA-Z]+$/";
private $languageId;
private $langCode;
private $langName;
private $modules;
protected $entries;
public function __construct($languageId, $langCode, $langName) {
$this->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;
}
// TODO: include dynamically wanted Language
return new Language($languageId, $langCode, $langName);
// $className = $langCode
// return new $className($languageId, $langCode);
}
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");
}
};
function L($key) {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return $key;
global $LANGUAGE;
return $LANGUAGE->translate($key);
}
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();
}
?>

View File

@@ -0,0 +1,105 @@
<?php
namespace Objects;
class Session extends ApiObject {
const DURATION = 120;
private $sessionId;
private $user;
private $expires;
private $ipAddress;
private $os;
private $browser;
public function __construct($user, $sessionId = NULL) {
$this->user = $user;
$this->sessionId = $sessionId;
}
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'];
}
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);
}
public function getExpiresTime() {
return $this->expires;
}
public function getExpiresSeconds() {
return ($this->expires - time());
}
public function jsonSerialize() {
return array(
'uid' => $this->sessionId,
'uidUser' => $this->user->getId(),
'expires' => $this->expires,
'ipAddress' => $this->ipAddress,
'os' => $this->os,
'browser' => $this->browser,
);
}
public function insert() {
$this->updateMetaData();
$query = 'INSERT INTO Session (expires, uidUser, ipAddress, os, browser)
VALUES (DATE_ADD(NOW(), INTERVAL ? MINUTE),?,?,?,?)';
$request = new CExecuteStatement($this->user);
$success = $request->execute(array(
'query' => $query,
Session::DURATION,
$this->user->getId(),
$this->ipAddress,
$this->os,
$this->browser,
));
if($success) {
$this->sessionId = $this->user->getSQL()->getLastInsertId();
return true;
}
return false;
}
public function destroy() {
$query = 'DELETE FROM Session WHERE Session.uid=? OR Session.expires<=NOW()';
$request = new CExecuteStatement($this->user);
$success = $request->execute(array('query' => $query, $this->sessionId));
return $success;
}
public function update() {
$this->updateMetaData();
$query = 'UPDATE Session
SET Session.expires=DATE_ADD(NOW(), INTERVAL ? MINUTE), Session.ipAddress=?,
Session.os=?, Session.browser=?
WHERE Session.uid=?';
$request = new CExecuteStatement($this->user);
$success = $request->execute(array(
'query' => $query,
Session::DURATION,
$this->ipAddress,
$this->os,
$this->browser,
$this->sessionId,
));
return $success;
}
}
?>

189
core/Objects/User.class.php Normal file
View File

@@ -0,0 +1,189 @@
<?php
namespace Objects;
class User extends ApiObject {
private $sql;
private $configuration;
private $loggedIn;
private $session;
private $uid;
private $username;
private $language;
public function __construct($configuration) {
$this->configuration = $configuration;
$this->setLangauge(Language::DEFAULT_LANGUAGE());
$this->reset();
$this->connectDb();
$this->parseCookies();
}
public function __destruct() {
if($this->sql && $this->sql->isConnected()) {
$this->sql->close();
}
}
private function connectDb() {
$databaseConf = $this->configuration->getDatabase();
if($databaseConf) {
$this->sql = \Driver\SQL::createConnection($databaseConf);
}
}
public function setSql($sql) { $this->sql = $sql; }
public function getId() { return $this->uid; }
public function isLoggedIn() { return $this->loggedIn; }
public function getUsername() { return $this->username; }
public function getSQL() { return $this->sql; }
public function getLanguage() { return $this->language; }
public function setLangauge($language) { $this->language = $language; $language->load(); }
public function getSession() { return $this->session; }
public function getConfiguration() { return $this->configuration; }
public function __debugInfo() {
$debugInfo = array(
'loggedIn' => $this->loggedIn,
'language' => $this->language->getName(),
);
if($this->loggedIn) {
$debugInfo['uid'] = $this->uid;
$debugInfo['username'] = $this->username;
}
return $debugInfo;
}
public function jsonSerialize() {
return array(
'uid' => $this->uid,
'name' => $this->username,
'language' => $this->language,
'session' => $this->session,
);
}
private function reset() {
$this->uid = 0;
$this->username = '';
$this->loggedIn = false;
$this->session = false;
}
public function logout() {
if($this->loggedIn) {
$this->session->destroy();
$this->reset();
}
}
public function updateLanguage($lang) {
$request = new CSetLanguage($this);
return $request->execute(array("langCode" => $lang));
}
public function sendCookies() {
if($this->loggedIn) {
$this->session->sendCookie();
}
$this->language->sendCookie();
}
public function readData($userId, $sessionId, $sessionUpdate = true) {
$query = 'SELECT User.name as userName, Language.uid as langId, Language.code as langCode
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);
$success = $request->execute(array('query' => $query, $userId, $sessionId));
if($success) {
if(count($request->getResult()['rows']) === 0) {
$success = false;
} else {
$row = $request->getResult()['rows'][0];
$this->username = $row['userName'];
$this->uid = $userId;
$this->session = new CSession($this, $sessionId);
if($sessionUpdate) $this->session->update();
$this->loggedIn = true;
if(!is_null($row['langId'])) {
$this->setLangauge(CLanguage::newInstance($row['langId'], $row['langCode']));
}
}
}
return $success;
}
private function parseCookies() {
if(isset($_COOKIE['session'])
&& is_string($_COOKIE['session'])
&& !empty($_COOKIE['session'])
&& ($jwt = $this->configuration->getJWT())) {
try {
$token = $_COOKIE['session'];
$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);
if(!is_null($userId) && !is_null($sessionId)) {
$this->readData($userId, $sessionId);
}
}
} catch(Exception $e) {
echo $e;
}
}
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"])) {
$this->updateLanguage($_COOKIE['lang']);
}
}
public function createSession($userId) {
$this->uid = $userId;
$this->session = new Session($this);
$this->loggedIn = $this->session->insert();
return $this->loggedIn;
}
public function authorize($apiKey) {
if($this->loggedIn)
return true;
$query = 'SELECT ApiKey.uidUser as uid, User.name as username, Language.uid as langId, Language.code as langCode
FROM ApiKey, User
LEFT JOIN Language ON User.uidLanguage=Language.uid
WHERE api_key=? AND valid_until > now() AND User.uid = ApiKey.uidUser';
$request = new CExecuteSelect($this);
$success = $request->execute(array('query' => $query, $apiKey));
if($success) {
if(count($request->getResult()['rows']) === 0) {
$success = false;
} else {
$row = $request->getResult()['rows'][0];
$this->uid = $row['uid'];
$this->username = $row['username'];
if(!is_null($row['langId'])) {
$this->setLangauge(Language::newInstance($row['langId'], $row['langCode']));
}
}
}
return $success;
}
}
?>

View File

@@ -0,0 +1,17 @@
<?php
require_once './LanguageModule.php';
abstract class CLanguageModuleGeneral {
public static function getEntries($langCode) {
switch($langCode) {
case "de_DE":
$this->entries[""] = "";
break;
}
}
}
?>

View File

@@ -0,0 +1,9 @@
<?php
abstract class CLanguageModule {
public abstract static function getEntries($langCode);
}
?>

229
core/View.class.php Normal file
View File

@@ -0,0 +1,229 @@
<?php
abstract class View {
private $document;
private $loadView;
protected $searchable;
protected $reference;
protected $title;
protected $langModules;
public function __construct($document, $loadView = true) {
$this->document = $document;
$this->searchable = false;
$this->printable = false;
$this->reference = "";
$this->title = "Untitled View";
$this->langModules = array();
$this->loadView = $loadView;
}
public function getTitle() { return $this->title; }
public function __toString() { return $this->getCode(); }
public function getDocument() { return $this->document; }
public function isSearchable() { return $this->searchable; }
public function getReference() { return $this->reference; }
private function loadLanguageModules() {
$lang = $this->document->getUser()->getLanguage();
foreach($this->langModules as $langModule) {
$lang->loadModule($langModule);
}
}
// Virtual Methods
public function loadView() { }
public function getCode() {
// Load translations
$this->loadLanguageModules();
// Load Meta Data + Head (title, scripts, includes, ...)
if($this->loadView) {
$this->loadView();
}
return '';
}
// UI Functions
// TODO: do we need this in our general web-base?
public function createFileIcon($mimeType) {
$mimeType = htmlspecialchars($mimeType);
return "<img src=\"/img/icons/admin/getIcon.php?mimeType=$mimeType\" class=\"file-icon\" alt=\"[$mimeType icon]\">";
}
public function createParagraph($title, $id, $content) {
$id = replaceCssSelector($id);
$iconId = urlencode("$id-icon");
return "
<div class=\"row\">
<div class=\"col-12\">
<i class=\"fas fa-link\" style=\"display:none;position:absolute\" id=\"$iconId\"></i>
<h2 id=\"$id\" data-target=\"$iconId\" class=\"inlineLink\">$title</h2>
<div class=\"margin-bottom-xl\"><hr>$content</div>
</div>
</div>";
}
public function createSimpleParagraph($content, $class="") {
if($class) $class = " class=\"$class\"";
return "<p$class>$content</p>";
}
private function createList($items, $tag) {
if(count($items) === 0)
return "<$tag></$tag>";
else
return "<$tag><li>" . implode("</li><li>", $items) . "</li></$tag>";
}
public function createOrderedList($items=array()) {
return $this->createList($items, "ol");
}
public function createUnorderedList($items=array()) {
return $this->createList($items, "ul");
}
public function createJumbotron($content, $lastModified=false) {
$lastModified = ($lastModified ? "<span class=\"float-right text-xxs margin-top-xxxl\">Last modified: $lastModified</span>" : "");
return "
<div class=\"row\">
<div class=\"col-12\">
<div class=\"jumbotron\">
$content
$lastModified
</div>
</div>
</div>";
}
protected function createLink($link, $title=null) {
if(is_null($title)) $title=$link;
return "<a href=\"$link\">$title</a>";
}
protected function createExternalLink($link, $title=null) {
if(is_null($title)) $title=$link;
return "<a href=\"$link\" target=\"_blank\" class=\"external\">$title</a>";
}
protected function createCodeBlock($code, $lang="") {
if($lang) $lang = " class=\"$lang\"";
$html = "<pre><code$lang>";
$html .= intendCode($code);
$html .= "</code></pre>";
return $html;
}
protected function createIcon($icon, $margin = NULL) {
$marginStr = (is_null($margin) ? "" : " margin-$margin");
$iconClass = $this->getIconClass($icon);
return "<i class=\"$iconClass$marginStr\"></i>";
}
protected function getIconClass($icon) {
$mappings = array(
"sign-out" => "sign-out-alt",
"bank" => "university",
"line-chart" => "chart-line",
"circle-right" => "arrow-alt-circle-right",
"refresh" => "sync"
);
if(isset($mappings[$icon]))
$icon = $mappings[$icon];
if($icon === "spinner")
$icon .= " fa-spin";
return "fas fa-$icon";
}
protected function createBootstrapTable($data) {
$code = "<div class=\"container\">";
foreach($data as $row) {
$code .= "<div class=\"row margin-top-xs margin-bottom-xs\">";
$columnCount = count($row);
if($columnCount > 0) {
$remainingSize = 12;
$columnSize = 12 / $columnCount;
foreach($row as $col) {
$size = ($columnSize <= $remainingSize ? $columnSize : $remainingSize);
$content = $col;
$class = "";
$code .= "<div";
if(is_array($col)) {
foreach($col as $key => $val) {
if(strcmp($key, "content") === 0) {
$content = $val;
} else if(strcmp($key, "class") === 0) {
$class = " " . $col["class"];
} else if(strcmp($key, "cols") === 0 && is_numeric($val)) {
$size = intval($val);
} else {
$code .= " $key=\"$val\"";
}
}
$content = (isset($col["content"]) ? $col["content"] : "");
if(isset($col["class"])) $class = " " . $col["class"];
}
if($size <= 6) $class .= " col-md-" . intval($size * 2);
$code .= " class=\"col-lg-$size$class\">$content</div>";
$remainingSize -= $size;
}
}
$code .= "</div>";
}
$code .= "</div>";
return $code;
}
protected function createBash($command, $output="", $prefix="") {
$command = htmlspecialchars($command);
$output = htmlspecialchars($output);
$output = str_replace("\n", "<br>", $output);
return "<div class=\"bash\">
<span>$prefix$</span>&nbsp;
<span>$command</span><br>
<span>$output</span>
</div>";
}
protected function createErrorText($text, $id="", $hidden=false) {
return $this->createStatusText("danger", $text, $id, $hidden);
}
protected function createWarningText($text, $id="", $hidden=false) {
return $this->createStatusText("warning", $text, $id, $hidden);
}
protected function createSuccessText($text, $id="", $hidden=false) {
return $this->createStatusText("success", $text, $id, $hidden);
}
protected function createSecondaryText($text, $id="", $hidden=false) {
return $this->createStatusText("secondary", $text, $id, $hidden);
}
protected function createInfoText($text, $id="", $hidden=false) {
return $this->createStatusText("info", $text, $id, $hidden);
}
protected function createStatusText($type, $text, $id="", $hidden=false) {
if(strlen($id) > 0) $id = " id=\"$id\"";
$hidden = ($hidden?" hidden" : "");
return "<div class=\"alert alert-$type$hidden\" role=\"alert\"$id>$text</div>";
}
};
?>

6
core/constants.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
const USER_GROUP_DEFAULT = 1;
const USER_GROUP_ADMIN = 2;
?>

137
core/core.php Normal file
View File

@@ -0,0 +1,137 @@
<?php
function clamp($val, $min, $max) {
if($val < $min) return $min;
if($val > $max) return $max;
return $val;
}
function downloadFile($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
function getSubclassesOf($parent) {
$result = array();
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, $parent))
$result[] = $class;
}
return $result;
}
function includeDir($dir, $aIgnore = array(), $recursive = false) {
$aIgnore[] = '.';
$aIgnore[] = '..';
$aFiles = array_diff(scandir($dir), $aIgnore);
foreach($aFiles as $file) {
$file = $dir . '/' . $file;
if(is_dir($file)) {
if($recursive) {
includeDir($file, $aIgnore, true);
}
} else {
require_once $file;
}
}
}
function generateRandomString($length) {
$randomString = '';
if($length > 0) {
$numCharacters = 26 + 26 + 10; // a-z + A-Z + 0-9
for ($i = 0; $i < $length; $i++)
{
$num = random_int(0, $numCharacters - 1);
if($num < 26) $randomString .= chr(ord('a') + $num);
else if($num - 26 < 26) $randomString .= chr(ord('A') + $num - 26);
else $randomString .= chr(ord('0') + $num - 26 - 26);
}
}
return $randomString;
}
function cleanPath($path) {
if($path === '')
return $path;
$path = str_replace('\\', '/', $path);
$path = str_replace('/./', '/', $path);
if($path[0] !== '/')
$path = '/' . $path;
$path = str_replace('/../', '/', $path);
return $path;
}
function startsWith($haystack, $needle) {
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
function endsWith($haystack, $needle) {
$length = strlen($needle);
if ($length == 0)
return true;
return (substr($haystack, -$length) === $needle);
}
function isCalledDirectly($file) {
return $_SERVER['SCRIPT_FILENAME'] === $file;
}
function anonymzeEmail($mail) {
if(($pos = strpos($mail, '@')) !== -1) {
$name = substr($mail, 0, $pos);
$host = substr($mail, $pos + 1);
if(strlen($name) > 2) $mail = substr($name, 0, 2) . str_repeat('*', strlen($name) - 2) . "@$host";
else $mail = $mail = str_repeat('*', strlen($name)) . "@$host";
}
return $mail;
}
function intendCode($code, $escape=true) {
$newCode = "";
$first = true;
$brackets = array();
$intend = 0;
foreach(explode("\n", $code) as $line) {
if(!$first) $newCode .= "\n";
if($escape) $line = htmlspecialchars($line);
$line = trim($line);
if(count($brackets) > 0 && startsWith($line, current($brackets))) {
$intend = max(0, $intend - 2);
array_pop($brackets);
}
$newCode .= str_repeat(" ", $intend);
$newCode .= $line;
$first = false;
if(endsWith($line, "{")) {
$intend += 2;
array_push($brackets, "}");
} else if(endsWith($line, "(")) {
$intend += 2;
array_push($brackets, ")");
}
}
return $newCode;
}
function replaceCssSelector($sel) {
return preg_replace("~[.#<>]~", "_", preg_replace("~[:\-]~", "", $sel));
}
?>

121
core/datetime.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
function getFirstWeekDayOfMonth($d = NULL) {
if(is_null($d)) $d = date('Y-m-d H:i:s');
$dt = new DateTime($d);
$dt->modify('first day of this month');
return intval($dt->format('N'));
}
function getDayCount($d = NULL) {
if(is_null($d)) $d = date('Y-m-d H:i:s');
$dt = new DateTime($d);
$dt->modify('last day of this month');
return intval($dt->format('j'));
}
function prevMonth($d = NULL) {
if(is_null($d)) $d = date('Y-m-d H:i:s');
$dt = new DateTime($d);
$dt->modify('previous month');
return $dt->format('Y-m-d H:i:s');
}
function prevDay($d = NULL) {
if(is_null($d)) $d = date('Y-m-d H:i:s');
$dt = new DateTime($d);
$dt->modify('previous day');
return $dt->format('Y-m-d H:i:s');
}
function nextDay($d = NULL) {
if(is_null($d)) $d = date('Y-m-d H:i:s');
$dt = new DateTime($d);
$dt->modify('next day');
return $dt->format('Y-m-d H:i:s');
}
function nextMonth($d = NULL) {
if(is_null($d)) $d = date('Y-m-d H:i:s');
$dt = new DateTime($d);
$dt->modify('next month');
return $dt->format('Y-m-d H:i:s');
}
function formatPeriod($d1, $d2) {
$timeStr = dateFunction('d.m.Y H:i', $d1);
if($timeStr == dateFunction('d.m.Y H:i', $d2)) {
if(dateFunction('H:i', $d1) === '00:00') {
return dateFunction('d.m.Y', $d1);
}
} else if(dateFunction('d.m.Y', $d1) !== dateFunction('d.m.Y', $d2)) {
$timeStr .= " - " . dateFunction('d.m.Y H:i', $d2);
} elseif(dateFunction('H:i', $d1) !== dateFunction('H:i', $d2)) {
$timeStr .= " - " . dateFunction('H:i', $d2);
}
return $timeStr;
}
function now() { return new DateTime(); }
function getTime($d = NULL) { return dateFunction('H:i', $d); }
function getHour($d = NULL){ return dateFunction('H', $d); }
function getMinute($d = NULL){ return dateFunction('i', $d); }
function getYear($d = NULL) { return intval(dateFunction('Y', $d)); }
function getMonth($d = NULL) { return intval(dateFunction('n', $d)); }
function getDay($d = NULL) { return intval(dateFunction('d', $d)); }
function getDayOfWeek($d = NULL) { return intval(dateFunction('N', $d)); }
function getDayNumberOfWeek($d = NULL) { return intval(dateFunction('N', $d)); }
function getDateTime($d = NULL) { return dateFunction('Y-m-d H:i:s', $d); }
function getFirstDay($d = NULL) { return dateFunction('Y-m-01', $d); }
function getLastDay($d = NULL) { return dateFunction('t', $d); }
function getPrevMonth($d = NULL) { return dateFunction("-1 month", $d); }
function getLocalizedDateTime($d = NULL) { return dateFunction(L('Y/m/d H:i'), $d); }
function getJavascriptDate($d = NULL) {
if(is_null($d)) $d = date('Y-m-d H:i:s');
if(!is_a($d, "DateTime")) $d = new DateTime($d);
$year = getYear($d);
$month = getMonth($d) - 1;
$day = getDay($d);
$hour = getHour($d);
$minute = getMinute($d);
return "new Date($year, $month, $day, $hour, $minute)";
}
function getWeekDayName($day) {
$aWeekDays = array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday');
return L($aWeekDays[$day - 1]);
}
function getMonthName($month) {
$aMonthNames = array("January", "February", "March", "April", "May", "Juni", "July", "August", "September", "October", "November", "December");
return L($aMonthNames[$month - 1]);
}
function isInPast($d) {
$now = date('Y-m-d H:i:s');
if(is_a($d, "DateTime")) $d = $d->format('Y-m-d H:i:s');
return (strtotime($d) < strtotime($now));
}
function datetimeDiff($d1, $d2) {
if(!($d1 instanceof DateTime)) $d1 = new DateTime($d1);
if(!($d2 instanceof DateTime)) $d2 = new DateTime($d2);
return $d2->getTimestamp() - $d1->getTimestamp();
}
function durationIn($d1, $d2, $str) {
if(!($d1 instanceof DateTime)) $d1 = new DateTime($d1);
if(!($d2 instanceof DateTime)) $d2 = new DateTime($d2);
$interval = $d1->diff($d2);
return intval($interval->format($str));
}
function dateFunction($str, $d = NULL) {
if(is_null($d)) $d = date('Y-m-d H:i:s');
if(!is_a($d, "DateTime")) $d = new DateTime($d);
return $d->format($str);
}
?>