master
wang 3 years ago
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, &reg);
}
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, &reg);
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, &reg);
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, &reg);
}
}

2
web/.gitignore vendored

@ -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,186 @@
<?php
if (!Auth::check()) {
redirectToLogin();
}
function handlePost() {
global $myUser;
if (!isset($_POST['old_password'])) {
return '无效表单';
}
$old_password = $_POST['old_password'];
if (!validatePassword($old_password) || !checkPassword($myUser, $old_password)) {
return "失败:密码错误。";
}
if ($_POST['ptag']) {
$password = $_POST['password'];
if (!validatePassword($password)) {
return "失败:无效密码。";
}
$password = getPasswordToStore($password, $myUser['username']);
DB::update("update user_info set password = '$password' where username = '{$myUser['username']}'");
}
$email = $_POST['email'];
if (!validateEmail($email)) {
return "失败:无效电子邮箱。";
}
$esc_email = DB::escape($email);
DB::update("update user_info set email = '$esc_email' where username = '{$myUser['username']}'");
if ($_POST['Qtag']) {
$qq = $_POST['qq'];
if (!validateQQ($qq)) {
return "失败无效QQ。";
}
$esc_qq = DB::escape($qq);
DB::update("update user_info set qq = '$esc_qq' where username = '{$myUser['username']}'");
} else {
DB::update("update user_info set QQ = NULL where username = '{$myUser['username']}'");
}
if ($_POST['sex'] == "U" || $_POST['sex'] == 'M' || $_POST['sex'] == 'F') {
$sex = $_POST['sex'];
$esc_sex = DB::escape($sex);
DB::update("update user_info set sex = '$esc_sex' where username = '{$myUser['username']}'");
}
if (validateMotto($_POST['motto'])) {
$esc_motto = DB::escape($_POST['motto']);
DB::update("update user_info set motto = '$esc_motto' where username = '{$myUser['username']}'");
}
return "ok";
}
if (isset($_POST['change'])) {
die(handlePost());
}
?>
<?php
$REQUIRE_LIB['dialog'] = '';
$REQUIRE_LIB['md5'] = '';
?>
<?php echoUOJPageHeader(UOJLocale::get('modify my profile')) ?>
<h2 class="page-header"><?= UOJLocale::get('modify my profile') ?></h2>
<form id="form-update" class="form-horizontal">
<h4><?= UOJLocale::get('please enter your password for authorization') ?></h4>
<div id="div-old_password" class="form-group">
<label for="input-old_password" class="col-sm-2 control-label"><?= UOJLocale::get('password') ?></label>
<div class="col-sm-3">
<input type="password" class="form-control" name="old_password" id="input-old_password" placeholder="<?= UOJLocale::get('enter your password') ?>" maxlength="20" />
<span class="help-block" id="help-old_password"></span>
</div>
</div>
<h4><?= UOJLocale::get('please enter your new profile') ?></h4>
<div id="div-password" class="form-group">
<label for="input-password" class="col-sm-2 control-label"><?= UOJLocale::get('new password') ?></label>
<div class="col-sm-3">
<input type="password" class="form-control" id="input-password" name="password" placeholder="<?= UOJLocale::get('enter your new password') ?>" maxlength="20" />
<input type="password" class="form-control top-buffer-sm" id="input-confirm_password" placeholder="<?= UOJLocale::get('re-enter your new password') ?>" maxlength="20" />
<span class="help-block" id="help-password"><?= UOJLocale::get('leave it blank if you do not want to change the password') ?></span>
</div>
</div>
<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" name="email" id="input-email" value="<?=$myUser['email']?>" placeholder="<?= UOJLocale::get('enter your email') ?>" maxlength="50" />
<span class="help-block" id="help-email"></span>
</div>
</div>
<div id="div-qq" class="form-group">
<label for="input-qq" class="col-sm-2 control-label"><?= UOJLocale::get('QQ') ?></label>
<div class="col-sm-3">
<input type="text" class="form-control" name="qq" id="input-qq" value="<?= $myUser['qq'] != 0 ? $myUser['qq'] : '' ?>" placeholder="<?= UOJLocale::get('enter your QQ') ?>" maxlength="50" />
<span class="help-block" id="help-qq"></span>
</div>
</div>
<div id="div-sex" class="form-group">
<label for="input-sex" class="col-sm-2 control-label"><?= UOJLocale::get('sex') ?></label>
<div class="col-sm-3">
<select class="form-control" id="input-sex" name="sex">
<option value="U"<?= Auth::user()['sex'] == 'U' ? ' selected="selected"' : ''?>><?= UOJLocale::get('refuse to answer') ?></option>
<option value="M"<?= Auth::user()['sex'] == 'M' ? ' selected="selected"' : ''?>><?= UOJLocale::get('male') ?></option>
<option value="F"<?= Auth::user()['sex'] == 'F' ? ' selected="selected"' : ''?>><?= UOJLocale::get('female') ?></option>
</select>
</div>
</div>
<div id="div-motto" class="form-group">
<label for="input-motto" class="col-sm-2 control-label"><?= UOJLocale::get('motto') ?></label>
<div class="col-sm-3">
<textarea class="form-control" id="input-motto" name="motto"><?=HTML::escape($myUser['motto'])?></textarea>
<span class="help-block" id="help-motto"></span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-3">
<p class="form-control-static"><strong><?= UOJLocale::get('change avatar help') ?></strong></p>
</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 validateUpdatePost() {
var ok = true;
ok &= getFormErrorAndShowHelp('email', validateEmail);
ok &= getFormErrorAndShowHelp('old_password', validatePassword);
if ($('#input-password').val().length > 0)
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
if ($('#input-qq').val().length > 0)
ok &= getFormErrorAndShowHelp('qq', validateQQ);
ok &= getFormErrorAndShowHelp('motto', validateMotto);
return ok;
}
function submitUpdatePost() {
if (!validateUpdatePost())
return;
$.post('/user/modify-profile', {
change : '',
etag : $('#input-email').val().length,
ptag : $('#input-password').val().length,
Qtag : $('#input-qq').val().length,
email : $('#input-email').val(),
password : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>"),
old_password : md5($('#input-old_password').val(), "<?= getPasswordClientSalt() ?>"),
qq : $('#input-qq').val(),
sex : $('#input-sex').val(),
motto : $('#input-motto').val()
}, function(msg) {
if (msg == 'ok') {
BootstrapDialog.show({
title : '修改成功',
message : '用户信息修改成功',
type : BootstrapDialog.TYPE_SUCCESS,
buttons : [{
label: '好的',
action: function(dialog) {
dialog.close();
}
}],
onhidden : function(dialog) {
window.location.href = '/user/profile/<?=$myUser['username']?>';
}
});
} else {
BootstrapDialog.show({
title : '修改失败',
message : msg,
type : BootstrapDialog.TYPE_DANGER,
buttons: [{
label: '好的',
action: function(dialog) {
dialog.close();
}
}],
});
}
});
}
$(document).ready(function(){$('#form-update').submit(function(e) {submitUpdatePost();e.preventDefault();});
});
</script>
<?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,553 @@
<?php
requirePHPLib('form');
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
become404Page();
}
genMoreContestInfo($contest);
if (!hasContestPermission(Auth::user(), $contest)) {
if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {
header("Location: /contest/{$contest['id']}/register");
die();
} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {
if ($myUser == null || !hasRegistered(Auth::user(), $contest)) {
becomeMsgPage("<h1>比赛正在进行中</h1><p>很遗憾,您尚未报名。比赛结束后再来看吧~</p>");
}
}
}
if (isset($_GET['tab'])) {
$cur_tab = $_GET['tab'];
} else {
$cur_tab = 'dashboard';
}
$tabs_info = array(
'dashboard' => array(
'name' => UOJLocale::get('contests::contest dashboard'),
'url' => "/contest/{$contest['id']}"
),
'submissions' => array(
'name' => UOJLocale::get('contests::contest submissions'),
'url' => "/contest/{$contest['id']}/submissions"
),
'standings' => array(
'name' => UOJLocale::get('contests::contest standings'),
'url' => "/contest/{$contest['id']}/standings"
)
);
if (hasContestPermission(Auth::user(), $contest)) {
$tabs_info['backstage'] = array(
'name' => UOJLocale::get('contests::contest backstage'),
'url' => "/contest/{$contest['id']}/backstage"
);
}
if (!isset($tabs_info[$cur_tab])) {
become404Page();
}
if (isset($_POST['check_notice'])) {
$result = DB::query("select * from contests_notice where contest_id = '${contest['id']}' order by time desc limit 10");
$ch = array();
$flag = false;
try {
while ($row = DB::fetch($result)) {
if (new DateTime($row['time']) > new DateTime($_POST['last_time'])) {
$ch[] = $row['title'].': '.$row['content'];
}
}
} catch (Exception $e) {
}
global $myUser;
$result=DB::query("select * from contests_asks where contest_id='${contest['id']}' and username='${myUser['username']}' order by reply_time desc limit 10");
try {
while ($row = DB::fetch($result)) {
if (new DateTime($row['reply_time']) > new DateTime($_POST['last_time'])) {
$ch[] = $row['question'].': '.$row['answer'];
}
}
} catch (Exception $e) {
}
if ($ch) {
die(json_encode(array('msg' => $ch, 'time' => UOJTime::$time_now_str)));
} else {
die(json_encode(array('time' => UOJTime::$time_now_str)));
}
}
if (isSuperUser($myUser)) {
if (CONTEST_PENDING_FINAL_TEST <= $contest['cur_progress'] && $contest['cur_progress'] <= CONTEST_TESTING) {
$start_test_form = new UOJForm('start_test');
$start_test_form->handle = function() {
global $contest;
$result = DB::query("select id, problem_id, content from submissions where contest_id = {$contest['id']}");
while ($submission = DB::fetch($result, MYSQLI_ASSOC)) {
if (!isset($contest['extra_config']["problem_{$submission['problem_id']}"])) {
$content = json_decode($submission['content'], true);
if (isset($content['final_test_config'])) {
$content['config'] = $content['final_test_config'];
unset($content['final_test_config']);
}
if (isset($content['first_test_config'])) {
unset($content['first_test_config']);
}
$esc_content = DB::escape(json_encode($content));
DB::update("update submissions set judge_time = NULL, result = '', score = NULL, status = 'Waiting Rejudge', content = '$esc_content' where id = {$submission['id']}");
}
}
DB::query("update contests set status = 'testing' where id = {$contest['id']}");
};
$start_test_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
$start_test_form->submit_button_config['smart_confirm'] = '';
if ($contest['cur_progress'] < CONTEST_TESTING) {
$start_test_form->submit_button_config['text'] = '开始最终测试';
} else {
$start_test_form->submit_button_config['text'] = '重新开始最终测试';
}
$start_test_form->runAtServer();
}
if ($contest['cur_progress'] >= CONTEST_TESTING) {
$publish_result_form = new UOJForm('publish_result');
$publish_result_form->handle = function() {
// time config
set_time_limit(0);
ignore_user_abort(true);
global $contest;
$contest_data = queryContestData($contest);
calcStandings($contest, $contest_data, $score, $standings, true);
if (!isset($contest['extra_config']['unrated'])) {
$rating_k = isset($contest['extra_config']['rating_k']) ? $contest['extra_config']['rating_k'] : 400;
$ratings = calcRating($standings, $rating_k);
} else {
$ratings = array();
for ($i = 0; $i < count($standings); $i++) {
$ratings[$i] = $standings[$i][2][1];
}
}
for ($i = 0; $i < count($standings); $i++) {
$user = queryUser($standings[$i][2][0]);
$change = $ratings[$i] - $user['rating'];
$user_link = getUserLink($user['username']);
if ($change != 0) {
$tail = '<strong style="color:red">' . ($change > 0 ? '+' : '') . $change . '</strong>';
$content = <<<EOD
<p>${user_link} 您好:</p>
<p class="indent2">您在 <a href="/contest/{$contest['id']}">{$contest['name']}</a> 这场比赛后的Rating变化为${tail}当前Rating为 <strong style="color:red">{$ratings[$i]}</strong></p>
EOD;
} else {
$content = <<<EOD
<p>${user_link} 您好:</p>
<p class="indent2">您在 <a href="/contest/{$contest['id']}">{$contest['name']}</a> 这场比赛后Rating没有变化。当前Rating为 <strong style="color:red">{$ratings[$i]}</strong></p>
EOD;
}
sendSystemMsg($user['username'], 'Rating变化通知', $content);
DB::query("update user_info set rating = {$ratings[$i]} where username = '{$standings[$i][2][0]}'");
DB::query("update contests_registrants set rank = {$standings[$i][3]} where contest_id = {$contest['id']} and username = '{$standings[$i][2][0]}'");
}
DB::query("update contests set status = 'finished' where id = {$contest['id']}");
};
$publish_result_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
$publish_result_form->submit_button_config['smart_confirm'] = '';
$publish_result_form->submit_button_config['text'] = '公布成绩';
$publish_result_form->runAtServer();
}
}
if ($cur_tab == 'dashboard') {
if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS) {
$post_question = new UOJForm('post_question');
$post_question->addVTextArea('qcontent', '问题', '',
function($content) {
if (!Auth::check()) {
return '您尚未登录';
}
if (!$content || strlen($content) == 0) {
return '问题不能为空';
}
if (strlen($content) > 140 * 4) {
return '问题太长';
}
return '';
},
null
);
$post_question->handle = function() {
global $contest;
$content = DB::escape($_POST['qcontent']);
$username = Auth::id();
DB::query("insert into contests_asks (contest_id, question, username, post_time, is_hidden) values ('{$contest['id']}', '$content', '$username', now(), 1)");
};
$post_question->runAtServer();
} else {
$post_question = null;
}
} elseif ($cur_tab == 'backstage') {
if (isSuperUser(Auth::user())) {
$post_notice = new UOJForm('post_notice');
$post_notice->addInput('title', 'text', '标题', '',
function($title) {
if (!$title) {
return '标题不能为空';
}
return '';
},
null
);
$post_notice->addTextArea('content', '正文', '',
function($content) {
if (!$content) {
return '公告不能为空';
}
return '';
},
null
);
$post_notice->handle = function() {
global $contest;
$title = DB::escape($_POST['title']);
$content = DB::escape($_POST['content']);
DB::insert("insert into contests_notice (contest_id, title, content, time) values ('{$contest['id']}', '$title', '$content', now())");
};
$post_notice->runAtServer();
} else {
$post_notice = null;
}
if (hasContestPermission(Auth::user(), $contest)) {
$reply_question = new UOJForm('reply_question');
$reply_question->addHidden('rid', '0',
function($id) {
global $contest;
if (!validateUInt($id)) {
return '无效ID';
}
$q = DB::selectFirst("select * from contests_asks where id = $id");
if ($q['contest_id'] != $contest['id']) {
return '无效ID';
}
return '';
},
null
);
$reply_question->addVSelect('rtype', [
'public' => '公开',
'private' => '非公开',
'statement' => '请仔细阅读题面(非公开)',
'no_comment' => '无可奉告(非公开)',
'no_play' => '请认真比赛(非公开)',
], '回复类型', 'private');
$reply_question->addVTextArea('rcontent', '回复', '',
function($content) {
if (!Auth::check()) {
return '您尚未登录';
}
switch ($_POST['rtype']) {
case 'public':
case 'private':
if (strlen($content) == 0) {
return '回复不能为空';
}
break;
}
return '';
},
null
);
$reply_question->handle = function() {
global $contest;
$content = DB::escape($_POST['rcontent']);
$is_hidden = 1;
switch ($_POST['rtype']) {
case 'statement':
$content = '请仔细阅读题面';
break;
case 'no_comment':
$content = '无可奉告 ╮(╯▽╰)╭ ';
break;
case 'no_play':
$content = '请认真比赛 ( ̄口 ̄)!!';
break;
case 'public':
$is_hidden = 0;
break;
default:
break;
}
DB::update("update contests_asks set answer = '$content', reply_time = now(), is_hidden = {$is_hidden} where id = {$_POST['rid']}");
};
$reply_question->runAtServer();
} else {
$reply_question = null;
}
}
function echoDashboard() {
global $contest, $post_notice, $post_question, $reply_question;
$myname = Auth::id();
$contest_problems = DB::selectAll("select contests_problems.problem_id, best_ac_submissions.submission_id from contests_problems left join best_ac_submissions on contests_problems.problem_id = best_ac_submissions.problem_id and submitter = '{$myname}' where contest_id = {$contest['id']} order by contests_problems.problem_id asc");
for ($i = 0; $i < count($contest_problems); $i++) {
$contest_problems[$i]['problem'] = queryProblemBrief($contest_problems[$i]['problem_id']);
}
$contest_notice = DB::selectAll("select * from contests_notice where contest_id = {$contest['id']} order by time desc");
if (Auth::check()) {
$my_questions = DB::selectAll("select * from contests_asks where contest_id = {$contest['id']} and username = '{$myname}' order by post_time desc");
$my_questions_pag = new Paginator([
'data' => $my_questions
]);
} else {
$my_questions_pag = null;
}
$others_questions_pag = new Paginator([
'col_names' => array('*'),
'table_name' => 'contests_asks',
'cond' => "contest_id = {$contest['id']} and username != '{$myname}' and is_hidden = 0",
'tail' => 'order by reply_time desc',
'page_len' => 10
]);
uojIncludeView('contest-dashboard', [
'contest' => $contest,
'contest_notice' => $contest_notice,
'contest_problems' => $contest_problems,
'post_question' => $post_question,
'my_questions_pag' => $my_questions_pag,
'others_questions_pag' => $others_questions_pag
]);
}
function echoBackstage() {
global $contest, $post_notice, $reply_question;
$questions_pag = new Paginator([
'col_names' => array('*'),
'table_name' => 'contests_asks',
'cond' => "contest_id = {$contest['id']}",
'tail' => 'order by post_time desc',
'page_len' => 50
]);
if ($contest['cur_progress'] < CONTEST_TESTING) {
$contest_data = queryContestData($contest, ['pre_final' => true]);
calcStandings($contest, $contest_data, $score, $standings);
$standings_data = [
'contest' => $contest,
'standings' => $standings,
'score' => $score,
'contest_data' => $contest_data
];
} else {
$standings_data = null;
}
uojIncludeView('contest-backstage', [
'contest' => $contest,
'post_notice' => $post_notice,
'reply_question' => $reply_question,
'questions_pag' => $questions_pag,
'standings_data' => $standings_data
]);
}
function echoMySubmissions() {
global $contest, $myUser;
$show_all_submissions_status = Cookie::get('show_all_submissions') !== null ? 'checked="checked" ' : '';
$show_all_submissions = UOJLocale::get('contests::show all submissions');
echo <<<EOD
<div class="checkbox text-right">
<label for="input-show_all_submissions"><input type="checkbox" id="input-show_all_submissions" $show_all_submissions_status/> $show_all_submissions</label>
</div>
<script type="text/javascript">
$('#input-show_all_submissions').click(function() {
if (this.checked) {
$.cookie('show_all_submissions', '');
} else {
$.removeCookie('show_all_submissions');
}
location.reload();
});
</script>
EOD;
if (Cookie::get('show_all_submissions') !== null) {
echoSubmissionsList("contest_id = {$contest['id']}", 'order by id desc', array('judge_time_hidden' => ''), $myUser);
} else {
echoSubmissionsList("submitter = '{$myUser['username']}' and contest_id = {$contest['id']}", 'order by id desc', array('judge_time_hidden' => ''), $myUser);
}
}
function echoStandings() {
global $contest;
$contest_data = queryContestData($contest);
calcStandings($contest, $contest_data, $score, $standings);
uojIncludeView('contest-standings', [
'contest' => $contest,
'standings' => $standings,
'score' => $score,
'contest_data' => $contest_data
]);
}
function echoContestCountdown() {
global $contest;
$rest_second = $contest['end_time']->getTimestamp() - UOJTime::$time_now->getTimestamp();
$time_str = UOJTime::$time_now_str;
$contest_ends_in = UOJLocale::get('contests::contest ends in');
echo <<<EOD
<div class="card border-info">
<div class="card-header bg-info">
<h3 class="card-title">$contest_ends_in</h3>
</div>
<div class="card-body text-center countdown" data-rest="$rest_second"></div>
</div>
<script type="text/javascript">
checkContestNotice({$contest['id']}, '$time_str');
</script>
EOD;
}
function echoContestJudgeProgress() {
global $contest;
if ($contest['cur_progress'] < CONTEST_TESTING) {
$rop = 0;
$title = UOJLocale::get('contests::contest pending final test');
} else {
$total = DB::selectCount("select count(*) from submissions where contest_id = {$contest['id']}");
$n_judged = DB::selectCount("select count(*) from submissions where contest_id = {$contest['id']} and status = 'Judged'");
$rop = $total == 0 ? 100 : (int)($n_judged / $total * 100);
$title = UOJLocale::get('contests::contest final testing');
}
echo <<<EOD
<div class="card border-info">
<div class="card-header bg-info">
<h3 class="card-title">$title</h3>
</div>
<div class="card-body">
<div class="progress bot-buffer-no">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="$rop" aria-valuemin="0" aria-valuemax="100" style="width: {$rop}%; min-width: 20px;">{$rop}%</div>
</div>
</div>
</div>
EOD;
}
function echoContestFinished() {
$title = UOJLocale::get('contests::contest ended');
echo <<<EOD
<div class="card border-info">
<div class="card-header bg-info">
<h3 class="card-title">$title</h3>
</div>
</div>
EOD;
}
$page_header = HTML::stripTags($contest['name']) . ' - ';
?>
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - ' . $tabs_info[$cur_tab]['name'] . ' - ' . UOJLocale::get('contests::contest')) ?>
<div class="text-center">
<h1><?= $contest['name'] ?></h1>
<?= getClickZanBlock('C', $contest['id'], $contest['zan']) ?>
</div>
<div class="row">
<?php if ($cur_tab == 'standings'): ?>
<div class="col-sm-12">
<?php else: ?>
<div class="col-sm-9">
<?php endif ?>
<?= HTML::tablist($tabs_info, $cur_tab) ?>
<div class="top-buffer-md">
<?php
if ($cur_tab == 'dashboard') {
echoDashboard();
} elseif ($cur_tab == 'submissions') {
echoMySubmissions();
} elseif ($cur_tab == 'standings') {
echoStandings();
} elseif ($cur_tab == 'backstage') {
echoBackstage();
}
?>
</div>
</div>
<?php if ($cur_tab == 'standings'): ?>
<div class="col-sm-12">
<hr />
</div>
<?php endif ?>
<div class="col-sm-3">
<?php
if ($contest['cur_progress'] <= CONTEST_IN_PROGRESS) {
echoContestCountdown();
} elseif ($contest['cur_progress'] <= CONTEST_TESTING) {
echoContestJudgeProgress();
} else {
echoContestFinished();
}
?>
<?php if ($cur_tab == 'standings'): ?>
</div>
<div class="col-sm-3">
<?php endif ?>
<?php if (!isset($contest['extra_config']['contest_type']) || $contest['extra_config']['contest_type']=='OI'):?>
<p>此次比赛为OI赛制。</p>
<p><strong>注意:比赛时只显示测样例的结果。</strong></p>
<?php elseif ($contest['extra_config']['contest_type']=='IOI'):?>
<p>此次比赛为IOI赛制。</p>
<p><strong>注意:比赛时显示测试所有数据的结果,但无法看到详细信息。</strong></p>
<?php endif?>
<a href="/contest/<?=$contest['id']?>/registrants" class="btn btn-info btn-block"><?= UOJLocale::get('contests::contest registrants') ?></a>
<?php if (isSuperUser($myUser)): ?>
<a href="/contest/<?=$contest['id']?>/manage" class="btn btn-primary btn-block">管理</a>
<?php if (isset($start_test_form)): ?>
<div class="top-buffer-sm">
<?php $start_test_form->printHTML(); ?>
</div>
<?php endif ?>
<?php if (isset($publish_result_form)): ?>
<div class="top-buffer-sm">
<?php $publish_result_form->printHTML(); ?>
</div>
<?php endif ?>
<?php endif ?>
<?php if ($contest['extra_config']['links']) { ?>
<?php if ($cur_tab == 'standings'): ?>
</div>
<div class="col-sm-3">
<div class="card border-info">
<?php else: ?>
<div class="card border-info top-buffer-lg">
<?php endif ?>
<div class="card-header bg-info">
<h3 class="card-title">比赛资料</h3>
</div>
<div class="list-group">
<?php foreach ($contest['extra_config']['links'] as $link) { ?>
<a href="/blogs/<?=$link[1]?>" class="list-group-item"><?=$link[0]?></a>
<?php } ?>
</div>
</div>
<?php } ?>
</div>
</div>
<?php echoUOJPageFooter() ?>

@ -0,0 +1,285 @@
<?php
requirePHPLib('form');
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
become404Page();
}
genMoreContestInfo($contest);
if (!isSuperUser($myUser)) {
become403Page();
}
$time_form = new UOJForm('time');
$time_form->addInput(
'name', 'text', '比赛标题', $contest['name'],
function($str) {
return '';
},
null
);
$time_form->addInput(
'start_time', 'text', '开始时间', $contest['start_time_str'],
function($str, &$vdata) {
try {
$vdata['start_time'] = new DateTime($str);
} catch (Exception $e) {
return '无效时间格式';
}
return '';
},
null
);
$time_form->addInput(
'last_min', 'text', '时长(单位:分钟)', $contest['last_min'],
function($str) {
return !validateUInt($str) ? '必须为一个整数' : '';
},
null
);
$time_form->handle = function(&$vdata) {
global $contest;
$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::update("update contests set start_time = '$start_time_str', last_min = {$_POST['last_min']}, name = '$esc_name' where id = {$contest['id']}");
};
$managers_form = newAddDelCmdForm('managers',
function($username) {
if (!validateUsername($username) || !queryUser($username)) {
return "不存在名为{$username}的用户";
}
return '';
},
function($type, $username) {
global $contest;
if ($type == '+') {
DB::query("insert into contests_permissions (contest_id, username) values (${contest['id']}, '$username')");
} elseif ($type == '-') {
DB::query("delete from contests_permissions where contest_id = ${contest['id']} and username = '$username'");
}
}
);
$problems_form = newAddDelCmdForm('problems',
function($cmd) {
if (!preg_match('/^(\d+)\s*(\[\S+\])?$/', $cmd, $matches)) {
return "无效题号";
}
$problem_id = $matches[1];
if (!validateUInt($problem_id) || !($problem = queryProblemBrief($problem_id))) {
return "不存在题号为{$problem_id}的题";
}
if (!hasProblemPermission(Auth::user(), $problem)) {
return "无权添加题号为{$problem_id}的题";
}
return '';
},
function($type, $cmd) {
global $contest;
if (!preg_match('/^(\d+)\s*(\[\S+\])?$/', $cmd, $matches)) {
return "无效题号";
}
$problem_id = $matches[1];
if ($type == '+') {
DB::insert("insert into contests_problems (contest_id, problem_id) values ({$contest['id']}, '$problem_id')");
} elseif ($type == '-') {
DB::delete("delete from contests_problems where contest_id = {$contest['id']} and problem_id = '$problem_id'");
}
if (isset($matches[2])) {
switch ($matches[2]) {
case '[sample]':
unset($contest['extra_config']["problem_$problem_id"]);
break;
case '[full]':
$contest['extra_config']["problem_$problem_id"] = 'full';
break;
case '[no-details]':
$contest['extra_config']["problem_$problem_id"] = 'no-details';
break;
}
$esc_extra_config = json_encode($contest['extra_config']);
$esc_extra_config = DB::escape($esc_extra_config);
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
}
}
);
if (isSuperUser($myUser)) {
$rating_k_form = new UOJForm('rating_k');
$rating_k_form->addInput('rating_k', 'text', 'rating 变化上限', isset($contest['extra_config']['rating_k']) ? $contest['extra_config']['rating_k'] : 400,
function ($x) {
if (!validateUInt($x) || $x < 1 || $x > 1000) {
return '不合法的上限';
}
return '';
},
null
);
$rating_k_form->handle = function() {
global $contest;
$contest['extra_config']['rating_k'] = $_POST['rating_k'];
$esc_extra_config = json_encode($contest['extra_config']);
$esc_extra_config = DB::escape($esc_extra_config);
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
};
$rating_k_form->runAtServer();
$rated_form = new UOJForm('rated');
$rated_form->handle = function() {
global $contest;
if (isset($contest['extra_config']['unrated'])) {
unset($contest['extra_config']['unrated']);
} else {
$contest['extra_config']['unrated'] = '';
}
$esc_extra_config = json_encode($contest['extra_config']);
$esc_extra_config = DB::escape($esc_extra_config);
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
};
$rated_form->submit_button_config['class_str'] = 'btn btn-warning btn-block';
$rated_form->submit_button_config['text'] = isset($contest['extra_config']['unrated']) ? '设置比赛为rated' : '设置比赛为unrated';
$rated_form->submit_button_config['smart_confirm'] = '';
$rated_form->runAtServer();
$version_form = new UOJForm('version');
$version_form->addInput('standings_version', 'text', '排名版本', $contest['extra_config']['standings_version'],
function ($x) {
if (!validateUInt($x) || $x < 1 || $x > 2) {
return '不是合法的版本号';
}
return '';
},
null
);
$version_form->handle = function() {
global $contest;
$contest['extra_config']['standings_version'] = $_POST['standings_version'];
$esc_extra_config = json_encode($contest['extra_config']);
$esc_extra_config = DB::escape($esc_extra_config);
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
};
$version_form->runAtServer();
$contest_type_form = new UOJForm('contest_type');
$contest_type_form->addInput('contest_type', 'text', '赛制', $contest['extra_config']['contest_type'],
function ($x) {
if ($x != 'OI' && $x != 'ACM' && $x != 'IOI') {
return '不是合法的赛制名';
}
return '';
},
null
);
$contest_type_form->handle = function() {
global $contest;
$contest['extra_config']['contest_type'] = $_POST['contest_type'];
$esc_extra_config = json_encode($contest['extra_config']);
$esc_extra_config = DB::escape($esc_extra_config);
DB::update("update contests set extra_config = '$esc_extra_config' where id = {$contest['id']}");
};
$contest_type_form->runAtServer();
}
$time_form->runAtServer();
$managers_form->runAtServer();
$problems_form->runAtServer();
?>
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - 比赛管理') ?>
<h1 class="page-header" align="center"><?=$contest['name']?> 管理</h1>
<ul class="nav nav-tabs mb-3" role="tablist">
<li class="nav-item"><a class="nav-link active" href="#tab-time" role="tab" data-toggle="tab">比赛时间</a></li>
<li class="nav-item"><a class="nav-link" href="#tab-managers" role="tab" data-toggle="tab">管理者</a></li>
<li class="nav-item"><a class="nav-link" href="#tab-problems" role="tab" data-toggle="tab">试题</a></li>
<?php if (isSuperUser($myUser)): ?>
<li class="nav-item"><a class="nav-link" href="#tab-others" role="tab" data-toggle="tab">其它</a></li>
<?php endif ?>
<li class="nav-item"><a class="nav-link" href="/contest/<?=$contest['id']?>" role="tab">返回</a></li>
</ul>
<div class="tab-content top-buffer-sm">
<div class="tab-pane active" id="tab-time">
<?php $time_form->printHTML(); ?>
</div>
<div class="tab-pane" id="tab-managers">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>用户名</th>
</tr>
</thead>
<tbody>
<?php
$row_id = 0;
$result = DB::query("select username from contests_permissions where contest_id = {$contest['id']}");
while ($row = DB::fetch($result, MYSQLI_ASSOC)) {
$row_id++;
echo '<tr>', '<td>', $row_id, '</td>', '<td>', getUserLink($row['username']), '</td>', '</tr>';
}
?>
</tbody>
</table>
<p class="text-center">命令格式:命令一行一个,+mike表示把mike加入管理者-mike表示把mike从管理者中移除</p>
<?php $managers_form->printHTML(); ?>
</div>
<div class="tab-pane" id="tab-problems">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>试题名</th>
</tr>
</thead>
<tbody>
<?php
$result = DB::query("select problem_id from contests_problems where contest_id = ${contest['id']} order by problem_id asc");
while ($row = DB::fetch($result, MYSQLI_ASSOC)) {
$problem = queryProblemBrief($row['problem_id']);
$problem_config_str = isset($contest['extra_config']["problem_{$problem['id']}"]) ? $contest['extra_config']["problem_{$problem['id']}"] : 'sample';
echo '<tr>', '<td>', $problem['id'], '</td>', '<td>', getProblemLink($problem), ' ', "[$problem_config_str]", '</td>', '</tr>';
}
?>
</tbody>
</table>
<p class="text-center">命令格式:命令一行一个,+233表示把题号为233的试题加入比赛-233表示把题号为233的试题从比赛中移除</p>
<?php $problems_form->printHTML(); ?>
</div>
<?php if (isSuperUser($myUser)): ?>
<div class="tab-pane" id="tab-others">
<div class="row">
<div class="col-sm-12">
<h3>Rating控制</h3>
<div class="row">
<div class="col-sm-3">
<?php $rated_form->printHTML(); ?>
</div>
</div>
<div class="top-buffer-sm"></div>
<?php $rating_k_form->printHTML(); ?>
</div>
<div class="col-sm-12 top-buffer-sm">
<h3>版本控制</h3>
<?php $version_form->printHTML(); ?>
</div>
<div class="col-sm-12 top-buffer-sm">
<h3>赛制</h3>
<?php $contest_type_form->printHTML(); ?>
</div>
</div>
</div>
<?php endif ?>
</div>
<?php echoUOJPageFooter() ?>

@ -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,37 @@
<?php
requirePHPLib('form');
if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
become404Page();
}
genMoreContestInfo($contest);
if ($myUser == null) {
redirectToLogin();
} elseif (hasContestPermission($myUser, $contest) || hasRegistered($myUser, $contest) || $contest['cur_progress'] != CONTEST_NOT_STARTED) {
redirectTo('/contests');
}
$register_form = new UOJForm('register');
$register_form->handle = function() {
global $myUser, $contest;
DB::query("insert into contests_registrants (username, user_rating, contest_id, has_participated) values ('{$myUser['username']}', {$myUser['rating']}, {$contest['id']}, 0)");
updateContestPlayerNum($contest);
};
$register_form->submit_button_config['class_str'] = 'btn btn-primary';
$register_form->submit_button_config['text'] = '报名比赛';
$register_form->succ_href = "/contests";
$register_form->runAtServer();
?>
<?php echoUOJPageHeader(HTML::stripTags($contest['name']) . ' - 报名') ?>
<h1 class="page-header">比赛规则</h1>
<ul>
<li>比赛报名后不算正式参赛,报名后进了比赛页面也不算参赛,<strong>看了题目才算正式参赛</strong>。如果未正式参赛则不算rating。</li>
<li>比赛中途可以提交,若同一题有多次提交按<strong>最后一次不是Compile Error的提交</strong>算成绩。其实UOJ会自动无视你所有Compile Error的提交当作没看见</li>
<li>比赛中途提交后,可以看到<strong>测样例</strong>的结果。(若为提交答案题则对于每个测试点,该测试点有分则该测试点为满分)</li>
<li>比赛结束后会进行最终测试,最终测试后的排名为最终排名。</li>
<li>比赛排名按分数为第一关键字,完成题目的总时间为第二关键字。完成题目的总时间等于完成每道题所花时间之和(无视掉爆零的题目)。</li>
<li>请遵守比赛规则一位选手在一场比赛内不得报名多个账号选手之间不能交流或者抄袭代码如果被检测到将以0分处理或者封禁。</li>
</ul>
<?php $register_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> &times;'.$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,210 @@
<?php
requireLib('shjs');
requireLib('mathjax');
echoUOJPageHeader(UOJLocale::get('help'))
?>
<article>
<header>
<h2 class="page-header">常见问题及其解答(FAQ)</h2>
</header>
<section>
<div class="card my-1">
<div class="card-header collapsed" id="headerOne" data-toggle="collapse" data-target="#collapseOne" style="cursor:pointer;">
<h5 class="mb-0">什么是<?= UOJConfig::$data['profile']['oj-name-short'] ?></h5>
</div>
<div id="collapseOne" class="collapse">
<div class="card-body">
<p>来了?坐,欢迎来到 <?= UOJConfig::$data['profile']['oj-name'] ?></p>
<p><img src="/images/utility/qpx_n/b37.gif" alt="小熊像超人一样飞" /></p>
<p>众所周知信息学的题目一般形式为给出XXXXX要你提交一份源代码输出XXXXX然后时限若干秒内存若干兆数据若干组每组数据与答案进行比较不对就不给分。</p>
<p>看起来挺合理的但是总是有意外。比如要求输出一个浮点数与答案接近就满分。于是只好引入Special Judge来判断选手输出的正确性。</p>
<p>但是还是有意外比如提交两个程序一个压缩另一个解压比如提交答案题只用提交文件比如给出音乐要求识别乐器达到90%的正确率就算满分……</p>
<p>这个时候UOJ出现了于是<?= UOJConfig::$data['profile']['oj-name-short'] ?>就使用了这套系统。Universal的中文意思是通用之所以称之为UOJ因为我们所有题目从编译、运行到评分都可以由出题人自定义。</p>
<p>如果你正在为没有地方测奇奇怪怪的题目而苦恼,那么你来对地方了。</p>
<p>当然了,<?= UOJConfig::$data['profile']['oj-name-short'] ?>对于传统题的评测也做了特别支持。平时做题时我很难容忍的地方就是数据出水了导致暴力得了好多分甚至过了,而出题人却委屈地说,总共才一百分,卡了这个暴力就不能卡另一个暴力,所以暴力过了就过了吧。</p>
<p>所以我们引入了Extra Tests和Hack机制。每道传统题的数据都分为Tests和Extra TestsTests满分100分如果你通过了所有的Tests那么就会为你测Extra Tests。如果过了Tests但没过Extra Tests那么倒扣3分变为97分。Extra Tests的来源一个是这道题没什么人可能会错的边界情况可以放在里面另一个就是各位平时做题的时候如果发现错误算法AC了可以使用hack将其卡掉<?= UOJConfig::$data['profile']['oj-name-short'] ?>会自动加入Extra Tests并重测。我们无法阻止暴力高分的脚步但是不让他得满分还是有心里安慰作用的</p>
<p><?= UOJConfig::$data['profile']['oj-name-short'] ?>还有比赛功能可以承办比赛赛制暂时只支持OI赛制。不过你可以利用现有方案变相实现ACM赛制未来将支持更多种多样的赛制甚至自定义赛制。</p>
<p>目前<?= UOJConfig::$data['profile']['oj-name-short'] ?>刚刚起步还有很多地方有待完善。想出题、想出比赛、发现BUG、发现槽点都可以联系我们联系方式见下。</p>
<p>祝各位在<?= UOJConfig::$data['profile']['oj-name-short'] ?>玩得愉快!(求不虐萌萌哒服务器~求不虐萌萌哒测评机~!)</p>
<p><img src="/images/utility/qpx_n/b54.gif" alt="小熊抱抱" /></p>
</div>
</div>
</div>
<div class="card my-1">
<div class="card-header collapsed" id="headerTwo" data-toggle="collapse" data-target="#collapseTwo" style="cursor:pointer;">
<h5 class="mb-0">注册后怎么上传头像</h5>
</div>
<div id="collapseTwo" class="collapse">
<div class="card-body">
<p><?= UOJConfig::$data['profile']['oj-name-short'] ?>不提供头像存储服务。每到一个网站都要上传一个头像挺烦的对不对我们支持Gravatar请使用Gravatar吧Gravatar是一个全球的头像存储服务你的头像将会与你的电子邮箱绑定。在各大网站比如各种Wordpress还有各种OJ比如Vijos、Contest Hunter上只要你电子邮箱填对了那么你的头像也就立即能显示了</p>
<p>快使用Gravatar吧 Gravatar地址<a href="https://cn.gravatar.com/">https://cn.gravatar.com/</a>。进去后注册个帐号然后与邮箱绑定并上传头像就ok啦</p>
</div>
</div>
</div>
<div class="card my-1">
<div class="card-header collapsed" id="headerThree" data-toggle="collapse" data-target="#collapseThree" style="cursor:pointer;">
<h5 class="mb-0"><?= UOJConfig::$data['profile']['oj-name-short'] ?>的测评环境?</h5>
</div>
<div id="collapseThree" class="collapse">
<div class="card-body">
<p>默认的测评环境是 Ubuntu Linux 18.04 LTS x64。</p>
<p>C的编译器是 gcc 7.4.0,编译命令:<code>gcc code.c -o code -lm -O2 -DONLINE_JUDGE</code></p>
<p>C++的编译器是 g++ 7.4.0,编译命令:<code>g++ code.cpp -o code -lm -O2 -DONLINE_JUDGE</code>。如果选择C++11会在编译命令后面添加<code>-std=c++11</code></p>
<p>Java8的JDK版本是 openjdk 1.8.0_222,编译命令:<code>javac code.java</code></p>
<p>Java11的JDK版本是 openjdk 11.0.4,编译命令:<code>javac code.java</code></p>
<p>Pascal的编译器是 fpc 3.0.4,编译命令:<code>fpc code.pas -O2</code></p>
<p>Python会先编译为优化过的字节码<samp>.pyo</samp>文件。支持的Python版本分别为Python 2.7和3.6。</p>
</div>
</div>
</div>
<div class="card my-1">
<div class="card-header collapsed" id="headerFour" data-toggle="collapse" data-target="#collapseFour" style="cursor:pointer;">
<h5 class="mb-0">各种评测状态的鸟语是什么意思?</h5>
</div>
<div id="collapseFour" class="collapse">
<div class="card-body">
<ul>
<li>Accepted: 答案正确。恭喜大佬,您通过了这道题。</li>
<li>Wrong Answer: 答案错误。仅仅通过样例数据的测试并不一定是正确答案,一定还有你没想到的地方。</li>
<li>Runtime Error: 运行时错误。像非法的内存访问,数组越界,指针漂移,调用禁用的系统函数都可能出现这类问题,请点击评测详情获得输出。</li>
<li>Time Limit Exceeded: 时间超限。请检查程序是否有死循环,或者应该有更快的计算方法。</li>
<li>Memory Limit Exceeded: 内存超限。数据可能需要压缩,或者您数组开太大了,请检查是否有内存泄露。</li>
<li>Output Limit Exceeded: 输出超限。你的输出居然比正确答案长了两倍!</li>
<li>Dangerous Syscalls: 危险系统调用你是不是带了文件或者使用了某些有意思的system函数</li>
<li>Judgement Failed: 评测失败。可能是评测机抽风了,也可能是服务器正在睡觉;反正不一定是你的锅啦!</li>
<li>No Comment: 没有详情。评测机对您的程序无话可说,那么我们也不知道到底发生了什么...</li>
</ul>
</div>
</div>
</div>
<div class="card my-1">
<div class="card-header collapsed" id="headerFive" data-toggle="collapse" data-target="#collapseFive" style="cursor:pointer;">
<h5 class="mb-0">递归 10<sup>7</sup> 层怎么没爆栈啊</h5>
</div>
<div id="collapseFive" class="collapse">
<div class="card-body">
<p>没错就是这样!除非是特殊情况,<?= UOJConfig::$data['profile']['oj-name-short'] ?>测评程序时的栈大小与该题的空间限制是相等的!</p>
</div>
</div>
</div>
<div class="card my-1">
<div class="card-header collapsed" id="headerSix" data-toggle="collapse" data-target="#collapseSix" style="cursor:pointer;">
<h5 class="mb-0">我在本地/某某OJ上AC了但在<?= UOJConfig::$data['profile']['oj-name-short'] ?>却过不了...这咋办?</h5>
</div>
<div id="collapseSix" class="collapse">
<div class="card-body">
<p>对于这类问题,我们在这里简单列一下可能原因:</p>
<ul>
<li>Linux中换行符是'\n'而windows中是'\r\n'多一个字符。有些数据在Windows下生成<?= UOJConfig::$data['profile']['oj-name-short'] ?>评测环境为Linux系统。这种情况在字符串输入中非常常见。</li>
<li>评测系统建立在Linux下可能由于使用了Linux的保留字而出现CE但在Windows下正常。</li>
<li>Linux对内存的访问控制更为严格因此在Windows上可能正常运行的无效指针或数组下标访问越界在评测系统上无法运行。</li>
<li>严重的内存泄露的问题很可能会引起系统的保护模块杀死你的进程。因此凡是使用malloc(或calloc,realloc,new)分配而得的内存空间请使用free(或delete)完全释放。</li>
<li>当然数据可能真的有问题。但是如果不止一个人通过了这道题,那最好不要怀疑是数据的锅。反之,可以立即联系我们上报!</li>
</ul>
</div>
</div>
</div>
<div class="card my-1">
<div class="card-header collapsed" id="headerSeven" data-toggle="collapse" data-target="#collapseSeven" style="cursor:pointer;">
<h5 class="mb-0">博客使用指南</h5>
</div>
<div id="collapseSeven" class="collapse">
<div class="card-body">
<p><?= UOJConfig::$data['profile']['oj-name-short'] ?>博客使用的是Markdown。好吧……好简陋的……好多功能还没写……</p>
<p>喂喂喂我们是OJ好吗……要那么完善的博客功能干啥呢……</p>
<p>其实我觉得Markdown不用教一学就会</p>
<p>(完蛋了……<?= UOJConfig::$data['profile']['oj-name-short'] ?>好像没有Markdown的语法高亮……= =……)</p>
<p>我就只介绍最基本的功能好了。其它的自己探索吧~比如<a href="http://wow.kuapp.com/markdown/">这里</a></p>
<!-- readmore -->
<p><code>**强调**</code> = <strong>强调</strong></p>
<hr /><p><code>*强调*</code> = <em>强调</em></p>
<hr /><p><code>[<?= UOJConfig::$data['profile']['oj-name-short'] ?>](<?= HTML::url('/') ?>)</code> = <a href="<?= HTML::url('/') ?>"><?= UOJConfig::$data['profile']['oj-name-short'] ?></a></p>
<hr /><p><code><?= HTML::url('/') ?></code> = <a href="http://<?= UOJConfig::$data['web']['main']['host'] ?>"><?= HTML::url('/') ?></a></p>
<hr /><p><code>![这个文字在图挂了的时候会显示](<?= HTML::url('/images/favicon.ico') ?>)</code> =
<img src="<?= HTML::url('/images/favicon.ico') ?>" alt="这个文字在图挂了的时候会显示" /></p>
<hr /><p><code>`rm orz`</code> = <code>rm orz</code></p>
<hr /><p><code>数学公式萌萌哒$(a + b)^2$萌萌哒</code> = 数学公式萌萌哒$(a + b)^2$萌萌哒</p>
<hr /><p><code>&lt;!-- readmore --&gt;</code> = 在外面看这篇博客时会到此为止然后显示一个“阅读更多”字样</p>
<hr /><p>来个更大的例子:</p>
<pre>
```c++
#include &lt;iostream&gt;
```
```c
#include &lt;stdio.h&gt;
```
```pascal
begin
```
```python
print '<?= UOJConfig::$data['profile']['oj-name-short'] ?>'
```
\begin{equation}
\frac{-b + \sqrt{b^2 - 4ac}}{2a}
\end{equation}
#一级标题
##二级标题
###三级标题
####四级标题
</pre>
<p>会转换为:</p>
<pre><code class="sh_cpp">#include &lt;iostream&gt;</code></pre>
<pre><code class="sh_c">#include &lt;stdio.h&gt;</code></pre>
<pre><code class="sh_pascal">begin</code></pre>
<pre><code class="sh_python">print '<?= UOJConfig::$data['profile']['oj-name-short'] ?>'</code></pre>
<p>\begin{equation}
\frac{-b + \sqrt{b^2 - 4ac}}{2a}
\end{equation}</p>
<h1>一级标题</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<h4>四级标题</h4>
<hr /><p>还有一个很重要的事情,就是你很容易以为<?= UOJConfig::$data['profile']['oj-name-short'] ?>在吃换行……</p>
<p>那是因为跟LaTeX一样你需要一个空行来分段。你可以粗略地认为两个换行会被替换成一换行。当然不完全是这样空行是用来分段的段落还有间距啊行首空两格啊之类的属性</p>
<p>唔……就介绍到这里吧。想要更详细的介绍上网搜搜吧~</p>
<p>评论区是不可以用任何HTML滴但是数学公式还是没问题滴</p>
</div>
</div>
</div>
<div class="card my-1">
<div class="card-header collapsed" id="headerEight" data-toggle="collapse" data-target="#collapseEight" style="cursor:pointer;">
<h5 class="mb-0">交互式类型的题怎么本地测试</h5>
</div>
<div id="collapseEight" class="collapse">
<div class="card-body">
<p>唔……好问题。交互式的题一般给了一个头文件要你include进来以及一个实现接口的源文件grader。好像大家对多个源文件一起编译还不太熟悉。</p>
<p>对于C++<code>g++ -o code grader.cpp code.cpp</code></p>
<p>对于C语言<code>gcc -o code grader.c code.c</code></p>
<p>如果你是悲催的电脑盲实在不会折腾没关系你可以把grader的文件内容完整地粘贴到你的code的include语句之后就可以了</p>
<p>什么你是萌萌哒Pascal选手一般来说都会给个grader你需要写一个Pascal单元。这个grader会使用你的单元。所以你只需要把源文件取名为单元名 + <code>.pas</code>,然后:</p>
<p>对于Pascal语言<code>fpc grader.pas</code></p>
<p>就可以啦!</p>
</div>
</div>
</div>
<div class="card my-1">
<div class="card-header collapsed" id="headerNine" data-toggle="collapse" data-target="#collapseNine" style="cursor:pointer;">
<h5 class="mb-0">联系方式</h5>
</div>
<div id="collapseNine" class="collapse">
<div class="card-body">
<p>如果你想出题、想办比赛、发现了BUG或者对网站有什么建议可以通过下面的方式联系我们</p>
<ul>
<li>私信联系<?= UOJConfig::$data['profile']['administrator'] ?></li>
<li>邮件联系<?= UOJConfig::$data['profile']['admin-email'] ?></li>
<?php if (UOJConfig::$data['profile']['QQ-group']!=''): ?>
<li>你也可以进QQ群水水群号是<?= UOJConfig::$data['profile']['QQ-group'] ?></li>
<?php endif ?>
</ul>
</div>
</div>
</div>
</section>
</article>
<?php echoUOJPageFooter() ?>

@ -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">&nbsp;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">&nbsp;</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,811 @@
<?php
requirePHPLib('form');
requirePHPLib('judger');
requirePHPLib('data');
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
become404Page();
}
if (!hasProblemPermission($myUser, $problem)) {
become403Page();
}
$oj_name = UOJConfig::$data['profile']['oj-name'];
$problem_extra_config = getProblemExtraConfig($problem);
$data_dir = "/var/uoj_data/${problem['id']}";
function echoFileNotFound($file_name) {
echo '<h4>', htmlspecialchars($file_name), '<sub class="text-danger"> ', '文件未找到', '</sub></h4>';
}
function echoFilePre($file_name) {
global $data_dir;
$file_full_name = $data_dir . '/' . $file_name;
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $file_full_name);
if ($mimetype === false) {
echoFileNotFound($file_name);
return;
}
finfo_close($finfo);
echo '<h4>', htmlspecialchars($file_name), '<sub> ', $mimetype, '</sub></h4>';
echo "<pre>\n";
$output_limit = 1000;
if (strStartWith($mimetype, 'text/')) {
echo htmlspecialchars(uojFilePreview($file_full_name, $output_limit));
} else {
echo htmlspecialchars(uojFilePreview($file_full_name, $output_limit, 'binary'));
}
echo "\n</pre>";
}
//上传数据
if ($_POST['problem_data_file_submit']=='submit') {
if ($_FILES["problem_data_file"]["error"] > 0) {
$errmsg = "Error: ".$_FILES["problem_data_file"]["error"];
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/'.$problem['id'].'/manage/data">返回</a>');
} else {
$zip_mime_types = array('application/zip', 'application/x-zip', 'application/x-zip-compressed');
if (in_array($_FILES["problem_data_file"]["type"], $zip_mime_types)) {
$up_filename="/tmp/".rand(0,100000000)."data.zip";
move_uploaded_file($_FILES["problem_data_file"]["tmp_name"], $up_filename);
$zip = new ZipArchive;
if ($zip->open($up_filename) === TRUE) {
$zip->extractTo("/var/uoj_data/upload/{$problem['id']}");
$zip->close();
exec("cd /var/uoj_data/upload/{$problem['id']}; if [ `find . -maxdepth 1 -type f`File = File ]; then for sub_dir in `find -maxdepth 1 -type d ! -name .`; do mv -f \$sub_dir/* . && rm -rf \$sub_dir; done; fi");
echo "<script>alert('上传成功!')</script>";
} else {
$errmsg = "解压失败!";
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/'.$problem['id'].'/manage/data">返回</a>');
}
unlink($up_filename);
} else {
$errmsg = "请上传zip格式";
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/'.$problem['id'].'/manage/data">返回</a>');
}
}
}
//添加配置文件
if ($_POST['problem_settings_file_submit']=='submit') {
if ($_POST['use_builtin_checker'] and $_POST['n_tests'] and $_POST['input_pre'] and $_POST['input_suf'] and $_POST['output_pre'] and $_POST['output_suf'] and $_POST['time_limit'] and $_POST['memory_limit']) {
$set_filename="/var/uoj_data/upload/{$problem['id']}/problem.conf";
$has_legacy=false;
if (file_exists($set_filename)) {
$has_legacy=true;
unlink($set_filename);
}
$setfile = fopen($set_filename, "w");
fwrite($setfile, "use_builtin_judger on\n");
if ($_POST['use_builtin_checker'] != 'ownchk') {
fwrite($setfile, "use_builtin_checker ".$_POST['use_builtin_checker']."\n");
}
fwrite($setfile, "n_tests ".$_POST['n_tests']."\n");
if ($_POST['n_ex_tests']) {
fwrite($setfile, "n_ex_tests ".$_POST['n_ex_tests']."\n");
} else {
fwrite($setfile, "n_ex_tests 0\n");
}
if ($_POST['n_sample_tests']) {
fwrite($setfile, "n_sample_tests ".$_POST['n_sample_tests']."\n");
} else {
fwrite($setfile, "n_sample_tests 0\n");
}
fwrite($setfile, "input_pre ".$_POST['input_pre']."\n");
fwrite($setfile, "input_suf ".$_POST['input_suf']."\n");
fwrite($setfile, "output_pre ".$_POST['output_pre']."\n");
fwrite($setfile, "output_suf ".$_POST['output_suf']."\n");
fwrite($setfile, "time_limit ".$_POST['time_limit']."\n");
fwrite($setfile, "memory_limit ".$_POST['memory_limit']."\n");
fclose($setfile);
if (!$has_legacy) {
echo "<script>alert('添加成功!')</script>";
} else {
echo "<script>alert('替换成功!')</script>";
}
} else {
$errmsg = "添加配置文件失败,请检查是否所有输入框都已填写!";
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/'.$problem['id'].'/manage/data">返回</a>');
}
}
$info_form = new UOJForm('info');
$http_host = HTML::escape(UOJContext::httpHost());
$download_url = HTML::url("/download.php?type=problem&id={$problem['id']}");
$info_form->appendHTML(<<<EOD
<div class="form-group row">
<!--<label class="col-sm-3 control-label">zip上传数据</label>
<div class="col-sm-9">
<div class="form-control-static">
<row>
<button type="button" style="width:30%" class="btn btn-primary" data-toggle="modal" data-target="#UploadDataModal">上传数据</button>
<button type="submit" style="width:30%" id="button-submit-data" name="submit-data" value="data" class="btn btn-danger">检验配置并同步数据</button>
</row>
</div>
</div>-->
</div>
EOD
);
$info_form->appendHTML(<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">problem_{$problem['id']}.zip</label>
<div class="col-sm-9">
<div class="form-control-static">
<a href="$download_url">$download_url</a>
</div>
</div>
</div>
EOD
);
$info_form->appendHTML(<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">testlib.h</label>
<div class="col-sm-9">
<div class="form-control-static">
<a href="/download.php?type=testlib.h">下载</a>
</div>
</div>
</div>
EOD
);
$esc_submission_requirement = HTML::escape(json_encode(json_decode($problem['submission_requirement']), JSON_PRETTY_PRINT));
$info_form->appendHTML(<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">提交文件配置</label>
<div class="col-sm-9">
<div class="form-control-static"><pre>
$esc_submission_requirement
</pre>
</div>
</div>
</div>
EOD
);
$esc_extra_config = HTML::escape(json_encode(json_decode($problem['extra_config']), JSON_PRETTY_PRINT));
$info_form->appendHTML(<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">其它配置</label>
<div class="col-sm-9">
<div class="form-control-static"><pre>
$esc_extra_config
</pre>
</div>
</div>
</div>
EOD
);
if (isSuperUser($myUser)) {
$info_form->addVInput('submission_requirement', 'text', '提交文件配置', $problem['submission_requirement'],
function ($submission_requirement, &$vdata) {
$submission_requirement = json_decode($submission_requirement, true);
if ($submission_requirement === null) {
return '不是合法的JSON';
}
$vdata['submission_requirement'] = json_encode($submission_requirement);
},
null);
$info_form->addVInput('extra_config', 'text', '其它配置', $problem['extra_config'],
function ($extra_config, &$vdata) {
$extra_config = json_decode($extra_config, true);
if ($extra_config === null) {
return '不是合法的JSON';
}
$vdata['extra_config'] = json_encode($extra_config);
},
null);
$info_form->handle = function(&$vdata) {
global $problem;
$esc_submission_requirement = DB::escape($vdata['submission_requirement']);
$esc_extra_config = DB::escape($vdata['extra_config']);
DB::update("update problems set submission_requirement = '$esc_submission_requirement', extra_config = '$esc_extra_config' where id = {$problem['id']}");
};
} else {
$info_form->no_submit = true;
}
class DataDisplayer {
public $problem_conf = array();
public $data_files = array();
public $displayers = array();
public function __construct($problem_conf = null, $data_files = null) {
global $data_dir;
if (isset($problem_conf)) {
foreach ($problem_conf as $key => $val) {
$this->problem_conf[$key] = array('val' => $val);
}
}
if (!isset($data_files)) {
$this->data_files = array_filter(scandir($data_dir), function($x) {
return $x !== '.' && $x !== '..' && $x !== 'problem.conf';
});
natsort($this->data_files);
array_unshift($this->data_files, 'problem.conf');
} else {
$this->data_files = $data_files;
}
$this->setDisplayer('problem.conf', function($self) {
global $info_form;
$info_form->printHTML();
echo '<div class="top-buffer-md"></div>';
echo '<table class="table table-bordered table-hover table-striped table-text-center">';
echo '<thead>';
echo '<tr>';
echo '<th>key</th>';
echo '<th>value</th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';
foreach ($self->problem_conf as $key => $info) {
if (!isset($info['status'])) {
echo '<tr>';
echo '<td>', htmlspecialchars($key), '</td>';
echo '<td>', htmlspecialchars($info['val']), '</td>';
echo '</tr>';
} elseif ($info['status'] == 'danger') {
echo '<tr class="text-danger">';
echo '<td>', htmlspecialchars($key), '</td>';
echo '<td>', htmlspecialchars($info['val']), ' <span class="glyphicon glyphicon-remove"></span>', '</td>';
echo '</tr>';
}
}
echo '</tbody>';
echo '</table>';
echoFilePre('problem.conf');
});
}
public function setProblemConfRowStatus($key, $status) {
$this->problem_conf[$key]['status'] = $status;
return $this;
}
public function setDisplayer($file_name, $fun) {
$this->displayers[$file_name] = $fun;
return $this;
}
public function addDisplayer($file_name, $fun) {
$this->data_files[] = $file_name;
$this->displayers[$file_name] = $fun;
return $this;
}
public function echoDataFilesList($active_file) {
foreach ($this->data_files as $file_name) {
echo '<li class="nav-item">';
if ($file_name != $active_file) {
echo '<a class="nav-link" href="#">';
} else {
echo '<a class="nav-link active" href="#">';
}
echo htmlspecialchars($file_name), '</a>', '</li>';
}
}
public function displayFile($file_name) {
global $data_dir;
if (isset($this->displayers[$file_name])) {
$fun = $this->displayers[$file_name];
$fun($this);
} elseif (in_array($file_name, $this->data_files)) {
echoFilePre($file_name);
} else {
echoFileNotFound($file_name);
}
}
}
function getDataDisplayer() {
global $data_dir;
global $problem;
$allow_files = array_flip(array_filter(scandir($data_dir), function($x) {
return $x !== '.' && $x !== '..';
}));
$getDisplaySrcFunc = function($name) use ($allow_files) {
return function() use ($name, $allow_files) {
$src_name = $name . '.cpp';
if (isset($allow_files[$src_name])) {
echoFilePre($src_name);
} else {
echoFileNotFound($src_name);
}
if (isset($allow_files[$name])) {
echoFilePre($name);
} else {
echoFileNotFound($name);
}
};
};
$problem_conf = getUOJConf("$data_dir/problem.conf");
if ($problem_conf === -1) {
return (new DataDisplayer())->setDisplayer('problem.conf', function() {
global $info_form;
$info_form->printHTML();
echoFileNotFound('problem.conf');
});
}
if ($problem_conf === -2) {
return (new DataDisplayer())->setDisplayer('problem.conf', function() {
global $info_form;
$info_form->printHTML();
echo '<h4 class="text-danger">problem.conf 格式有误</h4>';
echoFilePre('problem.conf');
});
}
$judger_name = getUOJConfVal($problem_conf, 'use_builtin_judger', null);
if (!isset($problem_conf['use_builtin_judger'])) {
return new DataDisplayer($problem_conf);
}
if ($problem_conf['use_builtin_judger'] == 'on') {
$n_tests = getUOJConfVal($problem_conf, 'n_tests', 10);
if (!validateUInt($n_tests)) {
return (new DataDisplayer($problem_conf))->setProblemConfRowStatus('n_tests', 'danger');
}
$has_extra_tests = !(isset($problem_conf['submit_answer']) && $problem_conf['submit_answer'] == 'on');
$data_disp = new DataDisplayer($problem_conf, array('problem.conf'));
$data_disp->addDisplayer('tests',
function($self) use ($problem_conf, $allow_files, $n_tests, $n_ex_tests) {
for ($num = 1; $num <= $n_tests; $num++) {
$input_file_name = getUOJProblemInputFileName($problem_conf, $num);
$output_file_name = getUOJProblemOutputFileName($problem_conf, $num);
echo '<div class="row">';
echo '<div class="col-md-6">';
if (isset($allow_files[$input_file_name])) {
echoFilePre($input_file_name);
} else {
echoFileNotFound($input_file_name);
}
echo '</div>';
echo '<div class="col-md-6">';
if (isset($allow_files[$output_file_name])) {
echoFilePre($output_file_name);
} else {
echoFileNotFound($output_file_name);
}
echo '</div>';
echo '</div>';
}
}
);
if ($has_extra_tests) {
$n_ex_tests = getUOJConfVal($problem_conf, 'n_ex_tests', 0);
if (!validateUInt($n_ex_tests)) {
return (new DataDisplayer($problem_conf))->setProblemConfRowStatus('n_ex_tests', 'danger');
}
$data_disp->addDisplayer('extra tests',
function($self) use ($problem_conf, $allow_files, $n_tests, $n_ex_tests) {
for ($num = 1; $num <= $n_ex_tests; $num++) {
$input_file_name = getUOJProblemExtraInputFileName($problem_conf, $num);
$output_file_name = getUOJProblemExtraOutputFileName($problem_conf, $num);
echo '<div class="row">';
echo '<div class="col-md-6">';
if (isset($allow_files[$input_file_name])) {
echoFilePre($input_file_name);
} else {
echoFileNotFound($input_file_name);
}
echo '</div>';
echo '<div class="col-md-6">';
if (isset($allow_files[$output_file_name])) {
echoFilePre($output_file_name);
} else {
echoFileNotFound($output_file_name);
}
echo '</div>';
echo '</div>';
}
}
);
}
if (!isset($problem_conf['interaction_mode'])) {
if (isset($problem_conf['use_builtin_checker'])) {
$data_disp->addDisplayer('checker', function($self) {
echo '<h4>use builtin checker : ', $self->problem_conf['use_builtin_checker']['val'], '</h4>';
});
} else {
$data_disp->addDisplayer('checker', $getDisplaySrcFunc('chk'));
}
}
if ($problem['hackable']) {
$data_disp->addDisplayer('standard', $getDisplaySrcFunc('std'));
$data_disp->addDisplayer('validator', $getDisplaySrcFunc('val'));
}
if (isset($problem_conf['interaction_mode'])) {
$data_disp->addDisplayer('interactor', $getDisplaySrcFunc('interactor'));
}
return $data_disp;
} else {
return (new DataDisplayer($problem_conf))->setProblemConfRowStatus('use_builtin_judger', 'danger');
}
}
$data_disp = getDataDisplayer();
if (isset($_GET['display_file'])) {
if (!isset($_GET['file_name'])) {
echoFileNotFound('');
} else {
$data_disp->displayFile($_GET['file_name']);
}
die();
}
$hackable_form = new UOJForm('hackable');
$hackable_form->handle = function() {
global $problem;
$problem['hackable'] = !$problem['hackable'];
//$problem['hackable'] = 0;
$ret = dataSyncProblemData($problem);
if ($ret) {
becomeMsgPage('<div>' . $ret . '</div><a href="/problem/'.$problem['id'].'/manage/data">返回</a>');
}
$hackable = $problem['hackable'] ? 1 : 0;
DB::query("update problems set hackable = $hackable where id = ${problem['id']}");
};
$hackable_form->submit_button_config['class_str'] = 'btn btn-warning btn-block';
$hackable_form->submit_button_config['text'] = $problem['hackable'] ? '禁止使用hack' : '允许使用hack';
$hackable_form->submit_button_config['smart_confirm'] = '';
$data_form = new UOJForm('data');
$data_form->handle = function() {
global $problem, $myUser;
set_time_limit(60 * 5);
$ret = dataSyncProblemData($problem, $myUser);
if ($ret) {
becomeMsgPage('<div>' . $ret . '</div><a href="/problem/'.$problem['id'].'/manage/data">返回</a>');
}
};
$data_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
$data_form->submit_button_config['text'] = '检验配置并同步数据';
$data_form->submit_button_config['smart_confirm'] = '';
$clear_data_form = new UOJForm('clear_data');
$clear_data_form->handle = function() {
global $problem;
dataClearProblemData($problem);
};
$clear_data_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
$clear_data_form->submit_button_config['text'] = '清空题目数据';
$clear_data_form->submit_button_config['smart_confirm'] = '';
$rejudge_form = new UOJForm('rejudge');
$rejudge_form->handle = function() {
global $problem;
rejudgeProblem($problem);
};
$rejudge_form->succ_href = "/submissions?problem_id={$problem['id']}";
$rejudge_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
$rejudge_form->submit_button_config['text'] = '重测该题';
$rejudge_form->submit_button_config['smart_confirm'] = '';
$rejudgege97_form = new UOJForm('rejudgege97');
$rejudgege97_form->handle = function() {
global $problem;
rejudgeProblemGe97($problem);
};
$rejudgege97_form->succ_href = "/submissions?problem_id={$problem['id']}";
$rejudgege97_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
$rejudgege97_form->submit_button_config['text'] = '重测 >=97 的程序';
$rejudgege97_form->submit_button_config['smart_confirm'] = '';
$view_type_form = new UOJForm('view_type');
$view_type_form->addVSelect('view_content_type',
array('NONE' => '禁止',
'SELF' => '仅自己',
'ALL_AFTER_AC' => 'AC后',
'ALL' => '所有人'
),
'查看提交文件:',
$problem_extra_config['view_content_type']
);
$view_type_form->addVSelect('view_all_details_type',
array('NONE' => '禁止',
'SELF' => '仅自己',
'ALL_AFTER_AC' => 'AC后',
'ALL' => '所有人'
),
'查看全部详细信息:',
$problem_extra_config['view_all_details_type']
);
$view_type_form->addVSelect('view_details_type',
array('NONE' => '禁止',
'SELF' => '仅自己',
'ALL_AFTER_AC' => 'AC后',
'ALL' => '所有人'
),
'查看测试点详细信息:',
$problem_extra_config['view_details_type']
);
$view_type_form->handle = function() {
global $problem, $problem_extra_config;
$config = $problem_extra_config;
$config['view_content_type'] = $_POST['view_content_type'];
$config['view_all_details_type'] = $_POST['view_all_details_type'];
$config['view_details_type'] = $_POST['view_details_type'];
$esc_config = DB::escape(json_encode($config));
DB::query("update problems set extra_config = '$esc_config' where id = '{$problem['id']}'");
};
$view_type_form->submit_button_config['class_str'] = 'btn btn-warning btn-block top-buffer-sm';
if ($problem['hackable']) {
$test_std_form = new UOJForm('test_std');
$test_std_form->handle = function() {
global $myUser, $problem;
$user_std = queryUser('std');
if (!$user_std) {
becomeMsgPage('请建立"std"账号。');
}
$requirement = json_decode($problem['submission_requirement'], true);
$zip_file_name = uojRandAvaiableSubmissionFileName();
$zip_file = new ZipArchive();
if ($zip_file->open(UOJContext::storagePath().$zip_file_name, ZipArchive::CREATE) !== true) {
becomeMsgPage('提交失败');
}
$content = array();
$content['file_name'] = $zip_file_name;
$content['config'] = array();
foreach ($requirement as $req) {
if ($req['type'] == "source code") {
$content['config'][] = array("{$req['name']}_language", "C++");
}
}
$tot_size = 0;
foreach ($requirement as $req) {
$zip_file->addFile("/var/uoj_data/{$problem['id']}/std.cpp", $req['file_name']);
$tot_size += $zip_file->statName($req['file_name'])['size'];
}
$zip_file->close();
$content['config'][] = array('validate_input_before_test', 'on');
$content['config'][] = array('problem_id', $problem['id']);
$esc_content = DB::escape(json_encode($content));
$esc_language = DB::escape('C++');
$result = array();
$result['status'] = "Waiting";
$result_json = json_encode($result);
$is_hidden = $problem['is_hidden'] ? 1 : 0;
DB::insert("insert into submissions (problem_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values ({$problem['id']}, now(), '{$user_std['username']}', '$esc_content', '$esc_language', $tot_size, '{$result['status']}', '$result_json', $is_hidden)");
};
$test_std_form->succ_href = "/submissions?problem_id={$problem['id']}";
$test_std_form->submit_button_config['class_str'] = 'btn btn-danger btn-block';
$test_std_form->submit_button_config['text'] = '检验数据正确性';
$test_std_form->runAtServer();
}
$hackable_form->runAtServer();
$view_type_form->runAtServer();
$data_form->runAtServer();
$clear_data_form->runAtServer();
$rejudge_form->runAtServer();
$rejudgege97_form->runAtServer();
$info_form->runAtServer();
?>
<?php
$REQUIRE_LIB['dialog'] = '';
?>
<?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" 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 active" 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>
<div class="row">
<div class="col-md-10 top-buffer-sm">
<div class="row">
<div class="col-md-3 top-buffer-sm" id="div-file_list">
<ul class="nav nav-pills flex-column">
<?php $data_disp->echoDataFilesList('problem.conf'); ?>
</ul>
</div>
<div class="col-md-9 top-buffer-sm" id="div-file_content">
<?php $data_disp->displayFile('problem.conf'); ?>
</div>
<script type="text/javascript">
curFileName = '';
$('#div-file_list a').click(function(e) {
$('#div-file_content').html('<h3>Loading...</h3>');
$(this).tab('show');
var fileName = $(this).text();
curFileName = fileName;
$.get('/problem/<?= $problem['id'] ?>/manage/data', {
display_file: '',
file_name: fileName
},
function(data) {
if (curFileName != fileName) {
return;
}
$('#div-file_content').html(data);
},
'html'
);
return false;
});
</script>
</div>
</div>
<div class="col-md-2 top-buffer-sm">
<div class="top-buffer-md">
<?php if ($problem['hackable']): ?>
<span class="glyphicon glyphicon-ok"></span> hack功能已启用
<?php else: ?>
<span class="glyphicon glyphicon-remove"></span> hack功能已禁止
<?php endif ?>
<?php $hackable_form->printHTML() ?>
</div>
<div class="top-buffer-md">
<?php if ($problem['hackable']): ?>
<?php $test_std_form->printHTML() ?>
<?php endif ?>
</div>
<div class="top-buffer-md">
<button id="button-display_view_type" type="button" class="btn btn-primary btn-block" onclick="$('#div-view_type').toggle('fast');">提交记录可视权限</button>
<div class="top-buffer-sm" id="div-view_type" style="display:none; padding-left:5px; padding-right:5px;">
<?php $view_type_form->printHTML(); ?>
</div>
</div>
<div class="top-buffer-md">
<?php $data_form->printHTML(); ?>
</div>
<div class="top-buffer-md">
<?php $clear_data_form->printHTML(); ?>
</div>
<div class="top-buffer-md">
<?php $rejudge_form->printHTML(); ?>
</div>
<div class="top-buffer-md">
<?php $rejudgege97_form->printHTML(); ?>
</div>
<div class="top-buffer-md">
<button type="button" class="btn btn-block btn-primary" data-toggle="modal" data-target="#UploadDataModal">上传数据</button>
</div>
<div class="top-buffer-md">
<button type="button" class="btn btn-block btn-primary" data-toggle="modal" data-target="#ProblemSettingsFileModal">试题配置</button>
</div>
</div>
<div class="modal fade" id="UploadDataModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">上传数据</h4>
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
</div>
<div class="modal-body">
<form action="" method="post" enctype="multipart/form-data" role="form">
<div class="form-group">
<label for="exampleInputFile">上传zip文件</label>
<input type="file" name="problem_data_file" id="problem_data_file">
<p class="help-block">说明:请将所有数据放置于压缩包根目录内。若压缩包内仅存在文件夹而不存在文件,则会将这些一级子文件夹下的内容移动到根目录下,然后这些一级子文件夹删除;若这些子文件夹内存在同名文件,则会发生随机替换,仅保留一个副本。</p>
</div>
<input type="hidden" name="problem_data_file_submit" value="submit">
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">上传</button>
</form>
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="ProblemSettingsFileModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">试题配置</h4>
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
</div>
<div class="modal-body">
<form class="form-horizontal" action="" method="post" role="form">
<div class="form-group row">
<label for="use_builtin_checker" class="col-sm-5 control-label">比对函数</label>
<div class="col-sm-7">
<select class="form-control" id="use_builtin_checker" name="use_builtin_checker">
<option value="ncmp">单行整数序列</option>
<option value="wcmp">单行字符串序列</option>
<option value="fcmp">多行数据(不忽略行末空格,但忽略文末回车)</option>
<option value="ownchk">自定义校验器</option>
</select>
<!--<input type="hidden" class="form-control" id="use_builtin_checker" name="use_builtin_checker" placeholder="比对函数">-->
</div>
</div>
<div class="form-group row">
<label for="n_tests" class="col-sm-5 control-label">n_tests</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="n_tests" name="n_tests" placeholder="数据点个数">
</div>
</div>
<div class="form-group row">
<label for="n_ex_tests" class="col-sm-5 control-label">n_ex_tests</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="n_ex_tests" name="n_ex_tests" placeholder="额外数据点个数">
</div>
</div>
<div class="form-group row">
<label for="n_sample_tests" class="col-sm-5 control-label">n_sample_tests</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="n_sample_tests" name="n_sample_tests" placeholder="样例测试点个数">
</div>
</div>
<div class="form-group row">
<label for="input_pre" class="col-sm-5 control-label">input_pre</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="input_pre" name="input_pre" placeholder="输入文件名称">
</div>
</div>
<div class="form-group row">
<label for="input_suf" class="col-sm-5 control-label">input_suf</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="input_suf" name="input_suf" placeholder="输入文件后缀">
</div>
</div>
<div class="form-group row">
<label for="output_pre" class="col-sm-5 control-label">output_pre</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="output_pre" name="output_pre" placeholder="输出文件名称">
</div>
</div>
<div class="form-group row">
<label for="output_suf" class="col-sm-5 control-label">output_suf</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="output_suf" name="output_suf" placeholder="输出文件后缀">
</div>
</div>
<div class="form-group row">
<label for="time_limit" class="col-sm-5 control-label">time_limit</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="time_limit" name="time_limit" placeholder="时间限制(不能为小数!)">
</div>
</div>
<div class="form-group row">
<label for="memory_limit" class="col-sm-5 control-label">memory_limit</label>
<div class="col-sm-7">
<input type="text" class="form-control" id="memory_limit" name="memory_limit" placeholder="内存限制">
</div>
</div>
<input type="hidden" name="problem_settings_file_submit" value="submit">
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">确定</button>
</form>
<button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</div>
<?php echoUOJPageFooter() ?>

@ -0,0 +1,59 @@
<?php
requirePHPLib('form');
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {
become404Page();
}
if (!hasProblemPermission($myUser, $problem)) {
become403Page();
}
$managers_form = newAddDelCmdForm('managers',
function($username) {
if (!validateUsername($username) || !queryUser($username)) {
return "不存在名为{$username}的用户";
}
return '';
},
function($type, $username) {
global $problem;
if ($type == '+') {
DB::query("insert into problems_permissions (problem_id, username) values (${problem['id']}, '$username')");
} elseif ($type == '-') {
DB::query("delete from problems_permissions where problem_id = ${problem['id']} and username = '$username'");
}
}
);
$managers_form->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" href="/problem/<?= $problem['id'] ?>/manage/statement" role="tab">编辑</a></li>
<li class="nav-item"><a class="nav-link active" 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>
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>用户名</th>
</tr>
</thead>
<tbody>
<?php
$row_id = 0;
$result = DB::query("select username from problems_permissions where problem_id = ${problem['id']}");
while ($row = DB::fetch($result, MYSQLI_ASSOC)) {
$row_id++;
echo '<tr>', '<td>', $row_id, '</td>', '<td>', getUserLink($row['username']), '</td>', '</tr>';
}
?>
</tbody>
</table>
<p class="text-center">命令格式:命令一行一个,+mike表示把mike加入管理者-mike表示把mike从管理者中移除</p>
<?php $managers_form->printHTML(); ?>
<?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">&times;{$problem['ac_num']}</a></td>
<td><a href="/submissions?problem_id={$problem['id']}">&times;{$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'] ?> + '&amp;min_score=' + scr + '&amp;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: &le;' + scr + '</div>' +
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;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: &ge;' + scr + '</div>' +
'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;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,12 @@
<?php requirePHPLib('form') ?>
<?php echoUOJPageHeader('关于我') ?>
<?php if (UOJContext::user()['username'] != 'vfleaking'): ?>
<h3>博主是个超级大神犇!</h3>
<?php else: ?>
<h3>博主太弱了!</h3>
<?php endif ?>
(好吧目前暂时不支持定制此页,我错了我会加这个功能的 T_T……
<?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,196 @@
<?php
requirePHPLib('form');
if (!isset($_GET['id']) || !validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHis($blog)) {
become404Page();
}
if ($blog['is_hidden'] && !UOJContext::hasBlogPermission()) {
become403Page();
}
$comment_form = new UOJForm('comment');
$comment_form->addVTextArea('comment', '内容', '',
function($comment) {
global $myUser;
if ($myUser == null) {
return '请先登录';
}
if (!$comment) {
return '评论不能为空';
}
if (strlen($comment) > 1000) {
return '不能超过1000个字节';
}
return '';
},
null
);
$comment_form->handle = function() {
global $myUser, $blog, $comment_form;
$comment = HTML::escape($_POST['comment']);
list($comment, $referrers) = uojHandleAtSign($comment, "/post/{$blog['id']}");
$esc_comment = DB::escape($comment);
DB::insert("insert into blogs_comments (poster, blog_id, content, reply_id, post_time, zan) values ('{$myUser['username']}', '{$blog['id']}', '$esc_comment', 0, now(), 0)");
$comment_id = DB::insert_id();
$rank = DB::selectCount("select count(*) from blogs_comments where blog_id = {$blog['id']} and reply_id = 0 and id < {$comment_id}");
$page = floor($rank / 20) + 1;
$uri = getLongTablePageUri($page) . '#' . "comment-{$comment_id}";
foreach ($referrers as $referrer) {
$content = '有人在博客 ' . $blog['title'] . ' 的评论里提到你:<a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($referrer, '有人提到你', $content);
}
if ($blog['poster'] !== $myUser['username']) {
$content = '有人回复了您的博客 ' . $blog['title'] . ' <a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($blog['poster'], '博客新回复通知', $content);
}
$comment_form->succ_href = getLongTablePageRawUri($page);
};
$comment_form->ctrl_enter_submit = true;
$comment_form->runAtServer();
$reply_form = new UOJForm('reply');
$reply_form->addHidden('reply_id', '0',
function($reply_id, &$vdata) {
global $blog;
if (!validateUInt($reply_id) || $reply_id == 0) {
return '您要回复的对象不存在';
}
$comment = queryBlogComment($reply_id);
if (!$comment || $comment['blog_id'] != $blog['id']) {
return '您要回复的对象不存在';
}
$vdata['parent'] = $comment;
return '';
},
null
);
$reply_form->addVTextArea('reply_comment', '内容', '',
function($comment) {
global $myUser;
if ($myUser == null) {
return '请先登录';
}
if (!$comment) {
return '评论不能为空';
}
if (strlen($comment) > 140) {
return '不能超过140个字节';
}
return '';
},
null
);
$reply_form->handle = function(&$vdata) {
global $myUser, $blog, $reply_form;
$comment = HTML::escape($_POST['reply_comment']);
list($comment, $referrers) = uojHandleAtSign($comment, "/post/{$blog['id']}");
$reply_id = $_POST['reply_id'];
$esc_comment = DB::escape($comment);
DB::insert("insert into blogs_comments (poster, blog_id, content, reply_id, post_time, zan) values ('{$myUser['username']}', '{$blog['id']}', '$esc_comment', $reply_id, now(), 0)");
$comment_id = DB::insert_id();
$rank = DB::selectCount("select count(*) from blogs_comments where blog_id = {$blog['id']} and reply_id = 0 and id < {$reply_id}");
$page = floor($rank / 20) + 1;
$uri = getLongTablePageUri($page) . '#' . "comment-{$reply_id}";
foreach ($referrers as $referrer) {
$content = '有人在博客 ' . $blog['title'] . ' 的评论里提到你:<a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($referrer, '有人提到你', $content);
}
$parent = $vdata['parent'];
$notified = array();
if ($parent['poster'] !== $myUser['username']) {
$notified[] = $parent['poster'];
$content = '有人回复了您在博客 ' . $blog['title'] . ' 下的评论 <a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($parent['poster'], '评论新回复通知', $content);
}
if ($blog['poster'] !== $myUser['username'] && !in_array($blog['poster'], $notified)) {
$notified[] = $blog['poster'];
$content = '有人回复了您的博客 ' . $blog['title'] . ' <a href="' . $uri . '">点击此处查看</a>';
sendSystemMsg($blog['poster'], '博客新回复通知', $content);
}
$reply_form->succ_href = getLongTablePageRawUri($page);
};
$reply_form->ctrl_enter_submit = true;
$reply_form->runAtServer();
$comments_pag = new Paginator(array(
'col_names' => array('*'),
'table_name' => 'blogs_comments',
'cond' => 'blog_id = ' . $blog['id'] . ' and reply_id = 0',
'tail' => 'order by id asc',
'page_len' => 20
));
?>
<?php
$REQUIRE_LIB['mathjax'] = '';
$REQUIRE_LIB['shjs'] = '';
?>
<?php echoUOJPageHeader(HTML::stripTags($blog['title']) . ' - 博客') ?>
<?php echoBlog($blog, array('show_title_only' => isset($_GET['page']) && $_GET['page'] != 1)) ?>
<h2>评论 <span class="glyphicon glyphicon-comment"></span></h2>
<div class="list-group">
<?php if ($comments_pag->isEmpty()): ?>
<div class="list-group-item text-muted">暂无评论</div>
<?php else: ?>
<?php foreach ($comments_pag->get() as $comment):
$poster = queryUser($comment['poster']);
$esc_email = HTML::escape($poster['email']);
$asrc = HTML::avatar_addr($poster, 80);
$replies = DB::selectAll("select id, poster, content, post_time from blogs_comments where reply_id = {$comment['id']} order by id");
foreach ($replies as $idx => $reply) {
$replies[$idx]['poster_rating'] = queryUser($reply['poster'])['rating'];
}
$replies_json = json_encode($replies);
?>
<div id="comment-<?= $comment['id'] ?>" class="list-group-item">
<div class="media">
<div class="media-left comtposterbox mr-3">
<a href="<?= HTML::url('/user/profile/'.$poster['username']) ?>" class="d-none d-sm-block">
<img class="media-object img-rounded" src="<?= $asrc ?>" alt="avatar" />
</a>
</div>
<div id="comment-body-<?= $comment['id'] ?>" class="media-body comtbox">
<div class="row">
<div class="col-sm-6"><?= getUserLink($poster['username']) ?></div>
<div class="col-sm-6 text-right"><?= getClickZanBlock('BC', $comment['id'], $comment['zan']) ?></div>
</div>
<div class="comtbox1"><?= $comment['content'] ?></div>
<ul class="text-right list-inline bot-buffer-no"><li><small class="text-muted"><?= $comment['post_time'] ?></small></li><li><a id="reply-to-<?= $comment['id'] ?>" href="#">回复</a></li></ul>
<?php if ($replies): ?>
<div id="replies-<?= $comment['id'] ?>" class="comtbox5"></div>
<?php endif ?>
<script type="text/javascript">showCommentReplies('<?= $comment['id'] ?>', <?= $replies_json ?>);</script>
</div>
</div>
</div>
<?php endforeach ?>
<?php endif ?>
</div>
<?= $comments_pag->pagination() ?>
<h3 class="mt-4">发表评论</h3>
<p>可以用@mike来提到mike这个用户mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。</p>
<?php $comment_form->printHTML() ?>
<div id="div-form-reply" style="display:none">
<?php $reply_form->printHTML() ?>
</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() ?>

@ -0,0 +1,183 @@
<?php
requirePHPLib('form');
requirePHPLib('judger');
if (!validateUInt($_GET['id']) || !($submission = querySubmission($_GET['id']))) {
become404Page();
}
$submission_result = json_decode($submission['result'], true);
$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 (!isSubmissionVisibleToUser($submission, $problem, $myUser)) {
become403Page();
}
$out_status = explode(', ', $submission['status'])[0];
if ($_GET['get'] == 'status-details' && Auth::check() && $submission['submitter'] === Auth::id()) {
echo json_encode(array(
'judged' => $out_status == 'Judged',
'html' => getSubmissionStatusDetails($submission)
));
die();
}
$hackable = $submission['score'] == 100 && $problem['hackable'] == 1;
if ($hackable) {
$hack_form = new UOJForm('hack');
$hack_form->addTextFileInput('input', '输入数据');
$hack_form->addCheckBox('use_formatter', '帮我整理文末回车、行末空格、换行符', true);
$hack_form->handle = function(&$vdata) {
global $myUser, $problem, $submission;
if ($myUser == null) {
redirectToLogin();
}
if ($_POST["input_upload_type"] == 'file') {
$tmp_name = UOJForm::uploadedFileTmpName("input_file");
if ($tmp_name == null) {
becomeMsgPage('你在干啥……怎么什么都没交过来……?');
}
}
$fileName = uojRandAvaiableTmpFileName();
$fileFullName = UOJContext::storagePath().$fileName;
if ($_POST["input_upload_type"] == 'editor') {
file_put_contents($fileFullName, $_POST['input_editor']);
} else {
move_uploaded_file($_FILES["input_file"]['tmp_name'], $fileFullName);
}
$input_type = isset($_POST['use_formatter']) ? "USE_FORMATTER" : "DONT_USE_FORMATTER";
DB::insert("insert into hacks (problem_id, submission_id, hacker, owner, input, input_type, submit_time, details, is_hidden) values ({$problem['id']}, {$submission['id']}, '{$myUser['username']}', '{$submission['submitter']}', '$fileName', '$input_type', now(), '', {$problem['is_hidden']})");
};
$hack_form->succ_href = "/hacks";
$hack_form->runAtServer();
}
if ($submission['status'] == 'Judged' && hasProblemPermission($myUser, $problem)) {
$rejudge_form = new UOJForm('rejudge');
$rejudge_form->handle = function() {
global $submission;
rejudgeSubmission($submission);
};
$rejudge_form->submit_button_config['class_str'] = 'btn btn-primary';
$rejudge_form->submit_button_config['text'] = '重新测试';
$rejudge_form->submit_button_config['align'] = 'right';
$rejudge_form->runAtServer();
}
if (isSuperUser($myUser)) {
$delete_form = new UOJForm('delete');
$delete_form->handle = function() {
global $submission;
$content = json_decode($submission['content'], true);
unlink(UOJContext::storagePath().$content['file_name']);
DB::delete("delete from submissions where id = {$submission['id']}");
updateBestACSubmissions($submission['submitter'], $submission['problem_id']);
};
$delete_form->submit_button_config['class_str'] = 'btn btn-danger';
$delete_form->submit_button_config['text'] = '删除此提交记录';
$delete_form->submit_button_config['align'] = 'right';
$delete_form->submit_button_config['smart_confirm'] = '';
$delete_form->succ_href = "/submissions";
$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 (explode(', ', $submission['status'])[0] != 'Judged') {
$should_show_all_details = false;
}
if ($contest != null && $contest['cur_progress'] == CONTEST_IN_PROGRESS) {
if ($contest['extra_config']["problem_{$submission['problem_id']}"] === 'no-details') {
$should_show_details = false;
}
}
if (!isSubmissionFullVisibleToUser($submission, $contest, $problem, $myUser)) {
$should_show_content = $should_show_all_details = false;
}
if ($contest != null && hasContestPermission($myUser, $contest)) {
$should_show_details_to_me = true;
$should_show_content = true;
$should_show_all_details = true;
}
if ($should_show_all_details) {
$styler = new SubmissionDetailsStyler();
if ((!$should_show_details || ($contest['extra_config']['contest_type']=='IOI' && $contest['cur_progress'] == CONTEST_IN_PROGRESS)) && !hasContestPermission($myUser, $contest)) {
$styler->fade_all_details = true;
$styler->show_small_tip = false;
if ($contest['extra_config']['contest_type']=='IOI' && $contest['cur_progress'] == CONTEST_IN_PROGRESS) {
$styler->ioi_contest_is_running = true;
}
}
}
?>
<?php
$REQUIRE_LIB['shjs'] = "";
?>
<?php echoUOJPageHeader(UOJLocale::get('problems::submission').' #'.$submission['id']) ?>
<?php echoSubmissionsListOnlyOne($submission, array(), $myUser) ?>
<?php if ($should_show_content): ?>
<?php echoSubmissionContent($submission, getProblemSubmissionRequirement($problem)) ?>
<?php if ($hackable): ?>
<p class="text-center">
这程序好像有点Bug我给组数据试试 <button id="button-display-hack" type="button" class="btn btn-danger btn-xs">Hack!</button>
</p>
<div id="div-form-hack" style="display:none" class="bot-buffer-md">
<?php $hack_form->printHTML() ?>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#button-display-hack').click(function() {
$('#div-form-hack').toggle('fast');
});
});
</script>
<?php endif ?>
<?php endif ?>
<?php if ($should_show_all_details): ?>
<div class="card border-info mb-3">
<div class="card-header bg-info">
<h4 class="card-title"><?= UOJLocale::get('details') ?></h4>
</div>
<div class="card-body">
<?php echoJudgementDetails($submission_result['details'], $styler, 'details') ?>
<?php if ($should_show_details_to_me): ?>
<?php if (isset($submission_result['final_result'])): ?>
<hr />
<?php echoSubmissionDetails($submission_result['final_result']['details'], 'final_details') ?>
<?php endif ?>
<?php if ($styler->fade_all_details): ?>
<hr />
<?php echoSubmissionDetails($submission_result['details'], 'final_details') ?>
<?php endif ?>
<?php endif ?>
</div>
</div>
<?php endif ?>
<?php if (isset($rejudge_form)): ?>
<?php $rejudge_form->printHTML() ?>
<?php endif ?>
<?php if (isset($delete_form)): ?>
<div class="top-buffer-sm">
<?php $delete_form->printHTML() ?>
</div>
<?php endif ?>
<?php echoUOJPageFooter() ?>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save