parent
53141147fa
commit
70c147463b
@ -0,0 +1,5 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/Uoj2.iml" filepath="$PROJECT_DIR$/.idea/Uoj2.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 vfleaking
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -0,0 +1,14 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a vulnerability, please send an email directly to:
|
||||
|
||||
- Tianyu Chen <billchenchina2001@gmail.com>
|
||||
- ceabrobot <ceba_robot@163.com>
|
||||
- ruanxingzhi <ruanxingzhi@gmail.com>
|
||||
|
||||
If the vulnerability has also affected the
|
||||
[upstream](https://github.com/vfleaking/uoj), please also cc:
|
||||
|
||||
- Kaifeng Lyu <vfleaking@163.com>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,25 @@
|
||||
FROM ubuntu:20.04
|
||||
MAINTAINER MascoSkray <MascoSkray@gmail.com>
|
||||
ARG CLONE_ADDFLAG
|
||||
|
||||
WORKDIR /opt
|
||||
#Update apt and install git
|
||||
RUN apt-get update && apt-get install -y git
|
||||
#Clone the latest UOJ Community verison to local
|
||||
RUN git clone https://github.com/UniversalOJ/UOJ-System.git --depth 1 --single-branch ${CLONE_ADDFLAG} uoj
|
||||
#Install environment and set startup script
|
||||
RUN cd uoj/install/bundle && sh install.sh -p && echo "\
|
||||
#!/bin/sh\n\
|
||||
chown -R mysql:mysql /var/lib/mysql /var/run/mysqld\n\
|
||||
if [ ! -f \"/var/uoj_data/.UOJSetupDone\" ]; then\n\
|
||||
cd /opt/uoj/install/bundle && sh install.sh -i\n\
|
||||
fi\n\
|
||||
service ntp start\n\
|
||||
service mysql start\n\
|
||||
service apache2 start\n\
|
||||
su local_main_judger -c \"/opt/uoj/judger/judge_client start\"\n\
|
||||
exec bash\n" >/opt/up && chmod +x /opt/up
|
||||
|
||||
ENV LANG=C.UTF-8 TZ=Asia/Shanghai
|
||||
EXPOSE 80 3690
|
||||
CMD /opt/up
|
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
CLONE_ADDFLAG="-b $DOCKER_TAG"
|
||||
if [ "$DOCKER_TAG" = "latest" ]; then
|
||||
CLONE_ADDFLAG="-b master"
|
||||
fi
|
||||
docker build -t $IMAGE_NAME --build-arg CLONE_ADDFLAG="$CLONE_ADDFLAG" .
|
@ -0,0 +1,162 @@
|
||||
#!/bin/bash
|
||||
genRandStr(){
|
||||
cat /dev/urandom | tr -dc [:alnum:] | head -c $1
|
||||
}
|
||||
#Set some vars
|
||||
_database_password_=root
|
||||
_judger_socket_port_=2333
|
||||
_judger_socket_password_=$(genRandStr 32)
|
||||
_main_judger_password_=$(genRandStr 32)
|
||||
|
||||
getAptPackage(){
|
||||
printf "\n\n==> Getting environment packages\n"
|
||||
#Set MySQL root password
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
(echo "mysql-server mysql-server/root_password password $_database_password_";echo "mysql-server mysql-server/root_password_again password $_database_password_") | debconf-set-selections
|
||||
#Update apt sources and install
|
||||
dpkg -s gnupg 2>/dev/null || (apt-get update && apt-get install -y gnupg)
|
||||
echo "deb http://ppa.launchpad.net/stesie/libv8/ubuntu bionic main" | tee /etc/apt/sources.list.d/stesie-libv8.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D858A0DF
|
||||
apt-get update && apt-get install -y vim ntp zip unzip curl wget apache2 libapache2-mod-xsendfile libapache2-mod-php php php-dev php-pear php-zip php-mysql php-mbstring mysql-server cmake fp-compiler re2c libv8-7.5-dev libyaml-dev python python3 python3-requests openjdk-8-jdk openjdk-11-jdk
|
||||
#Install PHP extensions
|
||||
yes | pecl install yaml
|
||||
git clone https://github.com/phpv8/v8js.git --depth=1 /tmp/pear/download/v8js-master && cd /tmp/pear/download/v8js-master
|
||||
phpize && ./configure --with-php-config=/usr/bin/php-config --with-v8js=/opt/libv8-7.5 && make install && cd -
|
||||
}
|
||||
|
||||
setLAMPConf(){
|
||||
printf "\n\n==> Setting LAMP configs\n"
|
||||
#Set Apache UOJ site conf
|
||||
cat >/etc/apache2/sites-available/000-uoj.conf <<UOJEOF
|
||||
<VirtualHost *:80>
|
||||
#ServerName local_uoj.ac
|
||||
ServerAdmin opensource@uoj.ac
|
||||
DocumentRoot /var/www/uoj
|
||||
|
||||
SetEnvIf Request_URI "^/judge/.*$" judgelog
|
||||
#LogLevel info ssl:warn
|
||||
ErrorLog \${APACHE_LOG_DIR}/uoj_error.log
|
||||
CustomLog \${APACHE_LOG_DIR}/uoj_judge.log common env=judgelog
|
||||
CustomLog \${APACHE_LOG_DIR}/uoj_access.log combined env=!judgelog
|
||||
|
||||
XSendFile On
|
||||
XSendFilePath /var/uoj_data
|
||||
XSendFilePath /var/www/uoj/app/storage
|
||||
XSendFilePath /opt/uoj/judger/uoj_judger/include
|
||||
</VirtualHost>
|
||||
UOJEOF
|
||||
#Enable modules and make UOJ site conf enabled
|
||||
a2ensite 000-uoj.conf && a2dissite 000-default.conf
|
||||
a2enmod rewrite headers && sed -i -e '172s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
|
||||
#Create UOJ session save dir and make PHP extensions available
|
||||
mkdir --mode=733 /var/lib/php/uoj_sessions && chmod +t /var/lib/php/uoj_sessions
|
||||
sed -i -e '912a\extension=v8js.so\nextension=yaml.so' /etc/php/7.4/apache2/php.ini
|
||||
#Set MySQL user directory and connection config
|
||||
usermod -d /var/lib/mysql/ mysql
|
||||
cat >/etc/mysql/mysql.conf.d/uoj_mysqld.cnf <<UOJEOF
|
||||
[mysqld]
|
||||
default-time-zone='+8:00'
|
||||
character-set-server=utf8mb4
|
||||
collation-server=utf8mb4_unicode_ci
|
||||
init_connect='SET NAMES utf8mb4'
|
||||
init_connect='SET collation_connection = utf8mb4_unicode_ci'
|
||||
skip-character-set-client-handshake
|
||||
sql-mode=ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
|
||||
UOJEOF
|
||||
}
|
||||
|
||||
setWebConf(){
|
||||
printf "\n\n==> Setting web files\n"
|
||||
#Set webroot path
|
||||
ln -sf /opt/uoj/web /var/www/uoj
|
||||
chown -R www-data /var/www/uoj/app/storage
|
||||
#Set web config file
|
||||
php -a <<UOJEOF
|
||||
\$config = include '/var/www/uoj/app/.default-config.php';
|
||||
\$config['database']['password']='$_database_password_';
|
||||
\$config['judger']['socket']['port']='$_judger_socket_port_';
|
||||
file_put_contents('/var/www/uoj/app/.config.php', "<?php\nreturn ".str_replace('\'_httpHost_\'','UOJContext::httpHost()',var_export(\$config, true)).";\n");
|
||||
UOJEOF
|
||||
#Import MySQL database
|
||||
service mysql restart
|
||||
mysql -u root --password=$_database_password_ <../db/app_uoj233.sql
|
||||
echo "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '$_database_password_';" | mysql -u root --password=$_database_password_
|
||||
}
|
||||
|
||||
setJudgeConf(){
|
||||
printf "\n\n==> Setting judger files\n"
|
||||
#Add local_main_judger user
|
||||
useradd -m local_main_judger && usermod -a -G www-data local_main_judger
|
||||
#Set uoj_data path
|
||||
mkdir -p /var/uoj_data/upload
|
||||
chown -R www-data:www-data /var/uoj_data
|
||||
#Compile uoj_judger and set runtime
|
||||
chown -R local_main_judger:local_main_judger /opt/uoj/judger
|
||||
su local_main_judger <<EOD
|
||||
ln -s /var/uoj_data /opt/uoj/judger/uoj_judger/data
|
||||
cd /opt/uoj/judger && chmod +x judge_client
|
||||
cat >uoj_judger/include/uoj_work_path.h <<UOJEOF
|
||||
#define UOJ_WORK_PATH "/opt/uoj/judger/uoj_judger"
|
||||
#define UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
#define UOJ_JUDGER_PYTHON3_VERSION "3.8"
|
||||
#define UOJ_JUDGER_FPC_VERSION "3.0.4"
|
||||
UOJEOF
|
||||
cd uoj_judger && make -j$(($(nproc) + 1))
|
||||
EOD
|
||||
#Set judge_client config file
|
||||
cat >../../judger/.conf.json <<UOJEOF
|
||||
{
|
||||
"uoj_protocol": "http",
|
||||
"uoj_host": "127.0.0.1",
|
||||
"judger_name": "main_judger",
|
||||
"judger_password": "_main_judger_password_",
|
||||
"socket_port": $_judger_socket_port_,
|
||||
"socket_password": "_judger_socket_password_"
|
||||
}
|
||||
UOJEOF
|
||||
chmod 600 ../../judger/.conf.json && chown local_main_judger ../../judger/.conf.json
|
||||
}
|
||||
|
||||
initProgress(){
|
||||
printf "\n\n==> Doing initial config and start service\n"
|
||||
#Replace password placeholders
|
||||
sed -i -e "s/_main_judger_password_/$_main_judger_password_/g" -e "s/_judger_socket_password_/$_judger_socket_password_/g" /opt/uoj/judger/.conf.json
|
||||
sed -i -e "s/salt0/$(genRandStr 32)/g" -e "s/salt1/$(genRandStr 16)/g" -e "s/salt2/$(genRandStr 16)/g" -e "s/salt3/$(genRandStr 16)/g" -e "s/_judger_socket_password_/$_judger_socket_password_/g" /var/www/uoj/app/.config.php
|
||||
#Import judge_client to MySQL database
|
||||
service mysql start
|
||||
echo "insert into judger_info (judger_name, password) values (\"main_judger\", \"$_main_judger_password_\")" | mysql app_uoj233 -u root --password=$_database_password_
|
||||
#Using cli upgrade to latest
|
||||
php /var/www/uoj/app/cli.php upgrade:latest
|
||||
#Start services
|
||||
service ntp restart
|
||||
service mysql restart
|
||||
service apache2 restart
|
||||
su local_main_judger -c '/opt/uoj/judger/judge_client start'
|
||||
#Touch SetupDone flag file
|
||||
touch /var/uoj_data/.UOJSetupDone
|
||||
printf "\n\n***Installation complete. Enjoy!***\n"
|
||||
}
|
||||
|
||||
prepProgress(){
|
||||
getAptPackage;setLAMPConf;setWebConf;setJudgeConf
|
||||
}
|
||||
|
||||
if [ $# -le 0 ]; then
|
||||
echo 'Installing UOJ System bundle...'
|
||||
prepProgress;initProgress
|
||||
fi
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-p | --prep)
|
||||
echo 'Preparing UOJ System bundle environment...'
|
||||
prepProgress
|
||||
;;
|
||||
-i | --init)
|
||||
echo 'Initing UOJ System bundle...'
|
||||
initProgress
|
||||
;;
|
||||
-? | --*)
|
||||
echo "Illegal option $1"
|
||||
;;
|
||||
esac
|
||||
shift $(( $#>0?1:0 ))
|
||||
done
|
@ -0,0 +1,47 @@
|
||||
version: "3"
|
||||
services:
|
||||
|
||||
uoj-db:
|
||||
image: universaloj/uoj-system:db
|
||||
container_name: uoj-db
|
||||
restart: always
|
||||
volumes:
|
||||
- ./uoj_data/db/mysql:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_DATABASE=app_uoj233
|
||||
- MYSQL_ROOT_PASSWORD=root
|
||||
|
||||
uoj-judger:
|
||||
image: universaloj/uoj-system:judger
|
||||
container_name: uoj-judger
|
||||
restart: always
|
||||
stdin_open: true
|
||||
tty: true
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
volumes:
|
||||
- ./uoj_data/judger/log:/opt/uoj/judger/log
|
||||
environment:
|
||||
- UOJ_PROTOCOL=http
|
||||
- UOJ_HOST=uoj-web
|
||||
- JUDGER_NAME=compose_judger
|
||||
- JUDGER_PASSWORD=_judger_password_
|
||||
- SOCKET_PORT=2333
|
||||
- SOCKET_PASSWORD=_judger_socket_password_
|
||||
|
||||
uoj-web:
|
||||
image: universaloj/uoj-system:web
|
||||
container_name: uoj-web
|
||||
restart: always
|
||||
stdin_open: true
|
||||
tty: true
|
||||
cap_add:
|
||||
- SYS_PTRACE
|
||||
depends_on:
|
||||
- uoj-db
|
||||
- uoj-judger
|
||||
volumes:
|
||||
- ./uoj_data/web/data:/var/uoj_data
|
||||
ports:
|
||||
- "80:80"
|
||||
- "3690:3690"
|
@ -0,0 +1,9 @@
|
||||
FROM mysql:latest
|
||||
MAINTAINER MascoSkray <MascoSkray@gmail.com>
|
||||
|
||||
#Update apt and install curl
|
||||
RUN apt-get update && apt-get install -y curl
|
||||
#Run the latest UOJ Community verison db install script
|
||||
RUN export RAW_URL=https://raw.githubusercontent.com/UniversalOJ/UOJ-System/master && curl $RAW_URL/install/db/install.sh | sh
|
||||
|
||||
ENV LANG=C.UTF-8 TZ=Asia/Shanghai
|
@ -0,0 +1,708 @@
|
||||
-- MySQL dump 10.13 Distrib 5.7.25, for Linux (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: app_uoj233
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 5.7.25-0ubuntu0.18.04.2
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+08:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Current Database: `app_uoj233`
|
||||
--
|
||||
|
||||
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `app_uoj233` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
|
||||
|
||||
USE `app_uoj233`;
|
||||
|
||||
--
|
||||
-- Table structure for table `best_ac_submissions`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `best_ac_submissions` (
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`submitter` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`submission_id` int(11) NOT NULL,
|
||||
`used_time` int(11) NOT NULL,
|
||||
`used_memory` int(11) NOT NULL,
|
||||
`tot_size` int(11) NOT NULL,
|
||||
`shortest_id` int(11) NOT NULL,
|
||||
`shortest_used_time` int(11) NOT NULL,
|
||||
`shortest_used_memory` int(11) NOT NULL,
|
||||
`shortest_tot_size` int(11) NOT NULL,
|
||||
PRIMARY KEY (`problem_id`,`submitter`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `best_ac_submissions`
|
||||
--
|
||||
|
||||
LOCK TABLES `best_ac_submissions` WRITE;
|
||||
/*!40000 ALTER TABLE `best_ac_submissions` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `best_ac_submissions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `blogs`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `blogs` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`title` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`content` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`post_time` datetime NOT NULL,
|
||||
`poster` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`content_md` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`zan` int(11) NOT NULL,
|
||||
`is_hidden` tinyint(1) NOT NULL,
|
||||
`type` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'B',
|
||||
`is_draft` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `blogs`
|
||||
--
|
||||
|
||||
LOCK TABLES `blogs` WRITE;
|
||||
/*!40000 ALTER TABLE `blogs` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `blogs` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `blogs_comments`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `blogs_comments` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`blog_id` int(11) NOT NULL,
|
||||
`content` text COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`post_time` datetime NOT NULL,
|
||||
`poster` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`zan` int(11) NOT NULL,
|
||||
`reply_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `blogs_comments`
|
||||
--
|
||||
|
||||
LOCK TABLES `blogs_comments` WRITE;
|
||||
/*!40000 ALTER TABLE `blogs_comments` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `blogs_comments` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `blogs_tags`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `blogs_tags` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`blog_id` int(11) NOT NULL,
|
||||
`tag` varchar(30) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `blog_id` (`blog_id`),
|
||||
KEY `tag` (`tag`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `blogs_tags`
|
||||
--
|
||||
|
||||
LOCK TABLES `blogs_tags` WRITE;
|
||||
/*!40000 ALTER TABLE `blogs_tags` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `blogs_tags` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `click_zans`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `click_zans` (
|
||||
`type` char(2) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`target_id` int(11) NOT NULL,
|
||||
`val` tinyint(4) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`type`,`target_id`,`username`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `click_zans`
|
||||
--
|
||||
|
||||
LOCK TABLES `click_zans` WRITE;
|
||||
/*!40000 ALTER TABLE `click_zans` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `click_zans` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `contests`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `contests` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(50) NOT NULL,
|
||||
`start_time` datetime NOT NULL,
|
||||
`last_min` int(11) NOT NULL,
|
||||
`player_num` int(11) NOT NULL,
|
||||
`status` varchar(50) NOT NULL,
|
||||
`extra_config` varchar(200) NOT NULL,
|
||||
`zan` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `contests`
|
||||
--
|
||||
|
||||
LOCK TABLES `contests` WRITE;
|
||||
/*!40000 ALTER TABLE `contests` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `contests` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `contests_asks`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `contests_asks` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`contest_id` int(11) NOT NULL,
|
||||
`username` varchar(20) NOT NULL,
|
||||
`question` text NOT NULL,
|
||||
`answer` text NOT NULL,
|
||||
`post_time` datetime NOT NULL,
|
||||
`reply_time` datetime NOT NULL,
|
||||
`is_hidden` tinyint(1) DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `contests_asks`
|
||||
--
|
||||
|
||||
LOCK TABLES `contests_asks` WRITE;
|
||||
/*!40000 ALTER TABLE `contests_asks` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `contests_asks` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `contests_notice`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `contests_notice` (
|
||||
`contest_id` int(11) NOT NULL,
|
||||
`title` varchar(30) NOT NULL,
|
||||
`content` varchar(500) NOT NULL,
|
||||
`time` datetime NOT NULL,
|
||||
KEY `contest_id` (`contest_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `contests_notice`
|
||||
--
|
||||
|
||||
LOCK TABLES `contests_notice` WRITE;
|
||||
/*!40000 ALTER TABLE `contests_notice` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `contests_notice` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `contests_permissions`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `contests_permissions` (
|
||||
`username` varchar(20) NOT NULL,
|
||||
`contest_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`username`,`contest_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `contests_permissions`
|
||||
--
|
||||
|
||||
LOCK TABLES `contests_permissions` WRITE;
|
||||
/*!40000 ALTER TABLE `contests_permissions` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `contests_permissions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `contests_problems`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `contests_problems` (
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`contest_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`problem_id`,`contest_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `contests_problems`
|
||||
--
|
||||
|
||||
LOCK TABLES `contests_problems` WRITE;
|
||||
/*!40000 ALTER TABLE `contests_problems` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `contests_problems` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `contests_registrants`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `contests_registrants` (
|
||||
`username` varchar(20) NOT NULL,
|
||||
`user_rating` int(11) NOT NULL,
|
||||
`contest_id` int(11) NOT NULL,
|
||||
`has_participated` tinyint(1) NOT NULL,
|
||||
`rank` int(11) NOT NULL,
|
||||
PRIMARY KEY (`contest_id`,`username`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `contests_registrants`
|
||||
--
|
||||
|
||||
LOCK TABLES `contests_registrants` WRITE;
|
||||
/*!40000 ALTER TABLE `contests_registrants` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `contests_registrants` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `contests_submissions`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `contests_submissions` (
|
||||
`contest_id` int(11) NOT NULL,
|
||||
`submitter` varchar(20) NOT NULL,
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`submission_id` int(11) NOT NULL,
|
||||
`score` int(11) NOT NULL,
|
||||
`penalty` int(11) NOT NULL,
|
||||
PRIMARY KEY (`contest_id`,`submitter`,`problem_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `contests_submissions`
|
||||
--
|
||||
|
||||
LOCK TABLES `contests_submissions` WRITE;
|
||||
/*!40000 ALTER TABLE `contests_submissions` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `contests_submissions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `custom_test_submissions`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `custom_test_submissions` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`problem_id` int(10) unsigned NOT NULL,
|
||||
`submit_time` datetime NOT NULL,
|
||||
`submitter` varchar(20) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`judge_time` datetime DEFAULT NULL,
|
||||
`result` blob NOT NULL,
|
||||
`status` varchar(20) NOT NULL,
|
||||
`status_details` varchar(100) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `custom_test_submissions`
|
||||
--
|
||||
|
||||
LOCK TABLES `custom_test_submissions` WRITE;
|
||||
/*!40000 ALTER TABLE `custom_test_submissions` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `custom_test_submissions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `hacks`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `hacks` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`problem_id` int(10) unsigned NOT NULL,
|
||||
`contest_id` int(10) unsigned DEFAULT NULL,
|
||||
`submission_id` int(10) unsigned NOT NULL,
|
||||
`hacker` varchar(20) NOT NULL,
|
||||
`owner` varchar(20) NOT NULL,
|
||||
`input` varchar(150) NOT NULL,
|
||||
`input_type` char(20) NOT NULL,
|
||||
`submit_time` datetime NOT NULL,
|
||||
`judge_time` datetime DEFAULT NULL,
|
||||
`success` tinyint(1) DEFAULT NULL,
|
||||
`details` blob NOT NULL,
|
||||
`is_hidden` tinyint(1) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `submission_id` (`submission_id`),
|
||||
KEY `is_hidden` (`is_hidden`,`problem_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `hacks`
|
||||
--
|
||||
|
||||
LOCK TABLES `hacks` WRITE;
|
||||
/*!40000 ALTER TABLE `hacks` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `hacks` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `important_blogs`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `important_blogs` (
|
||||
`blog_id` int(11) NOT NULL,
|
||||
`level` int(11) NOT NULL,
|
||||
PRIMARY KEY (`blog_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `important_blogs`
|
||||
--
|
||||
|
||||
LOCK TABLES `important_blogs` WRITE;
|
||||
/*!40000 ALTER TABLE `important_blogs` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `important_blogs` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `judger_info`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `judger_info` (
|
||||
`judger_name` varchar(50) NOT NULL,
|
||||
`password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`ip` char(20) NOT NULL,
|
||||
PRIMARY KEY (`judger_name`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `judger_info`
|
||||
--
|
||||
|
||||
LOCK TABLES `judger_info` WRITE;
|
||||
/*!40000 ALTER TABLE `judger_info` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `judger_info` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `problems`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `problems` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`title` text NOT NULL,
|
||||
`is_hidden` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`submission_requirement` text,
|
||||
`hackable` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`extra_config` varchar(500) NOT NULL DEFAULT '{"view_content_type":"ALL","view_details_type":"ALL"}',
|
||||
`zan` int(11) NOT NULL,
|
||||
`ac_num` int(11) NOT NULL DEFAULT '0',
|
||||
`submit_num` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `problems`
|
||||
--
|
||||
|
||||
LOCK TABLES `problems` WRITE;
|
||||
/*!40000 ALTER TABLE `problems` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `problems` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `problems_contents`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `problems_contents` (
|
||||
`id` int(11) NOT NULL,
|
||||
`statement` mediumtext NOT NULL,
|
||||
`statement_md` mediumtext NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `problems_contents`
|
||||
--
|
||||
|
||||
LOCK TABLES `problems_contents` WRITE;
|
||||
/*!40000 ALTER TABLE `problems_contents` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `problems_contents` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `problems_permissions`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `problems_permissions` (
|
||||
`username` varchar(20) NOT NULL,
|
||||
`problem_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`username`,`problem_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `problems_permissions`
|
||||
--
|
||||
|
||||
LOCK TABLES `problems_permissions` WRITE;
|
||||
/*!40000 ALTER TABLE `problems_permissions` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `problems_permissions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `problems_tags`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `problems_tags` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`problem_id` int(11) NOT NULL,
|
||||
`tag` varchar(30) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `problem_id` (`problem_id`),
|
||||
KEY `tag` (`tag`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `problems_tags`
|
||||
--
|
||||
|
||||
LOCK TABLES `problems_tags` WRITE;
|
||||
/*!40000 ALTER TABLE `problems_tags` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `problems_tags` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `search_requests`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `search_requests` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`created_at` datetime NOT NULL,
|
||||
`remote_addr` varchar(50) NOT NULL,
|
||||
`type` enum('search','autocomplete') NOT NULL,
|
||||
`cache_id` int(11) NOT NULL,
|
||||
`q` varchar(100) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`result` mediumtext NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `remote_addr` (`remote_addr`,`created_at`),
|
||||
KEY `created_at` (`created_at`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `search_requests`
|
||||
--
|
||||
|
||||
LOCK TABLES `search_requests` WRITE;
|
||||
/*!40000 ALTER TABLE `search_requests` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `search_requests` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `submissions`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `submissions` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`problem_id` int(10) unsigned NOT NULL,
|
||||
`contest_id` int(10) unsigned DEFAULT NULL,
|
||||
`submit_time` datetime NOT NULL,
|
||||
`submitter` varchar(20) NOT NULL,
|
||||
`content` text NOT NULL,
|
||||
`language` varchar(15) NOT NULL,
|
||||
`tot_size` int(11) NOT NULL,
|
||||
`judge_time` datetime DEFAULT NULL,
|
||||
`result` blob NOT NULL,
|
||||
`status` varchar(20) NOT NULL,
|
||||
`result_error` varchar(20) DEFAULT NULL,
|
||||
`score` int(11) DEFAULT NULL,
|
||||
`used_time` int(11) NOT NULL DEFAULT '0',
|
||||
`used_memory` int(11) NOT NULL DEFAULT '0',
|
||||
`is_hidden` tinyint(1) NOT NULL,
|
||||
`status_details` varchar(100) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `is_hidden` (`is_hidden`,`problem_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `submissions`
|
||||
--
|
||||
|
||||
LOCK TABLES `submissions` WRITE;
|
||||
/*!40000 ALTER TABLE `submissions` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `submissions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `user_info`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `user_info` (
|
||||
`usergroup` char(1) NOT NULL DEFAULT 'U',
|
||||
`username` varchar(20) NOT NULL,
|
||||
`email` varchar(50) NOT NULL,
|
||||
`password` char(32) NOT NULL,
|
||||
`svn_password` char(10) NOT NULL,
|
||||
`rating` int(11) NOT NULL DEFAULT '1500',
|
||||
`qq` bigint(20) NOT NULL,
|
||||
`sex` char(1) NOT NULL DEFAULT 'U',
|
||||
`ac_num` int(11) NOT NULL,
|
||||
`register_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`remote_addr` varchar(50) NOT NULL,
|
||||
`http_x_forwarded_for` varchar(50) NOT NULL,
|
||||
`remember_token` char(60) NOT NULL,
|
||||
`motto` varchar(200) NOT NULL,
|
||||
PRIMARY KEY (`username`),
|
||||
KEY `rating` (`rating`,`username`),
|
||||
KEY `ac_num` (`ac_num`,`username`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `user_info`
|
||||
--
|
||||
|
||||
LOCK TABLES `user_info` WRITE;
|
||||
/*!40000 ALTER TABLE `user_info` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `user_info` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `user_msg`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `user_msg` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`sender` varchar(20) NOT NULL,
|
||||
`receiver` varchar(20) NOT NULL,
|
||||
`message` varchar(5000) NOT NULL,
|
||||
`send_time` datetime NOT NULL,
|
||||
`read_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `user_msg`
|
||||
--
|
||||
|
||||
LOCK TABLES `user_msg` WRITE;
|
||||
/*!40000 ALTER TABLE `user_msg` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `user_msg` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `user_system_msg`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `user_system_msg` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`title` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`content` varchar(300) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`receiver` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`send_time` datetime NOT NULL,
|
||||
`read_time` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `user_system_msg`
|
||||
--
|
||||
|
||||
LOCK TABLES `user_system_msg` WRITE;
|
||||
/*!40000 ALTER TABLE `user_system_msg` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `user_system_msg` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
setLAMPConf(){
|
||||
printf "\n\n==> Setting LAMP configs\n"
|
||||
#Set MySQL connection config
|
||||
cat >/etc/mysql/conf.d/uoj_mysqld.cnf <<UOJEOF
|
||||
[mysqld]
|
||||
default-time-zone='+8:00'
|
||||
character-set-server=utf8mb4
|
||||
collation-server=utf8mb4_unicode_ci
|
||||
init_connect='SET NAMES utf8mb4'
|
||||
init_connect='SET collation_connection = utf8mb4_unicode_ci'
|
||||
skip-character-set-client-handshake
|
||||
sql-mode=ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
|
||||
UOJEOF
|
||||
}
|
||||
|
||||
setWebConf(){
|
||||
printf "\n\n==> Setting web files\n"
|
||||
#Import MySQL database
|
||||
cat >/docker-entrypoint-initdb.d/000-native_password.sql <<UOJEOF
|
||||
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
|
||||
UOJEOF
|
||||
curl $RAW_URL/install/db/app_uoj233.sql >/docker-entrypoint-initdb.d/001-app_uoj233.sql
|
||||
curl $RAW_URL/install/judger/add_judger.sql >/docker-entrypoint-initdb.d/002-add_judger.sql
|
||||
}
|
||||
|
||||
echo 'Preparing UOJ System db environment...'
|
||||
setLAMPConf;setWebConf
|
@ -0,0 +1,22 @@
|
||||
FROM ubuntu:18.04
|
||||
MAINTAINER MascoSkray <MascoSkray@gmail.com>
|
||||
ARG CLONE_ADDFLAG
|
||||
|
||||
WORKDIR /opt
|
||||
#Update apt and install git
|
||||
RUN apt-get update && apt-get install -y git
|
||||
#Clone the latest UOJ Community verison to local
|
||||
RUN git clone https://github.com/UniversalOJ/UOJ-System.git --depth 1 --single-branch ${CLONE_ADDFLAG} uoj
|
||||
#Install environment and set startup script
|
||||
RUN cd uoj/install/judger && sh install.sh -p && echo "\
|
||||
#!/bin/sh\n\
|
||||
if [ ! -f \"/opt/uoj/judger/.conf.json\" ]; then\n\
|
||||
cd /opt/uoj/install/judger && sh install.sh -i\n\
|
||||
fi\n\
|
||||
service ntp start\n\
|
||||
su judger -c \"/opt/uoj/judger/judge_client start\"\n\
|
||||
exec bash\n" >/opt/up && chmod +x /opt/up
|
||||
|
||||
ENV LANG=C.UTF-8 TZ=Asia/Shanghai
|
||||
EXPOSE 2333
|
||||
CMD /opt/up
|
@ -0,0 +1,2 @@
|
||||
USE `app_uoj233`;
|
||||
insert into judger_info (judger_name, password, ip) values ('compose_judger', '_judger_password_', 'uoj-judger');
|
@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
|
||||
getAptPackage(){
|
||||
printf "\n\n==> Getting environment packages\n"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update && apt-get install -y vim ntp zip unzip curl wget build-essential fp-compiler python python3 python3-requests openjdk-8-jdk openjdk-11-jdk
|
||||
}
|
||||
|
||||
setJudgeConf(){
|
||||
printf "\n\n==> Setting judger files\n"
|
||||
#Add judger user
|
||||
adduser judger --gecos "" --disabled-password
|
||||
#Set uoj_data path
|
||||
mkdir /var/uoj_data_copy && chown judger /var/uoj_data_copy
|
||||
#Compile uoj_judger and set runtime
|
||||
chown -R judger:judger /opt/uoj/judger
|
||||
su judger <<EOD
|
||||
ln -s /var/uoj_data_copy /opt/uoj/judger/uoj_judger/data
|
||||
cd /opt/uoj/judger && chmod +x judge_client
|
||||
cat >uoj_judger/include/uoj_work_path.h <<UOJEOF
|
||||
#define UOJ_WORK_PATH "/opt/uoj/judger/uoj_judger"
|
||||
#define UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
#define UOJ_JUDGER_PYTHON3_VERSION "3.6"
|
||||
#define UOJ_JUDGER_FPC_VERSION "3.0.4"
|
||||
UOJEOF
|
||||
cd uoj_judger && make -j$(($(nproc) + 1))
|
||||
EOD
|
||||
}
|
||||
|
||||
initProgress(){
|
||||
printf "\n\n==> Doing initial config and start service\n"
|
||||
#Check envs
|
||||
if [ -z "$UOJ_PROTOCOL" -o -z "$UOJ_HOST" -o -z "$JUDGER_NAME" -o -z "$JUDGER_PASSWORD" -o -z "$SOCKET_PORT" -o -z "$SOCKET_PASSWORD" ]; then
|
||||
echo "!! Environment variables not set! Please edit config file by yourself!"
|
||||
else
|
||||
#Set judge_client config file
|
||||
cat >../../judger/.conf.json <<UOJEOF
|
||||
{
|
||||
"uoj_protocol": "$UOJ_PROTOCOL",
|
||||
"uoj_host": "$UOJ_HOST",
|
||||
"judger_name": "$JUDGER_NAME",
|
||||
"judger_password": "$JUDGER_PASSWORD",
|
||||
"socket_port": $SOCKET_PORT,
|
||||
"socket_password": "$SOCKET_PASSWORD"
|
||||
}
|
||||
UOJEOF
|
||||
chmod 600 ../../judger/.conf.json && chown judger ../../judger/.conf.json
|
||||
chown -R judger:judger ../../judger/log
|
||||
#Start services
|
||||
service ntp restart
|
||||
su judger -c '/opt/uoj/judger/judge_client start'
|
||||
echo "please modify the database after getting the judger server ready:"
|
||||
echo "insert into judger_info (judger_name, password, ip) values ('$JUDGER_NAME', '$JUDGER_PASSWORD', '__judger_ip_here__');"
|
||||
printf "\n\n***Installation complete. Enjoy!***\n"
|
||||
fi
|
||||
}
|
||||
|
||||
prepProgress(){
|
||||
getAptPackage;setJudgeConf
|
||||
}
|
||||
|
||||
if [ $# -le 0 ]; then
|
||||
echo 'Installing UOJ System judger...'
|
||||
prepProgress;initProgress
|
||||
fi
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-p | --prep)
|
||||
echo 'Preparing UOJ System judger environment...'
|
||||
prepProgress
|
||||
;;
|
||||
-i | --init)
|
||||
echo 'Initing UOJ System judger...'
|
||||
initProgress
|
||||
;;
|
||||
-? | --*)
|
||||
echo "Illegal option $1"
|
||||
;;
|
||||
esac
|
||||
shift $(( $#>0?1:0 ))
|
||||
done
|
@ -0,0 +1,22 @@
|
||||
FROM ubuntu:18.04
|
||||
MAINTAINER MascoSkray <MascoSkray@gmail.com>
|
||||
ARG CLONE_ADDFLAG
|
||||
|
||||
WORKDIR /opt
|
||||
#Update apt and install git
|
||||
RUN apt-get update && apt-get install -y git
|
||||
#Clone the latest UOJ Community verison to local
|
||||
RUN git clone https://github.com/UniversalOJ/UOJ-System.git --depth 1 --single-branch ${CLONE_ADDFLAG} uoj
|
||||
#Install environment and set startup script
|
||||
RUN cd uoj/install/web && sh install.sh -p && echo "\
|
||||
#!/bin/sh\n\
|
||||
if [ ! -f \"/var/uoj_data/.UOJSetupDone\" ]; then\n\
|
||||
cd /opt/uoj/install/web && sh install.sh -i\n\
|
||||
fi\n\
|
||||
service ntp start\n\
|
||||
service apache2 start\n\
|
||||
exec bash\n" >/opt/up && chmod +x /opt/up
|
||||
|
||||
ENV LANG=C.UTF-8 TZ=Asia/Shanghai
|
||||
EXPOSE 80 3690
|
||||
CMD /opt/up
|
@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
genRandStr(){
|
||||
cat /dev/urandom | tr -dc [:alnum:] | head -c $1
|
||||
}
|
||||
#Set some vars
|
||||
_database_host_=uoj-db
|
||||
_database_password_=root
|
||||
_judger_socket_port_=2333
|
||||
_judger_socket_password_=_judger_socket_password_
|
||||
|
||||
getAptPackage(){
|
||||
printf "\n\n==> Getting environment packages\n"
|
||||
#Update apt sources and install
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
dpkg -s gnupg 2>/dev/null || (apt-get update && apt-get install -y gnupg)
|
||||
echo "deb http://ppa.launchpad.net/stesie/libv8/ubuntu bionic main" | tee /etc/apt/sources.list.d/stesie-libv8.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D858A0DF
|
||||
apt-get update && apt-get install -y vim ntp zip unzip curl wget apache2 libapache2-mod-xsendfile libapache2-mod-php php php-dev php-pear php-zip php-mysql php-mbstring g++ cmake re2c libv8-7.5-dev libyaml-dev
|
||||
#Install PHP extensions
|
||||
printf "/opt/libv8-7.5\n\n" | pecl install v8js yaml
|
||||
}
|
||||
|
||||
setLAMPConf(){
|
||||
printf "\n\n==> Setting LAMP configs\n"
|
||||
#Set Apache UOJ site conf
|
||||
cat >/etc/apache2/sites-available/000-uoj.conf <<UOJEOF
|
||||
<VirtualHost *:80>
|
||||
#ServerName local_uoj.ac
|
||||
ServerAdmin opensource@uoj.ac
|
||||
DocumentRoot /var/www/uoj
|
||||
|
||||
SetEnvIf Request_URI "^/judge/.*$" judgelog
|
||||
#LogLevel info ssl:warn
|
||||
ErrorLog \${APACHE_LOG_DIR}/uoj_error.log
|
||||
CustomLog \${APACHE_LOG_DIR}/uoj_judge.log common env=judgelog
|
||||
CustomLog \${APACHE_LOG_DIR}/uoj_access.log combined env=!judgelog
|
||||
|
||||
XSendFile On
|
||||
XSendFilePath /var/uoj_data
|
||||
XSendFilePath /var/www/uoj/app/storage
|
||||
XSendFilePath /opt/uoj/judger/uoj_judger/include
|
||||
</VirtualHost>
|
||||
UOJEOF
|
||||
#Enable modules and make UOJ site conf enabled
|
||||
a2ensite 000-uoj.conf && a2dissite 000-default.conf
|
||||
a2enmod rewrite headers && sed -i -e '172s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
|
||||
#Create UOJ session save dir and make PHP extensions available
|
||||
mkdir --mode=733 /var/lib/php/uoj_sessions && chmod +t /var/lib/php/uoj_sessions
|
||||
sed -i -e '865a\extension=v8js.so\nextension=yaml.so' /etc/php/7.2/apache2/php.ini
|
||||
}
|
||||
|
||||
setWebConf(){
|
||||
printf "\n\n==> Setting web files\n"
|
||||
#Set webroot path
|
||||
ln -sf /opt/uoj/web /var/www/uoj
|
||||
chown -R www-data /var/www/uoj/app/storage
|
||||
#Set web config file
|
||||
php -a <<UOJEOF
|
||||
\$config = include '/var/www/uoj/app/.default-config.php';
|
||||
\$config['database']['host']='$_database_host_';
|
||||
\$config['database']['password']='$_database_password_';
|
||||
\$config['judger']['socket']['port']='$_judger_socket_port_';
|
||||
file_put_contents('/var/www/uoj/app/.config.php', "<?php\nreturn ".str_replace('\'_httpHost_\'','UOJContext::httpHost()',var_export(\$config, true)).";\n");
|
||||
UOJEOF
|
||||
#Prepare local sandbox
|
||||
cd ../../judger/uoj_judger
|
||||
cat >include/uoj_work_path.h <<UOJEOF
|
||||
#define UOJ_WORK_PATH "/opt/uoj/judger/uoj_judger"
|
||||
#define UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
#define UOJ_JUDGER_PYTHON3_VERSION "3.6"
|
||||
#define UOJ_JUDGER_FPC_VERSION "3.0.4"
|
||||
UOJEOF
|
||||
make runner -j$(($(nproc) + 1)) && cd ../../install/web
|
||||
}
|
||||
|
||||
initProgress(){
|
||||
printf "\n\n==> Doing initial config and start service\n"
|
||||
#Set uoj_data path
|
||||
mkdir -p /var/uoj_data/upload
|
||||
chown -R www-data:www-data /var/uoj_data
|
||||
#Replace password placeholders
|
||||
sed -i -e "s/salt0/$(genRandStr 32)/g" -e "s/salt1/$(genRandStr 16)/g" -e "s/salt2/$(genRandStr 16)/g" -e "s/salt3/$(genRandStr 16)/g" -e "s/_judger_socket_password_/$_judger_socket_password_/g" /var/www/uoj/app/.config.php
|
||||
#Using cli upgrade to latest
|
||||
php /var/www/uoj/app/cli.php upgrade:latest
|
||||
#Start services
|
||||
service ntp restart
|
||||
service apache2 restart
|
||||
#Touch SetupDone flag file
|
||||
touch /var/uoj_data/.UOJSetupDone
|
||||
printf "\n\n***Installation complete. Enjoy!***\n"
|
||||
}
|
||||
|
||||
prepProgress(){
|
||||
getAptPackage;setLAMPConf;setWebConf
|
||||
}
|
||||
|
||||
if [ $# -le 0 ]; then
|
||||
echo 'Installing UOJ System web...'
|
||||
prepProgress;initProgress
|
||||
fi
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-p | --prep)
|
||||
echo 'Preparing UOJ System web environment...'
|
||||
prepProgress
|
||||
;;
|
||||
-i | --init)
|
||||
echo 'Initing UOJ System web...'
|
||||
initProgress
|
||||
;;
|
||||
-? | --*)
|
||||
echo "Illegal option $1"
|
||||
;;
|
||||
esac
|
||||
shift $(( $#>0?1:0 ))
|
||||
done
|
@ -0,0 +1,437 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import pipes
|
||||
import socket
|
||||
from threading import Thread
|
||||
import fcntl
|
||||
import shutil
|
||||
|
||||
import traceback
|
||||
import time
|
||||
from contextlib import closing
|
||||
|
||||
import requests
|
||||
|
||||
import queue as queue
|
||||
from queue import Queue, Empty
|
||||
|
||||
taskQ = Queue()
|
||||
submission = None
|
||||
|
||||
# path related function
|
||||
|
||||
def uoj_url(uri):
|
||||
return ("%s://%s%s" % (jconf['uoj_protocol'], jconf['uoj_host'], uri)).rstrip('/')
|
||||
def uoj_judger_path(path = ''):
|
||||
return "uoj_judger" + path
|
||||
|
||||
# os related funciton
|
||||
def clean_up_folder(path):
|
||||
for f in os.listdir(path):
|
||||
f_path = os.path.join(path, f)
|
||||
if os.path.isfile(f_path):
|
||||
os.unlink(f_path)
|
||||
else:
|
||||
shutil.rmtree(f_path)
|
||||
|
||||
def execute(cmd):
|
||||
if os.system(cmd):
|
||||
raise Exception('failed to execute: %s' % cmd)
|
||||
|
||||
def freopen(f, g):
|
||||
os.dup2(g.fileno(), f.fileno())
|
||||
g.close()
|
||||
|
||||
# init
|
||||
def init():
|
||||
global jconf
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
with open('.conf.json', 'r') as fp:
|
||||
jconf = json.load(fp)
|
||||
assert 'uoj_protocol' in jconf
|
||||
assert 'uoj_host' in jconf
|
||||
assert 'judger_name' in jconf
|
||||
assert 'judger_password' in jconf
|
||||
assert 'socket_port' in jconf
|
||||
assert 'socket_password' in jconf
|
||||
|
||||
# socket server
|
||||
def socket_server_loop():
|
||||
SOCK_CLOEXEC = 524288
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM | SOCK_CLOEXEC)) as s:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(('', jconf['socket_port']))
|
||||
s.listen(5)
|
||||
|
||||
while True:
|
||||
try:
|
||||
conn, addr = s.accept()
|
||||
with closing(conn) as conn:
|
||||
data = conn.recv(1024).decode()
|
||||
assert data != None
|
||||
task = json.loads(data)
|
||||
assert task['password'] == jconf['socket_password']
|
||||
assert 'cmd' in task
|
||||
|
||||
taskQ.put(task)
|
||||
|
||||
if task['cmd'] == 'stop':
|
||||
print('the judge client is closing...')
|
||||
taskQ.join()
|
||||
conn.sendall(b'ok')
|
||||
return 'stop'
|
||||
except Exception:
|
||||
print('['+time.asctime()+']', 'connection rejected', file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
else:
|
||||
print('['+time.asctime()+']', 'a new task accomplished', file=sys.stderr)
|
||||
|
||||
def start_judger_server():
|
||||
global socket_server_thread
|
||||
|
||||
print_judge_client_status()
|
||||
print('hello!', file=sys.stderr)
|
||||
|
||||
socket_server_thread = Thread(target = socket_server_loop)
|
||||
socket_server_thread.setDaemon(True)
|
||||
socket_server_thread.start()
|
||||
|
||||
judger_loop()
|
||||
|
||||
# report thread
|
||||
def report_loop():
|
||||
if 'is_hack' in submission:
|
||||
return
|
||||
while not submission_judged:
|
||||
try:
|
||||
with open(uoj_judger_path('/result/cur_status.txt'), 'r') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_SH)
|
||||
try:
|
||||
status = f.read(100)
|
||||
except Exception:
|
||||
status = None
|
||||
finally:
|
||||
fcntl.flock(f, fcntl.LOCK_UN)
|
||||
|
||||
if status != None:
|
||||
data = {}
|
||||
data['update-status'] = True
|
||||
data['id'] = submission['id']
|
||||
if 'is_custom_test' in submission:
|
||||
data['is_custom_test'] = True
|
||||
data['status'] = status
|
||||
uoj_interact(data)
|
||||
time.sleep(0.2)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# handle task in main thread
|
||||
def handle_task():
|
||||
need_restart = False
|
||||
try:
|
||||
while True:
|
||||
task = taskQ.get_nowait()
|
||||
|
||||
if task['cmd'] == 'update':
|
||||
try:
|
||||
uoj_download('/judger', 'judger_update.zip')
|
||||
execute('unzip -o judger_update.zip && cd %s && make clean && make' % uoj_judger_path())
|
||||
except:
|
||||
print(sys.stderr, "error when update")
|
||||
if jconf['judger_name'] == 'main_judger':
|
||||
uoj_sync_judge_client()
|
||||
need_restart = True
|
||||
elif task['cmd'] == 'stop':
|
||||
taskQ.task_done()
|
||||
socket_server_thread.join()
|
||||
|
||||
print_judge_client_status()
|
||||
print(sys.stderr, "goodbye!")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
taskQ.task_done()
|
||||
except Empty:
|
||||
pass
|
||||
|
||||
if need_restart:
|
||||
os.execl('./judge_client', './judge_client')
|
||||
|
||||
def print_judge_client_status():
|
||||
print('[' + time.asctime() + ']', end=' ', file=sys.stderr)
|
||||
if submission != None:
|
||||
print(submission, end=' ', file=sys.stderr)
|
||||
print(file=sys.stderr)
|
||||
|
||||
# interact with uoj_judger
|
||||
def get_judger_result():
|
||||
res = {}
|
||||
with open(uoj_judger_path('/result/result.txt'), 'r') as fres:
|
||||
res['score'] = 0
|
||||
res['time'] = 0
|
||||
res['memory'] = 0
|
||||
while True:
|
||||
line = fres.readline()
|
||||
if line == '':
|
||||
break
|
||||
line = line.strip()
|
||||
if line == 'details':
|
||||
res['details'] = fres.read()
|
||||
break
|
||||
|
||||
sp = line.split()
|
||||
assert len(sp) >= 1
|
||||
if sp[0] == 'error':
|
||||
res['error'] = line[len('error') + 1:]
|
||||
else:
|
||||
assert len(sp) == 2
|
||||
res[sp[0]] = sp[1]
|
||||
res['score'] = int(res['score'])
|
||||
res['time'] = int(res['time'])
|
||||
res['memory'] = int(res['memory'])
|
||||
res['status'] = 'Judged'
|
||||
return res
|
||||
|
||||
def update_problem_data(problem_id, problem_mtime):
|
||||
try:
|
||||
if jconf['judger_name'] == 'main_judger':
|
||||
return
|
||||
copy_name = uoj_judger_path('/data/%d' % problem_id)
|
||||
copy_zip_name = uoj_judger_path('/data/%d.zip' % problem_id)
|
||||
if os.path.isdir(copy_name):
|
||||
quoted_copy_name = pipes.quote(copy_name)
|
||||
if os.path.getmtime(copy_name) >= problem_mtime:
|
||||
execute('touch -a %s' % quoted_copy_name)
|
||||
return
|
||||
else:
|
||||
execute('chmod 700 %s -R && rm -rf %s' % (quoted_copy_name, quoted_copy_name))
|
||||
del_list = sorted(os.listdir(uoj_judger_path('/data')), key=lambda fname: os.path.getatime(uoj_judger_path('/data/%s' % fname)))[:-99]
|
||||
for fname in del_list:
|
||||
quoted_fname = pipes.quote(uoj_judger_path('/data/%s' % fname))
|
||||
os.system('chmod 700 %s -R && rm -rf %s' % (quoted_fname, quoted_fname))
|
||||
|
||||
uoj_download('/problem/%d' % problem_id, copy_zip_name)
|
||||
execute('cd %s && unzip -q %d.zip && rm %d.zip && chmod -w %d -R' % (uoj_judger_path('/data'), problem_id, problem_id, problem_id))
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
raise Exception('failed to update problem data of #%d' % problem_id)
|
||||
else:
|
||||
print_judge_client_status()
|
||||
print('updated problem data of #%d successfully' % problem_id, file=sys.stderr)
|
||||
|
||||
def judge():
|
||||
global report_thread
|
||||
global submission_judged
|
||||
|
||||
clean_up_folder(uoj_judger_path('/work'))
|
||||
clean_up_folder(uoj_judger_path('/result'))
|
||||
update_problem_data(submission['problem_id'], submission['problem_mtime'])
|
||||
|
||||
with open(uoj_judger_path('/work/submission.conf'), 'w') as fconf:
|
||||
uoj_download(submission['content']['file_name'], uoj_judger_path('/work/all.zip'))
|
||||
execute("cd %s && unzip -q all.zip && rm all.zip" % pipes.quote(uoj_judger_path('/work')))
|
||||
for k, v in submission['content']['config']:
|
||||
print(k, v, file=fconf)
|
||||
|
||||
if 'is_hack' in submission:
|
||||
if submission['hack']['input_type'] == 'USE_FORMATTER':
|
||||
uoj_download(submission['hack']['input'], uoj_judger_path('/work/hack_input_raw.txt'))
|
||||
execute('%s <%s >%s' % (
|
||||
pipes.quote(uoj_judger_path('/run/formatter')),
|
||||
pipes.quote(uoj_judger_path('/work/hack_input_raw.txt')),
|
||||
pipes.quote(uoj_judger_path('/work/hack_input.txt'))))
|
||||
else:
|
||||
uoj_download(submission['hack']['input'], uoj_judger_path('/work/hack_input.txt'))
|
||||
print('test_new_hack_only on', file=fconf)
|
||||
elif 'is_custom_test' in submission:
|
||||
print('custom_test on', file=fconf)
|
||||
|
||||
report_thread = Thread(target = report_loop)
|
||||
report_thread.setDaemon(True)
|
||||
|
||||
submission_judged = False
|
||||
report_thread.start()
|
||||
execute(pipes.quote(uoj_judger_path('/main_judger')))
|
||||
submission_judged = True
|
||||
report_thread.join()
|
||||
|
||||
return get_judger_result()
|
||||
|
||||
# interact with uoj web server
|
||||
def uoj_interact(data, files = {}):
|
||||
data = data.copy()
|
||||
data.update({
|
||||
'judger_name': jconf['judger_name'],
|
||||
'password': jconf['judger_password']
|
||||
})
|
||||
return requests.post(uoj_url('/judge/submit'), data=data, files=files).text
|
||||
def uoj_download(uri, filename):
|
||||
data = {
|
||||
'judger_name': jconf['judger_name'],
|
||||
'password': jconf['judger_password']
|
||||
}
|
||||
with open(filename, 'wb') as f:
|
||||
r = requests.post(uoj_url('/judge/download' + uri), data=data, stream=True)
|
||||
for chunk in r.iter_content(chunk_size=65536):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
def uoj_sync_judge_client():
|
||||
data = {
|
||||
'judger_name': jconf['judger_name'],
|
||||
'password': jconf['judger_password']
|
||||
}
|
||||
ret = requests.post(uoj_url('/judge/sync-judge-client'), data=data).text
|
||||
if ret != "ok":
|
||||
raise Exception('failed to sync judge clients: %s' % ret)
|
||||
|
||||
def send_and_fetch(result = None, fetch_new = True):
|
||||
global submission
|
||||
|
||||
"""send judgement result, and fetch new submission to judge"""
|
||||
|
||||
data = {}
|
||||
files = {}
|
||||
|
||||
if not fetch_new:
|
||||
data['fetch_new'] = False
|
||||
|
||||
if result != None:
|
||||
data['submit'] = True
|
||||
if 'is_hack' in submission:
|
||||
data['is_hack'] = True
|
||||
data['id'] = submission['hack']['id']
|
||||
if result != False and result['score']:
|
||||
try:
|
||||
print("succ hack!", file=sys.stderr)
|
||||
files = {
|
||||
('hack_input', open('uoj_judger/work/hack_input.txt', 'r')),
|
||||
('std_output', open('uoj_judger/work/std_output.txt', 'r'))
|
||||
}
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
result = False
|
||||
elif 'is_custom_test' in submission:
|
||||
data['is_custom_test'] = True
|
||||
data['id'] = submission['id']
|
||||
else:
|
||||
data['id'] = submission['id']
|
||||
|
||||
if result == False:
|
||||
result = {
|
||||
'score': 0,
|
||||
'error': 'Judgement Failed',
|
||||
'details': 'Unknown Error'
|
||||
}
|
||||
result['status'] = 'Judged'
|
||||
data['result'] = json.dumps(result, ensure_ascii=False)
|
||||
|
||||
while True:
|
||||
try:
|
||||
ret = uoj_interact(data, files)
|
||||
print(ret)
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
else:
|
||||
break
|
||||
time.sleep(2)
|
||||
|
||||
try:
|
||||
submission = json.loads(ret)
|
||||
except Exception as e:
|
||||
submission = None
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
# judge client
|
||||
def judger_loop():
|
||||
ok = False
|
||||
while True:
|
||||
fetch_new = True
|
||||
|
||||
if ok and not (taskQ.empty() and socket_server_thread.isAlive()):
|
||||
fetch_new = False
|
||||
|
||||
if not ok:
|
||||
while True:
|
||||
if not taskQ.empty():
|
||||
handle_task()
|
||||
if not socket_server_thread.isAlive():
|
||||
raise Exception('socket server exited unexpectedly')
|
||||
|
||||
if send_and_fetch():
|
||||
break
|
||||
|
||||
print('['+time.asctime()+']', 'Nothing to judge...')
|
||||
time.sleep(2)
|
||||
|
||||
ok = True
|
||||
print_judge_client_status()
|
||||
print('judging', file=sys.stderr)
|
||||
|
||||
try:
|
||||
res = judge()
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
res = False
|
||||
|
||||
ok = send_and_fetch(result=res,fetch_new=fetch_new)
|
||||
|
||||
# main function
|
||||
def main():
|
||||
init()
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
start_judger_server()
|
||||
if len(sys.argv) == 2:
|
||||
if sys.argv[1] == 'start':
|
||||
pid = os.fork()
|
||||
if pid == -1:
|
||||
raise Exception('fork failed')
|
||||
elif pid > 0:
|
||||
return
|
||||
else:
|
||||
freopen(sys.stdout, open(os.devnull, 'wb'))
|
||||
freopen(sys.stderr, open('log/judge.log', 'ab', buffering=0))
|
||||
start_judger_server()
|
||||
elif sys.argv[1] == 'update':
|
||||
try:
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
||||
s.connect(('127.0.0.1', jconf['socket_port']))
|
||||
s.sendall(json.dumps({
|
||||
'password': jconf['socket_password'],
|
||||
'cmd': 'update'
|
||||
}).encode())
|
||||
return
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
raise Exception('update failed')
|
||||
elif sys.argv[1] == 'stop':
|
||||
try:
|
||||
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
|
||||
s.connect(('127.0.0.1', jconf['socket_port']))
|
||||
s.sendall(json.dumps({
|
||||
'password': jconf['socket_password'],
|
||||
'cmd': 'stop'
|
||||
}).encode())
|
||||
if s.recv(10).decode() != 'ok':
|
||||
raise Exception('stop failed')
|
||||
return
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
raise Exception('stop failed')
|
||||
raise Exception('invalid argument')
|
||||
|
||||
try:
|
||||
main()
|
||||
except Exception:
|
||||
print_judge_client_status()
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
@ -0,0 +1,49 @@
|
||||
INCLUDE_PATH = include
|
||||
CXXFLAGS = -I./include -O2
|
||||
|
||||
EXE_CHECKER = \
|
||||
builtin/checker/bcmp \
|
||||
builtin/checker/acmp \
|
||||
builtin/checker/caseicmp \
|
||||
builtin/checker/casencmp \
|
||||
builtin/checker/casewcmp \
|
||||
builtin/checker/dcmp \
|
||||
builtin/checker/fcmp \
|
||||
builtin/checker/hcmp \
|
||||
builtin/checker/icmp \
|
||||
builtin/checker/lcmp \
|
||||
builtin/checker/ncmp \
|
||||
builtin/checker/rcmp \
|
||||
builtin/checker/rcmp4 \
|
||||
builtin/checker/rcmp6 \
|
||||
builtin/checker/rcmp9 \
|
||||
builtin/checker/rncmp \
|
||||
builtin/checker/uncmp \
|
||||
builtin/checker/wcmp \
|
||||
builtin/checker/yesno
|
||||
|
||||
EXE = main_judger \
|
||||
run/formatter \
|
||||
run/run_program \
|
||||
run/run_interaction \
|
||||
builtin/judger/judger
|
||||
|
||||
all: $(EXE) $(EXE_CHECKER)
|
||||
runner: $(EXE)
|
||||
checker: $(EXE_CHECKER)
|
||||
|
||||
% : %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(EXTRA_CXXFLAGS) $< -o $@
|
||||
|
||||
run/run_program: include/uoj_env.h run/run_program_conf.h
|
||||
run/formatter : include/testlib.h
|
||||
run/run_interaction: run/run_interaction.cpp include/uoj_env.h
|
||||
$(CXX) $(CXXFLAGS) --std=c++11 -pthread $< -o $@
|
||||
|
||||
builtin/judger/judger: include
|
||||
main_judger: include
|
||||
|
||||
$(EXE_CHECKER): include/testlib.h
|
||||
|
||||
clean:
|
||||
rm -f $(EXE) $(EXE_CHECKER)
|
@ -0,0 +1,19 @@
|
||||
#include "testlib.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
const double EPS = 1.5E-6;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two doubles, maximal absolute error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
double ja = ans.readDouble();
|
||||
double pa = ouf.readDouble();
|
||||
|
||||
if (fabs(ja - pa) > EPS + 1E-15)
|
||||
quitf(_wa, "expected %.10lf, found %.10lf", ja, pa);
|
||||
|
||||
quitf(_ok, "answer is %.10lf", ja);
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
#include <cstdio>
|
||||
|
||||
inline const char *englishEnding(int x)
|
||||
{
|
||||
x %= 100;
|
||||
if (x / 10 == 1)
|
||||
return "th";
|
||||
if (x % 10 == 1)
|
||||
return "st";
|
||||
if (x % 10 == 2)
|
||||
return "nd";
|
||||
if (x % 10 == 3)
|
||||
return "rd";
|
||||
return "th";
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
if (argc != 4)
|
||||
return 1;
|
||||
FILE *fout = fopen(argv[2], "r");
|
||||
FILE *fans = fopen(argv[3], "r");
|
||||
|
||||
if (fout == NULL || fans == NULL)
|
||||
return 1;
|
||||
|
||||
int n = 0;
|
||||
while (true)
|
||||
{
|
||||
n++;
|
||||
int c, d;
|
||||
c = fgetc(fout);
|
||||
d = fgetc(fans);
|
||||
if (c == EOF && d == EOF)
|
||||
break;
|
||||
if (c != d)
|
||||
{
|
||||
if (c == EOF)
|
||||
fprintf(stderr, "wrong answer %d%s byte differ - expected EOF found '%c'\n", n, englishEnding(n), (char)d);
|
||||
else if (d == EOF)
|
||||
fprintf(stderr, "wrong answer %d%s byte differ - expected '%c' found EOF\n", n, englishEnding(n), (char)c);
|
||||
else
|
||||
fprintf(stderr, "wrong answer %d%s byte differ - expected '%c' found '%c'\n", n, englishEnding(n), (char)c, (char)d);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
fprintf(stderr, "ok empty file\n");
|
||||
else if (n == 1)
|
||||
fprintf(stderr, "ok single byte\n");
|
||||
else
|
||||
fprintf(stderr, "ok %d byte\n", n);
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Checker to compare output and answer in the form:
|
||||
*
|
||||
* Case 1: <number>
|
||||
* Case 2: <number>
|
||||
* ...
|
||||
* Case n: <number>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define forn(i, n) for (int i = 0; i < int(n); i++)
|
||||
|
||||
vector<long long> readStream(InStream& in, TResult pe)
|
||||
{
|
||||
vector<long long> result;
|
||||
|
||||
for (int testCase = 1; !in.seekEof(); testCase++)
|
||||
{
|
||||
string caseStr = in.readToken();
|
||||
if (caseStr != "Case")
|
||||
quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
|
||||
|
||||
string numExpStr;
|
||||
stringstream ss;
|
||||
ss << testCase;
|
||||
ss >> numExpStr;
|
||||
numExpStr += ":";
|
||||
string numStr = in.readToken();
|
||||
if (numExpStr != numStr)
|
||||
quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
|
||||
|
||||
result.push_back(in.readLong());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
setName("Single int64 checker with testcase-support");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
vector<long long> ja = readStream(ans, _fail);
|
||||
vector<long long> pa = readStream(ouf, _pe);
|
||||
|
||||
forn(i, min(ja.size(), pa.size()))
|
||||
if (ja[i] != pa[i])
|
||||
quitf(_wa, "Expected %s found %s [test case %d]", vtos(ja[i]).c_str(), vtos(pa[i]).c_str(), i + 1);
|
||||
|
||||
if (ja.size() != pa.size())
|
||||
quitf(_pe, "Expected %d test case(s) but found %d", (int)ja.size(), (int)pa.size());
|
||||
|
||||
string message = format("%d case(s):", (int)ja.size());
|
||||
if (ja.size() <= 5)
|
||||
{
|
||||
forn(i, ja.size())
|
||||
message += " " + vtos(ja[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
forn(i, 3)
|
||||
message += " " + vtos(ja[i]);
|
||||
message += " ...";
|
||||
forn(i, 2)
|
||||
message += " " + vtos(ja[ja.size() - 2 + i]);
|
||||
}
|
||||
|
||||
quitf(_ok, "%s", message.c_str());
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Checker to compare output and answer in the form:
|
||||
*
|
||||
* Case 1: <number> <number> <number> ... <number>
|
||||
* Case 2: <number> <number> <number> ... <number>
|
||||
* ...
|
||||
* Case n: <number> <number> <number> ... <number>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define forn(i, n) for (int i = 0; i < int(n); i++)
|
||||
|
||||
string token;
|
||||
|
||||
vector<long long> readStreamCase(InStream& in, TResult pe, int testCase, bool& prereadCase)
|
||||
{
|
||||
if (!prereadCase)
|
||||
{
|
||||
string caseStr = in.readToken();
|
||||
if (caseStr != "Case")
|
||||
quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
|
||||
}
|
||||
|
||||
string numExpStr;
|
||||
stringstream ss;
|
||||
ss << testCase;
|
||||
ss >> numExpStr;
|
||||
numExpStr += ":";
|
||||
string numStr = in.readToken();
|
||||
if (numExpStr != numStr)
|
||||
quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
|
||||
|
||||
vector<long long> result;
|
||||
while (!in.seekEof())
|
||||
{
|
||||
in.readTokenTo(token);
|
||||
if (token == "Case")
|
||||
{
|
||||
prereadCase = true;
|
||||
break;
|
||||
}
|
||||
|
||||
result.push_back(stringToLongLong(in, token.c_str()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string longLongsToString(const vector<long long>& a)
|
||||
{
|
||||
if (a.empty())
|
||||
return "\"\" [size=0]";
|
||||
|
||||
string elems;
|
||||
if (a.size() <= 5)
|
||||
{
|
||||
forn(i, a.size())
|
||||
elems += vtos(a[i]) + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
forn(i, 3)
|
||||
elems += vtos(a[i]) + " ";
|
||||
elems += "... ";
|
||||
forn(i, 2)
|
||||
elems += vtos(a[a.size() - 2 + i]) + " ";
|
||||
}
|
||||
|
||||
return format("\"%s\" [size=%d]", trim(elems).c_str(), (int)a.size());
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
setName("Many int64s checker with testcase-support");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int testCase = 0;
|
||||
|
||||
bool ansPrereadCase = false;
|
||||
bool oufPrereadCase = false;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
testCase++;
|
||||
|
||||
vector<long long> ja = readStreamCase(ans, _fail, testCase, ansPrereadCase);
|
||||
vector<long long> pa = readStreamCase(ouf, _pe, testCase, oufPrereadCase);
|
||||
|
||||
if (ja != pa)
|
||||
{
|
||||
string js = longLongsToString(ja);
|
||||
string ps = longLongsToString(pa);
|
||||
quitf(_wa, "Sequences differ: jury has %s, but participant has %s [test case %d]", js.c_str(), ps.c_str(), testCase);
|
||||
}
|
||||
}
|
||||
|
||||
quitf(_ok, "%d test cases(s)", testCase);
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Checker to compare output and answer in the form:
|
||||
*
|
||||
* Case 1: <token> <token> ... <token>
|
||||
* Case 2: <token> <token> ... <token>
|
||||
* ...
|
||||
* Case n: <token> <token> ... <token>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <climits>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <bitset>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define forn(i, n) for (int i = 0; i < int(n); i++)
|
||||
|
||||
string token;
|
||||
|
||||
vector<string> readStreamCase(InStream& in, TResult pe, int testCase, bool& prereadCase)
|
||||
{
|
||||
if (!prereadCase)
|
||||
{
|
||||
string caseStr = in.readToken();
|
||||
if (caseStr != "Case")
|
||||
quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
|
||||
}
|
||||
|
||||
string numExpStr;
|
||||
stringstream ss;
|
||||
ss << testCase;
|
||||
ss >> numExpStr;
|
||||
numExpStr += ":";
|
||||
string numStr = in.readToken();
|
||||
if (numExpStr != numStr)
|
||||
quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
|
||||
|
||||
vector<string> result;
|
||||
while (!in.seekEof())
|
||||
{
|
||||
in.readTokenTo(token);
|
||||
if (token == "Case")
|
||||
{
|
||||
prereadCase = true;
|
||||
break;
|
||||
}
|
||||
|
||||
result.push_back(token);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string stringsToString(const vector<string>& a)
|
||||
{
|
||||
if (a.empty())
|
||||
return "\"\" [size=0]";
|
||||
|
||||
string elems;
|
||||
forn(i, a.size())
|
||||
elems += a[i] + " ";
|
||||
|
||||
return format("\"%s\" [size=%d]", compress(trim(elems)).c_str(), (int)a.size());
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
setName("Tokens checker with testcase-support");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int testCase = 0;
|
||||
|
||||
bool ansPrereadCase = false;
|
||||
bool oufPrereadCase = false;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
testCase++;
|
||||
|
||||
vector<string> ja = readStreamCase(ans, _fail, testCase, ansPrereadCase);
|
||||
vector<string> pa = readStreamCase(ouf, _pe, testCase, oufPrereadCase);
|
||||
|
||||
if (ja != pa)
|
||||
{
|
||||
string js = stringsToString(ja);
|
||||
string ps = stringsToString(pa);
|
||||
quitf(_wa, "Sequences differ: jury has %s, but participant has %s [test case %d]", js.c_str(), ps.c_str(), testCase);
|
||||
}
|
||||
}
|
||||
|
||||
quitf(_ok, "%d test cases(s)", testCase);
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
#include "testlib.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
const double EPS = 1E-6;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two doubles, maximal absolute or relative error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
double ja = ans.readDouble();
|
||||
double pa = ouf.readDouble();
|
||||
|
||||
if (!doubleCompare(ja, pa, EPS))
|
||||
quitf(_wa, "expected %.10lf, found %.10lf", ja, pa);
|
||||
|
||||
quitf(_ok, "answer is %.10lf", ja);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
#include "testlib.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare files as sequence of lines");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
std::string strAnswer;
|
||||
|
||||
int n = 0;
|
||||
while (!ans.eof())
|
||||
{
|
||||
std::string j = ans.readString();
|
||||
|
||||
if (j == "" && ans.eof())
|
||||
break;
|
||||
|
||||
strAnswer = j;
|
||||
std::string p = ouf.readString();
|
||||
|
||||
n++;
|
||||
|
||||
if (j != p)
|
||||
quitf(_wa, "%d%s lines differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "single line: '%s'", compress(strAnswer).c_str());
|
||||
|
||||
quitf(_ok, "%d lines", n);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#include "testlib.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
pattern pnum("0|-?[1-9][0-9]*");
|
||||
|
||||
bool isNumeric(const string& p)
|
||||
{
|
||||
return pnum.matches(p);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two signed huge integers");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
string ja = ans.readWord();
|
||||
string pa = ouf.readWord();
|
||||
|
||||
if (!isNumeric(ja))
|
||||
quitf(_fail, "%s is not a valid integer", compress(ja).c_str());
|
||||
|
||||
if (!ans.seekEof())
|
||||
quitf(_fail, "expected exactly one token in the answer file");
|
||||
|
||||
if (!isNumeric(pa))
|
||||
quitf(_pe, "%s is not a valid integer", compress(pa).c_str());
|
||||
|
||||
if (ja != pa)
|
||||
quitf(_wa, "expected '%s', found '%s'", compress(ja).c_str(), compress(pa).c_str());
|
||||
|
||||
quitf(_ok, "answer is '%s'", compress(ja).c_str());
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#include "testlib.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two signed int%d's", (int)(8 * sizeof(int)));
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int ja = ans.readInt();
|
||||
int pa = ouf.readInt();
|
||||
|
||||
if (ja != pa)
|
||||
quitf(_wa, "expected %d, found %d", ja, pa);
|
||||
|
||||
quitf(_ok, "answer is %d", ja);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
#include "testlib.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool compareWords(string a, string b)
|
||||
{
|
||||
vector<string> va, vb;
|
||||
stringstream sa;
|
||||
|
||||
sa << a;
|
||||
string cur;
|
||||
while (sa >> cur)
|
||||
va.push_back(cur);
|
||||
|
||||
stringstream sb;
|
||||
sb << b;
|
||||
while (sb >> cur)
|
||||
vb.push_back(cur);
|
||||
|
||||
return (va == vb);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare files as sequence of tokens in lines");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
std::string strAnswer;
|
||||
|
||||
int n = 0;
|
||||
while (!ans.eof())
|
||||
{
|
||||
std::string j = ans.readString();
|
||||
|
||||
if (j == "" && ans.eof())
|
||||
break;
|
||||
|
||||
std::string p = ouf.readString();
|
||||
strAnswer = p;
|
||||
|
||||
n++;
|
||||
|
||||
if (!compareWords(j, p))
|
||||
quitf(_wa, "%d%s lines differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "single line: '%s'", compress(strAnswer).c_str());
|
||||
|
||||
quitf(_ok, "%d lines", n);
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
#include "testlib.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare ordered sequences of signed int%d numbers", (int)(8 * sizeof(long long)));
|
||||
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
string firstElems;
|
||||
|
||||
while (!ans.seekEof() && !ouf.seekEof())
|
||||
{
|
||||
n++;
|
||||
long long j = ans.readLong();
|
||||
long long p = ouf.readLong();
|
||||
if (j != p)
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), vtos(j).c_str(), vtos(p).c_str());
|
||||
else
|
||||
if (n <= 5)
|
||||
{
|
||||
if (firstElems.length() > 0)
|
||||
firstElems += " ";
|
||||
firstElems += vtos(j);
|
||||
}
|
||||
}
|
||||
|
||||
int extraInAnsCount = 0;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
ans.readLong();
|
||||
extraInAnsCount++;
|
||||
}
|
||||
|
||||
int extraInOufCount = 0;
|
||||
|
||||
while (!ouf.seekEof())
|
||||
{
|
||||
ouf.readLong();
|
||||
extraInOufCount++;
|
||||
}
|
||||
|
||||
if (extraInAnsCount > 0)
|
||||
quitf(_wa, "Answer contains longer sequence [length = %d], but output contains %d elements", n + extraInAnsCount, n);
|
||||
|
||||
if (extraInOufCount > 0)
|
||||
quitf(_wa, "Output contains longer sequence [length = %d], but answer contains %d elements", n + extraInOufCount, n);
|
||||
|
||||
if (n <= 5)
|
||||
quitf(_ok, "%d number(s): \"%s\"", n, compress(firstElems).c_str());
|
||||
else
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
#include "testlib.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
const double EPS = 1.5E-6;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two doubles, maximal absolute error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
double ja = ans.readDouble();
|
||||
double pa = ouf.readDouble();
|
||||
|
||||
if (fabs(ja - pa) > EPS + 1E-15)
|
||||
quitf(_wa, "expected %.10lf, found %.10lf", ja, pa);
|
||||
|
||||
quitf(_ok, "answer is %.10lf", ja);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#include "testlib.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1E-4;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two sequences of doubles, max absolute or relative error = %.5lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
double j, p;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
n++;
|
||||
j = ans.readDouble();
|
||||
p = ouf.readDouble();
|
||||
if (!doubleCompare(j, p, EPS))
|
||||
{
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%.5lf', found: '%.5lf', error = '%.5lf'",
|
||||
n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "found '%.5lf', expected '%.5lf', error '%.5lf'", p, j, doubleDelta(j, p));
|
||||
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#include "testlib.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1E-6;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two sequences of doubles, max absolute or relative error = %.7lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
double j, p;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
n++;
|
||||
j = ans.readDouble();
|
||||
p = ouf.readDouble();
|
||||
if (!doubleCompare(j, p, EPS))
|
||||
{
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%.7lf', found: '%.7lf', error = '%.7lf'",
|
||||
n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "found '%.7lf', expected '%.7lf', error '%.7lf'", p, j, doubleDelta(j, p));
|
||||
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
#include "testlib.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1E-9;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two sequences of doubles, max absolute or relative error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
double j, p;
|
||||
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
n++;
|
||||
j = ans.readDouble();
|
||||
p = ouf.readDouble();
|
||||
if (!doubleCompare(j, p, EPS))
|
||||
{
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%.7lf', found: '%.7lf', error = '%.7lf'",
|
||||
n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
quitf(_ok, "found '%.9lf', expected '%.9lf', error '%.9lf'", p, j, doubleDelta(j, p));
|
||||
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#include "testlib.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const double EPS = 1.5E-5;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare two sequences of doubles, maximal absolute error = %.10lf", EPS);
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
while (!ans.seekEof())
|
||||
{
|
||||
n++;
|
||||
double j = ans.readDouble();
|
||||
double p = ouf.readDouble();
|
||||
if (fabs(j - p) > EPS + 1E-15)
|
||||
quitf(_wa, "%d%s numbers differ - expected: '%.10lf', found: '%.10lf'", n, englishEnding(n).c_str(), j, p);
|
||||
}
|
||||
|
||||
quitf(_ok, "%d numbers", n);
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
#include "testlib.h"
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare unordered sequences of signed int%d numbers", (int)(8 * sizeof(long long)));
|
||||
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
vector<long long> ja, pa;
|
||||
|
||||
while (!ans.seekEof())
|
||||
ja.push_back(ans.readLong());
|
||||
|
||||
while (!ouf.seekEof())
|
||||
pa.push_back(ouf.readLong());
|
||||
|
||||
if (ja.size() != pa.size())
|
||||
quitf(_wa, "Expected %d elements, but %d found", (int)ja.size(), (int)pa.size());
|
||||
|
||||
sort(ja.begin(), ja.end());
|
||||
sort(pa.begin(), pa.end());
|
||||
|
||||
if (ja != pa)
|
||||
quitf(_wa, "Expected sequence and output are different (as unordered sequences) [size=%d]", (int)ja.size());
|
||||
|
||||
string message;
|
||||
|
||||
if (ja.size() != 1)
|
||||
if (ja.empty())
|
||||
message = "empty sequence";
|
||||
else
|
||||
message = vtos(ja.size()) + " numbers (in increasing order):";
|
||||
else
|
||||
message = vtos(ja.size()) + " number:";
|
||||
|
||||
if (ja.size() <= 5)
|
||||
for (int i = 0; i < min(int(ja.size()), 5); i++)
|
||||
message += " " + vtos(ja[i]);
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
message += " " + vtos(ja[i]);
|
||||
message += " ...";
|
||||
for (int i = 0; i < 2; i++)
|
||||
message += " " + vtos(ja[ja.size() - 2 + i]);
|
||||
}
|
||||
|
||||
quitf(_ok, "%s", message.c_str());
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
#include "testlib.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("compare sequences of tokens");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int n = 0;
|
||||
string j, p;
|
||||
|
||||
while (!ans.seekEof() && !ouf.seekEof())
|
||||
{
|
||||
n++;
|
||||
|
||||
ans.readWordTo(j);
|
||||
ouf.readWordTo(p);
|
||||
|
||||
if (j != p)
|
||||
quitf(_wa, "%d%s words differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
|
||||
}
|
||||
|
||||
if (ans.seekEof() && ouf.seekEof())
|
||||
{
|
||||
if (n == 1)
|
||||
quitf(_ok, "\"%s\"", compress(j).c_str());
|
||||
else
|
||||
quitf(_ok, "%d tokens", n);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ans.seekEof())
|
||||
quitf(_wa, "Participant output contains extra tokens");
|
||||
else
|
||||
quitf(_wa, "Unexpected EOF in the participants output");
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#include "testlib.h"
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const string YES = "YES";
|
||||
const string NO = "NO";
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
setName("YES or NO (case insensetive)");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
std::string ja = upperCase(ans.readWord());
|
||||
std::string pa = upperCase(ouf.readWord());
|
||||
|
||||
if (ja != YES && ja != NO)
|
||||
quitf(_fail, "%s or %s expected in answer, but %s found", YES.c_str(), NO.c_str(), compress(ja).c_str());
|
||||
|
||||
if (pa != YES && pa != NO)
|
||||
quitf(_pe, "%s or %s expected, but %s found", YES.c_str(), NO.c_str(), compress(pa).c_str());
|
||||
|
||||
if (ja != pa)
|
||||
quitf(_wa, "expected %s, found %s", compress(ja).c_str(), compress(pa).c_str());
|
||||
|
||||
quitf(_ok, "answer is %s", ja.c_str());
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
#include "uoj_judger.h"
|
||||
|
||||
struct SubtaskInfo {
|
||||
bool passed;
|
||||
int score;
|
||||
|
||||
SubtaskInfo() {
|
||||
}
|
||||
SubtaskInfo(const bool &_p, const int &_s)
|
||||
: passed(_p), score(_s){}
|
||||
};
|
||||
|
||||
void ordinary_test() {
|
||||
int n = conf_int("n_tests", 10);
|
||||
int m = conf_int("n_ex_tests", 0);
|
||||
int nT = conf_int("n_subtasks", 0);
|
||||
|
||||
if (!conf_is("submit_answer", "on")) {
|
||||
report_judge_status_f("Compiling");
|
||||
RunCompilerResult c_ret = !conf_is("with_implementer", "on") ? compile("answer") : compile_with_implementer("answer");
|
||||
if (!c_ret.succeeded) {
|
||||
end_judge_compile_error(c_ret);
|
||||
}
|
||||
}
|
||||
|
||||
bool passed = true;
|
||||
if (nT == 0) {
|
||||
for (int i = 1; i <= n; i++) {
|
||||
report_judge_status_f("Judging Test #%d", i);
|
||||
PointInfo po = test_point("answer", i);
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
}
|
||||
po.scr = scale_score(po.scr, conf_int("point_score", i, 100 / n));
|
||||
add_point_info(po);
|
||||
}
|
||||
} else if (nT == 1) {
|
||||
for (int i = 1; i <= n; i++) {
|
||||
report_judge_status_f("Judging Test #%d", i);
|
||||
PointInfo po = test_point("answer", i);
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
po.scr = i == 1 ? 0 : -100;
|
||||
add_point_info(po);
|
||||
break;
|
||||
} else {
|
||||
po.scr = i == 1 ? 100 : 0;
|
||||
add_point_info(po);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
map<int, SubtaskInfo> subtasks;
|
||||
map<int,int> minScore;
|
||||
for (int t = 1; t <= nT; t++) {
|
||||
string subtaskType = conf_str("subtask_type", t, "packed");
|
||||
int startI = conf_int("subtask_end", t - 1, 0) + 1;
|
||||
int endI = conf_int("subtask_end", t, 0);
|
||||
|
||||
vector<PointInfo> points;
|
||||
minScore[t] = 100;
|
||||
|
||||
vector<int> dependences;
|
||||
if (conf_str("subtask_dependence", t, "none") == "many") {
|
||||
string cur = "subtask_dependence_" + vtos(t);
|
||||
int p = 1;
|
||||
while (conf_int(cur, p, 0) != 0) {
|
||||
dependences.push_back(conf_int(cur, p, 0));
|
||||
p++;
|
||||
}
|
||||
} else if (conf_int("subtask_dependence", t, 0) != 0) {
|
||||
dependences.push_back(conf_int("subtask_dependence", t, 0));
|
||||
}
|
||||
bool skipped = false;
|
||||
for (vector<int>::iterator it = dependences.begin(); it != dependences.end(); it++) {
|
||||
if (subtaskType == "packed") {
|
||||
if (!subtasks[*it].passed) {
|
||||
skipped = true;
|
||||
break;
|
||||
}
|
||||
} else if (subtaskType == "min") {
|
||||
minScore[t] = min(minScore[t], minScore[*it]);
|
||||
}
|
||||
}
|
||||
if (skipped) {
|
||||
add_subtask_info(t, 0, "Skipped", points);
|
||||
continue;
|
||||
}
|
||||
|
||||
int tfull = conf_int("subtask_score", t, 100 / nT);
|
||||
int tscore = scale_score(minScore[t], tfull);
|
||||
string info = "Accepted";
|
||||
for (int i = startI; i <= endI; i++) {
|
||||
report_judge_status_f("Judging Test #%d of Subtask #%d", i, t);
|
||||
PointInfo po = test_point("answer", i);
|
||||
if (subtaskType == "packed") {
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
po.scr = i == startI ? 0 : -tfull;
|
||||
tscore = 0;
|
||||
points.push_back(po);
|
||||
info = po.info;
|
||||
break;
|
||||
} else {
|
||||
po.scr = i == startI ? tfull : 0;
|
||||
tscore = tfull;
|
||||
points.push_back(po);
|
||||
}
|
||||
} else if (subtaskType == "min") {
|
||||
minScore[t] = min(minScore[t], po.scr);
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
}
|
||||
po.scr = scale_score(po.scr, tfull);
|
||||
if (po.scr <= tscore) {
|
||||
tscore = po.scr;
|
||||
points.push_back(po);
|
||||
info = po.info;
|
||||
} else {
|
||||
points.push_back(po);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subtasks[t] = SubtaskInfo(info == "Accepted", tscore);
|
||||
|
||||
add_subtask_info(t, tscore, info, points);
|
||||
}
|
||||
}
|
||||
if (conf_is("submit_answer", "on") || !passed) {
|
||||
end_judge_ok();
|
||||
}
|
||||
|
||||
tot_score = 100;
|
||||
for (int i = 1; i <= m; i++) {
|
||||
report_judge_status_f("Judging Extra Test #%d", i);
|
||||
PointInfo po = test_point("answer", -i);
|
||||
if (po.scr != 100) {
|
||||
po.num = -1;
|
||||
po.info = "Extra Test Failed : " + po.info + " on " + vtos(i);
|
||||
po.scr = -3;
|
||||
add_point_info(po);
|
||||
end_judge_ok();
|
||||
}
|
||||
}
|
||||
if (m != 0) {
|
||||
PointInfo po(-1, 0, -1, -1, "Extra Test Passed", "", "", "");
|
||||
add_point_info(po);
|
||||
}
|
||||
end_judge_ok();
|
||||
}
|
||||
|
||||
void hack_test() {
|
||||
if (conf_is("submit_answer", "on")) {
|
||||
end_judge_judgement_failed("Hack is not supported in this problem.");
|
||||
} else {
|
||||
RunCompilerResult c_ret = !conf_is("with_implementer", "on") ? compile("answer") : compile_with_implementer("answer");
|
||||
if (!c_ret.succeeded) {
|
||||
end_judge_compile_error(c_ret);
|
||||
}
|
||||
TestPointConfig tpc;
|
||||
tpc.input_file_name = work_path + "/hack_input.txt";
|
||||
tpc.output_file_name = work_path + "/pro_output.txt";
|
||||
tpc.answer_file_name = work_path + "/std_output.txt";
|
||||
|
||||
PointInfo po = test_hack_point("answer", tpc);
|
||||
add_point_info(po);
|
||||
end_judge_ok();
|
||||
}
|
||||
}
|
||||
|
||||
void sample_test() {
|
||||
if (conf_is("submit_answer", "on")) {
|
||||
int n = conf_int("n_tests", 10);
|
||||
for (int i = 1; i <= n; i++) {
|
||||
report_judge_status_f("Judging Test #%d", i);
|
||||
if (conf_is("check_existence_only_in_sample_test", "on")) {
|
||||
TestPointConfig tpc = TestPointConfig();
|
||||
tpc.auto_complete(i);
|
||||
|
||||
string usrout = file_preview(tpc.output_file_name);
|
||||
if (usrout == "") {
|
||||
add_point_info(PointInfo(i, 0, -1, -1,
|
||||
"default",
|
||||
file_preview(tpc.input_file_name), usrout,
|
||||
"wrong answer empty file\n"));
|
||||
} else {
|
||||
PointInfo po = PointInfo(i, 100, -1, -1,
|
||||
"default",
|
||||
file_preview(tpc.input_file_name), usrout,
|
||||
"ok nonempty file\n");
|
||||
po.scr = scale_score(po.scr, conf_int("point_score", i, 100 / n));
|
||||
add_point_info(po);
|
||||
}
|
||||
} else {
|
||||
PointInfo po = test_point("answer", i);
|
||||
if (po.scr != 0) {
|
||||
po.info = "Accepted";
|
||||
po.scr = 100;
|
||||
}
|
||||
po.scr = scale_score(po.scr, conf_int("point_score", i, 100 / n));
|
||||
po.res = "no comment";
|
||||
add_point_info(po);
|
||||
}
|
||||
}
|
||||
end_judge_ok();
|
||||
} else {
|
||||
report_judge_status_f("Compiling");
|
||||
RunCompilerResult c_ret = !conf_is("with_implementer", "on") ? compile("answer") : compile_with_implementer("answer");
|
||||
if (!c_ret.succeeded) {
|
||||
end_judge_compile_error(c_ret);
|
||||
}
|
||||
|
||||
int n = conf_int("n_sample_tests", 0);
|
||||
bool passed = true;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
report_judge_status_f("Judging Sample Test #%d", i);
|
||||
PointInfo po = test_point("answer", -i);
|
||||
po.num = i;
|
||||
if (po.scr != 100) {
|
||||
passed = false;
|
||||
}
|
||||
po.scr = scale_score(po.scr, 100 / n);
|
||||
add_point_info(po);
|
||||
}
|
||||
if (passed) {
|
||||
tot_score = 100;
|
||||
}
|
||||
end_judge_ok();
|
||||
}
|
||||
}
|
||||
|
||||
void custom_test() {
|
||||
if (conf_is("submit_answer", "on")) {
|
||||
end_judge_judgement_failed("Custom test is not supported in this problem.");
|
||||
} else {
|
||||
report_judge_status_f("Compiling");
|
||||
RunCompilerResult c_ret = !conf_is("with_implementer", "on") ? compile("answer") : compile_with_implementer("answer");
|
||||
if (!c_ret.succeeded) {
|
||||
end_judge_compile_error(c_ret);
|
||||
}
|
||||
|
||||
report_judge_status_f("Judging");
|
||||
add_custom_test_info(ordinary_custom_test("answer"));
|
||||
|
||||
end_judge_ok();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
judger_init(argc, argv);
|
||||
|
||||
if (conf_is("test_new_hack_only", "on")) {
|
||||
hack_test();
|
||||
} else if (conf_is("test_sample_only", "on")) {
|
||||
sample_test();
|
||||
} else if (conf_is("custom_test", "on")) {
|
||||
custom_test();
|
||||
} else {
|
||||
ordinary_test();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@
|
||||
#include "uoj_work_path.h"
|
||||
|
||||
#define UOJ_DATA_PATH UOJ_WORK_PATH "/data"
|
||||
#define UOJ_RESULT_PATH UOJ_WORK_PATH "/result"
|
||||
|
||||
#define RS_SPJ_BASE 1000
|
||||
#define failed_spj RS_SPJ_BASE
|
||||
#define successed_hack 100000
|
||||
#define RS_SPJ RS_SPJ_BASE
|
||||
#define RS_HACK successed_hack
|
||||
#define RS_AC 0
|
||||
#define RS_WA 1
|
||||
#define RS_RE 2
|
||||
#define RS_MLE 3
|
||||
#define RS_TLE 4
|
||||
#define RS_OLE 5
|
||||
#define RS_DGS 6
|
||||
#define RS_JGF 7
|
||||
#define RS_SPJ_AC (RS_SPJ + RS_AC)
|
||||
#define RS_SPJ_RE (RS_SPJ_BASE + RS_RE)
|
||||
#define RS_SPJ_MLE (RS_SPJ_BASE + RS_MLE)
|
||||
#define RS_SPJ_TLE (RS_SPJ_BASE + RS_TLE)
|
||||
#define RS_SPJ_OLE (RS_SPJ_BASE + RS_OLE)
|
||||
#define RS_SPJ_DGS (RS_SPJ_BASE + RS_DGS)
|
||||
#define RS_SPJ_JGF (RS_SPJ_BASE + RS_JGF)
|
||||
#define RS_HK_RE (successed_hack + RS_RE)
|
||||
#define RS_HK_MLE (successed_hack + RS_MLE)
|
||||
#define RS_HK_TLE (successed_hack + RS_TLE)
|
||||
#define RS_HK_OLE (successed_hack + RS_OLE)
|
||||
#define RS_HK_DGS (successed_hack + RS_DGS)
|
||||
#define RS_HK_JGF (successed_hack + RS_JGF)
|
||||
#define RS_HK_SPJ_RE (successed_hack + RS_SPJ_RE)
|
||||
#define RS_HK_SPJ_MLE (successed_hack + RS_SPJ_MLE)
|
||||
#define RS_HK_SPJ_TLE (successed_hack + RS_SPJ_TLE)
|
||||
#define RS_HK_SPJ_OLE (successed_hack + RS_SPJ_OLE)
|
||||
#define RS_HK_SPJ_DGS (successed_hack + RS_SPJ_DGS)
|
||||
#define RS_HK_SPJ_JGF (successed_hack + RS_SPJ_JGF)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
#include "uoj_judger.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
main_judger_init(argc, argv);
|
||||
RunResult res = run_program(
|
||||
(result_path + "/run_judger_result.txt").c_str(),
|
||||
"/dev/null",
|
||||
"/dev/null",
|
||||
"stderr",
|
||||
conf_run_limit("judger", 0, RL_JUDGER_DEFAULT),
|
||||
"--unsafe",
|
||||
conf_str("judger").c_str(),
|
||||
main_path.c_str(),
|
||||
work_path.c_str(),
|
||||
result_path.c_str(),
|
||||
data_path.c_str(),
|
||||
NULL);
|
||||
if (res.type != RS_AC) {
|
||||
end_judge_judgement_failed("Judgement Failed : Judger " + info_str(res));
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
#include<cstdio>
|
||||
#include<cstdlib>
|
||||
#include<cstring>
|
||||
#include<cmath>
|
||||
#include<algorithm>
|
||||
using namespace std;
|
||||
typedef long long LL;
|
||||
int main()
|
||||
{
|
||||
//freopen("1.in","r",stdin);
|
||||
char c,last;
|
||||
int nSpace=0,nR=0,first;
|
||||
while(1)
|
||||
{
|
||||
last=c,c=getchar();
|
||||
if(c==EOF)
|
||||
{
|
||||
if(last!='\n')
|
||||
putchar('\n');
|
||||
break;
|
||||
}
|
||||
else if(c!='\r'&&c!=' ')
|
||||
{
|
||||
if(c!='\n'&&first==0)
|
||||
{
|
||||
for(int j=1;j<=nSpace;++j)
|
||||
putchar(' ');
|
||||
for(int j=1;j<=nR;++j)
|
||||
putchar('\r');
|
||||
}
|
||||
else if(c!='\n')
|
||||
{
|
||||
for(int j=1;j<=nR;++j)
|
||||
putchar('\r');
|
||||
for(int j=1;j<=nSpace;++j)
|
||||
putchar(' ');
|
||||
}
|
||||
nSpace=nR=0;
|
||||
putchar(c);
|
||||
}
|
||||
else if(c==' ')
|
||||
{
|
||||
++nSpace;
|
||||
if(nR==0)
|
||||
first=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++nR;
|
||||
if(nSpace==0)
|
||||
first=1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/user.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <argp.h>
|
||||
#include "uoj_env.h"
|
||||
using namespace std;
|
||||
|
||||
struct RunResult {
|
||||
int result;
|
||||
int ust;
|
||||
int usm;
|
||||
int exit_code;
|
||||
|
||||
RunResult(int _result, int _ust = -1, int _usm = -1, int _exit_code = -1)
|
||||
: result(_result), ust(_ust), usm(_usm), exit_code(_exit_code) {
|
||||
if (result != RS_AC) {
|
||||
ust = -1, usm = -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct PipeConfig {
|
||||
int from, to;
|
||||
int from_fd, to_fd;
|
||||
|
||||
string saving_file_name; // empty for none
|
||||
|
||||
PipeConfig() {
|
||||
}
|
||||
PipeConfig(string str) {
|
||||
if (sscanf(str.c_str(), "%d:%d-%d:%d", &from, &from_fd, &to, &to_fd) != 4) {
|
||||
throw invalid_argument("bad init str for PipeConfig");
|
||||
}
|
||||
from -= 1;
|
||||
to -= 1;
|
||||
}
|
||||
};
|
||||
|
||||
struct RunInteractionConfig {
|
||||
vector<string> cmds;
|
||||
vector<PipeConfig> pipes;
|
||||
};
|
||||
|
||||
struct RunCmdData {
|
||||
string cmd;
|
||||
pid_t pid;
|
||||
|
||||
vector<int> ipipes, opipes;
|
||||
};
|
||||
struct PipeData {
|
||||
PipeConfig config;
|
||||
int ipipefd[2], opipefd[2];
|
||||
thread io_thread;
|
||||
exception_ptr eptr;
|
||||
};
|
||||
|
||||
class RunInteraction {
|
||||
private:
|
||||
vector<RunCmdData> cmds;
|
||||
vector<PipeData> pipes;
|
||||
|
||||
void prepare_fd() { // me
|
||||
for (int i = 0; i < (int)pipes.size(); i++) {
|
||||
close(pipes[i].ipipefd[1]);
|
||||
close(pipes[i].opipefd[0]);
|
||||
}
|
||||
}
|
||||
void prepare_fd_for_cmd(int id) {
|
||||
freopen("/dev/null", "r", stdin);
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
|
||||
for (int i = 0; i < (int)pipes.size(); i++) {
|
||||
if (pipes[i].config.from == id) {
|
||||
dup2(pipes[i].ipipefd[1], 128 + pipes[i].ipipefd[1]);
|
||||
}
|
||||
if (pipes[i].config.to == id) {
|
||||
dup2(pipes[i].opipefd[0], 128 + pipes[i].opipefd[0]);
|
||||
}
|
||||
close(pipes[i].ipipefd[0]);
|
||||
close(pipes[i].ipipefd[1]);
|
||||
close(pipes[i].opipefd[0]);
|
||||
close(pipes[i].opipefd[1]);
|
||||
}
|
||||
for (int i = 0; i < (int)pipes.size(); i++) {
|
||||
if (pipes[i].config.from == id) {
|
||||
dup2(128 + pipes[i].ipipefd[1], pipes[i].config.from_fd);
|
||||
close(128 + pipes[i].ipipefd[1]);
|
||||
}
|
||||
if (pipes[i].config.to == id) {
|
||||
dup2(128 + pipes[i].opipefd[0], pipes[i].config.to_fd);
|
||||
close(128 + pipes[i].opipefd[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wait_pipe_io(int pipe_id) {
|
||||
FILE *sf = NULL;
|
||||
if (!pipes[pipe_id].config.saving_file_name.empty())
|
||||
sf = fopen(pipes[pipe_id].config.saving_file_name.c_str(), "w");
|
||||
|
||||
int ifd = pipes[pipe_id].ipipefd[0];
|
||||
int ofd = pipes[pipe_id].opipefd[1];
|
||||
|
||||
FILE *inf = fdopen(ifd, "r");
|
||||
FILE *ouf = fdopen(ofd, "w");
|
||||
|
||||
try {
|
||||
pipes[pipe_id].eptr = nullptr;
|
||||
|
||||
const int L = 4096;
|
||||
char buf[L];
|
||||
|
||||
while (true) {
|
||||
int c = fgetc(inf);
|
||||
if (c == EOF) {
|
||||
if (errno) {
|
||||
throw system_error(errno, system_category());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (fputc(c, ouf) == EOF) {
|
||||
throw system_error(errno, system_category());
|
||||
}
|
||||
fflush(ouf);
|
||||
|
||||
if (fputc(c, sf) == EOF) {
|
||||
throw system_error(errno, system_category());
|
||||
}
|
||||
}
|
||||
} catch (exception &e) {
|
||||
cerr << e.what() << endl;
|
||||
pipes[pipe_id].eptr = current_exception();
|
||||
}
|
||||
|
||||
fclose(sf);
|
||||
fclose(inf);
|
||||
fclose(ouf);
|
||||
}
|
||||
public:
|
||||
RunInteraction(const RunInteractionConfig &config) {
|
||||
cmds.resize(config.cmds.size());
|
||||
for (int i = 0; i < (int)config.cmds.size(); i++) {
|
||||
cmds[i].cmd = config.cmds[i];
|
||||
}
|
||||
|
||||
pipes.resize(config.pipes.size());
|
||||
for (int i = 0; i < (int)config.pipes.size(); i++) {
|
||||
pipes[i].config = config.pipes[i];
|
||||
cmds[pipes[i].config.from].opipes.push_back(i);
|
||||
cmds[pipes[i].config.to].ipipes.push_back(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)pipes.size(); i++) {
|
||||
if (pipe(pipes[i].ipipefd) == -1 || pipe(pipes[i].opipefd) == -1) {
|
||||
throw system_error(errno, system_category());
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < (int)cmds.size(); i++) {
|
||||
cmds[i].pid = fork();
|
||||
if (cmds[i].pid == 0) {
|
||||
prepare_fd_for_cmd(i);
|
||||
system(cmds[i].cmd.c_str());
|
||||
exit(0);
|
||||
} else if (cmds[i].pid == -1) {
|
||||
throw system_error(errno, system_category());
|
||||
}
|
||||
}
|
||||
|
||||
prepare_fd();
|
||||
for (int i = 0; i < (int)pipes.size(); i++) {
|
||||
pipes[i].io_thread = thread(&RunInteraction::wait_pipe_io, this, i);
|
||||
}
|
||||
}
|
||||
|
||||
void join() {
|
||||
int status;
|
||||
while (wait(&status) > 0);
|
||||
|
||||
for (int i = 0; i < (int)pipes.size(); i++) {
|
||||
pipes[i].io_thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
error_t run_interaction_argp_parse_opt (int key, char *arg, struct argp_state *state) {
|
||||
RunInteractionConfig *config = (RunInteractionConfig*)state->input;
|
||||
|
||||
try {
|
||||
switch (key) {
|
||||
case 'p':
|
||||
config->pipes.push_back(PipeConfig(arg));
|
||||
break;
|
||||
case 's':
|
||||
if (config->pipes.empty()) {
|
||||
argp_usage(state);
|
||||
}
|
||||
config->pipes.back().saving_file_name = arg;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
config->cmds.push_back(arg);
|
||||
break;
|
||||
case ARGP_KEY_END:
|
||||
if (state->arg_num == 0) {
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
} catch (exception &e) {
|
||||
argp_usage(state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RunInteractionConfig parse_args(int argc, char **argv) {
|
||||
argp_option run_interaction_argp_options[] = {
|
||||
{"add-pipe" , 'p', "PIPE" , 0, "Add a pipe <from>:<fd>-<to>:<fd> (fd < 128)" , 1},
|
||||
{"save-pipe" , 's', "FILE" , 0, "Set last pipe saving file" , 2},
|
||||
{0}
|
||||
};
|
||||
char run_interaction_argp_args_doc[] = "cmd1 cmd2 ...";
|
||||
char run_interaction_argp_doc[] = "run_interaction: a tool to run multiple commands with interaction";
|
||||
|
||||
argp run_interaction_argp = {
|
||||
run_interaction_argp_options,
|
||||
run_interaction_argp_parse_opt,
|
||||
run_interaction_argp_args_doc,
|
||||
run_interaction_argp_doc
|
||||
};
|
||||
|
||||
RunInteractionConfig config;
|
||||
|
||||
argp_parse(&run_interaction_argp, argc, argv, ARGP_NO_ARGS | ARGP_IN_ORDER, 0, &config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
RunInteraction ri(parse_args(argc, argv));
|
||||
ri.join();
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,581 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/user.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <argp.h>
|
||||
#include "uoj_env.h"
|
||||
using namespace std;
|
||||
|
||||
struct RunResult {
|
||||
int result;
|
||||
int ust;
|
||||
int usm;
|
||||
int exit_code;
|
||||
|
||||
RunResult(int _result, int _ust = -1, int _usm = -1, int _exit_code = -1)
|
||||
: result(_result), ust(_ust), usm(_usm), exit_code(_exit_code) {
|
||||
if (result != RS_AC) {
|
||||
ust = -1, usm = -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct RunProgramConfig
|
||||
{
|
||||
int time_limit;
|
||||
int real_time_limit;
|
||||
int memory_limit;
|
||||
int output_limit;
|
||||
int stack_limit;
|
||||
string input_file_name;
|
||||
string output_file_name;
|
||||
string error_file_name;
|
||||
string result_file_name;
|
||||
string work_path;
|
||||
string type;
|
||||
vector<string> extra_readable_files;
|
||||
vector<string> extra_writable_files;
|
||||
bool allow_proc;
|
||||
bool safe_mode;
|
||||
bool need_show_trace_details;
|
||||
|
||||
string program_name;
|
||||
string program_basename;
|
||||
vector<string> argv;
|
||||
|
||||
};
|
||||
|
||||
int put_result(string result_file_name, RunResult res) {
|
||||
FILE *f;
|
||||
if (result_file_name == "stdout") {
|
||||
f = stdout;
|
||||
} else if (result_file_name == "stderr") {
|
||||
f = stderr;
|
||||
} else {
|
||||
f = fopen(result_file_name.c_str(), "w");
|
||||
}
|
||||
fprintf(f, "%d %d %d %d\n", res.result, res.ust, res.usm, res.exit_code);
|
||||
if (f != stdout && f != stderr) {
|
||||
fclose(f);
|
||||
}
|
||||
if (res.result == RS_JGF) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
char self_path[PATH_MAX + 1] = {};
|
||||
|
||||
#include "run_program_conf.h"
|
||||
|
||||
argp_option run_program_argp_options[] =
|
||||
{
|
||||
{"tl" , 'T', "TIME_LIMIT" , 0, "Set time limit (in second)" , 1},
|
||||
{"rtl" , 'R', "TIME_LIMIT" , 0, "Set real time limit (in second)" , 2},
|
||||
{"ml" , 'M', "MEMORY_LIMIT", 0, "Set memory limit (in mb)" , 3},
|
||||
{"ol" , 'O', "OUTPUT_LIMIT", 0, "Set output limit (in mb)" , 4},
|
||||
{"sl" , 'S', "STACK_LIMIT" , 0, "Set stack limit (in mb)" , 5},
|
||||
{"in" , 'i', "IN" , 0, "Set input file name" , 6},
|
||||
{"out" , 'o', "OUT" , 0, "Set output file name" , 7},
|
||||
{"err" , 'e', "ERR" , 0, "Set error file name" , 8},
|
||||
{"work-path" , 'w', "WORK_PATH" , 0, "Set the work path of the program" , 9},
|
||||
{"type" , 't', "TYPE" , 0, "Set the program type (for some program such as python)", 10},
|
||||
{"res" , 'r', "RESULT_FILE" , 0, "Set the file name for outputing the result ", 10},
|
||||
{"add-readable" , 500, "FILE" , 0, "Add a readable file" , 11},
|
||||
{"add-writable" , 505, "FILE" , 0, "Add a writable file" , 11},
|
||||
{"unsafe" , 501, 0 , 0, "Don't check dangerous syscalls" , 12},
|
||||
{"show-trace-details" , 502, 0 , 0, "Show trace details" , 13},
|
||||
{"allow-proc" , 503, 0 , 0, "Allow fork, exec... etc." , 14},
|
||||
{"add-readable-raw" , 504, "FILE" , 0, "Add a readable (don't transform to its real path)" , 15},
|
||||
{"add-writable-raw" , 506, "FILE" , 0, "Add a writable (don't transform to its real path)" , 15},
|
||||
{0}
|
||||
};
|
||||
error_t run_program_argp_parse_opt (int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
RunProgramConfig *config = (RunProgramConfig*)state->input;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case 'T':
|
||||
config->time_limit = atoi(arg);
|
||||
break;
|
||||
case 'R':
|
||||
config->real_time_limit = atoi(arg);
|
||||
break;
|
||||
case 'M':
|
||||
config->memory_limit = atoi(arg);
|
||||
break;
|
||||
case 'O':
|
||||
config->output_limit = atoi(arg);
|
||||
break;
|
||||
case 'S':
|
||||
config->stack_limit = atoi(arg);
|
||||
break;
|
||||
case 'i':
|
||||
config->input_file_name = arg;
|
||||
break;
|
||||
case 'o':
|
||||
config->output_file_name = arg;
|
||||
break;
|
||||
case 'e':
|
||||
config->error_file_name = arg;
|
||||
break;
|
||||
case 'w':
|
||||
config->work_path = realpath(arg);
|
||||
if (config->work_path.empty()) {
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
config->result_file_name = arg;
|
||||
break;
|
||||
case 't':
|
||||
config->type = arg;
|
||||
break;
|
||||
case 500:
|
||||
config->extra_readable_files.push_back(realpath(arg));
|
||||
break;
|
||||
case 501:
|
||||
config->safe_mode = false;
|
||||
break;
|
||||
case 502:
|
||||
config->need_show_trace_details = true;
|
||||
break;
|
||||
case 503:
|
||||
config->allow_proc = true;
|
||||
break;
|
||||
case 504:
|
||||
config->extra_readable_files.push_back(arg);
|
||||
break;
|
||||
case 505:
|
||||
config->extra_writable_files.push_back(realpath(arg));
|
||||
break;
|
||||
case 506:
|
||||
config->extra_writable_files.push_back(arg);
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
config->argv.push_back(arg);
|
||||
for (int i = state->next; i < state->argc; i++) {
|
||||
config->argv.push_back(state->argv[i]);
|
||||
}
|
||||
state->next = state->argc;
|
||||
break;
|
||||
case ARGP_KEY_END:
|
||||
if (state->arg_num == 0) {
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char run_program_argp_args_doc[] = "program arg1 arg2 ...";
|
||||
char run_program_argp_doc[] = "run_program: a tool to run program safely";
|
||||
|
||||
argp run_program_argp = {
|
||||
run_program_argp_options,
|
||||
run_program_argp_parse_opt,
|
||||
run_program_argp_args_doc,
|
||||
run_program_argp_doc
|
||||
};
|
||||
|
||||
RunProgramConfig run_program_config;
|
||||
|
||||
void parse_args(int argc, char **argv) {
|
||||
run_program_config.time_limit = 1;
|
||||
run_program_config.real_time_limit = -1;
|
||||
run_program_config.memory_limit = 256;
|
||||
run_program_config.output_limit = 64;
|
||||
run_program_config.stack_limit = 1024;
|
||||
run_program_config.input_file_name = "stdin";
|
||||
run_program_config.output_file_name = "stdout";
|
||||
run_program_config.error_file_name = "stderr";
|
||||
run_program_config.work_path = "";
|
||||
run_program_config.result_file_name = "stdout";
|
||||
run_program_config.type = "default";
|
||||
run_program_config.safe_mode = true;
|
||||
run_program_config.need_show_trace_details = false;
|
||||
run_program_config.allow_proc = false;
|
||||
|
||||
argp_parse(&run_program_argp, argc, argv, ARGP_NO_ARGS | ARGP_IN_ORDER, 0, &run_program_config);
|
||||
|
||||
if (run_program_config.real_time_limit == -1)
|
||||
run_program_config.real_time_limit = run_program_config.time_limit + 2;
|
||||
run_program_config.stack_limit = min(run_program_config.stack_limit, run_program_config.memory_limit);
|
||||
|
||||
if (!run_program_config.work_path.empty()) {
|
||||
if (chdir(run_program_config.work_path.c_str()) == -1) {
|
||||
exit(put_result(run_program_config.result_file_name, RS_JGF));
|
||||
}
|
||||
}
|
||||
|
||||
if (run_program_config.type == "java8" || run_program_config.type == "java11") {
|
||||
run_program_config.program_name = run_program_config.argv[0];
|
||||
} else {
|
||||
run_program_config.program_name = realpath(run_program_config.argv[0]);
|
||||
}
|
||||
if (run_program_config.work_path.empty()) {
|
||||
run_program_config.work_path = dirname(run_program_config.program_name);
|
||||
run_program_config.program_basename = basename(run_program_config.program_name);
|
||||
run_program_config.argv[0] = "./" + run_program_config.program_basename;
|
||||
|
||||
if (chdir(run_program_config.work_path.c_str()) == -1) {
|
||||
exit(put_result(run_program_config.result_file_name, RS_JGF));
|
||||
}
|
||||
}
|
||||
|
||||
if (run_program_config.type == "python2") {
|
||||
string pre[4] = {"/usr/bin/python2", "-E", "-s", "-B"};
|
||||
run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 4);
|
||||
} else if (run_program_config.type == "python3") {
|
||||
string pre[3] = {"/usr/bin/python3", "-I", "-B"};
|
||||
run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
|
||||
} else if (run_program_config.type == "java8") {
|
||||
string pre[3] = {"/usr/lib/jvm/java-8-openjdk-amd64/bin/java", "-Xmx1024m", "-Xss1024m"};
|
||||
run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
|
||||
} else if (run_program_config.type == "java11") {
|
||||
string pre[3] = {"/usr/lib/jvm/java-11-openjdk-amd64/bin/java", "-Xmx1024m", "-Xss1024m"};
|
||||
run_program_config.argv.insert(run_program_config.argv.begin(), pre, pre + 3);
|
||||
}
|
||||
}
|
||||
|
||||
void set_limit(int r, int rcur, int rmax = -1) {
|
||||
if (rmax == -1)
|
||||
rmax = rcur;
|
||||
struct rlimit l;
|
||||
if (getrlimit(r, &l) == -1) {
|
||||
exit(55);
|
||||
}
|
||||
l.rlim_cur = rcur;
|
||||
l.rlim_max = rmax;
|
||||
if (setrlimit(r, &l) == -1) {
|
||||
exit(55);
|
||||
}
|
||||
}
|
||||
void run_child() {
|
||||
set_limit(RLIMIT_CPU, run_program_config.time_limit, run_program_config.real_time_limit);
|
||||
set_limit(RLIMIT_FSIZE, run_program_config.output_limit << 20);
|
||||
set_limit(RLIMIT_STACK, run_program_config.stack_limit << 20);
|
||||
|
||||
if (run_program_config.input_file_name != "stdin") {
|
||||
if (freopen(run_program_config.input_file_name.c_str(), "r", stdin) == NULL) {
|
||||
exit(11);
|
||||
}
|
||||
}
|
||||
if (run_program_config.output_file_name != "stdout" && run_program_config.output_file_name != "stderr") {
|
||||
if (freopen(run_program_config.output_file_name.c_str(), "w", stdout) == NULL) {
|
||||
exit(12);
|
||||
}
|
||||
}
|
||||
if (run_program_config.error_file_name != "stderr") {
|
||||
if (run_program_config.error_file_name == "stdout") {
|
||||
if (dup2(1, 2) == -1) {
|
||||
exit(13);
|
||||
}
|
||||
} else {
|
||||
if (freopen(run_program_config.error_file_name.c_str(), "w", stderr) == NULL) {
|
||||
exit(14);
|
||||
}
|
||||
}
|
||||
|
||||
if (run_program_config.output_file_name == "stderr") {
|
||||
if (dup2(2, 1) == -1) {
|
||||
exit(15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *env_path_str = getenv("PATH");
|
||||
char *env_lang_str = getenv("LANG");
|
||||
char *env_shell_str = getenv("SHELL");
|
||||
string env_path = env_path_str ? env_path_str : "";
|
||||
string env_lang = env_lang_str ? env_lang_str : "";
|
||||
string env_shell = env_shell_str ? env_shell_str : "";
|
||||
|
||||
clearenv();
|
||||
setenv("USER", "poor_program", 1);
|
||||
setenv("LOGNAME", "poor_program", 1);
|
||||
setenv("HOME", run_program_config.work_path.c_str(), 1);
|
||||
if (env_lang_str) {
|
||||
setenv("LANG", env_lang.c_str(), 1);
|
||||
}
|
||||
if (env_path_str) {
|
||||
setenv("PATH", env_path.c_str(), 1);
|
||||
}
|
||||
setenv("PWD", run_program_config.work_path.c_str(), 1);
|
||||
if (env_shell_str) {
|
||||
setenv("SHELL", env_shell.c_str(), 1);
|
||||
}
|
||||
|
||||
char **program_c_argv = new char*[run_program_config.argv.size() + 1];
|
||||
for (size_t i = 0; i < run_program_config.argv.size(); i++) {
|
||||
program_c_argv[i] = new char[run_program_config.argv[i].size() + 1];
|
||||
strcpy(program_c_argv[i], run_program_config.argv[i].c_str());
|
||||
}
|
||||
program_c_argv[run_program_config.argv.size()] = NULL;
|
||||
|
||||
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
|
||||
exit(16);
|
||||
}
|
||||
if (execv(program_c_argv[0], program_c_argv) == -1) {
|
||||
exit(17);
|
||||
}
|
||||
}
|
||||
|
||||
const int MaxNRPChildren = 50;
|
||||
struct rp_child_proc {
|
||||
pid_t pid;
|
||||
int mode;
|
||||
};
|
||||
int n_rp_children;
|
||||
pid_t rp_timer_pid;
|
||||
rp_child_proc rp_children[MaxNRPChildren];
|
||||
|
||||
int rp_children_pos(pid_t pid) {
|
||||
for (int i = 0; i < n_rp_children; i++) {
|
||||
if (rp_children[i].pid == pid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int rp_children_add(pid_t pid) {
|
||||
if (n_rp_children == MaxNRPChildren) {
|
||||
return -1;
|
||||
}
|
||||
rp_children[n_rp_children].pid = pid;
|
||||
rp_children[n_rp_children].mode = -1;
|
||||
n_rp_children++;
|
||||
return 0;
|
||||
}
|
||||
void rp_children_del(pid_t pid) {
|
||||
int new_n = 0;
|
||||
for (int i = 0; i < n_rp_children; i++) {
|
||||
if (rp_children[i].pid != pid) {
|
||||
rp_children[new_n++] = rp_children[i];
|
||||
}
|
||||
}
|
||||
n_rp_children = new_n;
|
||||
}
|
||||
|
||||
void stop_child(pid_t pid) {
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
void stop_all() {
|
||||
kill(rp_timer_pid, SIGKILL);
|
||||
for (int i = 0; i < n_rp_children; i++) {
|
||||
kill(rp_children[i].pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
|
||||
RunResult trace_children() {
|
||||
rp_timer_pid = fork();
|
||||
if (rp_timer_pid == -1) {
|
||||
stop_all();
|
||||
return RunResult(RS_JGF);
|
||||
} else if (rp_timer_pid == 0) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = run_program_config.real_time_limit;
|
||||
ts.tv_nsec = 0;
|
||||
nanosleep(&ts, NULL);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
cerr << "timerpid " << rp_timer_pid << endl;
|
||||
}
|
||||
|
||||
pid_t prev_pid = -1;
|
||||
while (true) {
|
||||
int stat = 0;
|
||||
int sig = 0;
|
||||
struct rusage ruse;
|
||||
|
||||
pid_t pid = wait4(-1, &stat, __WALL, &ruse);
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
if (prev_pid != pid) {
|
||||
cerr << "----------" << pid << "----------" << endl;
|
||||
}
|
||||
prev_pid = pid;
|
||||
}
|
||||
if (pid == rp_timer_pid) {
|
||||
if (WIFEXITED(stat) || WIFSIGNALED(stat)) {
|
||||
stop_all();
|
||||
return RunResult(RS_TLE);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int p = rp_children_pos(pid);
|
||||
if (p == -1) {
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
fprintf(stderr, "new_proc %lld\n", (long long int)pid);
|
||||
}
|
||||
if (rp_children_add(pid) == -1) {
|
||||
stop_child(pid);
|
||||
stop_all();
|
||||
return RunResult(RS_DGS);
|
||||
}
|
||||
p = n_rp_children - 1;
|
||||
}
|
||||
|
||||
int usertim = ruse.ru_utime.tv_sec * 1000 + ruse.ru_utime.tv_usec / 1000;
|
||||
int usermem = ruse.ru_maxrss;
|
||||
if (usertim > run_program_config.time_limit * 1000) {
|
||||
stop_all();
|
||||
return RunResult(RS_TLE);
|
||||
}
|
||||
if (usermem > run_program_config.memory_limit * 1024) {
|
||||
stop_all();
|
||||
return RunResult(RS_MLE);
|
||||
}
|
||||
|
||||
if (WIFEXITED(stat)) {
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
fprintf(stderr, "exit : %d\n", WEXITSTATUS(stat));
|
||||
}
|
||||
if (rp_children[0].mode == -1) {
|
||||
stop_all();
|
||||
return RunResult(RS_JGF, -1, -1, WEXITSTATUS(stat));
|
||||
} else {
|
||||
if (pid == rp_children[0].pid) {
|
||||
stop_all();
|
||||
return RunResult(RS_AC, usertim, usermem, WEXITSTATUS(stat));
|
||||
} else {
|
||||
rp_children_del(pid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WIFSIGNALED(stat)) {
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
fprintf(stderr, "sig exit : %d\n", WTERMSIG(stat));
|
||||
}
|
||||
if (pid == rp_children[0].pid) {
|
||||
switch(WTERMSIG(stat)) {
|
||||
case SIGXCPU: // nearly impossible
|
||||
stop_all();
|
||||
return RunResult(RS_TLE);
|
||||
case SIGXFSZ:
|
||||
stop_all();
|
||||
return RunResult(RS_OLE);
|
||||
default:
|
||||
stop_all();
|
||||
return RunResult(RS_RE);
|
||||
}
|
||||
} else {
|
||||
rp_children_del(pid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (WIFSTOPPED(stat)) {
|
||||
sig = WSTOPSIG(stat);
|
||||
|
||||
if (rp_children[p].mode == -1) {
|
||||
if ((p == 0 && sig == SIGTRAP) || (p != 0 && sig == SIGSTOP)) {
|
||||
if (p == 0) {
|
||||
int ptrace_opt = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD;
|
||||
if (run_program_config.safe_mode) {
|
||||
ptrace_opt |= PTRACE_O_TRACECLONE | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK;
|
||||
ptrace_opt |= PTRACE_O_TRACEEXEC;
|
||||
}
|
||||
if (ptrace(PTRACE_SETOPTIONS, pid, NULL, ptrace_opt) == -1) {
|
||||
stop_all();
|
||||
return RunResult(RS_JGF);
|
||||
}
|
||||
}
|
||||
sig = 0;
|
||||
}
|
||||
rp_children[p].mode = 0;
|
||||
} else if (sig == (SIGTRAP | 0x80)) {
|
||||
if (rp_children[p].mode == 0) {
|
||||
if (run_program_config.safe_mode) {
|
||||
if (!check_safe_syscall(pid, run_program_config.need_show_trace_details)) {
|
||||
stop_all();
|
||||
return RunResult(RS_DGS);
|
||||
}
|
||||
}
|
||||
rp_children[p].mode = 1;
|
||||
} else {
|
||||
if (run_program_config.safe_mode) {
|
||||
on_syscall_exit(pid, run_program_config.need_show_trace_details);
|
||||
}
|
||||
rp_children[p].mode = 0;
|
||||
}
|
||||
|
||||
sig = 0;
|
||||
} else if (sig == SIGTRAP) {
|
||||
switch ((stat >> 16) & 0xffff) {
|
||||
case PTRACE_EVENT_CLONE:
|
||||
case PTRACE_EVENT_FORK:
|
||||
case PTRACE_EVENT_VFORK:
|
||||
sig = 0;
|
||||
break;
|
||||
case PTRACE_EVENT_EXEC:
|
||||
rp_children[p].mode = 1;
|
||||
sig = 0;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
stop_all();
|
||||
return RunResult(RS_JGF);
|
||||
}
|
||||
}
|
||||
|
||||
if (sig != 0) {
|
||||
if (run_program_config.need_show_trace_details) {
|
||||
fprintf(stderr, "sig : %d\n", sig);
|
||||
}
|
||||
}
|
||||
|
||||
switch(sig) {
|
||||
case SIGXCPU:
|
||||
stop_all();
|
||||
return RunResult(RS_TLE);
|
||||
case SIGXFSZ:
|
||||
stop_all();
|
||||
return RunResult(RS_OLE);
|
||||
}
|
||||
}
|
||||
|
||||
ptrace(PTRACE_SYSCALL, pid, NULL, sig);
|
||||
}
|
||||
}
|
||||
|
||||
RunResult run_parent(pid_t pid) {
|
||||
init_conf(run_program_config);
|
||||
|
||||
n_rp_children = 0;
|
||||
|
||||
rp_children_add(pid);
|
||||
return trace_children();
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
self_path[readlink("/proc/self/exe", self_path, PATH_MAX)] = '\0';
|
||||
parse_args(argc, argv);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
return put_result(run_program_config.result_file_name, RS_JGF);
|
||||
} else if (pid == 0) {
|
||||
run_child();
|
||||
} else {
|
||||
return put_result(run_program_config.result_file_name, run_parent(pid));
|
||||
}
|
||||
return put_result(run_program_config.result_file_name, RS_JGF);
|
||||
}
|
@ -0,0 +1,782 @@
|
||||
#ifdef __x86_64__
|
||||
typedef unsigned long long int reg_val_t;
|
||||
#define REG_SYSCALL orig_rax
|
||||
#define REG_RET rax
|
||||
#define REG_ARG0 rdi
|
||||
#define REG_ARG1 rsi
|
||||
#define REG_ARG2 rdx
|
||||
#define REG_ARG3 rcx
|
||||
#else
|
||||
typedef long int reg_val_t;
|
||||
#define REG_SYSCALL orig_eax
|
||||
#define REG_RET eax
|
||||
#define REG_ARG0 ebx
|
||||
#define REG_ARG1 ecx
|
||||
#define REG_ARG2 edx
|
||||
#define REG_ARG3 esx
|
||||
#endif
|
||||
|
||||
const size_t MaxPathLen = 200;
|
||||
|
||||
set<string> writable_file_name_set;
|
||||
set<string> readable_file_name_set;
|
||||
set<string> statable_file_name_set;
|
||||
set<string> soft_ban_file_name_set;
|
||||
int syscall_max_cnt[1000];
|
||||
bool syscall_should_soft_ban[1000];
|
||||
|
||||
string basename(const string &path) {
|
||||
size_t p = path.rfind('/');
|
||||
if (p == string::npos) {
|
||||
return path;
|
||||
} else {
|
||||
return path.substr(p + 1);
|
||||
}
|
||||
}
|
||||
string dirname(const string &path) {
|
||||
size_t p = path.rfind('/');
|
||||
if (p == string::npos) {
|
||||
return "";
|
||||
} else {
|
||||
return path.substr(0, p);
|
||||
}
|
||||
}
|
||||
string getcwdp(pid_t pid) {
|
||||
char s[20];
|
||||
char cwd[MaxPathLen + 1];
|
||||
if (pid != 0) {
|
||||
sprintf(s, "/proc/%lld/cwd", (long long int)pid);
|
||||
} else {
|
||||
sprintf(s, "/proc/self/cwd");
|
||||
}
|
||||
int l = readlink(s, cwd, MaxPathLen);
|
||||
if (l == -1) {
|
||||
return "";
|
||||
}
|
||||
cwd[l] = '\0';
|
||||
return cwd;
|
||||
}
|
||||
string abspath(pid_t pid, const string &path) {
|
||||
if (path.size() > MaxPathLen) {
|
||||
return "";
|
||||
}
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
string s;
|
||||
string b;
|
||||
size_t st;
|
||||
if (path[0] == '/') {
|
||||
s = "/";
|
||||
st = 1;
|
||||
} else {
|
||||
s = getcwdp(pid) + "/";
|
||||
st = 0;
|
||||
}
|
||||
for (size_t i = st; i < path.size(); i++) {
|
||||
b += path[i];
|
||||
if (path[i] == '/') {
|
||||
if (b == "../" && !s.empty()) {
|
||||
if (s == "./") {
|
||||
s = "../";
|
||||
} else if (s != "/") {
|
||||
size_t p = s.size() - 1;
|
||||
while (p > 0 && s[p - 1] != '/') {
|
||||
p--;
|
||||
}
|
||||
if (s.size() - p == 3 && s[p] == '.' && s[p + 1] == '.' && s[p + 2] == '/') {
|
||||
s += b;
|
||||
} else {
|
||||
s.resize(p);
|
||||
}
|
||||
}
|
||||
} else if (b != "./" && b != "/") {
|
||||
s += b;
|
||||
}
|
||||
b.clear();
|
||||
}
|
||||
}
|
||||
if (b == ".." && !s.empty()) {
|
||||
if (s == "./") {
|
||||
s = "..";
|
||||
} else if (s != "/") {
|
||||
size_t p = s.size() - 1;
|
||||
while (p > 0 && s[p - 1] != '/') {
|
||||
p--;
|
||||
}
|
||||
if (s.size() - p == 3 && s[p] == '.' && s[p + 1] == '.' && s[p + 2] == '/') {
|
||||
s += b;
|
||||
} else {
|
||||
s.resize(p);
|
||||
}
|
||||
}
|
||||
} else if (b != ".") {
|
||||
s += b;
|
||||
}
|
||||
if (s.size() >= 2 && s[s.size() - 1] == '/') {
|
||||
s.resize(s.size() - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
string realpath(const string &path) {
|
||||
char real[PATH_MAX + 1] = {};
|
||||
if (realpath(path.c_str(), real) == NULL) {
|
||||
return "";
|
||||
}
|
||||
return real;
|
||||
}
|
||||
|
||||
inline bool is_in_set_smart(string name, const set<string> &s) {
|
||||
if (name.size() > MaxPathLen) {
|
||||
return false;
|
||||
}
|
||||
if (s.count(name)) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i + 1 < name.size(); i++) {
|
||||
if ((i == 0 || name[i - 1] == '/') && name[i] == '.' && name[i + 1] == '.' && (i + 2 == name.size() || name[i + 2] == '.')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int level;
|
||||
for (level = 0; !name.empty(); name = dirname(name), level++) {
|
||||
if (level == 1 && s.count(name + "/*")) {
|
||||
return true;
|
||||
}
|
||||
if (s.count(name + "/")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (level == 1 && s.count("/*")) {
|
||||
return true;
|
||||
}
|
||||
if (s.count("/")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_writable_file(string name) {
|
||||
if (name == "/") {
|
||||
return writable_file_name_set.count("system_root");
|
||||
}
|
||||
return is_in_set_smart(name, writable_file_name_set) || is_in_set_smart(realpath(name), readable_file_name_set);
|
||||
}
|
||||
inline bool is_readable_file(const string &name) {
|
||||
if (is_writable_file(name)) {
|
||||
return true;
|
||||
}
|
||||
if (name == "/") {
|
||||
return readable_file_name_set.count("system_root");
|
||||
}
|
||||
return is_in_set_smart(name, readable_file_name_set) || is_in_set_smart(realpath(name), readable_file_name_set);
|
||||
}
|
||||
inline bool is_statable_file(const string &name) {
|
||||
if (is_readable_file(name)) {
|
||||
return true;
|
||||
}
|
||||
if (name == "/") {
|
||||
return statable_file_name_set.count("system_root");
|
||||
}
|
||||
return is_in_set_smart(name, statable_file_name_set) || is_in_set_smart(realpath(name), statable_file_name_set);
|
||||
}
|
||||
inline bool is_soft_ban_file(const string &name) {
|
||||
if (name == "/") {
|
||||
return soft_ban_file_name_set.count("system_root");
|
||||
}
|
||||
return is_in_set_smart(name, soft_ban_file_name_set) || is_in_set_smart(realpath(name), soft_ban_file_name_set);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
int syscall_max_cnt_list_default[][2] = {
|
||||
{__NR_read , -1},
|
||||
{__NR_write , -1},
|
||||
{__NR_readv , -1},
|
||||
{__NR_writev , -1},
|
||||
{__NR_open , -1},
|
||||
{__NR_unlink , -1},
|
||||
{__NR_close , -1},
|
||||
{__NR_readlink , -1},
|
||||
{__NR_openat , -1},
|
||||
{__NR_unlinkat , -1},
|
||||
{__NR_readlinkat , -1},
|
||||
{__NR_stat , -1},
|
||||
{__NR_fstat , -1},
|
||||
{__NR_lstat , -1},
|
||||
{__NR_lseek , -1},
|
||||
{__NR_access , -1},
|
||||
{__NR_dup , -1},
|
||||
{__NR_dup2 , -1},
|
||||
{__NR_dup3 , -1},
|
||||
{__NR_ioctl , -1},
|
||||
{__NR_fcntl , -1},
|
||||
|
||||
{__NR_mmap , -1},
|
||||
{__NR_mprotect , -1},
|
||||
{__NR_munmap , -1},
|
||||
{__NR_brk , -1},
|
||||
{__NR_mremap , -1},
|
||||
{__NR_msync , -1},
|
||||
{__NR_mincore , -1},
|
||||
{__NR_madvise , -1},
|
||||
|
||||
{__NR_rt_sigaction , -1},
|
||||
{__NR_rt_sigprocmask, -1},
|
||||
{__NR_rt_sigreturn , -1},
|
||||
{__NR_rt_sigpending , -1},
|
||||
{__NR_sigaltstack , -1},
|
||||
|
||||
{__NR_getcwd , -1},
|
||||
|
||||
{__NR_exit , -1},
|
||||
{__NR_exit_group , -1},
|
||||
|
||||
{__NR_arch_prctl , -1},
|
||||
|
||||
{__NR_gettimeofday , -1},
|
||||
{__NR_getrlimit , -1},
|
||||
{__NR_getrusage , -1},
|
||||
{__NR_times , -1},
|
||||
{__NR_time , -1},
|
||||
{__NR_clock_gettime , -1},
|
||||
|
||||
{__NR_restart_syscall, -1},
|
||||
|
||||
{-1 , -1}
|
||||
};
|
||||
|
||||
int syscall_soft_ban_list_default[] = {
|
||||
-1
|
||||
};
|
||||
|
||||
const char *readable_file_name_list_default[] = {
|
||||
"/etc/ld.so.nohwcap",
|
||||
"/etc/ld.so.preload",
|
||||
"/etc/ld.so.cache",
|
||||
"/lib/x86_64-linux-gnu/",
|
||||
"/usr/lib/x86_64-linux-gnu/",
|
||||
"/usr/lib/locale/locale-archive",
|
||||
"/proc/self/exe",
|
||||
"/etc/timezone",
|
||||
"/usr/share/zoneinfo/",
|
||||
"/dev/random",
|
||||
"/dev/urandom",
|
||||
"/proc/meminfo",
|
||||
"/etc/localtime",
|
||||
NULL
|
||||
};
|
||||
|
||||
#else
|
||||
#error T_T
|
||||
#endif
|
||||
|
||||
void add_file_permission(const string &file_name, char mode) {
|
||||
if (mode == 'w') {
|
||||
writable_file_name_set.insert(file_name);
|
||||
} else if (mode == 'r') {
|
||||
readable_file_name_set.insert(file_name);
|
||||
} else if (mode == 's') {
|
||||
statable_file_name_set.insert(file_name);
|
||||
}
|
||||
for (string name = dirname(file_name); !name.empty(); name = dirname(name)) {
|
||||
statable_file_name_set.insert(name);
|
||||
}
|
||||
}
|
||||
|
||||
void init_conf(const RunProgramConfig &config) {
|
||||
for (int i = 0; syscall_max_cnt_list_default[i][0] != -1; i++) {
|
||||
syscall_max_cnt[syscall_max_cnt_list_default[i][0]] = syscall_max_cnt_list_default[i][1];
|
||||
}
|
||||
for (int i = 0; syscall_soft_ban_list_default[i] != -1; i++) {
|
||||
syscall_should_soft_ban[syscall_soft_ban_list_default[i]] = true;
|
||||
}
|
||||
|
||||
for (int i = 0; readable_file_name_list_default[i]; i++) {
|
||||
readable_file_name_set.insert(readable_file_name_list_default[i]);
|
||||
}
|
||||
statable_file_name_set.insert(config.work_path + "/");
|
||||
|
||||
if (config.type != "java8" && config.type != "java11") {
|
||||
add_file_permission(config.program_name, 'r');
|
||||
} else {
|
||||
int p = config.program_name.find('.');
|
||||
if (p == string::npos) {
|
||||
readable_file_name_set.insert(config.work_path + "/");
|
||||
} else {
|
||||
readable_file_name_set.insert(config.work_path + "/" + config.program_name.substr(0, p) + "/");
|
||||
}
|
||||
}
|
||||
add_file_permission(config.work_path, 'r');
|
||||
|
||||
for (vector<string>::const_iterator it = config.extra_readable_files.begin(); it != config.extra_readable_files.end(); it++) {
|
||||
add_file_permission(*it, 'r');
|
||||
}
|
||||
for (vector<string>::const_iterator it = config.extra_writable_files.begin(); it != config.extra_writable_files.end(); it++) {
|
||||
add_file_permission(*it, 'w');
|
||||
}
|
||||
|
||||
writable_file_name_set.insert("/dev/null");
|
||||
|
||||
if (config.allow_proc) {
|
||||
syscall_max_cnt[__NR_clone ] = -1;
|
||||
syscall_max_cnt[__NR_fork ] = -1;
|
||||
syscall_max_cnt[__NR_vfork ] = -1;
|
||||
syscall_max_cnt[__NR_nanosleep ] = -1;
|
||||
syscall_max_cnt[__NR_execve ] = -1;
|
||||
}
|
||||
|
||||
if (config.type == "python2") {
|
||||
syscall_max_cnt[__NR_set_tid_address] = 1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = 1;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_getdents ] = -1;
|
||||
syscall_max_cnt[__NR_getdents64 ] = -1;
|
||||
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
syscall_max_cnt[__NR_prlimit64 ] = -1;
|
||||
syscall_max_cnt[__NR_getpid ] = -1;
|
||||
syscall_max_cnt[__NR_sysinfo ] = -1;
|
||||
# endif
|
||||
|
||||
readable_file_name_set.insert("/usr/bin/python2.7");
|
||||
readable_file_name_set.insert("/usr/lib/python2.7/");
|
||||
readable_file_name_set.insert("/usr/bin/lib/python2.7/");
|
||||
readable_file_name_set.insert("/usr/local/lib/python2.7/");
|
||||
readable_file_name_set.insert("/usr/lib/pymodules/python2.7/");
|
||||
readable_file_name_set.insert("/usr/bin/Modules/");
|
||||
readable_file_name_set.insert("/usr/bin/pybuilddir.txt");
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
readable_file_name_set.insert("/usr/lib/locale/");
|
||||
readable_file_name_set.insert(config.work_path + "/answer.code");
|
||||
# endif
|
||||
|
||||
statable_file_name_set.insert("/usr");
|
||||
statable_file_name_set.insert("/usr/bin");
|
||||
} else if (config.type == "python3") {
|
||||
syscall_max_cnt[__NR_set_tid_address] = 1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = 1;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_getdents ] = -1;
|
||||
syscall_max_cnt[__NR_getdents64 ] = -1;
|
||||
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
syscall_max_cnt[__NR_prlimit64 ] = -1;
|
||||
syscall_max_cnt[__NR_getrandom ] = -1;
|
||||
syscall_max_cnt[__NR_sysinfo ] = -1;
|
||||
syscall_max_cnt[__NR_getpid ] = -1;
|
||||
# endif
|
||||
|
||||
readable_file_name_set.insert("/usr/bin/python3");
|
||||
readable_file_name_set.insert("/usr/lib/python3/");
|
||||
# ifdef UOJ_JUDGER_PYTHON3_VERSION
|
||||
readable_file_name_set.insert("/usr/bin/python" UOJ_JUDGER_PYTHON3_VERSION);
|
||||
readable_file_name_set.insert("/usr/lib/python" UOJ_JUDGER_PYTHON3_VERSION "/");
|
||||
readable_file_name_set.insert("/usr/bin/lib/python" UOJ_JUDGER_PYTHON3_VERSION "/");
|
||||
readable_file_name_set.insert("/usr/local/lib/python" UOJ_JUDGER_PYTHON3_VERSION "/");
|
||||
# else
|
||||
readable_file_name_set.insert("/usr/bin/python3.4");
|
||||
readable_file_name_set.insert("/usr/lib/python3.4/");
|
||||
readable_file_name_set.insert("/usr/bin/lib/python3.4/");
|
||||
readable_file_name_set.insert("/usr/local/lib/python3.4/");
|
||||
# endif
|
||||
readable_file_name_set.insert("/usr/bin/pyvenv.cfg");
|
||||
readable_file_name_set.insert("/usr/pyvenv.cfg");
|
||||
readable_file_name_set.insert("/usr/bin/Modules/");
|
||||
readable_file_name_set.insert("/usr/bin/pybuilddir.txt");
|
||||
readable_file_name_set.insert("/usr/lib/dist-python");
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
readable_file_name_set.insert("/usr/lib/locale/");
|
||||
readable_file_name_set.insert(config.work_path + "/answer.code");
|
||||
# endif
|
||||
|
||||
statable_file_name_set.insert("/usr");
|
||||
statable_file_name_set.insert("/usr/bin");
|
||||
statable_file_name_set.insert("/usr/lib");
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
statable_file_name_set.insert("/usr/lib/python36.zip");
|
||||
# endif
|
||||
} else if (config.type == "java8") {
|
||||
syscall_max_cnt[__NR_gettid ] = -1;
|
||||
syscall_max_cnt[__NR_set_tid_address] = 1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = 14;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_uname ] = 1;
|
||||
|
||||
syscall_max_cnt[__NR_clone ] = 13;
|
||||
|
||||
syscall_max_cnt[__NR_getdents ] = 4;
|
||||
|
||||
syscall_max_cnt[__NR_clock_getres ] = 2;
|
||||
|
||||
syscall_max_cnt[__NR_setrlimit ] = 1;
|
||||
|
||||
syscall_max_cnt[__NR_sched_getaffinity] = -1;
|
||||
syscall_max_cnt[__NR_sched_yield ] = -1;
|
||||
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
syscall_max_cnt[__NR_prlimit64 ] = -1;
|
||||
syscall_max_cnt[__NR_getpid ] = -1;
|
||||
syscall_max_cnt[__NR_sysinfo ] = -1;
|
||||
syscall_max_cnt[__NR_clone ] = -1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = -1;
|
||||
syscall_max_cnt[__NR_prctl ] = -1;
|
||||
# endif
|
||||
|
||||
syscall_should_soft_ban[__NR_socket ] = true;
|
||||
syscall_should_soft_ban[__NR_connect ] = true;
|
||||
syscall_should_soft_ban[__NR_geteuid ] = true;
|
||||
syscall_should_soft_ban[__NR_getuid ] = true;
|
||||
|
||||
soft_ban_file_name_set.insert("/etc/nsswitch.conf");
|
||||
soft_ban_file_name_set.insert("/etc/passwd");
|
||||
|
||||
add_file_permission("/usr/lib/jvm/java-8-openjdk-amd64/", 'r');
|
||||
readable_file_name_set.insert("/sys/devices/system/cpu/");
|
||||
readable_file_name_set.insert("/proc/");
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/cpu/");
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/cpu,cpuacct/");
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/memory/");
|
||||
readable_file_name_set.insert("/usr/lib/locale/");
|
||||
# endif
|
||||
statable_file_name_set.insert("/usr/java/");
|
||||
statable_file_name_set.insert("/tmp/");
|
||||
} else if (config.type == "java11") {
|
||||
syscall_max_cnt[__NR_gettid ] = -1;
|
||||
syscall_max_cnt[__NR_set_tid_address] = 1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = 15;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_uname ] = 1;
|
||||
|
||||
syscall_max_cnt[__NR_clone ] = 14;
|
||||
|
||||
syscall_max_cnt[__NR_getdents ] = 4;
|
||||
|
||||
syscall_max_cnt[__NR_clock_getres ] = 2;
|
||||
|
||||
syscall_max_cnt[__NR_setrlimit ] = 1;
|
||||
|
||||
syscall_max_cnt[__NR_sched_getaffinity] = -1;
|
||||
syscall_max_cnt[__NR_sched_yield ] = -1;
|
||||
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
syscall_max_cnt[__NR_prlimit64 ] = -1;
|
||||
syscall_max_cnt[__NR_getpid ] = -1;
|
||||
syscall_max_cnt[__NR_sysinfo ] = -1;
|
||||
syscall_max_cnt[__NR_clone ] = -1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = -1;
|
||||
syscall_max_cnt[__NR_uname ] = -1;
|
||||
syscall_max_cnt[__NR_clock_getres ] = -1;
|
||||
syscall_max_cnt[__NR_pread64 ] = -1;
|
||||
syscall_max_cnt[__NR_prctl ] = -1;
|
||||
syscall_max_cnt[__NR_nanosleep ] = -1;
|
||||
# endif
|
||||
|
||||
syscall_should_soft_ban[__NR_socket ] = true;
|
||||
syscall_should_soft_ban[__NR_connect ] = true;
|
||||
syscall_should_soft_ban[__NR_geteuid ] = true;
|
||||
syscall_should_soft_ban[__NR_getuid ] = true;
|
||||
|
||||
soft_ban_file_name_set.insert("/etc/nsswitch.conf");
|
||||
soft_ban_file_name_set.insert("/etc/passwd");
|
||||
|
||||
add_file_permission("/usr/lib/jvm/java-11-openjdk-amd64/", 'r');
|
||||
readable_file_name_set.insert("/sys/devices/system/cpu/");
|
||||
readable_file_name_set.insert("/proc/");
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/cpu/");
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/cpu,cpuacct/");
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/memory/");
|
||||
readable_file_name_set.insert("/usr/share/java/");
|
||||
readable_file_name_set.insert("/usr/lib/locale/");
|
||||
readable_file_name_set.insert("/etc/oracle/java/usagetracker.properties");
|
||||
# endif
|
||||
statable_file_name_set.insert("/usr/java/");
|
||||
statable_file_name_set.insert("/tmp/");
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
statable_file_name_set.insert("/usr/share/");
|
||||
# endif
|
||||
} else if (config.type == "compiler") {
|
||||
syscall_max_cnt[__NR_gettid ] = -1;
|
||||
syscall_max_cnt[__NR_set_tid_address] = -1;
|
||||
syscall_max_cnt[__NR_set_robust_list] = -1;
|
||||
syscall_max_cnt[__NR_futex ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_getpid ] = -1;
|
||||
syscall_max_cnt[__NR_vfork ] = -1;
|
||||
syscall_max_cnt[__NR_fork ] = -1;
|
||||
syscall_max_cnt[__NR_clone ] = -1;
|
||||
syscall_max_cnt[__NR_execve ] = -1;
|
||||
syscall_max_cnt[__NR_wait4 ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_clock_gettime ] = -1;
|
||||
syscall_max_cnt[__NR_clock_getres ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_setrlimit ] = -1;
|
||||
syscall_max_cnt[__NR_pipe ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_getdents64 ] = -1;
|
||||
syscall_max_cnt[__NR_getdents ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_umask ] = -1;
|
||||
syscall_max_cnt[__NR_rename ] = -1;
|
||||
syscall_max_cnt[__NR_chmod ] = -1;
|
||||
syscall_max_cnt[__NR_mkdir ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_chdir ] = -1;
|
||||
syscall_max_cnt[__NR_fchdir ] = -1;
|
||||
|
||||
syscall_max_cnt[__NR_ftruncate ] = -1; // for javac = =
|
||||
|
||||
syscall_max_cnt[__NR_sched_getaffinity] = -1; // for javac = =
|
||||
syscall_max_cnt[__NR_sched_yield ] = -1; // for javac = =
|
||||
|
||||
syscall_max_cnt[__NR_uname ] = -1; // for javac = =
|
||||
syscall_max_cnt[__NR_sysinfo ] = -1; // for javac = =
|
||||
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
syscall_max_cnt[__NR_prlimit64 ] = -1;
|
||||
syscall_max_cnt[__NR_getrandom ] = -1;
|
||||
syscall_max_cnt[__NR_pread64 ] = -1;
|
||||
syscall_max_cnt[__NR_prctl ] = -1;
|
||||
syscall_max_cnt[__NR_nanosleep ] = -1;
|
||||
syscall_max_cnt[__NR_socketpair ] = -1;
|
||||
# endif
|
||||
|
||||
syscall_should_soft_ban[__NR_socket ] = true; // for javac
|
||||
syscall_should_soft_ban[__NR_connect ] = true; // for javac
|
||||
syscall_should_soft_ban[__NR_geteuid ] = true; // for javac
|
||||
syscall_should_soft_ban[__NR_getuid ] = true; // for javac
|
||||
|
||||
writable_file_name_set.insert("/tmp/");
|
||||
|
||||
readable_file_name_set.insert(config.work_path);
|
||||
writable_file_name_set.insert(config.work_path + "/");
|
||||
|
||||
readable_file_name_set.insert(abspath(0, string(self_path) + "/../runtime") + "/");
|
||||
# ifdef UOJ_JUDGER_BASESYSTEM_UBUNTU1804
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/cpu/");
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/cpu,cpuacct/");
|
||||
readable_file_name_set.insert("/sys/fs/cgroup/memory/");
|
||||
readable_file_name_set.insert("/etc/oracle/java/usagetracker.properties");
|
||||
# endif
|
||||
|
||||
readable_file_name_set.insert("system_root");
|
||||
readable_file_name_set.insert("/usr/");
|
||||
readable_file_name_set.insert("/lib/");
|
||||
readable_file_name_set.insert("/lib64/");
|
||||
readable_file_name_set.insert("/bin/");
|
||||
readable_file_name_set.insert("/sbin/");
|
||||
// readable_file_name_set.insert("/proc/meminfo");
|
||||
// readable_file_name_set.insert("/proc/self/");
|
||||
|
||||
readable_file_name_set.insert("/sys/devices/system/cpu/");
|
||||
readable_file_name_set.insert("/proc/");
|
||||
soft_ban_file_name_set.insert("/etc/nsswitch.conf"); // for javac = =
|
||||
soft_ban_file_name_set.insert("/etc/passwd"); // for javac = =
|
||||
|
||||
readable_file_name_set.insert("/etc/timezone");
|
||||
# ifdef UOJ_JUDGER_FPC_VERSION
|
||||
readable_file_name_set.insert("/etc/fpc-" UOJ_JUDGER_FPC_VERSION ".cfg.d/");
|
||||
# else
|
||||
readable_file_name_set.insert("/etc/fpc-2.6.2.cfg.d/");
|
||||
# endif
|
||||
readable_file_name_set.insert("/etc/fpc.cfg");
|
||||
|
||||
statable_file_name_set.insert("/*");
|
||||
}
|
||||
}
|
||||
|
||||
string read_string_from_regs(reg_val_t addr, pid_t pid) {
|
||||
char res[MaxPathLen + 1], *ptr = res;
|
||||
while (ptr != res + MaxPathLen) {
|
||||
*(reg_val_t*)ptr = ptrace(PTRACE_PEEKDATA, pid, addr, NULL);
|
||||
for (int i = 0; i < sizeof(reg_val_t); i++, ptr++, addr++) {
|
||||
if (*ptr == 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
res[MaxPathLen] = 0;
|
||||
return res;
|
||||
}
|
||||
string read_abspath_from_regs(reg_val_t addr, pid_t pid) {
|
||||
return abspath(pid, read_string_from_regs(addr, pid));
|
||||
}
|
||||
|
||||
inline void soft_ban_syscall(pid_t pid, user_regs_struct reg) {
|
||||
reg.REG_SYSCALL += 1024;
|
||||
ptrace(PTRACE_SETREGS, pid, NULL, ®);
|
||||
}
|
||||
|
||||
inline bool on_dgs_file_detect(pid_t pid, user_regs_struct reg, const string &fn) {
|
||||
if (is_soft_ban_file(fn)) {
|
||||
soft_ban_syscall(pid, reg);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool check_safe_syscall(pid_t pid, bool need_show_trace_details) {
|
||||
struct user_regs_struct reg;
|
||||
ptrace(PTRACE_GETREGS, pid, NULL, ®);
|
||||
|
||||
int cur_instruction = ptrace(PTRACE_PEEKTEXT, pid, reg.rip - 2, NULL) & 0xffff;
|
||||
if (cur_instruction != 0x050f) {
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "informal syscall %d\n", cur_instruction);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int syscall = (int)reg.REG_SYSCALL;
|
||||
if (0 > syscall || syscall >= 1000) {
|
||||
return false;
|
||||
}
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "syscall %d\n", (int)syscall);
|
||||
}
|
||||
|
||||
if (syscall_should_soft_ban[syscall]) {
|
||||
soft_ban_syscall(pid, reg);
|
||||
} else if (syscall_max_cnt[syscall]-- == 0) {
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "dgs %d\n", (int)syscall);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (syscall == __NR_open || syscall == __NR_openat) {
|
||||
reg_val_t fn_addr;
|
||||
reg_val_t flags;
|
||||
if (syscall == __NR_open) {
|
||||
fn_addr = reg.REG_ARG0;
|
||||
flags = reg.REG_ARG1;
|
||||
} else {
|
||||
fn_addr = reg.REG_ARG1;
|
||||
flags = reg.REG_ARG2;
|
||||
}
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "open ");
|
||||
|
||||
switch (flags & O_ACCMODE) {
|
||||
case O_RDONLY:
|
||||
fprintf(stderr, "r ");
|
||||
break;
|
||||
case O_WRONLY:
|
||||
fprintf(stderr, "w ");
|
||||
break;
|
||||
case O_RDWR:
|
||||
fprintf(stderr, "rw");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "??");
|
||||
break;
|
||||
}
|
||||
fprintf(stderr, " %s\n", fn.c_str());
|
||||
}
|
||||
|
||||
bool is_read_only = (flags & O_ACCMODE) == O_RDONLY &&
|
||||
(flags & O_CREAT) == 0 &&
|
||||
(flags & O_EXCL) == 0 &&
|
||||
(flags & O_TRUNC) == 0;
|
||||
if (is_read_only) {
|
||||
if (realpath(fn) != "" && !is_readable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else {
|
||||
if (!is_writable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
}
|
||||
} else if (syscall == __NR_readlink || syscall == __NR_readlinkat) {
|
||||
reg_val_t fn_addr;
|
||||
if (syscall == __NR_readlink) {
|
||||
fn_addr = reg.REG_ARG0;
|
||||
} else {
|
||||
fn_addr = reg.REG_ARG1;
|
||||
}
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "readlink %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_readable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_unlink || syscall == __NR_unlinkat) {
|
||||
reg_val_t fn_addr;
|
||||
if (syscall == __NR_unlink) {
|
||||
fn_addr = reg.REG_ARG0;
|
||||
} else {
|
||||
fn_addr = reg.REG_ARG1;
|
||||
}
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "unlink %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_writable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_access) {
|
||||
reg_val_t fn_addr = reg.REG_ARG0;
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "access %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_statable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_stat || syscall == __NR_lstat) {
|
||||
reg_val_t fn_addr = reg.REG_ARG0;
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "stat %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_statable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_execve) {
|
||||
reg_val_t fn_addr = reg.REG_ARG0;
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "execve %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_readable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
} else if (syscall == __NR_chmod || syscall == __NR_rename) {
|
||||
reg_val_t fn_addr = reg.REG_ARG0;
|
||||
string fn = read_abspath_from_regs(fn_addr, pid);
|
||||
if (need_show_trace_details) {
|
||||
fprintf(stderr, "change %s\n", fn.c_str());
|
||||
}
|
||||
if (!is_writable_file(fn)) {
|
||||
return on_dgs_file_detect(pid, reg, fn);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void on_syscall_exit(pid_t pid, bool need_show_trace_details) {
|
||||
struct user_regs_struct reg;
|
||||
ptrace(PTRACE_GETREGS, pid, NULL, ®);
|
||||
if (need_show_trace_details) {
|
||||
if ((long long int)reg.REG_SYSCALL >= 1024) {
|
||||
fprintf(stderr, "ban sys %lld\n", (long long int)reg.REG_SYSCALL - 1024);
|
||||
} else {
|
||||
fprintf(stderr, "exitsys %lld (ret %d)\n", (long long int)reg.REG_SYSCALL, (int)reg.REG_RET);
|
||||
}
|
||||
}
|
||||
|
||||
if ((long long int)reg.REG_SYSCALL >= 1024) {
|
||||
reg.REG_SYSCALL -= 1024;
|
||||
reg.REG_RET = -EACCES;
|
||||
ptrace(PTRACE_SETREGS, pid, NULL, ®);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
.idea
|
||||
.php_cs.cache
|
@ -0,0 +1,25 @@
|
||||
Options -Indexes
|
||||
|
||||
php_value session.save_path /var/lib/php/uoj_sessions
|
||||
php_value session.gc_maxlifetime 172800
|
||||
php_value session.cookie_lifetime 31536000
|
||||
|
||||
php_value post_max_size 1000M
|
||||
php_value upload_max_filesize 1000M
|
||||
|
||||
php_value session.gc_probability 1
|
||||
php_value session.gc_divisor 1000
|
||||
|
||||
php_value date.timezone 'Asia/Shanghai'
|
||||
|
||||
DirectorySlash Off
|
||||
|
||||
DirectoryIndex
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{QUERY_STRING} ^$
|
||||
RewriteRule ^(.*)/$ /$1 [L,R=301]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->exclude('vendor')
|
||||
->in(__DIR__)
|
||||
;
|
||||
|
||||
$config = new PhpCsFixer\Config();
|
||||
return $config
|
||||
->setIndent("\t")
|
||||
->setRules([
|
||||
'braces'=>[
|
||||
'position_after_functions_and_oop_constructs'=>'same'
|
||||
],
|
||||
'elseif' => true
|
||||
])
|
||||
->setFinder($finder)
|
||||
;
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
return [
|
||||
'profile' => [
|
||||
'oj-name' => 'Universal Online Judge',
|
||||
'oj-name-short' => 'UOJ',
|
||||
'administrator' => 'root',
|
||||
'admin-email' => 'admin@local_uoj.ac',
|
||||
'QQ-group' => '',
|
||||
'ICP-license' => ''
|
||||
],
|
||||
'database' => [
|
||||
'database' => 'app_uoj233',
|
||||
'username' => 'root',
|
||||
'password' => '_database_password_',
|
||||
'host' => '127.0.0.1'
|
||||
],
|
||||
'web' => [
|
||||
'domain' => null,
|
||||
'main' => [
|
||||
'protocol' => 'http',
|
||||
'host' => '_httpHost_',
|
||||
'port' => 80
|
||||
],
|
||||
'blog' => [
|
||||
'protocol' => 'http',
|
||||
'host' => '_httpHost_',
|
||||
'port' => 80
|
||||
]
|
||||
],
|
||||
'security' => [
|
||||
'user' => [
|
||||
'client_salt' => 'salt0'
|
||||
],
|
||||
'cookie' => [
|
||||
'checksum_salt' => ['salt1', 'salt2', 'salt3']
|
||||
],
|
||||
],
|
||||
'mail' => [
|
||||
'noreply' => [
|
||||
'username' => 'noreply@local_uoj.ac',
|
||||
'password' => '_mail_noreply_password_',
|
||||
'host' => 'smtp.local_uoj.ac',
|
||||
'secure' => 'tls',
|
||||
'port' => 587
|
||||
]
|
||||
],
|
||||
'judger' => [
|
||||
'socket' => [
|
||||
'port' => '233',
|
||||
'password' => '_judger_socket_password_'
|
||||
]
|
||||
],
|
||||
'switch' => [
|
||||
'web-analytics' => false,
|
||||
'blog-domain-mode' => 3
|
||||
]
|
||||
];
|
@ -0,0 +1 @@
|
||||
deny from all
|
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
$_SERVER['DOCUMENT_ROOT'] = dirname(__DIR__);
|
||||
|
||||
require $_SERVER['DOCUMENT_ROOT'] . '/app/libs/uoj-lib.php';
|
||||
|
||||
// TODO: more beautiful argv parser
|
||||
|
||||
$handlers = [
|
||||
'upgrade:up' => function ($name) {
|
||||
if (func_num_args() != 1) {
|
||||
die("php cli.php upgrade:up <name>\n");
|
||||
}
|
||||
Upgrader::transaction(function() use ($name) {
|
||||
Upgrader::up($name);
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:down' => function ($name) {
|
||||
if (func_num_args() != 1) {
|
||||
die("php cli.php upgrade:down <name>\n");
|
||||
}
|
||||
Upgrader::transaction(function() use ($name) {
|
||||
Upgrader::down($name);
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:refresh' => function ($name) {
|
||||
if (func_num_args() != 1) {
|
||||
die("php cli.php upgrade:refresh <name>\n");
|
||||
}
|
||||
Upgrader::transaction(function() use ($name) {
|
||||
Upgrader::refresh($name);
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:remove' => function ($name) {
|
||||
if (func_num_args() != 1) {
|
||||
die("php cli.php upgrade:remove <name>\n");
|
||||
}
|
||||
Upgrader::transaction(function() use ($name) {
|
||||
Upgrader::remove($name);
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:latest' => function () {
|
||||
if (func_num_args() != 0) {
|
||||
die("php cli.php upgrade:latest\n");
|
||||
}
|
||||
Upgrader::transaction(function() {
|
||||
Upgrader::upgradeToLatest();
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'upgrade:remove-all' => function () {
|
||||
if (func_num_args() != 0) {
|
||||
die("php cli.php upgrade:remove-all\n");
|
||||
}
|
||||
Upgrader::transaction(function() {
|
||||
Upgrader::removeAll();
|
||||
});
|
||||
die("finished!\n");
|
||||
},
|
||||
'help' => 'showHelp'
|
||||
];
|
||||
|
||||
function showHelp() {
|
||||
global $handlers;
|
||||
echo "UOJ Command-Line Interface\n";
|
||||
echo "php cli.php <task-name> params1 params2 ...\n";
|
||||
echo "\n";
|
||||
echo "The following tasks are available:\n";
|
||||
foreach ($handlers as $cmd => $handler) {
|
||||
echo "\t$cmd\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($argv) <= 1) {
|
||||
showHelp();
|
||||
die();
|
||||
}
|
||||
|
||||
if (!isset($handlers[$argv[1]])) {
|
||||
echo "Invalid parameters.\n";
|
||||
showHelp();
|
||||
die();
|
||||
}
|
||||
|
||||
call_user_func_array($handlers[$argv[1]], array_slice($argv, 2));
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!isSuperUser($myUser)) {
|
||||
become403Page();
|
||||
}
|
||||
$time_form = new UOJForm('time');
|
||||
$time_form->addInput(
|
||||
'name', 'text', '比赛标题', 'New Contest',
|
||||
function($str) {
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->addInput(
|
||||
'start_time', 'text', '开始时间', date("Y-m-d H:i:s"),
|
||||
function($str, &$vdata) {
|
||||
try {
|
||||
$vdata['start_time'] = new DateTime($str);
|
||||
} catch (Exception $e) {
|
||||
return '无效时间格式';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->addInput(
|
||||
'last_min', 'text', '时长(单位:分钟)', 180,
|
||||
function($str) {
|
||||
return !validateUInt($str) ? '必须为一个整数' : '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$time_form->handle = function(&$vdata) {
|
||||
$start_time_str = $vdata['start_time']->format('Y-m-d H:i:s');
|
||||
|
||||
$purifier = HTML::pruifier();
|
||||
|
||||
$esc_name = $_POST['name'];
|
||||
$esc_name = $purifier->purify($esc_name);
|
||||
$esc_name = DB::escape($esc_name);
|
||||
|
||||
DB::query("insert into contests (name, start_time, last_min, status) values ('$esc_name', '$start_time_str', ${_POST['last_min']}, 'unfinished')");
|
||||
};
|
||||
$time_form->succ_href="/contests";
|
||||
$time_form->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader('添加比赛') ?>
|
||||
<h1 class="page-header">添加比赛</h1>
|
||||
<div class="tab-pane active" id="tab-time">
|
||||
<?php
|
||||
$time_form->printHTML();
|
||||
?>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
function echoBlogCell($blog) {
|
||||
$level = $blog['level'];
|
||||
|
||||
switch ($level) {
|
||||
case 0:
|
||||
$level_str = '';
|
||||
break;
|
||||
case 1:
|
||||
$level_str = '<span style="color:red">[三级置顶]</span> ';
|
||||
break;
|
||||
case 2:
|
||||
$level_str = '<span style="color:red">[二级置顶]</span> ';
|
||||
break;
|
||||
case 3:
|
||||
$level_str = '<span style="color:red">[一级置顶]</span> ';
|
||||
break;
|
||||
}
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td>' . $level_str . getBlogLink($blog['id']) . '</td>';
|
||||
echo '<td>' . getUserLink($blog['poster']) . '</td>';
|
||||
echo '<td>' . $blog['post_time'] . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
$header = <<<EOD
|
||||
<tr>
|
||||
<th width="60%">标题</th>
|
||||
<th width="20%">发表者</th>
|
||||
<th width="20%">发表日期</th>
|
||||
</tr>
|
||||
EOD;
|
||||
$config = [
|
||||
'table_classes' => ['table', 'table-hover'],
|
||||
'page_len' => 100
|
||||
];
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('announcements')) ?>
|
||||
<h3>公告</h3>
|
||||
<?php echoLongTable(array('blogs.id', 'poster', 'title', 'post_time', 'zan', 'level'), 'important_blogs, blogs', 'is_hidden = 0 and important_blogs.blog_id = blogs.id', 'order by level desc, important_blogs.blog_id desc', $header, 'echoBlogCell', $config); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
if (!validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
redirectTo(HTML::blog_url($blog['poster'], '/post/'.$_GET['id']));
|
||||
?>
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
function echoBlogCell($blog) {
|
||||
echo '<tr>';
|
||||
echo '<td>' . getBlogLink($blog['id']) . '</td>';
|
||||
echo '<td>' . getUserLink($blog['poster']) . '</td>';
|
||||
echo '<td>' . $blog['post_time'] . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
$header = <<<EOD
|
||||
<tr>
|
||||
<th width="60%">标题</th>
|
||||
<th width="20%">发表者</th>
|
||||
<th width="20%">发表日期</th>
|
||||
</tr>
|
||||
EOD;
|
||||
$config = array();
|
||||
$config['table_classes'] = array('table', 'table-hover');
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('blogs')) ?>
|
||||
<?php if (Auth::check()): ?>
|
||||
<div class="float-right">
|
||||
<div class="btn-group">
|
||||
<a href="<?= HTML::blog_url(Auth::id(), '/') ?>" class="btn btn-secondary btn-sm">我的博客首页</a>
|
||||
<a href="<?= HTML::blog_url(Auth::id(), '/post/new/write')?>" class="btn btn-primary btn-sm"><span class="glyphicon glyphicon-edit"></span> 写新博客</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<h3>博客总览</h3>
|
||||
<?php echoLongTable(array('id', 'poster', 'title', 'post_time', 'zan'), 'blogs', 'is_hidden = 0', 'order by post_time desc', $header, 'echoBlogCell', $config); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
function validateZan() {
|
||||
if (!validateUInt($_POST['id'])) {
|
||||
return false;
|
||||
}
|
||||
if (!validateInt($_POST['delta'])) {
|
||||
return false;
|
||||
}
|
||||
if ($_POST['delta'] != 1 && $_POST['delta'] != -1) {
|
||||
return false;
|
||||
}
|
||||
if ($_POST['type'] != 'B' && $_POST['type'] != 'BC' && $_POST['type'] != 'P' && $_POST['type'] != 'C') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!validateZan()) {
|
||||
die('<div class="text-danger">failed</div>');
|
||||
}
|
||||
if ($myUser == null) {
|
||||
die('<div class="text-danger">please <a href="'.HTML::url('/login').'">log in</a></div>');
|
||||
}
|
||||
|
||||
$id = $_POST['id'];
|
||||
$delta = $_POST['delta'];
|
||||
$type = $_POST['type'];
|
||||
|
||||
switch ($type) {
|
||||
case 'B':
|
||||
$table_name = 'blogs';
|
||||
break;
|
||||
case 'BC':
|
||||
$table_name = 'blogs_comments';
|
||||
break;
|
||||
case 'P':
|
||||
$table_name = 'problems';
|
||||
break;
|
||||
case 'C':
|
||||
$table_name = 'contests';
|
||||
break;
|
||||
}
|
||||
|
||||
$cur = queryZanVal($id, $type, $myUser);
|
||||
|
||||
if ($cur != $delta) {
|
||||
$row = DB::selectFirst("select zan from $table_name where id = $id");
|
||||
if ($row == null) {
|
||||
die('<div class="text-danger">failed</div>');
|
||||
}
|
||||
$cur += $delta;
|
||||
if ($cur == 0) {
|
||||
DB::query("delete from click_zans where username = '{$myUser['username']}' and type = '$type' and target_id = $id");
|
||||
} elseif ($cur != $delta) {
|
||||
DB::query("update click_zans set val = '$cur' where username = '{$myUser['username']}' and type = '$type' and target_id = $id");
|
||||
} else {
|
||||
DB::query("insert into click_zans (username, type, target_id, val) values ('{$myUser['username']}', '$type', $id, $cur)");
|
||||
}
|
||||
$cnt = $row['zan'] + $delta;
|
||||
DB::query("update $table_name set zan = $cnt where id = $id");
|
||||
} else {
|
||||
$row = DB::selectFirst("select zan from $table_name where id = $id");
|
||||
if ($row == null) {
|
||||
die('<div class="text-danger">failed</div>');
|
||||
}
|
||||
$cnt = $row['zan'];
|
||||
}
|
||||
?>
|
||||
<?= getClickZanBlock($type, $id, $cnt, $cur) ?>
|
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
genMoreContestInfo($contest);
|
||||
|
||||
$has_contest_permission = hasContestPermission($myUser, $contest);
|
||||
$show_ip = $has_contest_permission;
|
||||
|
||||
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
|
||||
$iHasRegistered = $myUser != null && hasRegistered($myUser, $contest);
|
||||
|
||||
if ($iHasRegistered) {
|
||||
$unregister_form = new UOJForm('unregister');
|
||||
$unregister_form->handle = function() {
|
||||
global $myUser, $contest;
|
||||
DB::query("delete from contests_registrants where username = '{$myUser['username']}' and contest_id = {$contest['id']}");
|
||||
updateContestPlayerNum($contest);
|
||||
};
|
||||
$unregister_form->submit_button_config['class_str'] = 'btn btn-danger btn-xs';
|
||||
$unregister_form->submit_button_config['text'] = '取消报名';
|
||||
$unregister_form->succ_href = "/contests";
|
||||
|
||||
$unregister_form->runAtServer();
|
||||
}
|
||||
|
||||
if ($has_contest_permission) {
|
||||
$pre_rating_form = new UOJForm('pre_rating');
|
||||
$pre_rating_form->handle = function() {
|
||||
global $contest;
|
||||
foreach (DB::selectAll("select * from contests_registrants where contest_id = {$contest['id']}") as $reg) {
|
||||
$user = queryUser($reg['username']);
|
||||
DB::update("update contests_registrants set user_rating = {$user['rating']} where contest_id = {$contest['id']} and username = '{$user['username']}'");
|
||||
}
|
||||
};
|
||||
$pre_rating_form->submit_button_config['align'] = 'right';
|
||||
$pre_rating_form->submit_button_config['class_str'] = 'btn btn-warning';
|
||||
$pre_rating_form->submit_button_config['text'] = '重新计算参赛前的 rating';
|
||||
$pre_rating_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$pre_rating_form->runAtServer();
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - ' . UOJLocale::get('contests::contest registrants')) ?>
|
||||
|
||||
<h1 class="text-center"><?= $contest['name'] ?></h1>
|
||||
<?php if ($contest['cur_progress'] == CONTEST_NOT_STARTED): ?>
|
||||
<?php if ($iHasRegistered): ?>
|
||||
<div class="float-right">
|
||||
<?php $unregister_form->printHTML(); ?>
|
||||
</div>
|
||||
<div><a style="color:green">已报名</a></div>
|
||||
<?php else: ?>
|
||||
<div>当前尚未报名,您可以<a style="color:red" href="/contest/<?= $contest['id'] ?>/register">报名</a>。</div>
|
||||
<?php endif ?>
|
||||
<div class="top-buffer-sm"></div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php
|
||||
if ($show_ip) {
|
||||
$header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th><th>remote_addr</th><th>rating</th></tr>';
|
||||
|
||||
$ip_owner = array();
|
||||
foreach (DB::selectAll("select * from contests_registrants where contest_id = {$contest['id']} order by user_rating asc") as $reg) {
|
||||
$user = queryUser($reg['username']);
|
||||
$ip_owner[$user['remote_addr']] = $reg['username'];
|
||||
}
|
||||
} else {
|
||||
$header_row = '<tr><th>#</th><th>'.UOJLocale::get('username').'</th><th>rating</th></tr>';
|
||||
}
|
||||
|
||||
echoLongTable(array('*'), 'contests_registrants', "contest_id = {$contest['id']}", 'order by user_rating desc',
|
||||
$header_row,
|
||||
function($contest, $num) {
|
||||
global $myUser;
|
||||
global $show_ip, $ip_owner;
|
||||
|
||||
$user = queryUser($contest['username']);
|
||||
$user_link = getUserLink($contest['username'], $contest['user_rating']);
|
||||
if (!$show_ip) {
|
||||
echo '<tr>';
|
||||
} else {
|
||||
if ($ip_owner[$user['remote_addr']] != $user['username']) {
|
||||
echo '<tr class="danger">';
|
||||
} else {
|
||||
echo '<tr>';
|
||||
}
|
||||
}
|
||||
echo '<td>'.$num.'</td>';
|
||||
echo '<td>'.$user_link.'</td>';
|
||||
if ($show_ip) {
|
||||
echo '<td>'.$user['remote_addr'].'</td>';
|
||||
}
|
||||
echo '<td>'.$contest['user_rating'].'</td>';
|
||||
echo '</tr>';
|
||||
},
|
||||
array('page_len' => 100,
|
||||
'get_row_index' => '',
|
||||
'print_after_table' => function() {
|
||||
global $pre_rating_form;
|
||||
if (isset($pre_rating_form)) {
|
||||
$pre_rating_form->printHTML();
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
$upcoming_contest_name = null;
|
||||
$upcoming_contest_href = null;
|
||||
$rest_second = 1000000;
|
||||
function echoContest($contest) {
|
||||
global $myUser, $upcoming_contest_name, $upcoming_contest_href, $rest_second;
|
||||
|
||||
$contest_name_link = <<<EOD
|
||||
<a href="/contest/{$contest['id']}">{$contest['name']}</a>
|
||||
EOD;
|
||||
genMoreContestInfo($contest);
|
||||
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
|
||||
$cur_rest_second = $contest['start_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
|
||||
if ($cur_rest_second < $rest_second) {
|
||||
$upcoming_contest_name = $contest['name'];
|
||||
$upcoming_contest_href = "/contest/{$contest['id']}";
|
||||
$rest_second = $cur_rest_second;
|
||||
}
|
||||
if ($myUser != null && hasRegistered($myUser, $contest)) {
|
||||
$contest_name_link .= '<sup><a style="color:green">'.UOJLocale::get('contests::registered').'</a></sup>';
|
||||
} else {
|
||||
$contest_name_link .= '<sup><a style="color:red" href="/contest/'.$contest['id'].'/register">'.UOJLocale::get('contests::register').'</a></sup>';
|
||||
}
|
||||
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
|
||||
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::in progress').'</a></sup>';
|
||||
} elseif ($contest['cur_progress'] == CONTEST_PENDING_FINAL_TEST) {
|
||||
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::pending final test').'</a></sup>';
|
||||
} elseif ($contest['cur_progress'] == CONTEST_TESTING) {
|
||||
$contest_name_link .= '<sup><a style="color:blue" href="/contest/'.$contest['id'].'">'.UOJLocale::get('contests::final testing').'</a></sup>';
|
||||
} elseif ($contest['cur_progress'] == CONTEST_FINISHED) {
|
||||
$contest_name_link .= '<sup><a style="color:grey" href="/contest/'.$contest['id'].'/standings">'.UOJLocale::get('contests::ended').'</a></sup>';
|
||||
}
|
||||
|
||||
$last_hour = round($contest['last_min'] / 60, 2);
|
||||
|
||||
$click_zan_block = getClickZanBlock('C', $contest['id'], $contest['zan']);
|
||||
echo '<tr>';
|
||||
echo '<td>', $contest_name_link, '</td>';
|
||||
echo '<td>', '<a href="'.HTML::timeanddate_url($contest['start_time'], array('duration' => $contest['last_min'])).'">'.$contest['start_time_str'].'</a>', '</td>';
|
||||
echo '<td>', UOJLocale::get('hours', $last_hour), '</td>';
|
||||
echo '<td>', '<a href="/contest/'.$contest['id'].'/registrants"><span class="glyphicon glyphicon-user"></span> ×'.$contest['player_num'].'</a>', '</td>';
|
||||
echo '<td>', '<div class="text-left">'.$click_zan_block.'</div>', '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('contests')) ?>
|
||||
<h4><?= UOJLocale::get('contests::current or upcoming contests') ?></h4>
|
||||
<?php
|
||||
$table_header = '';
|
||||
$table_header .= '<tr>';
|
||||
$table_header .= '<th>'.UOJLocale::get('contests::contest name').'</th>';
|
||||
$table_header .= '<th style="width:15em;">'.UOJLocale::get('contests::start time').'</th>';
|
||||
$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::duration').'</th>';
|
||||
$table_header .= '<th style="width:100px;">'.UOJLocale::get('contests::the number of registrants').'</th>';
|
||||
$table_header .= '<th style="width:180px;">'.UOJLocale::get('appraisal').'</th>';
|
||||
$table_header .= '</tr>';
|
||||
echoLongTable(array('*'), 'contests', "status != 'finished'", 'order by id desc', $table_header,
|
||||
echoContest,
|
||||
array('page_len' => 100)
|
||||
);
|
||||
|
||||
if ($rest_second <= 86400) {
|
||||
echo <<<EOD
|
||||
<div class="text-center bot-buffer-lg">
|
||||
<div class="text-warning">$upcoming_contest_name 倒计时</div>
|
||||
<div id="contest-countdown"></div>
|
||||
<script type="text/javascript">
|
||||
$('#contest-countdown').countdown($rest_second, function() {
|
||||
if (confirm('$upcoming_contest_name 已经开始了。是否要跳转到比赛页面?')) {
|
||||
window.location.href = "$upcoming_contest_href";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
EOD;
|
||||
}
|
||||
?>
|
||||
|
||||
<h4><?= UOJLocale::get('contests::ended contests') ?></h4>
|
||||
<?php
|
||||
echoLongTable(array('*'), 'contests', "status = 'finished'", 'order by id desc', $table_header,
|
||||
echoContest,
|
||||
array('page_len' => 100,
|
||||
'print_after_table' => function() {
|
||||
global $myUser;
|
||||
if (isSuperUser($myUser)) {
|
||||
echo '<div class="text-right">';
|
||||
echo '<a href="/contest/new" class="btn btn-primary">'.UOJLocale::get('contests::add new contest').'</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
requirePHPLib('judger');
|
||||
switch ($_GET['type']) {
|
||||
case 'problem':
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$visible = isProblemVisibleToUser($problem, $myUser);
|
||||
if (!$visible && $myUser != null) {
|
||||
$result = DB::query("select contest_id from contests_problems where problem_id = {$_GET['id']}");
|
||||
while (list($contest_id) = DB::fetch($result, MYSQLI_NUM)) {
|
||||
$contest = queryContest($contest_id);
|
||||
genMoreContestInfo($contest);
|
||||
if ($contest['cur_progress'] != CONTEST_NOT_STARTED && hasRegistered($myUser, $contest) && queryContestProblemRank($contest, $problem)) {
|
||||
$visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$visible) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$id = $_GET['id'];
|
||||
|
||||
$file_name = "/var/uoj_data/$id/download.zip";
|
||||
$download_name = "problem_$id.zip";
|
||||
break;
|
||||
case 'testlib.h':
|
||||
$file_name = "/opt/uoj/judger/uoj_judger/include/testlib.h";
|
||||
$download_name = "testlib.h";
|
||||
break;
|
||||
default:
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$finfo = finfo_open(FILEINFO_MIME);
|
||||
$mimetype = finfo_file($finfo, $file_name);
|
||||
if ($mimetype === false) {
|
||||
become404Page();
|
||||
}
|
||||
finfo_close($finfo);
|
||||
|
||||
header("X-Sendfile: $file_name");
|
||||
header("Content-type: $mimetype");
|
||||
header("Content-Disposition: attachment; filename=$download_name");
|
||||
?>
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
$forgot_form = new UOJForm('forgot');
|
||||
$forgot_form->addInput('username', 'text', '用户名', '',
|
||||
function($username, &$vdata) {
|
||||
if (!validateUsername($username)) {
|
||||
return '用户名不合法';
|
||||
}
|
||||
$vdata['user'] = queryUser($username);
|
||||
if (!$vdata['user']) {
|
||||
return '该用户不存在';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
null
|
||||
);
|
||||
$forgot_form->handle = function(&$vdata) {
|
||||
$user = $vdata['user'];
|
||||
$password = $user["password"];
|
||||
|
||||
$oj_name = UOJConfig::$data['profile']['oj-name'];
|
||||
$oj_name_short = UOJConfig::$data['profile']['oj-name-short'];
|
||||
$sufs = base64url_encode($user['username'] . "." . md5($user['username'] . "+" . $password));
|
||||
$url = HTML::url("/reset-password", array('params' => array('p' => $sufs)));
|
||||
$html = <<<EOD
|
||||
<base target="_blank" />
|
||||
|
||||
<p>{$user['username']}您好,</p>
|
||||
<p>您刚刚启用了{$oj_name_short}密码找回功能,请进入下面的链接重设您的密码:</p>
|
||||
<p><a href="$url">$url</a></p>
|
||||
<p>{$oj_name}</p>
|
||||
|
||||
<style type="text/css">
|
||||
body{font-size:14px;font-family:arial,verdana,sans-serif;line-height:1.666;padding:0;margin:0;overflow:auto;white-space:normal;word-wrap:break-word;min-height:100px}
|
||||
pre {white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}
|
||||
</style>
|
||||
EOD;
|
||||
|
||||
$mailer = UOJMail::noreply();
|
||||
$mailer->addAddress($user['email'], $user['username']);
|
||||
$mailer->Subject = $oj_name_short."密码找回";
|
||||
$mailer->msgHTML($html);
|
||||
if (!$mailer->send()) {
|
||||
error_log($mailer->ErrorInfo);
|
||||
becomeMsgPage('<div class="text-center"><h2>邮件发送失败,请重试 <span class="glyphicon glyphicon-remove"></span></h2></div>');
|
||||
} else {
|
||||
becomeMsgPage('<div class="text-center"><h2>邮件发送成功 <span class="glyphicon glyphicon-ok"></span></h2></div>');
|
||||
}
|
||||
};
|
||||
$forgot_form->submit_button_config['align'] = 'offset';
|
||||
|
||||
$forgot_form->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader('找回密码') ?>
|
||||
<h2 class="page-header">找回密码</h2>
|
||||
<h4>请输入需要找回密码的用户名:</h4>
|
||||
<?php $forgot_form->printHTML(); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($hack = queryHack($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
$submission = querySubmission($hack['submission_id']);
|
||||
$problem = queryProblemBrief($submission['problem_id']);
|
||||
$problem_extra_config = getProblemExtraConfig($problem);
|
||||
|
||||
if ($submission['contest_id']) {
|
||||
$contest = queryContest($submission['contest_id']);
|
||||
genMoreContestInfo($contest);
|
||||
} else {
|
||||
$contest = null;
|
||||
}
|
||||
|
||||
if (!isHackVisibleToUser($hack, $problem, $myUser)) {
|
||||
become403Page();
|
||||
}
|
||||
|
||||
if (isSuperUser($myUser)) {
|
||||
$delete_form = new UOJForm('delete');
|
||||
$delete_form->handle = function() {
|
||||
global $hack;
|
||||
DB::query("delete from hacks where id = {$hack['id']}");
|
||||
};
|
||||
$delete_form->submit_button_config['class_str'] = 'btn btn-danger';
|
||||
$delete_form->submit_button_config['text'] = '删除此Hack';
|
||||
$delete_form->submit_button_config['align'] = 'right';
|
||||
$delete_form->submit_button_config['smart_confirm'] = '';
|
||||
$delete_form->succ_href = "/hacks";
|
||||
$delete_form->runAtServer();
|
||||
}
|
||||
|
||||
$should_show_content = hasViewPermission($problem_extra_config['view_content_type'], $myUser, $problem, $submission);
|
||||
$should_show_all_details = hasViewPermission($problem_extra_config['view_all_details_type'], $myUser, $problem, $submission);
|
||||
$should_show_details = hasViewPermission($problem_extra_config['view_details_type'], $myUser, $problem, $submission);
|
||||
$should_show_details_to_me = isSuperUser($myUser);
|
||||
if ($hack['success'] === null) {
|
||||
$should_show_all_details = false;
|
||||
}
|
||||
if (!isSubmissionFullVisibleToUser($submission, $contest, $problem, $myUser)
|
||||
|| !isHackFullVisibleToUser($hack, $contest, $problem, $myUser)) {
|
||||
$should_show_content = $should_show_all_details = false;
|
||||
}
|
||||
|
||||
if ($should_show_all_details) {
|
||||
$styler = new HackDetailsStyler();
|
||||
if (!$should_show_details) {
|
||||
$styler->fade_all_details = true;
|
||||
$styler->show_small_tip = false;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['shjs'] = "";
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('problems::hack').' #'.$hack['id']) ?>
|
||||
|
||||
<?php echoHackListOnlyOne($hack, array(), $myUser) ?>
|
||||
<?php if ($should_show_all_details): ?>
|
||||
<div class="card border-info">
|
||||
<div class="card-header bg-info">
|
||||
<h4 class="card-title"><?= UOJLocale::get('details') ?></h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php echoJudgementDetails($hack['details'], $styler, 'details') ?>
|
||||
<?php if ($should_show_details_to_me): ?>
|
||||
<?php if ($styler->fade_all_details): ?>
|
||||
<hr />
|
||||
<?php echoHackDetails($hack['details'], 'final_details') ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<?php echoSubmissionsListOnlyOne($submission, array(), $myUser) ?>
|
||||
<?php if ($should_show_content): ?>
|
||||
<?php echoSubmissionContent($submission, getProblemSubmissionRequirement($problem)) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (isset($delete_form)): ?>
|
||||
<?php $delete_form->printHTML() ?>
|
||||
<?php endif ?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
$conds = array();
|
||||
|
||||
$q_problem_id = isset($_GET['problem_id']) && validateUInt($_GET['problem_id']) ? $_GET['problem_id'] : null;
|
||||
$q_submission_id = isset($_GET['submission_id']) && validateUInt($_GET['submission_id']) ? $_GET['submission_id'] : null;
|
||||
$q_hacker = isset($_GET['hacker']) && validateUsername($_GET['hacker']) ? $_GET['hacker'] : null;
|
||||
$q_owner = isset($_GET['owner']) && validateUsername($_GET['owner']) ? $_GET['owner'] : null;
|
||||
if ($q_problem_id != null) {
|
||||
$conds[] = "problem_id = $q_problem_id";
|
||||
}
|
||||
if ($q_submission_id != null) {
|
||||
$conds[] = "submission_id = $q_submission_id";
|
||||
}
|
||||
if ($q_hacker != null) {
|
||||
$conds[] = "hacker = '$q_hacker'";
|
||||
}
|
||||
if ($q_owner != null) {
|
||||
$conds[] = "owner = '$q_owner'";
|
||||
}
|
||||
|
||||
$selected_all = ' selected="selected"';
|
||||
$selected_succ ='';
|
||||
$selected_fail ='';
|
||||
if (isset($_GET['status']) && validateUInt($_GET['status'])) {
|
||||
if ($_GET['status'] == 1) {
|
||||
$selected_all = '';
|
||||
$selected_succ =' selected="selected"';
|
||||
$conds[] = 'success = 1';
|
||||
}
|
||||
if ($_GET['status'] == 2) {
|
||||
$selected_all = '';
|
||||
$selected_fail = ' selected="selected"';
|
||||
$conds[] = 'success = 0';
|
||||
}
|
||||
}
|
||||
|
||||
if ($conds) {
|
||||
$cond = join($conds, ' and ');
|
||||
} else {
|
||||
$cond = '1';
|
||||
}
|
||||
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('hacks')) ?>
|
||||
<div class="d-none d-sm-block">
|
||||
<?php if ($myUser != null): ?>
|
||||
<div class="float-right">
|
||||
<a href="/hacks?hacker=<?= $myUser['username'] ?>" class="btn btn-success btn-sm"><?= UOJLocale::get('problems::hacks by me') ?></a>
|
||||
<a href="/hacks?owner=<?= $myUser['username'] ?>" class="btn btn-danger btn-sm"><?= UOJLocale::get('problems::hacks to me') ?></a>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<form id="form-search" class="form-inline" role="form">
|
||||
<div id="form-group-submission_id" class="form-group">
|
||||
<label for="input-submission_id" class="control-label"><?= UOJLocale::get('problems::submission id') ?>:</label>
|
||||
<input type="text" class="form-control input-sm" name="submission_id" id="input-submission_id" value="<?= $q_submission_id ?>" maxlength="6" style="width:5em" />
|
||||
</div>
|
||||
<div id="form-group-problem_id" class="form-group">
|
||||
<label for="input-problem_id" class="control-label"><?= UOJLocale::get('problems::problem id') ?>:</label>
|
||||
<input type="text" class="form-control input-sm" name="problem_id" id="input-problem_id" value="<?= $q_problem_id ?>" maxlength="4" style="width:4em" />
|
||||
</div>
|
||||
<div id="form-group-hacker" class="form-group">
|
||||
<label for="input-hacker" class="control-label"><?= UOJLocale::get('problems::hacker') ?>:</label>
|
||||
<input type="text" class="form-control input-sm" name="hacker" id="input-hacker" value="<?= $q_hacker ?>" maxlength="100" style="width:10em" />
|
||||
</div>
|
||||
<div id="form-group-owner" class="form-group">
|
||||
<label for="input-owner" class="control-label"><?= UOJLocale::get('problems::owner') ?>:</label>
|
||||
<input type="text" class="form-control input-sm" name="owner" id="input-owner" value="<?= $q_owner ?>" maxlength="100" style="width:10em" />
|
||||
</div>
|
||||
<div id="form-group-status" class="form-group">
|
||||
<label for="input-status" class="control-label"><?= UOJLocale::get('problems::result') ?>:</label>
|
||||
<select class="form-control input-sm" id="input-status" name="status">
|
||||
<option value=""<?= $selected_all?>>All</option>
|
||||
<option value="1"<?= $selected_succ ?>>Success!</option>
|
||||
<option value="2"<?= $selected_fail ?>>Failed.</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" id="submit-search" class="btn btn-secondary btn-sm ml-2"><?= UOJLocale::get('search') ?></button>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
$('#form-search').submit(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
url = '/hacks';
|
||||
qs = [];
|
||||
$(['submission_id', 'problem_id', 'hacker', 'owner', 'status']).each(function () {
|
||||
if ($('#input-' + this).val()) {
|
||||
qs.push(this + '=' + encodeURIComponent($('#input-' + this).val()));
|
||||
}
|
||||
});
|
||||
if (qs.length > 0) {
|
||||
url += '?' + qs.join('&');
|
||||
}
|
||||
location.href = url;
|
||||
});
|
||||
</script>
|
||||
<div class="top-buffer-sm"></div>
|
||||
</div>
|
||||
<?php
|
||||
echoHacksList($cond, 'order by id desc', array('judge_time_hidden' => ''), $myUser);
|
||||
?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
$blogs = DB::selectAll("select blogs.id, title, poster, post_time from important_blogs, blogs where is_hidden = 0 and important_blogs.blog_id = blogs.id order by level desc, important_blogs.blog_id desc limit 5");
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJConfig::$data['profile']['oj-name-short']) ?>
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-9">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60%"><?= UOJLocale::get('announcements') ?></th>
|
||||
<th style="width:20%"></th>
|
||||
<th style="width:20%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $now_cnt = 0; ?>
|
||||
<?php foreach ($blogs as $blog): ?>
|
||||
<?php
|
||||
$now_cnt++;
|
||||
$new_tag = '';
|
||||
if ((time() - strtotime($blog['post_time'])) / 3600 / 24 <= 7) {
|
||||
$new_tag = '<sup style="color:red"> new</sup>';
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td><a href="/blogs/<?= $blog['id'] ?>"><?= $blog['title'] ?></a><?= $new_tag ?></td>
|
||||
<td>by <?= getUserLink($blog['poster']) ?></td>
|
||||
<td><small><?= $blog['post_time'] ?></small></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
<?php for ($i = $now_cnt + 1; $i <= 5; $i++): ?>
|
||||
<tr><td colspan="233"> </td></tr>
|
||||
<?php endfor ?>
|
||||
<tr><td class="text-right" colspan="233"><a href="/announcements"><?= UOJLocale::get('all the announcements') ?></a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-3">
|
||||
<img class="media-object img-thumbnail" src="/images/logo.png" alt="Logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 mt-4">
|
||||
<h3><?= UOJLocale::get('top rated') ?></h3>
|
||||
<?php echoRanklist(array('echo_full' => '', 'top10' => '')) ?>
|
||||
<div class="text-center">
|
||||
<a href="/ranklist"><?= UOJLocale::get('view all') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
requirePHPLib('judger');
|
||||
|
||||
if (!authenticateJudger()) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
switch ($_GET['type']) {
|
||||
case 'submission':
|
||||
$file_name = UOJContext::storagePath()."/submission/{$_GET['id']}/{$_GET['rand_str_id']}";
|
||||
$download_name = "submission.zip";
|
||||
break;
|
||||
case 'tmp':
|
||||
$file_name = UOJContext::storagePath()."/tmp/{$_GET['rand_str_id']}";
|
||||
$download_name = "tmp";
|
||||
break;
|
||||
case 'problem':
|
||||
$id = $_GET['id'];
|
||||
if (!validateUInt($id) || !($problem = queryProblemBrief($id))) {
|
||||
become404Page();
|
||||
}
|
||||
$file_name = "/var/uoj_data/$id.zip";
|
||||
$download_name = "$id.zip";
|
||||
break;
|
||||
case 'judger':
|
||||
$file_name = UOJContext::storagePath()."/judge_client.zip";
|
||||
$download_name = "judge_client.zip";
|
||||
break;
|
||||
default:
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$finfo = finfo_open(FILEINFO_MIME);
|
||||
$mimetype = finfo_file($finfo, $file_name);
|
||||
if ($mimetype === false) {
|
||||
become404Page();
|
||||
}
|
||||
finfo_close($finfo);
|
||||
|
||||
header("X-Sendfile: $file_name");
|
||||
header("Content-type: $mimetype");
|
||||
header("Content-Disposition: attachment; filename=$download_name");
|
||||
?>
|
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('data');
|
||||
|
||||
if (!authenticateJudger()) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
function submissionJudged() {
|
||||
$submission = DB::selectFirst("select submitter, status, content, result, problem_id from submissions where id = {$_POST['id']}");
|
||||
if ($submission == null) {
|
||||
return;
|
||||
}
|
||||
if ($submission['status'] != 'Judging' && $submission['status'] != 'Judged, Judging') {
|
||||
return;
|
||||
}
|
||||
$content = json_decode($submission['content'], true);
|
||||
|
||||
if (isset($content['first_test_config'])) {
|
||||
$result = json_decode($submission['result'], true);
|
||||
$result['final_result'] = json_decode($_POST['result'], true);
|
||||
$result['final_result']['details'] = uojTextEncode($result['final_result']['details']);
|
||||
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$content['final_test_config'] = $content['config'];
|
||||
$content['config'] = $content['first_test_config'];
|
||||
unset($content['first_test_config']);
|
||||
$esc_content = DB::escape(json_encode($content));
|
||||
|
||||
DB::update("update submissions set status = 'Judged', result = '$esc_result', content = '$esc_content' where id = {$_POST['id']}");
|
||||
} else {
|
||||
$result = json_decode($_POST['result'], true);
|
||||
$result['details'] = uojTextEncode($result['details']);
|
||||
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
if (isset($result["error"])) {
|
||||
DB::update("update submissions set status = '{$result['status']}', result_error = '{$result['error']}', result = '$esc_result', score = null, used_time = null, used_memory = null where id = {$_POST['id']}");
|
||||
} else {
|
||||
DB::update("update submissions set status = '{$result['status']}', result_error = null, result = '$esc_result', score = {$result['score']}, used_time = {$result['time']}, used_memory = {$result['memory']} where id = {$_POST['id']}");
|
||||
}
|
||||
|
||||
if (isset($content['final_test_config'])) {
|
||||
$content['first_test_config'] = $content['config'];
|
||||
$content['config'] = $content['final_test_config'];
|
||||
unset($content['final_test_config']);
|
||||
$esc_content = DB::escape(json_encode($content));
|
||||
|
||||
DB::update("update submissions set status = 'Judged, Waiting', content = '$esc_content' where id = ${_POST['id']}");
|
||||
}
|
||||
}
|
||||
DB::update("update submissions set status_details = '' where id = {$_POST['id']}");
|
||||
updateBestACSubmissions($submission['submitter'], $submission['problem_id']);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
$content = json_decode($submission['content'], true);
|
||||
$result = json_decode($_POST['result'], true);
|
||||
$result['details'] = uojTextEncode($result['details']);
|
||||
$esc_result = DB::escape(json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
if (isset($result["error"])) {
|
||||
DB::update("update custom_test_submissions set status = '{$result['status']}', result = '$esc_result' where id = {$_POST['id']}");
|
||||
} else {
|
||||
DB::update("update custom_test_submissions set status = '{$result['status']}', result = '$esc_result' where id = {$_POST['id']}");
|
||||
}
|
||||
DB::update("update custom_test_submissions set status_details = '' where id = {$_POST['id']}");
|
||||
}
|
||||
|
||||
function hackJudged() {
|
||||
$result = json_decode($_POST['result'], true);
|
||||
$esc_details = DB::escape(uojTextEncode($result['details']));
|
||||
$ok = DB::update("update hacks set success = {$result['score']}, details = '$esc_details' where id = {$_POST['id']}");
|
||||
|
||||
if ($ok) {
|
||||
list($hack_input) = DB::fetch(DB::query("select input from hacks where id = {$_POST['id']}"), MYSQLI_NUM);
|
||||
unlink(UOJContext::storagePath().$hack_input);
|
||||
|
||||
if ($result['score']) {
|
||||
list($problem_id) = DB::selectFirst("select problem_id from hacks where id = ${_POST['id']}", MYSQLI_NUM);
|
||||
if (validateUploadedFile('hack_input') && validateUploadedFile('std_output')) {
|
||||
dataAddExtraTest(queryProblemBrief($problem_id), $_FILES["hack_input"]["tmp_name"], $_FILES["std_output"]["tmp_name"]);
|
||||
} else {
|
||||
error_log("hack successfully but received no data. 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....");
|
||||
}
|
||||
$esc_status_details = DB::escape($_POST['status']);
|
||||
if (isset($_POST['is_custom_test'])) {
|
||||
DB::update("update custom_test_submissions set status_details = '$esc_status_details' where id = {$_POST['id']}");
|
||||
} else {
|
||||
DB::update("update submissions set status_details = '$esc_status_details' where id = {$_POST['id']}");
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
$submission = null;
|
||||
$hack = null;
|
||||
function querySubmissionToJudge($status, $set_q) {
|
||||
global $submission;
|
||||
$submission = DB::selectFirst("select id, problem_id, content from submissions where status = '$status' order by id limit 1");
|
||||
if ($submission) {
|
||||
DB::update("update submissions set $set_q where id = {$submission['id']} and status = '$status'");
|
||||
if (DB::affected_rows() != 1) {
|
||||
$submission = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
function queryCustomTestSubmissionToJudge() {
|
||||
global $submission;
|
||||
$submission = DB::selectFirst("select id, problem_id, content from custom_test_submissions where judge_time is null order by id limit 1");
|
||||
if ($submission) {
|
||||
DB::update("update custom_test_submissions set judge_time = now(), status = 'Judging' where id = {$submission['id']} and judge_time is null");
|
||||
if (DB::affected_rows() != 1) {
|
||||
$submission = null;
|
||||
}
|
||||
}
|
||||
if ($submission) {
|
||||
$submission['is_custom_test'] = '';
|
||||
}
|
||||
}
|
||||
function queryHackToJudge() {
|
||||
global $hack;
|
||||
$hack = DB::selectFirst("select id, submission_id, input, input_type from hacks where judge_time is null order by id limit 1");
|
||||
if ($hack) {
|
||||
DB::update("update hacks set judge_time = now() where id = {$hack['id']} and judge_time is null");
|
||||
if (DB::affected_rows() != 1) {
|
||||
$hack = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
function findSubmissionToJudge() {
|
||||
global $submission, $hack;
|
||||
querySubmissionToJudge('Waiting', "judge_time = now(), status = 'Judging'");
|
||||
if ($submission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
queryCustomTestSubmissionToJudge();
|
||||
if ($submission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
querySubmissionToJudge('Waiting Rejudge', "judge_time = now(), status = 'Judging'");
|
||||
if ($submission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
querySubmissionToJudge('Judged, Waiting', "status = 'Judged, Judging'");
|
||||
if ($submission) {
|
||||
return true;
|
||||
}
|
||||
|
||||
queryHackToJudge();
|
||||
if ($hack) {
|
||||
$submission = DB::selectFirst("select id, problem_id, content from submissions where id = {$hack['submission_id']} and score = 100");
|
||||
if (!$submission) {
|
||||
$details = "<error>the score gained by the hacked submission is not 100.\n</error>";
|
||||
$esc_details = DB::escape(uojTextEncode($details));
|
||||
DB::update("update hacks set success = 0, details = '$esc_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']);
|
||||
|
||||
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);
|
||||
?>
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
requirePHPLib('judger');
|
||||
|
||||
if (!authenticateJudger()) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
foreach (DB::selectAll("select * from judger_info where ip != ''") as $judger) {
|
||||
$socket = fsockopen($judger['ip'], UOJConfig::$data['judger']['socket']['port']);
|
||||
if ($socket === false) {
|
||||
die("judge client {$judger['ip']} lost.");
|
||||
}
|
||||
fwrite($socket, json_encode([
|
||||
'password' => UOJConfig::$data['judger']['socket']['password'],
|
||||
'cmd' => 'update'
|
||||
]));
|
||||
fclose($socket);
|
||||
}
|
||||
|
||||
die("ok");
|
||||
?>
|
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
if (Auth::check()) {
|
||||
redirectTo('/');
|
||||
}
|
||||
|
||||
function handleLoginPost() {
|
||||
if (!crsf_check()) {
|
||||
return 'expired';
|
||||
}
|
||||
if (!isset($_POST['username'])) {
|
||||
return "failed";
|
||||
}
|
||||
if (!isset($_POST['password'])) {
|
||||
return "failed";
|
||||
}
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
if (!validateUsername($username)) {
|
||||
return "failed";
|
||||
}
|
||||
if (!validatePassword($password)) {
|
||||
return "failed";
|
||||
}
|
||||
|
||||
$user = queryUser($username);
|
||||
if (!$user || !checkPassword($user, $password)) {
|
||||
return "failed";
|
||||
}
|
||||
|
||||
if ($user['usergroup'] == 'B') {
|
||||
return "banned";
|
||||
}
|
||||
|
||||
Auth::login($user['username']);
|
||||
return "ok";
|
||||
}
|
||||
|
||||
if (isset($_POST['login'])) {
|
||||
echo handleLoginPost();
|
||||
die();
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['md5'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('login')) ?>
|
||||
<h2 class="page-header"><?= UOJLocale::get('login') ?></h2>
|
||||
<form id="form-login" class="form-horizontal" method="post">
|
||||
<div id="div-username" class="form-group">
|
||||
<label for="input-username" class="col-sm-2 control-label"><?= UOJLocale::get('username') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="text" class="form-control" id="input-username" name="username" placeholder="<?= UOJLocale::get('enter your username') ?>" maxlength="20" />
|
||||
<span class="help-block" id="help-username"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div-password" class="form-group">
|
||||
<label for="input-password" class="col-sm-2 control-label"><?= UOJLocale::get('password') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" />
|
||||
<span class="help-block" id="help-password"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-3">
|
||||
<button type="submit" id="button-submit" class="btn btn-secondary"><?= UOJLocale::get('submit') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
function validateLoginPost() {
|
||||
var ok = true;
|
||||
ok &= getFormErrorAndShowHelp('username', validateUsername);
|
||||
ok &= getFormErrorAndShowHelp('password', validatePassword);
|
||||
return ok;
|
||||
}
|
||||
|
||||
function submitLoginPost() {
|
||||
if (!validateLoginPost()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$.post('/login', {
|
||||
_token : "<?= crsf_token() ?>",
|
||||
login : '',
|
||||
username : $('#input-username').val(),
|
||||
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
|
||||
}, function(msg) {
|
||||
if (msg == 'ok') {
|
||||
var prevUrl = document.referrer;
|
||||
if (prevUrl == '' || /.*\/login.*/.test(prevUrl) || /.*\/logout.*/.test(prevUrl) || /.*\/register.*/.test(prevUrl) || /.*\/reset-password.*/.test(prevUrl)) {
|
||||
prevUrl = '/';
|
||||
};
|
||||
window.location.href = prevUrl;
|
||||
} else if (msg == 'banned') {
|
||||
$('#div-username').addClass('has-error');
|
||||
$('#help-username').html('该用户已被封停,请联系管理员。');
|
||||
} else if (msg == 'expired') {
|
||||
$('#div-username').addClass('has-error');
|
||||
$('#help-username').html('页面会话已过期。');
|
||||
} else {
|
||||
$('#div-username').addClass('has-error');
|
||||
$('#help-username').html('用户名或密码错误。');
|
||||
$('#div-password').addClass('has-error');
|
||||
$('#help-password').html('用户名或密码错误。<a href="/forgot-password">忘记密码?</a>');
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#form-login').submit(function(e) {
|
||||
e.preventDefault();
|
||||
submitLoginPost();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
crsf_defend();
|
||||
Auth::logout();
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
var prevUrl = document.referrer;
|
||||
if (!prevUrl) {
|
||||
prevUrl = '/';
|
||||
};
|
||||
window.location.href = prevUrl;
|
||||
</script>
|
@ -0,0 +1,274 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('judger');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$problem_content = queryProblemContent($problem['id']);
|
||||
|
||||
$contest = validateUInt($_GET['contest_id']) ? queryContest($_GET['contest_id']) : null;
|
||||
if ($contest != null) {
|
||||
genMoreContestInfo($contest);
|
||||
$problem_rank = queryContestProblemRank($contest, $problem);
|
||||
if ($problem_rank == null) {
|
||||
become404Page();
|
||||
} else {
|
||||
$problem_letter = chr(ord('A') + $problem_rank - 1);
|
||||
}
|
||||
}
|
||||
|
||||
$is_in_contest = false;
|
||||
$ban_in_contest = false;
|
||||
if ($contest != null) {
|
||||
if (!hasContestPermission($myUser, $contest)) {
|
||||
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
|
||||
become404Page();
|
||||
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
|
||||
if ($myUser == null || !hasRegistered($myUser, $contest)) {
|
||||
becomeMsgPage("<h1>比赛正在进行中</h1><p>很遗憾,您尚未报名。比赛结束后再来看吧~</p>");
|
||||
} else {
|
||||
$is_in_contest = true;
|
||||
DB::update("update contests_registrants set has_participated = 1 where username = '{$myUser['username']}' and contest_id = {$contest['id']}");
|
||||
}
|
||||
} else {
|
||||
$ban_in_contest = !isProblemVisibleToUser($problem, $myUser);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!isProblemVisibleToUser($problem, $myUser)) {
|
||||
become404Page();
|
||||
}
|
||||
}
|
||||
|
||||
$submission_requirement = json_decode($problem['submission_requirement'], true);
|
||||
$problem_extra_config = getProblemExtraConfig($problem);
|
||||
$custom_test_requirement = getProblemCustomTestRequirement($problem);
|
||||
|
||||
if ($custom_test_requirement && Auth::check()) {
|
||||
$custom_test_submission = DB::selectFirst("select * from custom_test_submissions where submitter = '".Auth::id()."' and problem_id = {$problem['id']} order by id desc limit 1");
|
||||
$custom_test_submission_result = json_decode($custom_test_submission['result'], true);
|
||||
}
|
||||
if ($custom_test_requirement && $_GET['get'] == 'custom-test-status-details' && Auth::check()) {
|
||||
if ($custom_test_submission == null) {
|
||||
echo json_encode(null);
|
||||
} elseif ($custom_test_submission['status'] != 'Judged') {
|
||||
echo json_encode(array(
|
||||
'judged' => false,
|
||||
'html' => getSubmissionStatusDetails($custom_test_submission)
|
||||
));
|
||||
} else {
|
||||
ob_start();
|
||||
$styler = new CustomTestSubmissionDetailsStyler();
|
||||
if (!hasViewPermission($problem_extra_config['view_details_type'], $myUser, $problem, $submission)) {
|
||||
$styler->fade_all_details = true;
|
||||
}
|
||||
echoJudgementDetails($custom_test_submission_result['details'], $styler, 'custom_test_details');
|
||||
$result = ob_get_contents();
|
||||
ob_end_clean();
|
||||
echo json_encode(array(
|
||||
'judged' => true,
|
||||
'html' => getSubmissionStatusDetails($custom_test_submission),
|
||||
'result' => $result
|
||||
));
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
$can_use_zip_upload = true;
|
||||
foreach ($submission_requirement as $req) {
|
||||
if ($req['type'] == 'source code') {
|
||||
$can_use_zip_upload = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleUpload($zip_file_name, $content, $tot_size) {
|
||||
global $problem, $contest, $myUser, $is_in_contest;
|
||||
|
||||
$content['config'][] = array('problem_id', $problem['id']);
|
||||
if ($is_in_contest && $contest['extra_config']["contest_type"]!='IOI' && !isset($contest['extra_config']["problem_{$problem['id']}"])) {
|
||||
$content['final_test_config'] = $content['config'];
|
||||
$content['config'][] = array('test_sample_only', 'on');
|
||||
}
|
||||
$esc_content = DB::escape(json_encode($content));
|
||||
|
||||
$language = '/';
|
||||
foreach ($content['config'] as $row) {
|
||||
if (strEndWith($row[0], '_language')) {
|
||||
$language = $row[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($language != '/') {
|
||||
Cookie::set('uoj_preferred_language', $language, time() + 60 * 60 * 24 * 365, '/');
|
||||
}
|
||||
$esc_language = DB::escape($language);
|
||||
|
||||
$result = array();
|
||||
$result['status'] = "Waiting";
|
||||
$result_json = json_encode($result);
|
||||
|
||||
if ($is_in_contest) {
|
||||
DB::query("insert into submissions (problem_id, contest_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values (${problem['id']}, ${contest['id']}, now(), '${myUser['username']}', '$esc_content', '$esc_language', $tot_size, '${result['status']}', '$result_json', 0)");
|
||||
} else {
|
||||
DB::query("insert into submissions (problem_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values (${problem['id']}, now(), '${myUser['username']}', '$esc_content', '$esc_language', $tot_size, '${result['status']}', '$result_json', {$problem['is_hidden']})");
|
||||
}
|
||||
}
|
||||
function handleCustomTestUpload($zip_file_name, $content, $tot_size) {
|
||||
global $problem, $contest, $myUser;
|
||||
|
||||
$content['config'][] = array('problem_id', $problem['id']);
|
||||
$content['config'][] = array('custom_test', 'on');
|
||||
$esc_content = DB::escape(json_encode($content));
|
||||
|
||||
$language = '/';
|
||||
foreach ($content['config'] as $row) {
|
||||
if (strEndWith($row[0], '_language')) {
|
||||
$language = $row[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($language != '/') {
|
||||
Cookie::set('uoj_preferred_language', $language, time() + 60 * 60 * 24 * 365, '/');
|
||||
}
|
||||
$esc_language = DB::escape($language);
|
||||
|
||||
$result = array();
|
||||
$result['status'] = "Waiting";
|
||||
$result_json = json_encode($result);
|
||||
|
||||
DB::insert("insert into custom_test_submissions (problem_id, submit_time, submitter, content, status, result) values ({$problem['id']}, now(), '{$myUser['username']}', '$esc_content', '{$result['status']}', '$result_json')");
|
||||
}
|
||||
|
||||
if ($can_use_zip_upload) {
|
||||
$zip_answer_form = newZipSubmissionForm('zip_answer',
|
||||
$submission_requirement,
|
||||
'uojRandAvaiableSubmissionFileName',
|
||||
'handleUpload');
|
||||
$zip_answer_form->extra_validator = function() {
|
||||
global $ban_in_contest;
|
||||
if ($ban_in_contest) {
|
||||
return '请耐心等待比赛结束后题目对所有人可见了再提交';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
$zip_answer_form->succ_href = $is_in_contest ? "/contest/{$contest['id']}/submissions" : '/submissions';
|
||||
$zip_answer_form->runAtServer();
|
||||
}
|
||||
|
||||
$answer_form = newSubmissionForm('answer',
|
||||
$submission_requirement,
|
||||
'uojRandAvaiableSubmissionFileName',
|
||||
'handleUpload');
|
||||
$answer_form->extra_validator = function() {
|
||||
global $ban_in_contest;
|
||||
if ($ban_in_contest) {
|
||||
return '请耐心等待比赛结束后题目对所有人可见了再提交';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
$answer_form->succ_href = $is_in_contest ? "/contest/{$contest['id']}/submissions" : '/submissions';
|
||||
$answer_form->runAtServer();
|
||||
|
||||
if ($custom_test_requirement) {
|
||||
$custom_test_form = newSubmissionForm('custom_test',
|
||||
$custom_test_requirement,
|
||||
function() {
|
||||
return uojRandAvaiableFileName('/tmp/');
|
||||
},
|
||||
'handleCustomTestUpload');
|
||||
$custom_test_form->appendHTML(<<<EOD
|
||||
<div id="div-custom_test_result"></div>
|
||||
EOD
|
||||
);
|
||||
$custom_test_form->succ_href = 'none';
|
||||
$custom_test_form->extra_validator = function() {
|
||||
global $ban_in_contest, $custom_test_submission;
|
||||
if ($ban_in_contest) {
|
||||
return '请耐心等待比赛结束后题目对所有人可见了再提交';
|
||||
}
|
||||
if ($custom_test_submission && $custom_test_submission['status'] != 'Judged') {
|
||||
return '上一个测评尚未结束';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
$custom_test_form->ctrl_enter_submit = true;
|
||||
$custom_test_form->setAjaxSubmit(<<<EOD
|
||||
function(response_text) {custom_test_onsubmit(response_text, $('#div-custom_test_result')[0], '{$_SERVER['REQUEST_URI']}?get=custom-test-status-details')}
|
||||
EOD
|
||||
);
|
||||
$custom_test_form->submit_button_config['text'] = UOJLocale::get('problems::run');
|
||||
$custom_test_form->runAtServer();
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['mathjax'] = '';
|
||||
$REQUIRE_LIB['shjs'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - ' . UOJLocale::get('problems::problem')) ?>
|
||||
<?php
|
||||
$limit = getUOJConf("/var/uoj_data/{$problem['id']}/problem.conf");
|
||||
$time_limit = $limit['time_limit'];
|
||||
$memory_limit = $limit['memory_limit'];
|
||||
?>
|
||||
<div class="row d-flex justify-content-center">
|
||||
<span class="badge badge-secondary mr-1">时间限制:<?=$time_limit!=null?"$time_limit s":"N/A"?></span>
|
||||
<span class="badge badge-secondary mr-1">空间限制:<?=$memory_limit!=null?"$memory_limit MB":"N/A"?></span>
|
||||
</div>
|
||||
<div class="float-right">
|
||||
<?= getClickZanBlock('P', $problem['id'], $problem['zan']) ?>
|
||||
</div>
|
||||
|
||||
<?php if ($contest): ?>
|
||||
<div class="page-header row">
|
||||
<h1 class="col-md-3 text-left"><small><?= $contest['name'] ?></small></h1>
|
||||
<h1 class="col-md-7 text-center"><?= $problem_letter ?>. <?= $problem['title'] ?></h1>
|
||||
<div class="col-md-2 text-right" id="contest-countdown"></div>
|
||||
</div>
|
||||
<a role="button" class="btn btn-info float-right" href="/contest/<?= $contest['id'] ?>/problem/<?= $problem['id'] ?>/statistics"><span class="glyphicon glyphicon-stats"></span> <?= UOJLocale::get('problems::statistics') ?></a>
|
||||
<?php if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?>
|
||||
<script type="text/javascript">
|
||||
checkContestNotice(<?= $contest['id'] ?>, '<?= UOJTime::$time_now_str ?>');
|
||||
$('#contest-countdown').countdown(<?= $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp() ?>);
|
||||
</script>
|
||||
<?php endif ?>
|
||||
<?php else: ?>
|
||||
<h1 class="page-header text-center">#<?= $problem['id']?>. <?= $problem['title'] ?></h1>
|
||||
<a role="button" class="btn btn-info float-right" href="/problem/<?= $problem['id'] ?>/statistics"><span class="glyphicon glyphicon-stats"></span> <?= UOJLocale::get('problems::statistics') ?></a>
|
||||
<?php endif ?>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item"><a class="nav-link active" href="#tab-statement" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-book"></span> <?= UOJLocale::get('problems::statement') ?></a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#tab-submit-answer" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-upload"></span> <?= UOJLocale::get('problems::submit') ?></a></li>
|
||||
<?php if ($custom_test_requirement): ?>
|
||||
<li class="nav-item"><a class="nav-link" href="#tab-custom-test" role="tab" data-toggle="tab"><span class="glyphicon glyphicon-console"></span> <?= UOJLocale::get('problems::custom test') ?></a></li>
|
||||
<?php endif ?>
|
||||
<?php if (hasProblemPermission($myUser, $problem)): ?>
|
||||
<li class="nav-item"><a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab"><?= UOJLocale::get('problems::manage') ?></a></li>
|
||||
<?php endif ?>
|
||||
<?php if ($contest): ?>
|
||||
<li class="nav-item"><a class="nav-link" href="/contest/<?= $contest['id'] ?>" role="tab"><?= UOJLocale::get('contests::back to the contest') ?></a></li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tab-statement">
|
||||
<article class="top-buffer-md"><?= $problem_content['statement'] ?></article>
|
||||
</div>
|
||||
<div class="tab-pane" id="tab-submit-answer">
|
||||
<div class="top-buffer-sm"></div>
|
||||
<?php if ($can_use_zip_upload): ?>
|
||||
<?php $zip_answer_form->printHTML(); ?>
|
||||
<hr />
|
||||
<strong><?= UOJLocale::get('problems::or upload files one by one') ?><br /></strong>
|
||||
<?php endif ?>
|
||||
<?php $answer_form->printHTML(); ?>
|
||||
</div>
|
||||
<?php if ($custom_test_requirement): ?>
|
||||
<div class="tab-pane" id="tab-custom-test">
|
||||
<div class="top-buffer-sm"></div>
|
||||
<?php $custom_test_form->printHTML(); ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('judger');
|
||||
requirePHPLib('data');
|
||||
|
||||
if (isSuperUser($myUser)) {
|
||||
$new_problem_form = new UOJForm('new_problem');
|
||||
$new_problem_form->handle = function() {
|
||||
DB::query("insert into problems (title, is_hidden, submission_requirement) values ('New Problem', 1, '{}')");
|
||||
$id = DB::insert_id();
|
||||
DB::query("insert into problems_contents (id, statement, statement_md) values ($id, '', '')");
|
||||
dataNewProblem($id);
|
||||
};
|
||||
$new_problem_form->submit_button_config['align'] = 'right';
|
||||
$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';
|
||||
$new_problem_form->submit_button_config['text'] = UOJLocale::get('problems::add new');
|
||||
$new_problem_form->submit_button_config['smart_confirm'] = '';
|
||||
|
||||
$new_problem_form->runAtServer();
|
||||
}
|
||||
|
||||
function echoProblem($problem) {
|
||||
global $myUser;
|
||||
if (isProblemVisibleToUser($problem, $myUser)) {
|
||||
echo '<tr class="text-center">';
|
||||
if ($problem['submission_id']) {
|
||||
echo '<td class="success">';
|
||||
} else {
|
||||
echo '<td>';
|
||||
}
|
||||
echo '#', $problem['id'], '</td>';
|
||||
echo '<td class="text-left">';
|
||||
if ($problem['is_hidden']) {
|
||||
echo ' <span class="text-danger">[隐藏]</span> ';
|
||||
}
|
||||
echo '<a href="/problem/', $problem['id'], '">', $problem['title'], '</a>';
|
||||
if (isset($_COOKIE['show_tags_mode'])) {
|
||||
foreach (queryProblemTags($problem['id']) as $tag) {
|
||||
echo '<a class="uoj-problem-tag">', '<span class="badge badge-pill badge-secondary">', HTML::escape($tag), '</span>', '</a>';
|
||||
}
|
||||
}
|
||||
echo '</td>';
|
||||
if (isset($_COOKIE['show_submit_mode'])) {
|
||||
$perc = $problem['submit_num'] > 0 ? round(100 * $problem['ac_num'] / $problem['submit_num']) : 0;
|
||||
echo <<<EOD
|
||||
<td><a href="/submissions?problem_id={$problem['id']}&min_score=100&max_score=100">×{$problem['ac_num']}</a></td>
|
||||
<td><a href="/submissions?problem_id={$problem['id']}">×{$problem['submit_num']}</a></td>
|
||||
<td>
|
||||
<div class="progress bot-buffer-no">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="$perc" aria-valuemin="0" aria-valuemax="100" style="width: $perc%; min-width: 20px;">{$perc}%</div>
|
||||
</div>
|
||||
</td>
|
||||
EOD;
|
||||
}
|
||||
echo '<td class="text-left">', getClickZanBlock('P', $problem['id'], $problem['zan']), '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
|
||||
$cond = array();
|
||||
|
||||
$search_tag = null;
|
||||
|
||||
$cur_tab = isset($_GET['tab']) ? $_GET['tab'] : 'all';
|
||||
if ($cur_tab == 'template') {
|
||||
$search_tag = "模板题";
|
||||
}
|
||||
if (isset($_GET['tag'])) {
|
||||
$search_tag = $_GET['tag'];
|
||||
}
|
||||
if ($search_tag) {
|
||||
$cond[] = "'".DB::escape($search_tag)."' in (select tag from problems_tags where problems_tags.problem_id = problems.id)";
|
||||
}
|
||||
if (isset($_GET["search"])) {
|
||||
$cond[]="title like '%".DB::escape($_GET["search"])."%' or id like '%".DB::escape($_GET["search"])."%'";
|
||||
}
|
||||
|
||||
if ($cond) {
|
||||
$cond = join($cond, ' and ');
|
||||
} else {
|
||||
$cond = '1';
|
||||
}
|
||||
|
||||
$header = '<tr>';
|
||||
$header .= '<th class="text-center" style="width:5em;">ID</th>';
|
||||
$header .= '<th>'.UOJLocale::get('problems::problem').'</th>';
|
||||
if (isset($_COOKIE['show_submit_mode'])) {
|
||||
$header .= '<th class="text-center" style="width:5em;">'.UOJLocale::get('problems::ac').'</th>';
|
||||
$header .= '<th class="text-center" style="width:5em;">'.UOJLocale::get('problems::submit').'</th>';
|
||||
$header .= '<th class="text-center" style="width:150px;">'.UOJLocale::get('problems::ac ratio').'</th>';
|
||||
}
|
||||
$header .= '<th class="text-center" style="width:180px;">'.UOJLocale::get('appraisal').'</th>';
|
||||
$header .= '</tr>';
|
||||
|
||||
$tabs_info = array(
|
||||
'all' => array(
|
||||
'name' => UOJLocale::get('problems::all problems'),
|
||||
'url' => "/problems"
|
||||
),
|
||||
'template' => array(
|
||||
'name' => UOJLocale::get('problems::template problems'),
|
||||
'url' => "/problems/template"
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
<?php
|
||||
echoLongTable(array('*'),
|
||||
"problems left join best_ac_submissions on best_ac_submissions.submitter = '{$myUser['username']}' and problems.id = best_ac_submissions.problem_id", $cond, 'order by id asc',
|
||||
$header,
|
||||
'echoProblem',
|
||||
array('page_len' => 3,
|
||||
'table_classes' => array('table', 'table-bordered', 'table-hover', 'table-striped'),
|
||||
'print_after_table' => function() {
|
||||
global $myUser;
|
||||
if (isSuperUser($myUser)) {
|
||||
global $new_problem_form;
|
||||
$new_problem_form->printHTML();
|
||||
}
|
||||
},
|
||||
'head_pagination' => true
|
||||
)
|
||||
);
|
||||
?>*/
|
||||
|
||||
$pag_config = array('page_len' => 100);
|
||||
$pag_config['col_names'] = array('*');
|
||||
$pag_config['table_name'] = "problems left join best_ac_submissions on best_ac_submissions.submitter = '{$myUser['username']}' and problems.id = best_ac_submissions.problem_id";
|
||||
$pag_config['cond'] = $cond;
|
||||
$pag_config['tail'] = "order by id asc";
|
||||
$pag = new Paginator($pag_config);
|
||||
|
||||
$div_classes = array('table-responsive');
|
||||
$table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped');
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('problems')) ?>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<?= HTML::tablist($tabs_info, $cur_tab, 'nav-pills') ?>
|
||||
</div>
|
||||
<div class="col-sm-4 order-sm-9 checkbox text-right">
|
||||
<label class="checkbox-inline" for="input-show_tags_mode"><input type="checkbox" id="input-show_tags_mode" <?= isset($_COOKIE['show_tags_mode']) ? 'checked="checked" ': ''?>/> <?= UOJLocale::get('problems::show tags') ?></label>
|
||||
<label class="checkbox-inline" for="input-show_submit_mode"><input type="checkbox" id="input-show_submit_mode" <?= isset($_COOKIE['show_submit_mode']) ? 'checked="checked" ': ''?>/> <?= UOJLocale::get('problems::show statistics') ?></label>
|
||||
</div>
|
||||
<div class="col-sm-4 order-sm-5">
|
||||
<?php echo $pag->pagination(); ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-buffer-sm"></div>
|
||||
<script type="text/javascript">
|
||||
$('#input-show_tags_mode').click(function() {
|
||||
if (this.checked) {
|
||||
$.cookie('show_tags_mode', '', {path: '/problems'});
|
||||
} else {
|
||||
$.removeCookie('show_tags_mode', {path: '/problems'});
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
$('#input-show_submit_mode').click(function() {
|
||||
if (this.checked) {
|
||||
$.cookie('show_submit_mode', '', {path: '/problems'});
|
||||
} else {
|
||||
$.removeCookie('show_submit_mode', {path: '/problems'});
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
echo '<div class="', join($div_classes, ' '), '">';
|
||||
echo '<table class="', join($table_classes, ' '), '">';
|
||||
echo '<thead>';
|
||||
echo $header;
|
||||
echo '</thead>';
|
||||
echo '<tbody>';
|
||||
|
||||
foreach ($pag->get() as $idx => $row) {
|
||||
echoProblem($row);
|
||||
echo "\n";
|
||||
}
|
||||
if ($pag->isEmpty()) {
|
||||
echo '<tr><td class="text-center" colspan="233">'.UOJLocale::get('none').'</td></tr>';
|
||||
}
|
||||
|
||||
echo '</tbody>';
|
||||
echo '</table>';
|
||||
echo '</div>';
|
||||
|
||||
if (isSuperUser($myUser)) {
|
||||
$new_problem_form->printHTML();
|
||||
}
|
||||
|
||||
echo $pag->pagination();
|
||||
?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
if (!hasProblemPermission($myUser, $problem)) {
|
||||
become403Page();
|
||||
}
|
||||
|
||||
$problem_content = queryProblemContent($problem['id']);
|
||||
$problem_tags = queryProblemTags($problem['id']);
|
||||
|
||||
$problem_editor = new UOJBlogEditor();
|
||||
$problem_editor->name = 'problem';
|
||||
$problem_editor->blog_url = "/problem/{$problem['id']}";
|
||||
$problem_editor->cur_data = array(
|
||||
'title' => $problem['title'],
|
||||
'content_md' => $problem_content['statement_md'],
|
||||
'content' => $problem_content['statement'],
|
||||
'tags' => $problem_tags,
|
||||
'is_hidden' => $problem['is_hidden']
|
||||
);
|
||||
$problem_editor->label_text = array_merge($problem_editor->label_text, array(
|
||||
'view blog' => '查看题目',
|
||||
'blog visibility' => '题目可见性'
|
||||
));
|
||||
|
||||
$problem_editor->save = function($data) {
|
||||
global $problem, $problem_tags;
|
||||
DB::update("update problems set title = '".DB::escape($data['title'])."' where id = {$problem['id']}");
|
||||
DB::update("update problems_contents set statement = '".DB::escape($data['content'])."', statement_md = '".DB::escape($data['content_md'])."' where id = {$problem['id']}");
|
||||
|
||||
if ($data['tags'] !== $problem_tags) {
|
||||
DB::delete("delete from problems_tags where problem_id = {$problem['id']}");
|
||||
foreach ($data['tags'] as $tag) {
|
||||
DB::insert("insert into problems_tags (problem_id, tag) values ({$problem['id']}, '".DB::escape($tag)."')");
|
||||
}
|
||||
}
|
||||
if ($data['is_hidden'] != $problem['is_hidden'] ) {
|
||||
DB::update("update problems set is_hidden = {$data['is_hidden']} where id = {$problem['id']}");
|
||||
DB::update("update submissions set is_hidden = {$data['is_hidden']} where problem_id = {$problem['id']}");
|
||||
DB::update("update hacks set is_hidden = {$data['is_hidden']} where problem_id = {$problem['id']}");
|
||||
}
|
||||
};
|
||||
|
||||
$problem_editor->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - 编辑 - 题目管理') ?>
|
||||
<h1 class="page-header" align="center">#<?=$problem['id']?> : <?=$problem['title']?> 管理</h1>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item"><a class="nav-link active" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">编辑</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/managers" role="tab">管理者</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/problem/<?= $problem['id'] ?>/manage/data" role="tab">数据</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/problem/<?=$problem['id']?>" role="tab">返回</a></li>
|
||||
</ul>
|
||||
<?php $problem_editor->printHTML() ?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$contest = validateUInt($_GET['contest_id']) ? queryContest($_GET['contest_id']) : null;
|
||||
if ($contest != null) {
|
||||
genMoreContestInfo($contest);
|
||||
if (!isContestProblemVisibleToUser($problem, $contest, $myUser)) {
|
||||
become404Page();
|
||||
}
|
||||
} else {
|
||||
if (!isProblemVisibleToUser($problem, $myUser)) {
|
||||
become404Page();
|
||||
}
|
||||
}
|
||||
|
||||
function scoreDistributionData() {
|
||||
$data = array();
|
||||
$result = DB::select("select score, count(*) from submissions where problem_id = {$_GET['id']} and score is not null group by score");
|
||||
$is_res_empty = true;
|
||||
$has_score_0 = false;
|
||||
$has_score_100 = false;
|
||||
while ($row = DB::fetch($result, MYSQLI_NUM)) {
|
||||
if ($row[0] == 0) {
|
||||
$has_score_0 = true;
|
||||
} elseif ($row[0] == 100) {
|
||||
$has_score_100 = true;
|
||||
}
|
||||
$score = $row[0] * 100;
|
||||
$data[] = array('score' => $score, 'count' => $row[1]);
|
||||
}
|
||||
if (!$has_score_0) {
|
||||
array_unshift($data, array('score' => 0, 'count' => 0));
|
||||
}
|
||||
if (!$has_score_100) {
|
||||
$data[] = array('score' => 10000, 'count' => 0);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data = scoreDistributionData();
|
||||
$pre_data = $data;
|
||||
$suf_data = $data;
|
||||
for ($i = 0; $i < count($data); $i++) {
|
||||
$data[$i]['score'] /= 100;
|
||||
}
|
||||
for ($i = 1; $i < count($data); $i++) {
|
||||
$pre_data[$i]['count'] += $pre_data[$i - 1]['count'];
|
||||
}
|
||||
for ($i = count($data) - 1; $i > 0; $i--) {
|
||||
$suf_data[$i - 1]['count'] += $suf_data[$i]['count'];
|
||||
}
|
||||
|
||||
$submissions_sort_by_choice = !isset($_COOKIE['submissions-sort-by-code-length']) ? 'time' : 'tot_size';
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['morris'] = "";
|
||||
?>
|
||||
<?php echoUOJPageHeader(HTML::stripTags($problem['title']) . ' - ' . UOJLocale::get('problems::statistics')) ?>
|
||||
|
||||
<h1 class="page-header text-center"><?= $problem['title'] ?> <?= UOJLocale::get('problems::statistics') ?></h1>
|
||||
|
||||
<?php if ($contest && !hasContestPermission($myUser, $contest) && $contest['cur_progress'] <= CONTEST_IN_PROGRESS): ?>
|
||||
<h2 class="text-center text-muted">比赛尚未结束</h2>
|
||||
<?php else: ?>
|
||||
<h2 class="text-center"><?= UOJLocale::get('problems::accepted submissions') ?></h2>
|
||||
<div class="text-right bot-buffer-sm">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<a href="<?=$SERVER['REQUEST_URI']?>" class="<?=$submissions_sort_by_choice == 'time' ? 'btn btn-info btn-xs active' : 'btn btn-info btn-xs'?>" id="submissions-sort-by-run-time"><?= UOJLocale::get('problems::fastest') ?></a>
|
||||
<a href="<?=$SERVER['REQUEST_URI']?>" class="<?=$submissions_sort_by_choice == 'tot_size' ? 'btn btn-info btn-xs active' : 'btn btn-info btn-xs'?>" id="submissions-sort-by-code-length"><?= UOJLocale::get('problems::shortest') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#submissions-sort-by-run-time').click(function() {
|
||||
$.cookie('submissions-sort-by-run-time', '');
|
||||
$.removeCookie('submissions-sort-by-code-length');
|
||||
});
|
||||
$('#submissions-sort-by-code-length').click(function() {
|
||||
$.cookie('submissions-sort-by-code-length', '');
|
||||
$.removeCookie('submissions-sort-by-run-time');
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php if ($submissions_sort_by_choice == 'time'): ?>
|
||||
<?php echoSubmissionsList("best_ac_submissions.submission_id = submissions.id and best_ac_submissions.problem_id = {$problem['id']}", 'order by best_ac_submissions.used_time, best_ac_submissions.used_memory, best_ac_submissions.tot_size', array('judge_time_hidden' => '', 'table_name' => 'best_ac_submissions, submissions'), $myUser); ?>
|
||||
<?php else: ?>
|
||||
<?php echoSubmissionsList("best_ac_submissions.shortest_id = submissions.id and best_ac_submissions.problem_id = {$problem['id']}", 'order by best_ac_submissions.shortest_tot_size, best_ac_submissions.shortest_used_time, best_ac_submissions.shortest_used_memory', array('judge_time_hidden' => '', 'table_name' => 'best_ac_submissions, submissions'), $myUser); ?>
|
||||
<?php endif ?>
|
||||
|
||||
<h2 class="text-center"><?= UOJLocale::get('problems::score distribution') ?></h2>
|
||||
<div id="score-distribution-chart" style="height: 250px;"></div>
|
||||
<script type="text/javascript">
|
||||
new Morris.Bar({
|
||||
element: 'score-distribution-chart',
|
||||
data: <?= json_encode($data) ?>,
|
||||
barColors: function(r, s, type) {
|
||||
return getColOfScore(r.label);
|
||||
},
|
||||
xkey: 'score',
|
||||
ykeys: ['count'],
|
||||
labels: ['number'],
|
||||
hoverCallback: function(index, options, content, row) {
|
||||
var scr = row.score;
|
||||
return '<div class="morris-hover-row-label">' + 'score: ' + scr + '</div>' +
|
||||
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&min_score=' + scr + '&max_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
|
||||
},
|
||||
resize: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<h2 class="text-center"><?= UOJLocale::get('problems::prefix sum of score distribution') ?></h2>
|
||||
<div id="score-distribution-chart-pre" style="height: 250px;"></div>
|
||||
<script type="text/javascript">
|
||||
new Morris.Line({
|
||||
element: 'score-distribution-chart-pre',
|
||||
data: <?= json_encode($pre_data) ?>,
|
||||
xkey: 'score',
|
||||
ykeys: ['count'],
|
||||
labels: ['number'],
|
||||
lineColors: function(row, sidx, type) {
|
||||
if (type == 'line') {
|
||||
return '#0b62a4';
|
||||
}
|
||||
return getColOfScore(row.src.score / 100);
|
||||
},
|
||||
xLabelFormat: function(x) {
|
||||
return (x.getTime() / 100).toString();
|
||||
},
|
||||
hoverCallback: function(index, options, content, row) {
|
||||
var scr = row.score / 100;
|
||||
return '<div class="morris-hover-row-label">' + 'score: ≤' + scr + '</div>' +
|
||||
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&max_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
|
||||
},
|
||||
resize: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<h2 class="text-center"><?= UOJLocale::get('problems::suffix sum of score distribution') ?></h2>
|
||||
<div id="score-distribution-chart-suf" style="height: 250px;"></div>
|
||||
<script type="text/javascript">
|
||||
new Morris.Line({
|
||||
element: 'score-distribution-chart-suf',
|
||||
data: <?= json_encode($suf_data) ?>,
|
||||
xkey: 'score',
|
||||
ykeys: ['count'],
|
||||
labels: ['number'],
|
||||
lineColors: function(row, sidx, type) {
|
||||
if (type == 'line') {
|
||||
return '#0b62a4';
|
||||
}
|
||||
return getColOfScore(row.src.score / 100);
|
||||
},
|
||||
xLabelFormat: function(x) {
|
||||
return (x.getTime() / 100).toString();
|
||||
},
|
||||
hoverCallback: function(index, options, content, row) {
|
||||
var scr = row.score / 100;
|
||||
return '<div class="morris-hover-row-label">' + 'score: ≥' + scr + '</div>' +
|
||||
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&min_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
|
||||
},
|
||||
resize: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
if (isset($_GET['type']) && $_GET['type'] == 'rating') {
|
||||
$config = array('page_len' => 100);
|
||||
} else {
|
||||
become404Page();
|
||||
}
|
||||
?>
|
||||
<?php echoUOJPageHeader('比赛排行榜') ?>
|
||||
<?php echoRanklist($config) ?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
function handleRegisterPost() {
|
||||
if (!crsf_check()) {
|
||||
return '页面已过期';
|
||||
}
|
||||
if (!isset($_POST['username'])) {
|
||||
return "无效表单";
|
||||
}
|
||||
if (!isset($_POST['password'])) {
|
||||
return "无效表单";
|
||||
}
|
||||
if (!isset($_POST['email'])) {
|
||||
return "无效表单";
|
||||
}
|
||||
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
$email = $_POST['email'];
|
||||
if (!validateUsername($username)) {
|
||||
return "失败:无效用户名。";
|
||||
}
|
||||
if (queryUser($username)) {
|
||||
return "失败:用户名已存在。";
|
||||
}
|
||||
if (!validatePassword($password)) {
|
||||
return "失败:无效密码。";
|
||||
}
|
||||
if (!validateEmail($email)) {
|
||||
return "失败:无效电子邮箱。";
|
||||
}
|
||||
|
||||
$password = getPasswordToStore($password, $username);
|
||||
|
||||
$esc_email = DB::escape($email);
|
||||
|
||||
$svn_pw = uojRandString(10);
|
||||
if (!DB::selectCount("SELECT COUNT(*) FROM user_info")) {
|
||||
DB::query("insert into user_info (username, email, password, svn_password, register_time, usergroup) values ('$username', '$esc_email', '$password', '$svn_pw', now(), 'S')");
|
||||
} else {
|
||||
DB::query("insert into user_info (username, email, password, svn_password, register_time) values ('$username', '$esc_email', '$password', '$svn_pw', now())");
|
||||
}
|
||||
|
||||
return "欢迎你!" . $username . ",你已成功注册。";
|
||||
}
|
||||
|
||||
if (isset($_POST['register'])) {
|
||||
echo handleRegisterPost();
|
||||
die();
|
||||
} elseif (isset($_POST['check_username'])) {
|
||||
$username = $_POST['username'];
|
||||
if (validateUsername($username) && !queryUser($username)) {
|
||||
echo '{"ok" : true}';
|
||||
} else {
|
||||
echo '{"ok" : false}';
|
||||
}
|
||||
die();
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['md5'] = '';
|
||||
$REQUIRE_LIB['dialog'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('register')) ?>
|
||||
<h2 class="page-header"><?= UOJLocale::get('register') ?></h2>
|
||||
<form id="form-register" class="form-horizontal">
|
||||
<div id="div-email" class="form-group">
|
||||
<label for="input-email" class="col-sm-2 control-label"><?= UOJLocale::get('email') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="email" class="form-control" id="input-email" name="email" placeholder="<?= UOJLocale::get('enter your email') ?>" maxlength="50" />
|
||||
<span class="help-block" id="help-email"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div-username" class="form-group">
|
||||
<label for="input-username" class="col-sm-2 control-label"><?= UOJLocale::get('username') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="text" class="form-control" id="input-username" name="username" placeholder="<?= UOJLocale::get('enter your username') ?>" maxlength="20" />
|
||||
<span class="help-block" id="help-username"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div-password" class="form-group">
|
||||
<label for="input-password" class="col-sm-2 control-label"><?= UOJLocale::get('password') ?></label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" />
|
||||
<input type="password" class="form-control top-buffer-sm" id="input-confirm_password" placeholder="<?= UOJLocale::get('re-enter your password') ?>" maxlength="20" />
|
||||
<span class="help-block" id="help-password"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-3">
|
||||
<button type="submit" id="button-submit" class="btn btn-secondary"><?= UOJLocale::get('submit') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
function checkUsernameNotInUse() {
|
||||
var ok = false;
|
||||
$.ajax({
|
||||
url : '/register',
|
||||
type : 'POST',
|
||||
dataType : 'json',
|
||||
async : false,
|
||||
|
||||
data : {
|
||||
check_username : '',
|
||||
username : $('#input-username').val()
|
||||
},
|
||||
success : function(data) {
|
||||
ok = data.ok;
|
||||
},
|
||||
error : function(XMLHttpRequest, textStatus, errorThrown) {
|
||||
alert(XMLHttpRequest.responseText);
|
||||
ok = false;
|
||||
}
|
||||
});
|
||||
return ok;
|
||||
}
|
||||
function validateRegisterPost() {
|
||||
var ok = true;
|
||||
ok &= getFormErrorAndShowHelp('email', validateEmail);
|
||||
ok &= getFormErrorAndShowHelp('username', function(str) {
|
||||
var err = validateUsername(str);
|
||||
if (err)
|
||||
return err;
|
||||
if (!checkUsernameNotInUse())
|
||||
return '该用户名已被人使用了。';
|
||||
return '';
|
||||
})
|
||||
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
|
||||
return ok;
|
||||
}
|
||||
|
||||
function submitRegisterPost() {
|
||||
if (!validateRegisterPost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.post('/register', {
|
||||
_token : "<?= crsf_token() ?>",
|
||||
register : '',
|
||||
username : $('#input-username').val(),
|
||||
email : $('#input-email').val(),
|
||||
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
|
||||
}, function(msg) {
|
||||
if (/^欢迎你!/.test(msg)) {
|
||||
BootstrapDialog.show({
|
||||
title : '注册成功',
|
||||
message : msg,
|
||||
type : BootstrapDialog.TYPE_SUCCESS,
|
||||
buttons: [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}],
|
||||
onhidden : function(dialog) {
|
||||
var prevUrl = document.referrer;
|
||||
if (!prevUrl) {
|
||||
prevUrl = '/';
|
||||
};
|
||||
window.location.href = prevUrl;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
BootstrapDialog.show({
|
||||
title : '注册失败',
|
||||
message : msg,
|
||||
type : BootstrapDialog.TYPE_DANGER,
|
||||
buttons: [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
$(document).ready(function() {
|
||||
$('#form-register').submit(function(e) {
|
||||
submitRegisterPost();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
if (!isset($_GET['p'])) {
|
||||
become404Page();
|
||||
}
|
||||
function resetPassword() {
|
||||
list($username, $check_code) = explode('.', base64url_decode($_GET['p']));
|
||||
if (!isset($_POST['newPW']) || !validatePassword($_POST['newPW'])) {
|
||||
return '操作失败,无效密码';
|
||||
}
|
||||
if (!isset($username) || !validateUsername($username)) {
|
||||
return '不明错误';
|
||||
}
|
||||
if (!isset($check_code)) {
|
||||
return '不明错误';
|
||||
}
|
||||
|
||||
$newPW = $_POST['newPW'];
|
||||
$user = queryUser($username);
|
||||
if ($user == null) {
|
||||
return '不明错误';
|
||||
}
|
||||
if ($check_code !== md5($user['username'] . '+' . $user['password'])) {
|
||||
return '不明错误';
|
||||
}
|
||||
$newPW = getPasswordToStore($newPW, $user['username']);
|
||||
DB::update("update user_info set password = '$newPW' where username = '{$user['username']}'");
|
||||
return 'ok';
|
||||
}
|
||||
if (isset($_POST['reset'])) {
|
||||
die(resetPassword());
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['dialog'] = '';
|
||||
$REQUIRE_LIB['md5'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader('更改密码') ?>
|
||||
<h2 class="page-header">更改密码</h2>
|
||||
<form id="form-reset" class="form-horizontal">
|
||||
<div id="div-password" class="form-group">
|
||||
<label for="input-password" class="col-sm-2 control-label">新密码</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="password" class="form-control" id="input-password" name="password" placeholder="输入新密码" maxlength="20" />
|
||||
<input type="password" class="form-control top-buffer-sm" id="input-confirm_password" placeholder="再次输入新密码" maxlength="20" />
|
||||
<span class="help-block" id="help-password"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-3">
|
||||
<button type="submit" id="button-submit" class="btn btn-secondary">提交</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
function validateResetPwPost() {
|
||||
var ok = true;
|
||||
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
|
||||
return ok;
|
||||
}
|
||||
$(document).ready(function() {
|
||||
$('#form-reset').submit(function(e) {
|
||||
if (!validateResetPwPost()) {
|
||||
return false;
|
||||
}
|
||||
$.post(<?= json_encode($_SERVER['REQUEST_URI']) ?>, {
|
||||
reset : '',
|
||||
newPW : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
|
||||
}, function(res) {
|
||||
if (res == 'ok') {
|
||||
BootstrapDialog.show({
|
||||
title : '提示',
|
||||
message : '密码更改成功',
|
||||
type : BootstrapDialog.TYPE_SUCCESS,
|
||||
buttons: [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}],
|
||||
onhidden : function(dialog) {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
BootstrapDialog.show({
|
||||
title : '提示',
|
||||
message : res,
|
||||
type : BootstrapDialog.TYPE_DANGER,
|
||||
buttons: [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}]
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
$blogs_cond = "poster = '".UOJContext::userid()."' and is_draft = false";
|
||||
if (!UOJContext::hasBlogPermission()) {
|
||||
$blogs_cond .= " and is_hidden = false";
|
||||
}
|
||||
|
||||
$display_blogs_cond = $blogs_cond;
|
||||
|
||||
if (isset($_GET['tag'])) {
|
||||
$blog_tag_required = $_GET['tag'];
|
||||
$display_blogs_cond .= " and '".DB::escape($blog_tag_required)."' in (select tag from blogs_tags where blogs_tags.blog_id = blogs.id)";
|
||||
} else {
|
||||
$blog_tag_required = null;
|
||||
}
|
||||
|
||||
$blogs_pag = new Paginator(array(
|
||||
'col_names' => array('*'),
|
||||
'table_name' => 'blogs',
|
||||
'cond' => $display_blogs_cond,
|
||||
'tail' => 'order by post_time desc',
|
||||
'page_len' => 10
|
||||
));
|
||||
|
||||
$all_tags = DB::selectAll("select distinct tag from blogs_tags where blog_id in (select id from blogs where $blogs_cond)");
|
||||
|
||||
requireLib('mathjax');
|
||||
requireLib('shjs');
|
||||
?>
|
||||
<?php echoUOJPageHeader('日志') ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<?php if (UOJContext::hasBlogPermission()): ?>
|
||||
<div class="btn-group d-flex">
|
||||
<a href="<?=HTML::blog_url(UOJContext::userid(), '/post/new/write')?>" class="btn btn-primary"><span class="glyphicon glyphicon-edit"></span> 写新博客</a>
|
||||
<a href="<?=HTML::blog_url(UOJContext::userid(), '/slide/new/write')?>" class="btn btn-primary"><span class="glyphicon glyphicon-edit"></span> 写新幻灯片</a>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div class="card border-info top-buffer-sm">
|
||||
<div class="card-header bg-info">标签</div>
|
||||
<div class="card-body">
|
||||
<?php if ($all_tags): ?>
|
||||
<?php foreach ($all_tags as $tag): ?>
|
||||
<?php echoBlogTag($tag['tag']) ?>
|
||||
<?php endforeach ?>
|
||||
<?php else: ?>
|
||||
<div class="text-muted">暂无</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<?php if (!$blog_tag_required): ?>
|
||||
<?php if ($blogs_pag->isEmpty()): ?>
|
||||
<div class="text-muted">此人很懒,什么博客也没留下。</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($blogs_pag->get() as $blog): ?>
|
||||
<?php echoBlog($blog, array('is_preview' => true)) ?>
|
||||
<?php endforeach ?>
|
||||
<div class="text-right text-muted">共 <?= $blogs_pag->n_rows ?> 篇博客</div>
|
||||
<?php endif ?>
|
||||
<?php else: ?>
|
||||
<?php if ($blogs_pag->isEmpty()): ?>
|
||||
<div class="alert alert-danger">
|
||||
没有找到包含 “<?= HTML::escape($blog_tag_required) ?>” 标签的博客:
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-success">
|
||||
共找到 <?= $blogs_pag->n_rows ?> 篇包含 “<?= HTML::escape($blog_tag_required) ?>” 标签的博客:
|
||||
</div>
|
||||
<?php foreach ($blogs_pag->get() as $blog): ?>
|
||||
<?php echoBlog($blog, array('is_preview' => true)) ?>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?= $blogs_pag->pagination() ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!UOJContext::hasBlogPermission()) {
|
||||
become403Page();
|
||||
}
|
||||
if (!isset($_GET['id']) || !validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHis($blog)) {
|
||||
become404Page();
|
||||
}
|
||||
|
||||
$delete_form = new UOJForm('delete');
|
||||
$delete_form->handle = function() {
|
||||
global $blog;
|
||||
deleteBlog($blog['id']);
|
||||
};
|
||||
$delete_form->submit_button_config['class_str'] = 'btn btn-danger';
|
||||
$delete_form->submit_button_config['text'] = '是的,我确定要删除';
|
||||
$delete_form->succ_href = "/archive";
|
||||
|
||||
$delete_form->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader('删除博客 - ' . HTML::stripTags($blog['title'])) ?>
|
||||
<h3>您真的要删除博客 <?= $blog['title'] ?> 吗?该操作不可逆!</h3>
|
||||
<?php $delete_form->printHTML(); ?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!UOJContext::hasBlogPermission()) {
|
||||
become403Page();
|
||||
}
|
||||
if (isset($_GET['id'])) {
|
||||
if (!validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHisBlog($blog)) {
|
||||
become404Page();
|
||||
}
|
||||
} else {
|
||||
$blog = DB::selectFirst("select * from blogs where poster = '".UOJContext::user()['username']."' and type = 'B' and is_draft = true");
|
||||
}
|
||||
|
||||
$blog_editor = new UOJBlogEditor();
|
||||
$blog_editor->name = 'blog';
|
||||
if ($blog) {
|
||||
$blog_editor->cur_data = array(
|
||||
'title' => $blog['title'],
|
||||
'content_md' => $blog['content_md'],
|
||||
'content' => $blog['content'],
|
||||
'tags' => queryBlogTags($blog['id']),
|
||||
'is_hidden' => $blog['is_hidden']
|
||||
);
|
||||
} else {
|
||||
$blog_editor->cur_data = array(
|
||||
'title' => '新博客',
|
||||
'content_md' => '',
|
||||
'content' => '',
|
||||
'tags' => array(),
|
||||
'is_hidden' => true
|
||||
);
|
||||
}
|
||||
if ($blog && !$blog['is_draft']) {
|
||||
$blog_editor->blog_url = HTML::blog_url(UOJContext::user()['username'], "/post/{$blog['id']}");
|
||||
} else {
|
||||
$blog_editor->blog_url = null;
|
||||
}
|
||||
|
||||
function updateBlog($id, $data) {
|
||||
DB::update("update blogs set title = '".DB::escape($data['title'])."', content = '".DB::escape($data['content'])."', content_md = '".DB::escape($data['content_md'])."', is_hidden = {$data['is_hidden']} where id = {$id}");
|
||||
}
|
||||
function insertBlog($data) {
|
||||
DB::insert("insert into blogs (title, content, content_md, poster, is_hidden, is_draft, post_time) values ('".DB::escape($data['title'])."', '".DB::escape($data['content'])."', '".DB::escape($data['content_md'])."', '".Auth::id()."', {$data['is_hidden']}, {$data['is_draft']}, now())");
|
||||
}
|
||||
|
||||
$blog_editor->save = function($data) {
|
||||
global $blog;
|
||||
$ret = array();
|
||||
if ($blog) {
|
||||
if ($blog['is_draft']) {
|
||||
if ($data['is_hidden']) {
|
||||
updateBlog($blog['id'], $data);
|
||||
} else {
|
||||
deleteBlog($blog['id']);
|
||||
insertBlog(array_merge($data, array('is_draft' => 0)));
|
||||
$blog = array('id' => DB::insert_id(), 'tags' => array());
|
||||
$ret['blog_write_url'] = HTML::blog_url(UOJContext::user()['username'], "/post/{$blog['id']}/write");
|
||||
$ret['blog_url'] = HTML::blog_url(UOJContext::user()['username'], "/post/{$blog['id']}");
|
||||
}
|
||||
} else {
|
||||
updateBlog($blog['id'], $data);
|
||||
}
|
||||
} else {
|
||||
insertBlog(array_merge($data, array('is_draft' => $data['is_hidden'] ? 1 : 0)));
|
||||
$blog = array('id' => DB::insert_id(), 'tags' => array());
|
||||
if (!$data['is_hidden']) {
|
||||
$ret['blog_write_url'] = HTML::blog_url(UOJContext::user()['username'], "/post/{$blog['id']}/write");
|
||||
$ret['blog_url'] = HTML::blog_url(UOJContext::user()['username'], "/post/{$blog['id']}");
|
||||
}
|
||||
}
|
||||
if ($data['tags'] !== $blog['tags']) {
|
||||
DB::delete("delete from blogs_tags where blog_id = {$blog['id']}");
|
||||
foreach ($data['tags'] as $tag) {
|
||||
DB::insert("insert into blogs_tags (blog_id, tag) values ({$blog['id']}, '".DB::escape($tag)."')");
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
};
|
||||
|
||||
$blog_editor->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader('写博客') ?>
|
||||
<div class="text-right">
|
||||
<a href="http://uoj.ac/blog/7">这玩意儿怎么用?</a>
|
||||
</div>
|
||||
<?php $blog_editor->printHTML() ?>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
$blogs_pag = new Paginator(array(
|
||||
'col_names' => array('*'),
|
||||
'table_name' => 'blogs',
|
||||
'cond' => "poster = '".UOJContext::user()['username']."' and is_hidden = 0",
|
||||
'tail' => 'order by post_time desc limit 5',
|
||||
'echo_full' => true
|
||||
));
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['mathjax'] = '';
|
||||
$REQUIRE_LIB['shjs'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader(UOJContext::user()['username'] . '的博客') ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<?php if ($blogs_pag->isEmpty()): ?>
|
||||
<div class="text-muted">此人很懒,什么博客也没留下。</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($blogs_pag->get() as $blog): ?>
|
||||
<?php echoBlog($blog, array('is_preview' => true)) ?>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<img class="media-object img-thumbnail center-block" alt="<?= UOJContext::user()['username'] ?> Avatar" src="<?= HTML::avatar_addr(UOJContext::user(), 256) ?>" />
|
||||
</div>
|
||||
</div>
|
||||
<?php echoUOJPageFooter() ?>
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
call_user_func(function() { // to prevent variable scope leak
|
||||
|
||||
Route::pattern('id', '[1-9][0-9]{0,9}');
|
||||
Route::pattern('blog_username', '[a-zA-Z0-9_\-]{1,20}');
|
||||
|
||||
switch (UOJConfig::$data['switch']['blog-domain-mode']) {
|
||||
case 1:
|
||||
$domain = '{blog_username}.'.UOJConfig::$data['web']['blog']['host'];
|
||||
$prefix = '';
|
||||
break;
|
||||
case 2:
|
||||
$domain = UOJConfig::$data['web']['blog']['host'];
|
||||
$prefix = '/{blog_username}';
|
||||
break;
|
||||
case 3:
|
||||
$domain = UOJConfig::$data['web']['main']['host'];
|
||||
$prefix = '/blog/{blog_username}';
|
||||
break;
|
||||
}
|
||||
|
||||
Route::group([
|
||||
'domain' => UOJConfig::$data['web']['blog']['host']
|
||||
], function() {
|
||||
Route::any("/", '/blogs.php');
|
||||
Route::any("/blogs/{id}", '/blog_show.php');
|
||||
Route::any("/post/{id}", '/blog_show.php');
|
||||
}
|
||||
);
|
||||
Route::group([
|
||||
'domain' => $domain,
|
||||
'onload' => function() {
|
||||
UOJContext::setupBlog();
|
||||
}
|
||||
], function() use ($prefix) {
|
||||
Route::any("$prefix/", '/subdomain/blog/index.php');
|
||||
Route::any("$prefix/archive", '/subdomain/blog/archive.php');
|
||||
Route::any("$prefix/aboutme", '/subdomain/blog/aboutme.php');
|
||||
Route::any("$prefix/click-zan", '/click_zan.php');
|
||||
Route::any("$prefix/post/{id}", '/subdomain/blog/blog.php');
|
||||
Route::any("$prefix/slide/{id}", '/subdomain/blog/slide.php');
|
||||
Route::any("$prefix/post/(?:{id}|new)/write", '/subdomain/blog/blog_write.php');
|
||||
Route::any("$prefix/slide/(?:{id}|new)/write", '/subdomain/blog/slide_write.php');
|
||||
Route::any("$prefix/post/{id}/delete", '/subdomain/blog/blog_delete.php');
|
||||
}
|
||||
);
|
||||
});
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!isset($_GET['id']) || !validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHisSlide($blog)) {
|
||||
become404Page();
|
||||
}
|
||||
if ($blog['is_hidden'] && !UOJContext::hasBlogPermission()) {
|
||||
become403Page();
|
||||
}
|
||||
|
||||
$page_config = UOJContext::pageConfig();
|
||||
$page_config['PageTitle'] = HTML::stripTags($blog['title']) . ' - 幻灯片';
|
||||
$page_config['content'] = $blog['content'];
|
||||
uojIncludeView('slide', $page_config);
|
||||
?>
|
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
|
||||
if (!UOJContext::hasBlogPermission()) {
|
||||
become403Page();
|
||||
}
|
||||
if (isset($_GET['id'])) {
|
||||
if (!validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHisSlide($blog)) {
|
||||
become404Page();
|
||||
}
|
||||
} else {
|
||||
$blog = DB::selectFirst("select * from blogs where poster = '".UOJContext::user()['username']."' and type = 'S' and is_draft = true");
|
||||
}
|
||||
|
||||
$blog_editor = new UOJBlogEditor();
|
||||
$blog_editor->type = 'slide';
|
||||
$blog_editor->name = 'blog';
|
||||
if ($blog) {
|
||||
$blog_editor->cur_data = array(
|
||||
'title' => $blog['title'],
|
||||
'content_md' => $blog['content_md'],
|
||||
'content' => $blog['content'],
|
||||
'tags' => queryBlogTags($blog['id']),
|
||||
'is_hidden' => $blog['is_hidden']
|
||||
);
|
||||
} else {
|
||||
$blog_editor->cur_data = array(
|
||||
'title' => '新幻灯片',
|
||||
'content_md' => '',
|
||||
'content' => '',
|
||||
'tags' => array(),
|
||||
'is_hidden' => true
|
||||
);
|
||||
}
|
||||
if ($blog && !$blog['is_draft']) {
|
||||
$blog_editor->blog_url = HTML::blog_url(UOJContext::user()['username'], "/post/{$blog['id']}");
|
||||
} else {
|
||||
$blog_editor->blog_url = null;
|
||||
}
|
||||
|
||||
function updateBlog($id, $data) {
|
||||
DB::update("update blogs set title = '".DB::escape($data['title'])."', content = '".DB::escape($data['content'])."', content_md = '".DB::escape($data['content_md'])."', is_hidden = {$data['is_hidden']} where id = {$id}");
|
||||
}
|
||||
function insertSlide($data) {
|
||||
DB::insert("insert into blogs (type, title, content, content_md, poster, is_hidden, is_draft, post_time) values ('S', '".DB::escape($data['title'])."', '".DB::escape($data['content'])."', '".DB::escape($data['content_md'])."', '".Auth::id()."', {$data['is_hidden']}, {$data['is_draft']}, now())");
|
||||
}
|
||||
|
||||
$blog_editor->save = function($data) {
|
||||
global $blog;
|
||||
$ret = array();
|
||||
if ($blog) {
|
||||
if ($blog['is_draft']) {
|
||||
if ($data['is_hidden']) {
|
||||
updateBlog($blog['id'], $data);
|
||||
} else {
|
||||
deleteBlog($blog['id']);
|
||||
insertSlide(array_merge($data, array('is_draft' => 0)));
|
||||
$blog = array('id' => DB::insert_id(), 'tags' => array());
|
||||
$ret['blog_write_url'] = HTML::blog_url(UOJContext::user()['username'], "/slide/{$blog['id']}/write");
|
||||
$ret['blog_url'] = HTML::blog_url(UOJContext::user()['username'], "/post/{$blog['id']}");
|
||||
}
|
||||
} else {
|
||||
updateBlog($blog['id'], $data);
|
||||
}
|
||||
} else {
|
||||
insertSlide(array_merge($data, array('is_draft' => $data['is_hidden'] ? 1 : 0)));
|
||||
$blog = array('id' => DB::insert_id(), 'tags' => array());
|
||||
}
|
||||
if ($data['tags'] !== $blog['tags']) {
|
||||
DB::delete("delete from blogs_tags where blog_id = {$blog['id']}");
|
||||
foreach ($data['tags'] as $tag) {
|
||||
DB::insert("insert into blogs_tags (blog_id, tag) values ({$blog['id']}, '".DB::escape($tag)."')");
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
};
|
||||
|
||||
$blog_editor->runAtServer();
|
||||
?>
|
||||
<?php echoUOJPageHeader('写幻灯片') ?>
|
||||
<div class="text-right">
|
||||
<a href="http://uoj.ac/blog/75">这玩意儿怎么用?</a>
|
||||
</div>
|
||||
<?php $blog_editor->printHTML() ?>
|
||||
<?php echoUOJPageFooter() ?>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue