Controller.php 20 KB

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