* @license https://www.gnu.org/licenses/gpl-3.0.en.html GNU General Public License V3 * @package SWDB */ require_once(__DIR__ . "/config.php"); require_once(PATH::HELPER . "DB.php"); require_once(PATH::HELPER . "TEXT.php"); require_once(PATH::HELPER . "NET.php"); require_once(PATH::HELPER . "HTML.php"); require_once(PATH::HELPER . "APPLICATION.php"); require_once(PATH::ENTITY . "User.php"); require_once(PATH::ENTITY . "Player.php"); require_once(__DIR__ . "/Constant.php"); require_once(__DIR__ . "/Context.php"); /** * Application controller. * * Handles every request, creating the required models and selecting the * view. * * @category Controller */ class Controller extends Context{ /** * @var string[] GET parameters received by the controller. */ private $params = []; /** * @var Context Publicly available context. */ private $context; /** * @var string Command to execute (firs part of the url in the domain). */ private $command = ""; /** * @var int Response status */ private $status = 201; /** * @var string Response message. */ private $message = "OK"; /** * Retrieves the application context. * * @return Context The context. */ public function get_context(){ return $this->context; } /** * Controller constructor. * * Initializes a database connection and sets up the public context. */ public function __construct(){ $this->context = new Context(); session_start(); $this->db = DB::start(); $this->context->set_db($this->db); } private function execute_action($action){ require_once(PATH::ACTION . $action . ".php"); action(); return; } /** * Handles the request parameters. * * Receives the request params and gets itself ready. Authenticates the user (if needed) * and validates the URL. * * @param string[] $params GET parameters of the request. */ public function prepare($params){ // Parse parameters. foreach($params as $p){ if (strlen($p) > 0){ array_push($this->params, $p); } } // Chack if a user is authenticated. if (array_key_exists("user_token", $_COOKIE) && array_key_exists("user_id", $_COOKIE)){ // Authenticated user, check token. $statement = $this->db->prepare(" SELECT count(token) AS c FROM token WHERE user = :user AND token = :token AND strftime('%s', datetime(expiry, 'unixepoch', 'localtime')) > strftime('%s','now') "); $statement->bindValue(":user", $_COOKIE["user_id"], SQLITE3_INTEGER); $statement->bindValue(":token", hash('sha256', $_COOKIE["user_token"]), SQLITE3_TEXT); $r = $statement->execute()->fetchArray(SQLITE3_ASSOC); if ($r["c"] == 0){ // Token is invalid, expired, clear cookies, session, and logout. execute_action("logout"); return; } else{ // Token is valid, renew session, and assign user. $_SESSION["user_id"] = $_COOKIE["user_id"]; $_SESSION["session"] = true; $this->context->set_user(new User($_COOKIE["user_id"])); } } else{ // Unauthenticated user. Continue } $auth_required = false; $player_id = null; // Obtain the command and process it if (sizeof($this->params) > 0){ $this->command = strtoupper($this->params[0]); } switch ($this->command){ case "": // Main page break; case "API": // An API call return; // End preparation here, the API controller will deal with everything. break; case "ACTION": // Execute an action //$auth_required = true; break; case "PLAYER": // A player page if (sizeof($this->params) > 1){ $player_id = $this->params[1]; } else{ $this->status = 400; $this->message = "Bad request"; return; } break; case "LOGIN": // The login page break; case "REGISTER": // Registration page break; case "SEARCH": // Search results break; case "DUNGEON": // Dungeon static pages break; case "BUILDING": // Building static pages break; case "FUSION": // Fusion static pages break; case "HELP": // Help static page break; case "ERROR": // Error page break; default: // Unknown page } if ($player_id != null){ $this->context->set_player(new Player($player_id)); if ($this->context->get_player()->get_id() == null){ $this->status = 404; $this->message = "Requested player doesn't exist"; return; } } if ($auth_required && $this->context->get_user() == null){ $this->status = 401; $this->message = "Authentication required to perform this action"; return; } // Get game mode if ($this->context->get_player() == null){ $this->context->set_game_mode(GAME_MODE_ID::NORMAL); } else{ $statement = $this->db->prepare("SELECT mode FROM player WHERE id = :id;"); $statement->bindValue(":id", $this->context->get_player()->get_id(), SQLITE3_INTEGER); $r = $statement->execute()->fetchArray(SQLITE3_ASSOC); if ($r && $r["mode"] == GAME_MODE_ID::RTA){ $this->context->set_game_mode(GAME_MODE_ID::RTA); } else{ $this->context->set_game_mode(GAME_MODE_ID::NORMAL); } } } /** * Executes the required action. * * Must be called after {@see Controller::prepare()}. * @return void|number */ public function action(){ // If there was an error in preparation, it's time to throw it. if ($this->status >= 400 && $this->status < 500){ require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page($this->status, $this->message); require_once($page->get_view()); http_response_code(404); return 404; } // Process the command $page = null; switch ($this->command){ case "": // Main page // Two options: // If user authenticated, retrieve last used player acocunt and load main page // If not, login page. if ($this->context->get_user() == null){ require_once(PATH::PAGE . "Login_Page.php"); $page = new Login_Page(); } else{ $statement = $this->context->get_db()->prepare(" SELECT id FROM player WHERE user = :user ORDER BY last_access DESC LIMIT 1; "); $statement->bindValue(":user", $this->context->get_user()->get_id(), SQLITE3_INTEGER); $r = $statement->execute()->fetchArray(SQLITE3_ASSOC); if ($r){ header("Location: " . URL::BASE . "player/" . $r["id"]); return; //$this->context->set_player(new Player($r["id"])); //require_once(PATH::PAGE . "Player_Page.php"); //$page = new Player_Page(); } else{ // TODO: weird case: user with no players } } break; case "API": // An API call if (count($this->params) == 1){ // TODO: Redirect to current version } else{ $version = strtolower($this->params[1]); $api_params = $this->params; array_shift($api_params); array_shift($api_params); switch ($version){ case "v1": require_once(__DIR__ . "/API/v1/API_Controller.php"); new API_Controller($this->params); return; break; default: require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page(404, "API version doesn't exist."); require_once($page->get_view()); http_response_code(404); return 404; } } break; case "ACTION": // Execute an action // If no action specified if (sizeof($this->params) < 2){ require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page(400, "No action specified."); require_once($page->get_view()); http_response_code(400); return 400; } // Process action $action = strtoupper($this->params[1]); switch ($action){ // Special cases where the output must be printed: case "CHANGE_GAME_MODE": require_once(PATH::ACTION . "Change_Game_Mode_Action.php"); $action = new Change_Game_Mode_Action(); break; case "LOGIN": require_once(PATH::ACTION . "Login_Action.php"); $action = new Login_Action(); break; case "LOGOUT": require_once(PATH::ACTION . "Logout_Action.php"); $action = new Logout_Action(); break; case "REGISTER": require_once(PATH::ACTION . "Register_Action.php"); $action = new Register_Action(); break; case "ADD_TEAM": require_once(PATH::ACTION . "Add_Team_Action.php"); $action = new Add_Team_Action(); break; case "EDIT_PROFILE": require_once(PATH::ACTION . "Edit_Profile_Action.php"); $action = new Edit_Profile_Action(); break; case "DELETE_TEAM": require_once(PATH::ACTION . "Delete_Team_Action.php"); $action = new Delete_Team_Action(); break; case "RATE_TEAM": require_once(PATH::ACTION . "Rate_Team_Action.php"); $action = new Rate_Team_Action(); break; case "SAVE_TEAM": require_once(PATH::ACTION . "Save_Team_Action.php"); $action = new Save_Team_Action(); break; default: require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page(404, "Action not found"); require_once($page->get_view()); http_response_code(404); return; } $action->execute(); /*if ($action->get_code() >= 400 && $action->get_code() < 500){ require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page($action->get_code(), $action->get_message()); require_once($page->get_view()); http_response_code($action->get_code()); }*/ if (strlen($action->get_output()) > 0){ echo($action->get_output()); } http_response_code($action->get_code()); return $action->get_code(); break; case "PLAYER": // A player page if ($this->context->get_player() == null || $this->context->get_player()->get_id() == null){ require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page(404, "Player not found"); require_once($page->get_view()); http_response_code(404); return; } if ( $this->context->get_player()->is_public() == false && ( $this->context->get_user() == null || $this->context->get_player()->get_user() != $this->context->get_user()->get_id() ) ){ require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page(401, "Player profile set to private"); require_once($page->get_view()); http_response_code(401); } $subcommand = ""; if (sizeof($this->params) > 2){ $subcommand = strtoupper($this->params[2]); } $page = null; switch ($subcommand){ case "": // Player profile require_once(PATH::PAGE . "Player_Page.php"); $page = new Player_Page($this->context->get_player()->get_id()); break; case "RUNES": require_once(PATH::PAGE . "Runes_Page.php"); $page = new Runes_Page(); break; case "ENCHANTMENTS": require_once(PATH::PAGE . "Enchantments_Page.php"); $page = new Enchantments_Page(); break; case "ARTIFACTS": require_once(PATH::PAGE . "Artifacts_Page.php"); $page = new Artifacts_Page(); break; case "GUILD": require_once(PATH::PAGE . "Guild_Page.php"); $page = new Guild_Page(); break; case "RUNS": require_once(PATH::PAGE . "Runs_Page.php"); $page = new Runs_Page(); break; /*case "STATS": require_once(PATH::PAGE . "Stats_Page.php"); $page = new Stats_Page(); break;*/ case "TEAMS": require_once(PATH::PAGE . "Teams_Page.php"); $page = new Teams_Page(); break; case "UNITS": if (sizeof($this->params) > 3){ require_once(PATH::PAGE . "Unit_Page.php"); $page = new Unit_Page($this->params[3]); } else{ require_once(PATH::PAGE . "Units_Page.php"); $page = new Units_Page(); } break; case "REPORT": if (sizeof($this->params) > 3){ if (strtoupper($this->params[3]) == "SKILLUP"){ require_once(PATH::PAGE . "Report_Skillup_Page.php"); $page = new Report_Skillup_Page(); } elseif (strtoupper($this->params[3]) == "RUNE_ENCHANTMENT"){ require_once(PATH::PAGE . "Report_Rune_Enchantment_Page.php"); $page = new Report_Rune_Enchantment_Page(); } } else{ require_once(PATH::PAGE . "Report_Page.php"); $page = new Report_Page(); } break; } break; case "MONSTERS": if (sizeof($this->params) > 1){ require_once(PATH::PAGE . "Monster_Page.php"); $page = new Monster_Page($this->params[1]); } else{ require_once(PATH::PAGE . "Monsters_Page.php"); $page = new Monsters_Page(); } break; case "LOGIN": // The login page require_once(PATH::PAGE . "Login_Page.php"); $page = new Login_Page(); break; case "REGISTER": // Registration page require_once(PATH::PAGE . "Register_Page.php"); $page = new Register_Page(); break; case "SEARCH": // Search results if (sizeof($this->params) > 1){ $arg = $this->params[1]; } else{ $arg = ""; } require_once(PATH::PAGE . "Search_Page.php"); $page = new Search_Page($arg); break; /*case "DUNGEONS": // Dungeon static pages require_once(PATH::PAGE . "Login_Page.php"); $page = new Dungeons_Page(); break;*/ case "BUILDINGS": // Building static pages if (sizeof($this->params) > 1){ require_once(PATH::PAGE . "Building_Page.php"); $page = new Building_Page($this->params[1]); } else{ require_once(PATH::PAGE . "Buildings_Page.php"); $page = new Buildings_Page(); } break; break; /*case "FUSION": // Fusion static pages require_once(PATH::PAGE . "Fusion_Page.php"); $page = new Fusion_Page(); break;*/ case "HELP": // Help static page require_once(PATH::PAGE . "Help_Page.php"); $page = new Help_Page(); break; } // If page is not set, it's a not found. if ($page == null){ require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page(404, "Page not found"); } // Once the page is loaded, chack if it can be seen // TODO verify if (! $page->is_public() && $this->context->get_user() == null){ require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page(401, "Authentication required"); http_response_code(401); } if ($page->get_code() >= 400 && $page->get_code() < 600){ require_once(PATH::PAGE . "Error_Page.php"); $page = new Error_Page($page->get_code(), $page->get_message()); http_response_code($page->get_code()); } // Load the view, or set an error code. require_once($page->get_view()); http_response_code($page->get_code()); } }