diff --git a/remote_judger/src/providers/uoj.ts b/remote_judger/src/providers/uoj.ts index ee54086..a8ffda7 100644 --- a/remote_judger/src/providers/uoj.ts +++ b/remote_judger/src/providers/uoj.ts @@ -100,45 +100,60 @@ export default class UOJProvider implements IBasicProvider { } static constructFromAccountData(data) { - throw new Error('Method not implemented.'); + return new this({ + type: 'uoj', + cookie: Object.entries(data).map(([key, value]) => `${key}=${value}`), + }); } cookie: string[] = []; csrf: string; get(url: string) { - logger.debug('get', url); + logger.debug('get', url, this.cookie); + if (!url.includes('//')) url = `${this.account.endpoint || 'https://uoj.ac'}${url}`; + const req = superagent .get(url) .set('Cookie', this.cookie) .set('User-Agent', USER_AGENT); + if (this.account.proxy) return req.proxy(this.account.proxy); + return req; } post(url: string) { logger.debug('post', url, this.cookie); + if (!url.includes('//')) url = `${this.account.endpoint || 'https://uoj.ac'}${url}`; + const req = superagent .post(url) .set('Cookie', this.cookie) .set('User-Agent', USER_AGENT) .type('form'); + if (this.account.proxy) return req.proxy(this.account.proxy); + return req; } async getCsrfToken(url: string) { const { text: html, header } = await this.get(url); + if (header['set-cookie']) { this.cookie = header['set-cookie']; } + let value = /_token *: *"(.+?)"/g.exec(html); if (value) return value[1]; + value = /_token" value="(.+?)"/g.exec(html); + return value?.[1]; } @@ -150,7 +165,11 @@ export default class UOJProvider implements IBasicProvider { async ensureLogin() { if (await this.loggedIn) return true; + + if (!this.account.handle) return false; + logger.info('retry login'); + const _token = await this.getCsrfToken('/login'); const { header, text } = await this.post('/login').send({ _token, @@ -159,11 +178,14 @@ export default class UOJProvider implements IBasicProvider { // NOTE: you should pass a pre-hashed key! password: this.account.password, }); + if (header['set-cookie'] && this.cookie.length === 1) { header['set-cookie'].push(...this.cookie); this.cookie = header['set-cookie']; } + if (text === 'ok') return true; + return text; } @@ -175,6 +197,16 @@ export default class UOJProvider implements IBasicProvider { next, end ) { + if (!(await this.ensureLogin())) { + await end({ + error: true, + status: 'Judgment Failed', + message: 'Login failed', + }); + + return null; + } + const programType = LANGS_MAP[lang] || LANGS_MAP['C++']; const comment = programType.comment; diff --git a/web/app/controllers/subdomain/api/remote_judge/custom_account_validator.php b/web/app/controllers/subdomain/api/remote_judge/custom_account_validator.php index 7e1b8d7..71cc969 100644 --- a/web/app/controllers/subdomain/api/remote_judge/custom_account_validator.php +++ b/web/app/controllers/subdomain/api/remote_judge/custom_account_validator.php @@ -97,6 +97,29 @@ if ($type == 'luogu') { return true; } + return false; + }, 3); +} else if ($type == 'uoj') { + $curl->setFollowLocation(); + $curl->setCookie('UOJSESSID', UOJRequest::post('UOJSESSID', 'is_string', '')); + + retry_loop(function () use (&$curl, &$res) { + $curl->get(UOJRemoteProblem::$providers['uoj']['url'] . '/login'); + + if ($curl->error) { + return false; + } + + if (str_starts_with($curl->responseHeaders['Content-Type'], 'text/html')) { + if (str_contains($curl->response, '登录')) { + return false; + } + + $res = true; + + return true; + } + return false; }, 3); } else { diff --git a/web/app/models/UOJRemoteProblem.php b/web/app/models/UOJRemoteProblem.php index e1e6d88..48a551d 100644 --- a/web/app/models/UOJRemoteProblem.php +++ b/web/app/models/UOJRemoteProblem.php @@ -35,7 +35,7 @@ class UOJRemoteProblem { '未找到该页面', ], 'languages' => ['C', 'C++03', 'C++11', 'C++', 'C++17', 'C++20', 'Python3', 'Python2.7', 'Java8', 'Java11', 'Java17', 'Pascal'], - 'submit_type' => ['bot'], + 'submit_type' => ['bot', 'my'], ], 'loj' => [ 'name' => 'LibreOJ', diff --git a/web/js/uoj.js b/web/js/uoj.js index b8f5299..3c4420e 100644 --- a/web/js/uoj.js +++ b/web/js/uoj.js @@ -1271,6 +1271,53 @@ $.fn.remote_submit_type_group = function(oj, pid, url, submit_type) { .append($('<div class="col-sm-4" />').append(input_loj_token)) .append($('<div class="col-sm-6" />').append($('<div class="form-text mt-0" />').append('请前往 <a href="https://loj.ac" target="_blank">LibreOJ</a> 登录账号,然后输入在控制台中运行 <code>console.log(JSON.parse(localStorage.appState).token)</code> 的输出结果。'))) ); + } else if (oj == 'uoj') { + var uoj_account_data = {"UOJSESSID": ""}; + var input_uoj_uojsessid = $('<input class="form-control font-monospace" type="text" name="uoj_uojsessid" id="input-uoj_uojsessid" />'); + + if ('localStorage' in window) { + try { + var uoj_account_data_str = localStorage.getItem('uoj_remote_judge_uoj_account_data'); + if (uoj_account_data_str) { + uoj_account_data = JSON.parse(uoj_account_data_str); + } + } catch (e) {} + + var save_uoj_account_data = function() { + localStorage.setItem('uoj_remote_judge_uoj_account_data', JSON.stringify(uoj_account_data)); + } + } + + input_uoj_uojsessid.change(function() { + uoj_account_data.UOJSESSID = $(this).val(); + input_my_account_data.val(JSON.stringify(uoj_account_data)); + save_uoj_account_data(); + my_account_validation_status.html('<span class="text-secondary">待验证</span>'); + }); + + my_account_validation_btn.click(function() { + validate_my_account({ + type: 'uoj', + UOJSESSID: input_uoj_uojsessid.val(), + }); + }); + + input_my_account_data.val(JSON.stringify(uoj_account_data)); + input_uoj_uojsessid.val(uoj_account_data.UOJSESSID); + + if (uoj_account_data.UOJSESSID) { + validate_my_account({ + type: 'uoj', + UOJSESSID: uoj_account_data.UOJSESSID, + }); + } + + div_submit_type_my.append( + $('<div class="row mt-3 align-items-center" />') + .append($('<div class="col-sm-2" />').append('<label for="input-uoj_uojsessid" class="col-form-label">UOJSESSID</label>')) + .append($('<div class="col-sm-4" />').append(input_uoj_uojsessid)) + .append($('<div class="col-sm-6" />').append($('<div class="form-text mt-0" />').append('请填入 Cookie 中的 <code>UOJSESSID</code>。'))) + ).append(input_my_account_data); } $(this).append(