Controller.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. <?php
  2. /**
  3. * Controller file.
  4. *
  5. * Provides a class to handle all posible request.
  6. *
  7. * @author Iñigo Valentin <i@inigovalentin.com>
  8. * @license https://www.gnu.org/licenses/gpl-3.0.en.html GNU General Public License V3
  9. * @package SWDB
  10. */
  11. require_once(__DIR__ . "/config.php");
  12. require_once(PATH::HELPER . "DB_Helper.php");
  13. require_once(PATH::HELPER . "TEXT_Helper.php");
  14. require_once(PATH::HELPER . "NET_Helper.php");
  15. require_once(PATH::HELPER . "HTML_Helper.php");
  16. require_once(PATH::HELPER . "APPLICATION_Helper.php");
  17. require_once(PATH::ENTITY . "User.php");
  18. require_once(PATH::ENTITY . "Player.php");
  19. require_once(__DIR__ . "/Constant.php");
  20. require_once(__DIR__ . "/Context.php");
  21. /**
  22. * Application controller.
  23. *
  24. * Handles every request, creating the required models and selecting the
  25. * view.
  26. *
  27. * @category Controller
  28. */
  29. class Controller extends Context{
  30. /**
  31. * @var string[] GET parameters received by the controller.
  32. */
  33. private $params = [];
  34. /**
  35. * @var Context Publicly available context.
  36. */
  37. private $context;
  38. private $command = "";
  39. private $status = 201;
  40. private $message = "OK";
  41. /**
  42. * Retrieves the application context.
  43. *
  44. * @return Context The context.
  45. */
  46. public function get_context(){
  47. return $this->context;
  48. }
  49. /**
  50. * Controller constructor.
  51. *
  52. * Initializes a database connection and sets up the public context.
  53. */
  54. public function __construct(){
  55. $this->context = new Context();
  56. session_start();
  57. $this->db = DB::start();
  58. $this->context->set_db($this->db);
  59. }
  60. private function execute_action($action){
  61. require_once(PATH::ACTION . $action . ".php");
  62. action();
  63. return;
  64. }
  65. /**
  66. * Handles the request parameters.
  67. *
  68. * Receives the request params and gets itself ready. Authenticates the user (if needed)
  69. * and validates the URL.
  70. *
  71. * @param string[] $params GET parameters of the request.
  72. */
  73. public function prepare($params){
  74. // Parse parameters.
  75. foreach($params as $p){
  76. if (strlen($p) > 0){
  77. array_push($this->params, $p);
  78. }
  79. }
  80. // Chack if a user is authenticated.
  81. if (array_key_exists("user_token", $_COOKIE) && array_key_exists("user_id", $_COOKIE)){
  82. // Authenticated user, check token.
  83. $statement = $this->db->prepare("
  84. SELECT count(token) AS c
  85. FROM token
  86. WHERE
  87. user = :user AND
  88. token = :token AND
  89. strftime('%s', datetime(expiry, 'unixepoch', 'localtime')) > strftime('%s','now')
  90. ");
  91. $statement->bindValue(":user", $_COOKIE["user_id"], SQLITE3_INTEGER);
  92. $statement->bindValue(":token", hash('sha256', $_COOKIE["user_token"]), SQLITE3_TEXT);
  93. $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
  94. if ($r["c"] == 0){
  95. // Token is invalid, expired, clear cookies, session, and logout.
  96. execute_action("logout");
  97. return;
  98. }
  99. else{
  100. // Token is valid, renew session, and assign user.
  101. $_SESSION["user_id"] = $_COOKIE["user_id"];
  102. $_SESSION["session"] = true;
  103. $this->context->set_user(new User($_COOKIE["user_id"]));
  104. }
  105. }
  106. else{
  107. // Unauthenticated user. Continue
  108. }
  109. $auth_required = false;
  110. $player_id = null;
  111. // Obtain the command and process it
  112. if (sizeof($this->params) > 0){
  113. $this->command = strtoupper($this->params[0]);
  114. }
  115. switch ($this->command){
  116. case "": // Main page
  117. break;
  118. case "API": // An API call
  119. return; // End preparation here, the API controller will deal with everything.
  120. break;
  121. case "ACTION": // Execute an action
  122. //$auth_required = true;
  123. break;
  124. case "PLAYER": // A player page
  125. if (sizeof($this->params) > 1){
  126. $player_id = $this->params[1];
  127. }
  128. else{
  129. $this->status = 400;
  130. $this->message = "Bad request";
  131. return;
  132. }
  133. break;
  134. case "LOGIN": // The login page
  135. break;
  136. case "REGISTER": // Registration page
  137. break;
  138. case "SEARCH": // Search results
  139. break;
  140. case "DUNGEON": // Dungeon static pages
  141. break;
  142. case "BUILDING": // Building static pages
  143. break;
  144. case "FUSION": // Fusion static pages
  145. break;
  146. case "HELP": // Help static page
  147. break;
  148. case "ERROR": // Error page
  149. break;
  150. default: // Unknown page
  151. }
  152. if ($player_id != null){
  153. $this->context->set_player(new Player($player_id));
  154. if ($this->context->get_player()->get_id() == null){
  155. $this->status = 404;
  156. $this->message = "Requested player doesn't exist";
  157. return;
  158. }
  159. }
  160. if ($auth_required && $this->context->get_user() == null){
  161. $this->status = 401;
  162. $this->message = "Authentication required to perform this action";
  163. return;
  164. }
  165. // Get game mode
  166. if ($this->context->get_player() == null){
  167. $this->context->set_game_mode(GAME_MODE_ID::NORMAL);
  168. }
  169. else{
  170. $statement = $this->db->prepare("SELECT mode FROM player WHERE id = :id;");
  171. $statement->bindValue(":id", $this->context->get_player()->get_id(), SQLITE3_INTEGER);
  172. $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
  173. if ($r && $r["mode"] == GAME_MODE_ID::RTA){
  174. $this->context->set_game_mode(GAME_MODE_ID::RTA);
  175. }
  176. else{
  177. $this->context->set_game_mode(GAME_MODE_ID::NORMAL);
  178. }
  179. }
  180. }
  181. /**
  182. * Executes the required action.
  183. *
  184. * Must be called after {@see Controller::prepare()}.
  185. * @return void|number
  186. */
  187. public function action(){
  188. // If there was an error in preparation, it's time to throw it.
  189. if ($this->status >= 400 && $this->status < 500){
  190. require_once(PATH::PAGE . "Error_Page.php");
  191. $page = new Error_Page($this->status, $this->message);
  192. require_once($page->get_view());
  193. http_response_code(404);
  194. return 404;
  195. }
  196. // Process the command
  197. $page = null;
  198. switch ($this->command){
  199. case "": // Main page
  200. // Two options:
  201. // If user authenticated, retrieve last used player acocunt and load main page
  202. // If not, login page.
  203. if ($this->context->get_user() == null){
  204. require_once(PATH::PAGE . "Login_Page.php");
  205. $page = new Login_Page();
  206. }
  207. else{
  208. $statement = $this->context->get_db()->prepare("
  209. SELECT id
  210. FROM player
  211. WHERE user = :user
  212. ORDER BY last_access DESC
  213. LIMIT 1;
  214. ");
  215. $statement->bindValue(":user", $this->context->get_user()->get_id(), SQLITE3_INTEGER);
  216. $r = $statement->execute()->fetchArray(SQLITE3_ASSOC);
  217. if ($r){
  218. header("Location: " . URL::BASE . "player/" . $r["id"]);
  219. return;
  220. //$this->context->set_player(new Player($r["id"]));
  221. //require_once(PATH::PAGE . "Player_Page.php");
  222. //$page = new Player_Page();
  223. }
  224. else{
  225. // TODO: weird case: user with no players
  226. }
  227. }
  228. break;
  229. case "API": // An API call
  230. if (count($this->params) == 1){
  231. // TODO: Redirect to current version
  232. }
  233. else{
  234. $version = strtolower($this->params[1]);
  235. $api_params = $this->params;
  236. array_shift($api_params);
  237. array_shift($api_params);
  238. switch ($version){
  239. case "v1":
  240. require_once(__DIR__ . "/API/v1/API_Controller.php");
  241. new API_Controller($this->params);
  242. return;
  243. break;
  244. default:
  245. require_once(PATH::PAGE . "Error_Page.php");
  246. $page = new Error_Page(404, "API version doesn't exist.");
  247. require_once($page->get_view());
  248. http_response_code(404);
  249. return 404;
  250. }
  251. }
  252. break;
  253. case "ACTION": // Execute an action
  254. // If no action specified
  255. if (sizeof($this->params) < 2){
  256. require_once(PATH::PAGE . "Error_Page.php");
  257. $page = new Error_Page(400, "No action specified.");
  258. require_once($page->get_view());
  259. http_response_code(400);
  260. return 400;
  261. }
  262. // Process action
  263. $action = strtoupper($this->params[1]);
  264. switch ($action){
  265. // Special cases where the output must be printed:
  266. case "OPTIMIZE_LIST":
  267. require_once(PATH::ACTION . "Optimize_List_Action.php");
  268. $action = new Optimize_List_Action();
  269. break;
  270. case "OPTIMIZE_OPTIONS":
  271. require_once(PATH::ACTION . "Optimize_Options_Action.php");
  272. $action = new Optimize_Options_Action();
  273. break;
  274. case "CHANGE_GAME_MODE":
  275. require_once(PATH::ACTION . "Change_Game_Mode_Action.php");
  276. $action = new Change_Game_Mode_Action();
  277. break;
  278. case "LOGIN":
  279. require_once(PATH::ACTION . "Login_Action.php");
  280. $action = new Login_Action();
  281. break;
  282. case "LOGOUT":
  283. require_once(PATH::ACTION . "Logout_Action.php");
  284. $action = new Logout_Action();
  285. break;
  286. case "REGISTER":
  287. require_once(PATH::ACTION . "Register_Action.php");
  288. $action = new Register_Action();
  289. break;
  290. case "ADD_TEAM":
  291. require_once(PATH::ACTION . "Add_Team_Action.php");
  292. $action = new Add_Team_Action();
  293. break;
  294. case "EDIT_PROFILE":
  295. require_once(PATH::ACTION . "Edit_Profile_Action.php");
  296. $action = new Edit_Profile_Action();
  297. break;
  298. case "DELETE_TEAM":
  299. require_once(PATH::ACTION . "Delete_Team_Action.php");
  300. $action = new Delete_Team_Action();
  301. break;
  302. case "RATE_TEAM":
  303. require_once(PATH::ACTION . "rate_team.php");
  304. $action = new Rate_Team_Action();
  305. break;
  306. case "SAVE_TEAM":
  307. require_once(PATH::ACTION . "Save_Team_Action.php");
  308. $action = new Save_Team_Action();
  309. break;
  310. default:
  311. require_once(PATH::PAGE . "Error_Page.php");
  312. $page = new Error_Page(404, "Action not found");
  313. require_once($page->get_view());
  314. http_response_code(404);
  315. return;
  316. }
  317. $action->execute();
  318. /*if ($action->get_code() >= 400 && $action->get_code() < 500){
  319. require_once(PATH::PAGE . "Error_Page.php");
  320. $page = new Error_Page($action->get_code(), $action->get_message());
  321. require_once($page->get_view());
  322. http_response_code($action->get_code());
  323. }*/
  324. if (strlen($action->get_output()) > 0){
  325. echo($action->get_output());
  326. }
  327. http_response_code($action->get_code());
  328. return $action->get_code();
  329. break;
  330. case "PLAYER": // A player page
  331. if ($this->context->get_player() == null || $this->context->get_player()->get_id() == null){
  332. require_once(PATH::PAGE . "Error_Page.php");
  333. $page = new Error_Page(404, "Player not found");
  334. require_once($page->get_view());
  335. http_response_code(404);
  336. return;
  337. }
  338. if (
  339. $this->context->get_player()->is_public() == false &&
  340. (
  341. $this->context->get_user() == null ||
  342. $this->context->get_player()->get_user() != $this->context->get_user()->get_id()
  343. )
  344. ){
  345. require_once(PATH::PAGE . "Error_Page.php");
  346. $page = new Error_Page(401, "Player profile set to private");
  347. require_once($page->get_view());
  348. http_response_code(401);
  349. }
  350. $subcommand = "";
  351. if (sizeof($this->params) > 2){
  352. $subcommand = strtoupper($this->params[2]);
  353. }
  354. $page = null;
  355. switch ($subcommand){
  356. case "": // Player profile
  357. require_once(PATH::PAGE . "Player_Page.php");
  358. $page = new Player_Page($this->context->get_player()->get_id());
  359. break;
  360. case "RUNES":
  361. require_once(PATH::PAGE . "Runes_Page.php");
  362. $page = new Runes_Page();
  363. break;
  364. case "ENCHANTMENTS":
  365. require_once(PATH::PAGE . "Enchantments_Page.php");
  366. $page = new Enchantments_Page();
  367. break;
  368. case "ARTIFACTS":
  369. require_once(PATH::PAGE . "Artifacts_Page.php");
  370. $page = new Artifacts_Page();
  371. break;
  372. case "GUILD":
  373. require_once(PATH::PAGE . "Guild_Page.php");
  374. $page = new Guild_Page();
  375. break;
  376. case "RUNS":
  377. require_once(PATH::PAGE . "Runs_Page.php");
  378. $page = new Runs_Page();
  379. break;
  380. /*case "STATS":
  381. require_once(PATH::PAGE . "Stats_Page.php");
  382. $page = new Stats_Page();
  383. break;*/
  384. case "TEAMS":
  385. require_once(PATH::PAGE . "Teams_Page.php");
  386. $page = new Teams_Page();
  387. break;
  388. case "UNITS":
  389. if (sizeof($this->params) > 3){
  390. require_once(PATH::PAGE . "Unit_Page.php");
  391. $page = new Unit_Page($this->params[3]);
  392. }
  393. else{
  394. require_once(PATH::PAGE . "Units_Page.php");
  395. $page = new Units_Page();
  396. }
  397. break;
  398. case "REPORT":
  399. if (sizeof($this->params) > 3){
  400. if (strtoupper($this->params[3]) == "SKILLUP"){
  401. require_once(PATH::PAGE . "Report_Skillup_Page.php");
  402. $page = new Report_Skillup_Page();
  403. }
  404. elseif (strtoupper($this->params[3]) == "RUNE_ENCHANTMENT"){
  405. require_once(PATH::PAGE . "Report_Rune_Enchantment_Page.php");
  406. $page = new Report_Rune_Enchantment_Page();
  407. }
  408. }
  409. else{
  410. require_once(PATH::PAGE . "Report_Page.php");
  411. $page = new Report_Page();
  412. }
  413. break;
  414. case "OPTIMIZER":
  415. if (sizeof($this->params) > 3){
  416. require_once(PATH::PAGE . "Optimizer_Unit_Page.php");
  417. $page = new Optimizer_Unit_Page($this->params[3]);
  418. }
  419. else{
  420. require_once(PATH::PAGE . "Optimizer_Page.php");
  421. $page = new Optimizer_Page();
  422. }
  423. break;
  424. }
  425. break;
  426. case "MONSTERS":
  427. if (sizeof($this->params) > 1){
  428. require_once(PATH::PAGE . "Monster_Page.php");
  429. $page = new Monster_Page($this->params[1]);
  430. }
  431. else{
  432. require_once(PATH::PAGE . "Monsters_Page.php");
  433. $page = new Monsters_Page();
  434. }
  435. break;
  436. case "LOGIN": // The login page
  437. require_once(PATH::PAGE . "Login_Page.php");
  438. $page = new Login_Page();
  439. break;
  440. case "REGISTER": // Registration page
  441. require_once(PATH::PAGE . "Register_Page.php");
  442. $page = new Register_Page();
  443. break;
  444. case "SEARCH": // Search results
  445. if (sizeof($this->params) > 1){
  446. $arg = $this->params[1];
  447. }
  448. else{
  449. $arg = "";
  450. }
  451. require_once(PATH::PAGE . "Search_Page.php");
  452. $page = new Search_Page($arg);
  453. break;
  454. /*case "DUNGEONS": // Dungeon static pages
  455. require_once(PATH::PAGE . "Login_Page.php");
  456. $page = new Dungeons_Page();
  457. break;*/
  458. case "BUILDINGS": // Building static pages
  459. if (sizeof($this->params) > 1){
  460. require_once(PATH::PAGE . "Building_Page.php");
  461. $page = new Building_Page($this->params[1]);
  462. }
  463. else{
  464. require_once(PATH::PAGE . "Buildings_Page.php");
  465. $page = new Buildings_Page();
  466. }
  467. break;
  468. break;
  469. /*case "FUSION": // Fusion static pages
  470. require_once(PATH::PAGE . "Fusion_Page.php");
  471. $page = new Fusion_Page();
  472. break;*/
  473. case "HELP": // Help static page
  474. require_once(PATH::PAGE . "Help_Page.php");
  475. $page = new Help_Page();
  476. break;
  477. }
  478. // If page is not set, it's a not found.
  479. if ($page == null){
  480. require_once(PATH::PAGE . "Error_Page.php");
  481. $page = new Error_Page(404, "Page not found");
  482. }
  483. // Once the page is loaded, chack if it can be seen
  484. // TODO verify
  485. if (! $page->is_public() && $this->context->get_user() == null){
  486. require_once(PATH::PAGE . "Error_Page.php");
  487. $page = new Error_Page(401, "Authentication required");
  488. http_response_code(401);
  489. }
  490. if ($page->get_code() >= 400 && $page->get_code() < 600){
  491. require_once(PATH::PAGE . "Error_Page.php");
  492. $page = new Error_Page($page->get_code(), $page->get_message());
  493. http_response_code($page->get_code());
  494. }
  495. // Load the view, or set an error code.
  496. require_once($page->get_view());
  497. http_response_code($page->get_code());
  498. }
  499. }