submit.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <?php
  2. requirePHPLib('judger');
  3. requirePHPLib('data');
  4. if (!authenticateJudger()) {
  5. become404Page();
  6. }
  7. // $_POST['result'] is attacker-controllable whenever the judger password is
  8. // weak or leaked (the repo used to ship a placeholder), so decode it once
  9. // and refuse anything that is not an object.
  10. function judgeResultFromPost() {
  11. $result = json_decode($_POST['result'] ?? '', true);
  12. if (!is_array($result)) {
  13. die("Wow! hacker! T_T....");
  14. }
  15. return $result;
  16. }
  17. function submissionJudged() {
  18. $submission = DB::selectFirst("select submitter, status, content, result, problem_id from submissions where id = {$_POST['id']}");
  19. if ($submission == null) {
  20. return;
  21. }
  22. if ($submission['status'] != 'Judging' && $submission['status'] != 'Judged, Judging') {
  23. return;
  24. }
  25. $content = json_decode($submission['content'], true);
  26. if (isset($content['first_test_config'])) {
  27. $result = json_decode($submission['result'], true);
  28. $result['final_result'] = judgeResultFromPost();
  29. $result['final_result']['details'] = uojTextEncode($result['final_result']['details']);
  30. $esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
  31. $content['final_test_config'] = $content['config'];
  32. $content['config'] = $content['first_test_config'];
  33. unset($content['first_test_config']);
  34. $esc_content = DB::escape(json_encode($content));
  35. DB::update("update submissions set status = 'Judged', result = '$esc_result', content = '$esc_content' where id = {$_POST['id']}");
  36. } else {
  37. $result = judgeResultFromPost();
  38. $result['details'] = uojTextEncode($result['details']);
  39. $esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
  40. // status/result_error are varchar and score/used_* are int, so
  41. // escape the former and cast the latter.
  42. $status = DB::escape($result['status']);
  43. if (isset($result["error"])) {
  44. $error = DB::escape($result['error']);
  45. DB::update("update submissions set status = '$status', result_error = '$error', result = '$esc_result', score = null, used_time = null, used_memory = null where id = {$_POST['id']}");
  46. } else {
  47. $score = (int)$result['score'];
  48. $time = (int)$result['time'];
  49. $memory = (int)$result['memory'];
  50. DB::update("update submissions set status = '$status', result_error = null, result = '$esc_result', score = $score, used_time = $time, used_memory = $memory where id = {$_POST['id']}");
  51. }
  52. if (isset($content['final_test_config'])) {
  53. $content['first_test_config'] = $content['config'];
  54. $content['config'] = $content['final_test_config'];
  55. unset($content['final_test_config']);
  56. $esc_content = DB::escape(json_encode($content));
  57. DB::update("update submissions set status = 'Judged, Waiting', content = '$esc_content' where id = ${_POST['id']}");
  58. }
  59. }
  60. DB::update("update submissions set status_details = '' where id = {$_POST['id']}");
  61. updateBestACSubmissions($submission['submitter'], $submission['problem_id']);
  62. }
  63. function customTestSubmissionJudged() {
  64. $submission = DB::selectFirst("select submitter, status, content, result, problem_id from custom_test_submissions where id = {$_POST['id']}");
  65. if ($submission == null) {
  66. return;
  67. }
  68. if ($submission['status'] != 'Judging') {
  69. return;
  70. }
  71. $content = json_decode($submission['content'], true);
  72. $result = judgeResultFromPost();
  73. $result['details'] = uojTextEncode($result['details']);
  74. $esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
  75. $status = DB::escape($result['status']);
  76. if (isset($result["error"])) {
  77. DB::update("update custom_test_submissions set status = '$status', result = '$esc_result' where id = {$_POST['id']}");
  78. } else {
  79. DB::update("update custom_test_submissions set status = '$status', result = '$esc_result' where id = {$_POST['id']}");
  80. }
  81. DB::update("update custom_test_submissions set status_details = '' where id = {$_POST['id']}");
  82. }
  83. function hackJudged() {
  84. $result = judgeResultFromPost();
  85. $esc_details = DB::escape(uojTextEncode($result['details']));
  86. $success = (int)$result['score'];
  87. $ok = DB::update("update hacks set success = $success, details = '$esc_details' where id = {$_POST['id']}");
  88. if ($ok) {
  89. list($hack_input) = DB::fetch(DB::query("select input from hacks where id = {$_POST['id']}"), MYSQLI_NUM);
  90. unlink(UOJContext::storagePath().$hack_input);
  91. if ($result['score']) {
  92. list($problem_id) = DB::selectFirst("select problem_id from hacks where id = ${_POST['id']}", MYSQLI_NUM);
  93. if (validateUploadedFile('hack_input') && validateUploadedFile('std_output')) {
  94. dataAddExtraTest(queryProblemBrief($problem_id), $_FILES["hack_input"]["tmp_name"], $_FILES["std_output"]["tmp_name"]);
  95. } else {
  96. error_log("hack successfully but received no data. id: ${_POST['id']}");
  97. }
  98. }
  99. }
  100. }
  101. if (isset($_POST['submit'])) {
  102. if (!validateUInt($_POST['id'])) {
  103. die("Wow! hacker! T_T....");
  104. }
  105. if (isset($_POST['is_hack'])) {
  106. hackJudged();
  107. } elseif (isset($_POST['is_custom_test'])) {
  108. customTestSubmissionJudged();
  109. } else {
  110. submissionJudged();
  111. }
  112. }
  113. if (isset($_POST['update-status'])) {
  114. if (!validateUInt($_POST['id'])) {
  115. die("Wow! hacker! T_T....");
  116. }
  117. $esc_status_details = DB::escape($_POST['status']);
  118. if (isset($_POST['is_custom_test'])) {
  119. DB::update("update custom_test_submissions set status_details = '$esc_status_details' where id = {$_POST['id']}");
  120. } else {
  121. DB::update("update submissions set status_details = '$esc_status_details' where id = {$_POST['id']}");
  122. }
  123. die();
  124. }
  125. $submission = null;
  126. $hack = null;
  127. function querySubmissionToJudge($status, $set_q) {
  128. global $submission;
  129. $submission = DB::selectFirst("select id, problem_id, content from submissions where status = '$status' order by id limit 1");
  130. if ($submission) {
  131. DB::update("update submissions set $set_q where id = {$submission['id']} and status = '$status'");
  132. if (DB::affected_rows() != 1) {
  133. $submission = null;
  134. }
  135. }
  136. }
  137. function queryCustomTestSubmissionToJudge() {
  138. global $submission;
  139. $submission = DB::selectFirst("select id, problem_id, content from custom_test_submissions where judge_time is null order by id limit 1");
  140. if ($submission) {
  141. DB::update("update custom_test_submissions set judge_time = now(), status = 'Judging' where id = {$submission['id']} and judge_time is null");
  142. if (DB::affected_rows() != 1) {
  143. $submission = null;
  144. }
  145. }
  146. if ($submission) {
  147. $submission['is_custom_test'] = '';
  148. }
  149. }
  150. function queryHackToJudge() {
  151. global $hack;
  152. $hack = DB::selectFirst("select id, submission_id, input, input_type from hacks where judge_time is null order by id limit 1");
  153. if ($hack) {
  154. DB::update("update hacks set judge_time = now() where id = {$hack['id']} and judge_time is null");
  155. if (DB::affected_rows() != 1) {
  156. $hack = null;
  157. }
  158. }
  159. }
  160. function findSubmissionToJudge() {
  161. global $submission, $hack;
  162. querySubmissionToJudge('Waiting', "judge_time = now(), status = 'Judging'");
  163. if ($submission) {
  164. return true;
  165. }
  166. queryCustomTestSubmissionToJudge();
  167. if ($submission) {
  168. return true;
  169. }
  170. querySubmissionToJudge('Waiting Rejudge', "judge_time = now(), status = 'Judging'");
  171. if ($submission) {
  172. return true;
  173. }
  174. querySubmissionToJudge('Judged, Waiting', "status = 'Judged, Judging'");
  175. if ($submission) {
  176. return true;
  177. }
  178. queryHackToJudge();
  179. if ($hack) {
  180. $submission = DB::selectFirst("select id, problem_id, content from submissions where id = {$hack['submission_id']} and score = 100");
  181. if (!$submission) {
  182. $details = "<error>the score gained by the hacked submission is not 100.\n</error>";
  183. $esc_details = DB::escape(uojTextEncode($details));
  184. DB::update("update hacks set success = 0, details = '$esc_details' where id = {$hack['id']}");
  185. return false;
  186. }
  187. return true;
  188. }
  189. return false;
  190. }
  191. if (isset($_POST['fetch_new']) && !$_POST['fetch_new']) {
  192. die("Nothing to judge");
  193. }
  194. if (!findSubmissionToJudge()) {
  195. die("Nothing to judge");
  196. }
  197. $submission['id'] = (int)$submission['id'];
  198. $submission['problem_id'] = (int)$submission['problem_id'];
  199. $submission['problem_mtime'] = filemtime("/var/uoj_data/{$submission['problem_id']}");
  200. $submission['content'] = json_decode($submission['content']);
  201. if ($hack) {
  202. $submission['is_hack'] = "";
  203. $submission['hack']['id'] = (int)$hack['id'];
  204. $submission['hack']['input'] = $hack['input'];
  205. $submission['hack']['input_type'] = $hack['input_type'];
  206. }
  207. echo json_encode($submission);
  208. ?>