S2OJ/web/app/controllers/judge/submit.php
Baoshuo 1f11278815
All checks were successful
continuous-integration/drone/push Build is passing
feat(hack): hack success message
2023-02-17 10:57:55 +08:00

432 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
requirePHPLib('judger');
requirePHPLib('data');
if (!authenticateJudger()) {
UOJResponse::page404();
}
function submissionJudged() {
UOJSubmission::onJudged(UOJRequest::post('id'), UOJRequest::post('result'), UOJRequest::post('judge_time'));
}
function customTestSubmissionJudged() {
$submission = DB::selectFirst([
"select submitter, status, content, result, problem_id from custom_test_submissions",
"where", ['id' => $_POST['id']]
]);
if ($submission == null) {
return;
}
if ($submission['status'] != 'Judging') {
return;
}
$result = json_decode($_POST['result'], true);
$result['details'] = uojTextEncode($result['details']);
DB::update([
"update custom_test_submissions",
"set", [
'status' => $result['status'],
'status_details' => '',
'result' => json_encode($result, JSON_UNESCAPED_UNICODE)
], "where", ['id' => $_POST['id']]
]);
}
function hackJudged() {
$result = json_decode($_POST['result'], true);
UOJHack::init($_POST['id']);
UOJHack::cur()->setProblem();
UOJHack::cur()->setSubmission();
if ($result['score']) {
$status = 'Judged, Waiting';
} else {
$status = 'Judged';
}
$ok = DB::update([
"update hacks",
"set", [
'success' => $result['score'],
'status' => $status,
'details' => uojTextEncode($result['details'])
], "where", ['id' => $_POST['id']]
]);
if (!$result['score']) {
return;
} else {
$problem_link = UOJHack::cur()->problem->getLink();
$submission_link = UOJHack::cur()->submission->getLink();
$hacker_link = UOJUser::getLink(UOJHack::info('hacker'), ['color' => false]);
$hack_link = UOJHack::cur()->getLink();
sendSystemMsg(UOJHack::info('owner'), '提交被 Hack 通知', <<<EOD
您对题目 {$problem_link} 的提交 {$submission_link} {$hacker_link} 成功 Hack请查看 Hack {$hack_link} 获取详情。
EOD);
}
if (!$ok) {
return;
}
if (!(validateUploadedFile('hack_input') && validateUploadedFile('std_output'))) {
UOJLog::error("hack successfully but received no data. id: {$_POST['id']}");
return;
}
$input = UOJContext::storagePath() . UOJHack::info('input');
$up_in = $_FILES["hack_input"]['tmp_name'];
$up_out = $_FILES["std_output"]['tmp_name'];
if (!UOJHack::cur()->problem->needToReviewHack()) {
$err = UOJHack::cur()->problem->addHackPoint($up_in, $up_out);
if ($err === '') {
unlink($input);
DB::update([
"update hacks",
"set", [
'status' => 'Judged'
], "where", ['id' => $_POST['id']]
]);
return;
} else {
UOJLog::error("hack successfully but failed to add an extra test: {$err}");
}
}
move_uploaded_file($up_in, "{$input}_in");
move_uploaded_file($up_out, "{$input}_out");
DB::update([
"update hacks",
"set", [
'status' => 'Judged, WaitingM'
], "where", ['id' => $_POST['id']]
]);
}
if (isset($_POST['submit'])) {
if (!validateUInt($_POST['id'])) {
die("Wow! hacker! T_T....");
}
if (isset($_POST['is_hack'])) {
hackJudged();
} elseif (isset($_POST['is_custom_test'])) {
customTestSubmissionJudged();
} else {
submissionJudged();
}
}
if (isset($_POST['update-status'])) {
if (!validateUInt($_POST['id'])) {
die("Wow! hacker! T_T....");
}
$status_details = $_POST['status'];
if (isset($_POST['is_custom_test'])) {
DB::update([
"update custom_test_submissions",
"set", ["status_details" => $status_details],
"where", ["id" => $_POST['id']]
]);
} else {
DB::update([
"update submissions",
"set", ["status_details" => $status_details],
"where", ["id" => $_POST['id']]
]);
}
die();
}
$assignCond = [];
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
"select id from problems",
"where", [
["assigned_to_judger", "!=", "any"],
["assigned_to_judger", "!=", $_POST['judger_name']]
]
]));
if ($problem_ban_list) {
$assignCond[] = ["problem_id", "not in", DB::rawtuple($problem_ban_list)];
}
if ($_POST['judger_name'] == "remote_judger") {
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
"select id from problems",
"where", [
["type", "!=", "remote"],
],
]));
} else {
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
"select id from problems",
"where", [
["type", "!=", "local"],
],
]));
}
if ($problem_ban_list) {
$assignCond[] = ["problem_id", "not in", DB::rawtuple($problem_ban_list)];
}
$submission = null;
$hack = null;
function querySubmissionToJudge($status, $set_q) {
global $assignCond;
for ($times = 0; $times < 10; $times++) {
$submission = DB::selectFirst([
"select id from submissions",
"where", array_merge(["status" => $status], $assignCond),
"order by id limit 1"
]);
if (!$submission) {
return null;
}
$ok = DB::transaction(function () use (&$submission, $set_q, $status) {
DB::update([
"update submissions",
"set", $set_q,
"where", [
"id" => $submission['id'],
"status" => $status
]
]);
if (DB::affected_rows() == 1) {
$submission = DB::selectFirst([
"select id, problem_id, content, status, judge_time from submissions",
"where", ["id" => $submission['id']]
]);
return true;
} else {
return false;
}
});
if ($ok) {
return $submission;
}
}
}
function queryMinorSubmissionToJudge($status, $set_q) {
global $assignCond;
for ($times = 0; $times < 10; $times++) {
$submission = null;
$his = DB::selectFirst([
"select id, submission_id from submissions_history",
"where", ["status" => $status, "major" => 0], // $assignCond is removed!!! fix this bug in the future!
"order by id limit 1"
]);
if (!$his) {
return null;
}
$ok = DB::transaction(function () use (&$submission, &$his, $set_q, $status) {
$submission = DB::selectFirst([
"select id, problem_id, content from submissions",
"where", ["id" => $his['submission_id']], DB::for_share()
]);
if (!$submission) {
return false;
}
DB::update([
"update submissions_history",
"set", $set_q,
"where", [
"id" => $his['id'],
"status" => $status
]
]);
if (DB::affected_rows() == 1) {
$ret = DB::selectFirst([
"select status, judge_time from submissions_history",
"where", ["id" => $his['id']]
]);
if ($ret === false) {
return false;
}
$submission += $ret;
return true;
} else {
return false;
}
});
if ($ok) {
return $submission;
}
}
}
function queryCustomTestSubmissionToJudge() {
global $assignCond;
while (true) {
$submission = DB::selectFirst([
"select id, problem_id, content from custom_test_submissions",
"where", array_merge(["judge_time" => null], $assignCond),
"order by id limit 1"
]);
if (!$submission) {
return null;
}
$submission['is_custom_test'] = '';
DB::update([
"update custom_test_submissions",
"set", [
"judge_time" => DB::now(),
"status" => 'Judging'
], "where", [
"id" => $submission['id'],
"judge_time" => null
]
]);
if (DB::affected_rows() == 1) {
$submission['status'] = 'Judging';
return $submission;
}
}
}
function queryHackToJudge() {
global $assignCond;
while (true) {
if (DB::selectFirst([
"select 1 from hacks",
"where", [
["status", "!=", "Waiting"],
["status", "!=", "Judged"],
], "order by id limit 1"
])) {
return null;
}
$hack = DB::selectFirst([
"select id, submission_id, input, input_type from hacks",
"where", array_merge(["judge_time" => null], $assignCond),
"order by id limit 1"
]);
if (!$hack) {
return null;
}
DB::update([
"update hacks",
"set", [
"judge_time" => DB::now(),
"status" => 'Judging'
],
"where", [
"id" => $hack['id'],
"judge_time" => null
]
]);
if (DB::affected_rows() == 1) {
$hack['status'] = 'Judging';
return $hack;
}
}
}
function findSubmissionToJudge() {
global $submission, $hack;
$submission = querySubmissionToJudge('Waiting', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = queryCustomTestSubmissionToJudge();
if ($submission) {
return true;
}
$submission = querySubmissionToJudge('Waiting Rejudge', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = querySubmissionToJudge('Judged, Waiting', [
"status" => 'Judged, Judging'
]);
if ($submission) {
return true;
}
$submission = queryMinorSubmissionToJudge('Waiting Rejudge', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = queryMinorSubmissionToJudge('Judged, Waiting', [
"status" => 'Judged, Judging'
]);
if ($submission) {
return true;
}
$hack = queryHackToJudge();
if ($hack) {
$submission = DB::selectFirst([
"select id, problem_id, content from submissions",
"where", [
"id" => $hack['submission_id'],
"score" => 100
]
]);
if (!$submission) {
$details = "<error>the score gained by the hacked submission is not 100.</error>";
DB::update([
"update hacks",
"set", [
'success' => 0,
'status' => 'Judged',
'details' => uojTextEncode($details)
], "where", ["id" => $hack['id']]
]);
return false;
}
return true;
}
return false;
}
if (isset($_POST['fetch_new']) && !$_POST['fetch_new']) {
die("Nothing to judge");
}
if (!findSubmissionToJudge()) {
die("Nothing to judge");
}
$submission['id'] = (int)$submission['id'];
$submission['problem_id'] = (int)$submission['problem_id'];
$submission['problem_mtime'] = filemtime("/var/uoj_data/{$submission['problem_id']}");
$submission['content'] = json_decode($submission['content'], true);
if (isset($submission['status']) && $submission['status'] == 'Judged, Judging' && isset($submission['content']['final_test_config'])) {
$submission['content']['config'] = $submission['content']['final_test_config'];
unset($submission['content']['final_test_config']);
}
if ($hack) {
$submission['is_hack'] = "";
$submission['hack']['id'] = (int)$hack['id'];
$submission['hack']['input'] = $hack['input'];
$submission['hack']['input_type'] = $hack['input_type'];
}
echo json_encode($submission);