"", // Route of page module. Has to be set, at least to "/" [GET(all), POST] "mainID" => null, // Filter of main resource. [GET(one), PUT, PATCH, DELETE] with ID, [GET(all)] with condition "sub" => null, // Route of sub module. Requests are specific to a main resource, so only valid if filter is an ID. [GET(all), POST] "subID" => null // Filter of sub resource. [GET(one), PUT, PATCH, DELETE] with ID, [GET(all)] with condition ]; private $availablePages = []; // Array of scanned and accessible page modules private $availableLinks = []; // Array of scanned and accessible link modules private $availableObjects = []; // Array of scanned and accessible object modules private $responseCode = 401; // HTTP code to be returned public $objects = []; public $input = []; // Submitted JSON data after sanitation. ONLY SET IF Security token was valid. public $filter = []; public $selected = []; private $output = [ "messages" => [], "admin" => false // TODO DEPRECATED ]; public function __construct($db, $kv, $key) { self::$db = $db; self::$kv = $kv; $jwt = $this->readRequest(); if (isset($_SERVER["CONTENT_TYPE"]) && "application/json"==$_SERVER["CONTENT_TYPE"]) { $input = $this->cleanInput(json_decode(file_get_contents("php://input"), true)); } else { $input = []; if (isset($_GET["filter"])) { $input["filter"] = $this->cleanInput(json_decode($_GET["filter"], true)); } if (isset($_GET["selected"])) { $input["selected"] = $this->cleanInput(json_decode($_GET["selected"], true)); } else { $input["selected"] = []; } if (isset($_GET["print"])) { $input["print"] = $_GET["print"]; } } if ("HTML" == $this->requestedContentType && isset($input["print"])) { $this->requestedContentType = "PRINT"; } $this->user = new Login($db, $kv, $key, $this, $jwt, $input, "GET"!=$this->method); if (in_array("Logout", $this->route)) { $this->user->Logout(); $this->AddMessage("Logout erkannt!"); } // Output status of user login to client $this->output["status"] = $this->user->Status(); // Login was successful? if ($this->user->LoggedIn()) { // Überprüfe gesendetes Security-Token und veröffentliche ggfs. $input if ("GET"==$this->method) { // Maximal Filter oder Print als Input erlaubt if (isset($input["print"])) { $this->input["print"] = $input["print"]; } if (isset($input["filter"])) { $this->filter = $input["filter"]; } if (isset($input["selected"])) { $this->selected = $input["selected"]; } } else if ("OPTIONS"==$this->method) { // No filter or input at the moment } else if (!empty($input)) { if (isset($input["secToken"]) && $this->user->VerifySecToken($input["secToken"])) { $this->input = $input; } else { $this->addMessage("Input wird ignoriert. Senden Sie den secToken mit!"); } } else { } $this->detectObjects(); $this->detectLinks(); $this->detectPages(); if ("OPTIONS"==$this->method) { $pages = $this->output["pages"]; $this->output["pages"] = []; foreach ($pages as $page) { $this->output["pages"][] = $page["route"]; } $this->output["pagesChecksum"] = sha1(implode(",", $this->output["pages"])); $this->responseCode = 200; } else { $this->loadPage(); $this->detectPrints(); $this->output["filter"] = $this->filter; } } } public function JWT() {return $this->jwt;} public function Method() {return $this->method;} public function RequestedContentType() {return $this->requestedContentType;} public function Output() {return $this->output;} public function Route() {return $this->route["route"];} public function Main() {return $this->route["mainID"];} public function Sub() {return $this->route["sub"];} public function SubID() {return $this->route["subID"];} public function ResponseCode() {return $this->responseCode;} public function Filter() {return $this->filter;} public function AddMessage($message, $ident = null) {$this->output["messages"][] = $message;} public function ArrayToString($a) { $ret = ""; foreach ($a as $k => $v) { $ret.=$k." => ".(is_array($v) ? "[".$this->ArrayToString($v)."]" : $v).", "; } return $ret; } /******************** PRIVATE FUNCTIONS ********************/ private function readRequest() { $jwt = null; // Copied from https://stackoverflow.com/a/40582472 $headers = null; if (isset($_SERVER['Authorization'])) { $headers = trim($_SERVER["Authorization"]); } else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI $headers = trim($_SERVER["HTTP_AUTHORIZATION"]); } elseif (function_exists('apache_request_headers')) { $requestHeaders = apache_request_headers(); // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization) $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders)); //print_r($requestHeaders); if (isset($requestHeaders['Authorization'])) { $headers = trim($requestHeaders['Authorization']); } } // Get the access token from the header if (!empty($headers)) { if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) { $jwt = $matches[1]; } } // End of copied // Read accepted/requested content type if (isset($_SERVER["HTTP_ACCEPT"]) && "application/json"==$_SERVER["HTTP_ACCEPT"]) { $this->requestedContentType = "JSON"; } elseif (isset($_SERVER["HTTP_ACCEPT"]) && "application/pdf"==$_SERVER["HTTP_ACCEPT"]) { $this->requestedContentType = "PDF"; } // Read the requested route (resource) and method $this->method = $_SERVER['REQUEST_METHOD']; if (preg_match('$/([^/]+)/?([^/]+)?/?([^/]+)?/?([^/]+)?$', $_SERVER['PATH_INFO'] ?? "", $matches, PREG_UNMATCHED_AS_NULL)) { $this->route["route"] = $matches[1]; $this->route["mainID"] = $matches[2]; $this->route["sub"] = $matches[3]; $this->route["subID"] = $matches[4]; } return $jwt; } private function cleanInput($array) { // Cleans input $ret = []; if ($array!==null) { foreach ($array as $key => $value) { $ret[$key] = is_null($value) ? null : (is_array($value) ? $this->cleanInput($value) : (is_string($value) ? self::$db->escape_string($value) : intval($value))); } } return $ret; } private function detectObjects() { $dir = scandir("obj"); foreach ($dir as $val) { if ($val!="." && $val!="..") { if (file_exists("obj/".$val."/module.json")) { $json = json_decode(file_get_contents("obj/".$val."/module.json"), true); $this->availableObjects[$json["name"]] = [ "name" => $json["name"], "class" => $json["class"], "path" => $val, "useRight" => $json["useRight"] ?? null, "adminRight" => $json["adminRight"] ?? null ]; } } } } private function detectLinks() { $dir = scandir("lnk"); foreach ($dir as $val) { if ($val!="." && $val!="..") { if (file_exists("lnk/".$val."/module.json")) { $json = json_decode(file_get_contents("lnk/".$val."/module.json"), true); if ($this->user->HasRight($json["useRight"])) { $this->availableLinks[$json["route"]] = [ "route" => $json["route"], "table" => $json["table"] ?? "", "path" => $val, "dependencies" => $json["dependencies"] ?? [], "useRight" => $json["useRight"] ?? null, "adminRight" => $json["adminRight"] ?? null, ]; } } } } } private function detectPages() { $dir = scandir("pgs"); foreach ($dir as $val) { if ($val!="." && $val!="..") { if (file_exists("pgs/".$val."/module.json")) { $json = json_decode(file_get_contents("pgs/".$val."/module.json"), true); if ($this->user->HasRight($json["useRight"])) { $this->availablePages[$json["route"]] = [ "title" => $json["title"] ?? "", "route" => $json["route"], "table" => $json["table"] ?? "", "path" => $val, "links" => $json["links"] ?? [], "dependencies" => $json["dependencies"] ?? [], "useRight" => $json["useRight"] ?? null, "adminRight" => $json["adminRight"] ?? null, ]; $page = [ "link" => "/index.php/".$json["route"], "route" => $json["route"], "isPage" => $json["isPage"] ?? false, ]; if ($page["isPage"]) { $page["name"] = $json["title"]; $page["template"] = isset($json["template"]) ? "/pgs/".$val."/".$json["template"] : null; $page["marker"] = $json["marker"]; $page["mainFields"] = $json["mainFields"]; } $this->output["pages"][] = $page; } } } } } private function loadPage() { if (isset($this->availablePages[$this->Route()])) { $info = $this->availablePages[$this->Route()]; foreach ($info["dependencies"] as $dep) { if (isset($this->availableObjects[$dep]) && !isset($this->objects[$dep])) { include "obj/".$this->availableObjects[$dep]["path"]."/object.php"; $className = "\\".$this->availableObjects[$dep]["name"]."\\".$this->availableObjects[$dep]["class"]; $this->objects[$dep] = new $className(self::$db, $this, $this->Main()); } } $file = "pgs/".$info["path"]."/page.php"; if (file_exists($file)) { include $file; $className = $info["route"]."Page"; $page = new $className(self::$db, self::$kv, $this, $info); if (isset($this->route["sub"])) { if ($this->Main()!==null && $this->SubID()!==null) { $sub = $page->GetLink($this->route["sub"]); if (isset($sub, $this->availableLinks[$sub["route"]])) { $link = $this->availableLinks[$sub["route"]]; include "lnk/".$link["path"]."/link.php"; $subClassName = $link["route"]."Link"; $subClass = new $subClassName(self::$db, self::$kv, $this, $link, $page); $ids[$sub["mainName"]] = $this->Main(); $ids[$sub["subName"]] = $this->SubID(); $this->responseCode = $subClass->DoRequest($this->method, $ids); $page->DoRequest("GET"); } else { $this->responseCode = 404; } } else { $this->responseCode = 400; } } else { $this->responseCode = $page->DoRequest($this->method); } if ($this->responseCode>=200 && $this->responseCode<300) { $this->output["meta"] = $page->GetMeta(); $this->output["content"] = $page->GetResult(); $this->output["template"] = $page->GetTemplate(); if (isset($info["adminRight"]) && $this->user->HasRight($info["adminRight"])) { $this->output["admin"] = true; } $this->output["options"] = $page->GetOptions($this->output["admin"]); } } else { $this->responseCode = 500; // Missing file means misconfiguration $this->AddMessage($this->Route()." seems to be misconfigured. A required file is missing!"); } } else if ($this->Route()==null) { // Dashboard with Stubs (hopefully)? $this->responseCode = 200; $this->output["content"] = []; $this->output["template"] = ""; } else { // Load nothing, because an unknown page was requested $this->responseCode = 404; $this->AddMessage($this->Route()." doesn't exist"); $this->output["content"] = []; $this->output["template"] = ""; } } private function detectPrints() { $this->printer = new Printer(self::$db, $this); $this->output["prints"] = $this->printer->GetPrints(); } }