Example #1
0
    }
    // end: foreach $data
    if (!empty($_FILES['add_input']['name']) || !empty($_FILES['add_output']['name'])) {
        $content = array();
        $rank = $maxrank + 1;
        foreach ($FILES as $file) {
            if (empty($_FILES['add_' . $file]['name'])) {
                warning("No {$file} file specified for new testcase, ignoring.");
            } else {
                checkFileUpload($_FILES['add_' . $file]['error']);
                $content[$file] = file_get_contents($_FILES['add_' . $file]['tmp_name']);
            }
        }
        $DB->q("INSERT INTO testcase\n\t\t        (probid,rank,md5sum_input,md5sum_output,input,output,description,sample)\n\t\t        VALUES (%i,%i,%s,%s,%s,%s,%s,%i)", $probid, $rank, md5(@$content['input']), md5(@$content['output']), @$content['input'], @$content['output'], @$_POST['add_desc'], @$_POST['add_sample']);
        if (!empty($content['image'])) {
            list($thumb, $type) = get_image_thumb_type($content['image']);
            $DB->q('UPDATE testcase SET image = %s, image_thumb = %s, image_type = %s
			        WHERE probid = %i AND rank = %i', @$content['image'], $thumb, $type, $probid, $rank);
        }
        auditlog('testcase', $probid, 'added', "rank {$rank}");
        $result .= "<li>Added new testcase {$rank} from files " . htmlspecialchars($_FILES['add_input']['name']) . " (" . printsize($_FILES['add_input']['size']) . ") and " . htmlspecialchars($_FILES['add_output']['name']) . " (" . printsize($_FILES['add_output']['size']) . ").";
        if ($_FILES['add_output']['size'] > dbconfig_get('output_limit') * 1024) {
            $result .= "<br /><b>Warning: output file size exceeds " . "<code>output_limit</code> of " . dbconfig_get('output_limit') . " kB. This will always result in wrong answers!</b>";
        }
        if (empty($content['input']) || empty($content['output'])) {
            $result .= "<br /><b>Warning: empty testcase file(s)!</b>";
        }
        $result .= "</li>\n";
    }
}
if (!empty($result)) {
Example #2
0
/**
 * Read problem description file and testdata from zip archive
 * and update problem with it, or insert new problem when probid=NULL.
 * Returns probid on success, or generates error on failure.
 */
function importZippedProblem($zip, $probid = NULL, $cid = -1)
{
    global $DB, $teamid, $cdatas, $matchstrings;
    $prop_file = 'domjudge-problem.ini';
    $yaml_file = 'problem.yaml';
    $ini_keys_problem = array('name', 'timelimit', 'special_run', 'special_compare');
    $ini_keys_contest_problem = array('probid', 'allow_submit', 'allow_judge', 'points', 'color');
    $def_timelimit = 10;
    // Read problem properties
    $ini_array = parse_ini_string($zip->getFromName($prop_file));
    if (empty($ini_array)) {
        if ($probid === NULL) {
            error("Need '" . $prop_file . "' file when adding a new problem.");
        }
    } else {
        // Only preserve valid keys:
        $ini_array_problem = array_intersect_key($ini_array, array_flip($ini_keys_problem));
        $ini_array_contest_problem = array_intersect_key($ini_array, array_flip($ini_keys_contest_problem));
        // Set default of 1 point for a problem if not specified
        if (!isset($ini_array_contest_problem['points'])) {
            $ini_array_contest_problem['points'] = 1;
        }
        if ($probid === NULL) {
            if (!isset($ini_array_contest_problem['probid'])) {
                error("Need 'probid' in '" . $prop_file . "' when adding a new problem.");
            }
            // Set sensible defaults for name and timelimit if not specified:
            if (!isset($ini_array_problem['name'])) {
                $ini_array_problem['name'] = $ini_array_contest_problem['probid'];
            }
            if (!isset($ini_array_problem['timelimit'])) {
                $ini_array_problem['timelimit'] = $def_timelimit;
            }
            // rename probid to shortname
            $shortname = $ini_array_contest_problem['probid'];
            unset($ini_array_contest_problem['probid']);
            $ini_array_contest_problem['shortname'] = $shortname;
            $probid = $DB->q('RETURNID INSERT INTO problem (' . implode(', ', array_keys($ini_array_problem)) . ') VALUES (%As)', $ini_array_problem);
            if ($cid != -1) {
                $ini_array_contest_problem['cid'] = $cid;
                $ini_array_contest_problem['probid'] = $probid;
                $DB->q('INSERT INTO contestproblem (' . implode(', ', array_keys($ini_array_contest_problem)) . ') VALUES (%As)', $ini_array_contest_problem);
            }
        } else {
            if (count($ini_array_problem) > 0) {
                $DB->q('UPDATE problem SET %S WHERE probid = %i', $ini_array_problem, $probid);
            }
            if ($cid != -1) {
                if ($DB->q("MAYBEVALUE SELECT probid FROM contestproblem\n\t\t\t\t             WHERE probid = %i AND cid = %i", $probid, $cid)) {
                    // Remove keys that cannot be modified:
                    unset($ini_array_contest_problem['probid']);
                    if (count($ini_array_contest_problem) != 0) {
                        $DB->q('UPDATE contestproblem SET %S WHERE probid = %i AND cid = %i', $ini_array_contest_problem, $probid, $cid);
                    }
                } else {
                    $shortname = $ini_array_contest_problem['probid'];
                    unset($ini_array_contest_problem['probid']);
                    $ini_array_contest_problem['shortname'] = $shortname;
                    $ini_array_contest_problem['cid'] = $cid;
                    $ini_array_contest_problem['probid'] = $probid;
                    $DB->q('INSERT INTO contestproblem (' . implode(', ', array_keys($ini_array_contest_problem)) . ') VALUES (%As)', $ini_array_contest_problem);
                }
            }
        }
    }
    // parse problem.yaml
    $problem_yaml = $zip->getFromName($yaml_file);
    if ($problem_yaml !== FALSE) {
        $problem_yaml_data = spyc_load($problem_yaml);
        if (!empty($problem_yaml_data)) {
            if (isset($problem_yaml_data['uuid']) && $cid != -1) {
                $DB->q('UPDATE contestproblem SET shortname=%s
				        WHERE cid=%i AND probid=%i', $problem_yaml_data['uuid'], $cid, $probid);
            }
            $yaml_array_problem = array();
            if (isset($problem_yaml_data['name'])) {
                if (is_array($problem_yaml_data['name'])) {
                    foreach ($problem_yaml_data['name'] as $lang => $name) {
                        // TODO: select a specific instead of the first language
                        $yaml_array_problem['name'] = $name;
                        break;
                    }
                } else {
                    $yaml_array_problem['name'] = $problem_yaml_data['name'];
                }
            }
            if (isset($problem_yaml_data['validator_flags'])) {
                $yaml_array_problem['special_compare_args'] = $problem_yaml_data['validator_flags'];
            }
            if (isset($problem_yaml_data['validation']) && $problem_yaml_data['validation'] == 'custom') {
                // search for validator
                $validator_files = array();
                for ($j = 0; $j < $zip->numFiles; $j++) {
                    $filename = $zip->getNameIndex($j);
                    if (starts_with($filename, "output_validators/") && !ends_with($filename, "/")) {
                        $validator_files[] = $filename;
                    }
                }
                if (sizeof($validator_files) == 0) {
                    echo "<p>Custom validator specified but not found.</p>\n";
                } else {
                    // file(s) have to share common directory
                    $validator_dir = mb_substr($validator_files[0], 0, mb_strrpos($validator_files[0], "/")) . "/";
                    $same_dir = TRUE;
                    foreach ($validator_files as $validator_file) {
                        if (!starts_with($validator_file, $validator_dir)) {
                            $same_dir = FALSE;
                            echo "<p>{$validator_file} does not start with {$validator_dir}</p>\n";
                            break;
                        }
                    }
                    if (!$same_dir) {
                        echo "<p>Found multiple custom output validators.</p>\n";
                    } else {
                        $tmpzipfiledir = exec("mktemp -d --tmpdir=" . TMPDIR, $dontcare, $retval);
                        if ($retval != 0) {
                            error("failed to create temporary directory");
                        }
                        chmod($tmpzipfiledir, 0700);
                        foreach ($validator_files as $validator_file) {
                            $content = $zip->getFromName($validator_file);
                            $filebase = basename($validator_file);
                            $newfilename = $tmpzipfiledir . "/" . $filebase;
                            file_put_contents($newfilename, $content);
                            if ($filebase === 'build' || $filebase === 'run') {
                                // mark special files as executable
                                chmod($newfilename, 0755);
                            }
                        }
                        exec("zip -r -j '{$tmpzipfiledir}/outputvalidator.zip' '{$tmpzipfiledir}'", $dontcare, $retval);
                        if ($retval != 0) {
                            error("failed to create zip file for output validator.");
                        }
                        $ovzip = file_get_contents("{$tmpzipfiledir}/outputvalidator.zip");
                        $probname = $DB->q("VALUE SELECT name FROM problem\n\t\t\t\t\t\t                    WHERE probid=%i", $probid);
                        $ovname = preg_replace('/[^a-zA-Z0-9]/', '_', $probname) . "_cmp";
                        if ($DB->q("MAYBEVALUE SELECT execid FROM executable\n\t\t\t\t\t\t             WHERE execid=%s", $ovname)) {
                            // avoid name clash
                            $clashcnt = 2;
                            while ($DB->q("MAYBEVALUE SELECT execid FROM executable\n\t\t\t\t\t\t\t                WHERE execid=%s", $ovname . "_" . $clashcnt)) {
                                $clashcnt++;
                            }
                            $ovname = $ovname . "_" . $clashcnt;
                        }
                        $DB->q("INSERT INTO executable (execid, md5sum, zipfile,\n\t\t\t\t\t\t        description, type) VALUES (%s, %s, %s, %s, %s)", $ovname, md5($ovzip), $ovzip, 'output validator for ' . $probname, 'compare');
                        $DB->q("UPDATE problem SET special_compare=%s\n\t\t\t\t\t\t        WHERE probid=%i", $ovname, $probid);
                        echo "<p>Added output validator '{$ovname}'.</p>\n";
                    }
                }
            }
            if (isset($problem_yaml_data['limits'])) {
                if (isset($problem_yaml_data['limits']['memory'])) {
                    $yaml_array_problem['memlimit'] = 1024 * $problem_yaml_data['limits']['memory'];
                }
                if (isset($problem_yaml_data['limits']['output'])) {
                    $yaml_array_problem['outputlimit'] = 1024 * $problem_yaml_data['limits']['output'];
                }
            }
            if (sizeof($yaml_array_problem) > 0) {
                $DB->q('UPDATE problem SET %S WHERE probid = %i', $yaml_array_problem, $probid);
            }
        }
    }
    // Add problem statement
    foreach (array('pdf', 'html', 'txt') as $type) {
        $text = $zip->getFromName('problem.' . $type);
        if ($text !== FALSE) {
            $DB->q('UPDATE problem SET problemtext = %s, problemtext_type = %s
			        WHERE probid = %i', $text, $type, $probid);
            echo "<p>Added problem statement from: <tt>problem.{$type}</tt></p>\n";
            break;
        }
    }
    // Insert/update testcases
    $maxrank = 1 + $DB->q('VALUE SELECT max(rank) FROM testcase
	                       WHERE probid = %i', $probid);
    // first insert sample, then secret data in alphabetical order
    foreach (array('sample', 'secret') as $type) {
        $ncases = 0;
        $datafiles = array();
        for ($j = 0; $j < $zip->numFiles; $j++) {
            $filename = $zip->getNameIndex($j);
            if (starts_with($filename, "data/{$type}/") && ends_with($filename, ".in")) {
                $basename = basename($filename, ".in");
                $fileout = "data/{$type}/" . $basename . ".ans";
                if ($zip->locateName($fileout) !== FALSE) {
                    $datafiles[] = $basename;
                }
            }
        }
        asort($datafiles);
        echo "<ul>\n";
        foreach ($datafiles as $datafile) {
            $testin = $zip->getFromName("data/{$type}/{$datafile}.in");
            $testout = $zip->getFromName("data/{$type}/{$datafile}.ans");
            $description = $datafile;
            if (($descfile = $zip->getFromName("data/{$type}/{$datafile}.desc")) !== FALSE) {
                $description .= ": \n" . $descfile;
            }
            $image_file = $image_type = $image_thumb = FALSE;
            foreach (array('png', 'jpg', 'jpeg', 'gif') as $img_ext) {
                if (($image_file = $zip->getFromName("data/{$type}/{$datafile}" . "." . $img_ext)) !== FALSE) {
                    list($image_thumb, $image_type) = get_image_thumb_type($image_file);
                    break;
                }
            }
            $md5in = md5($testin);
            $md5out = md5($testout);
            // Skip testcases that already exist identically
            $id = $DB->q('MAYBEVALUE SELECT testcaseid FROM testcase
			              WHERE md5sum_input = %s AND md5sum_output = %s AND
			              description = %s AND sample = %i AND probid = %i', $md5in, $md5out, $description, $type == 'sample' ? 1 : 0, $probid);
            if (isset($id)) {
                echo "<li>Skipped {$type} testcase <tt>{$datafile}</tt>: already exists</li>\n";
                continue;
            }
            $DB->q('INSERT INTO testcase (probid, rank, sample,
			        md5sum_input, md5sum_output, input, output, description' . ($image_file !== FALSE ? ', image, image_thumb, image_type' : '') . ')' . 'VALUES (%i, %i, %i, %s, %s, %s, %s, %s' . ($image_file !== FALSE ? ', %s, %s, %s' : '%_ %_ %_') . ')', $probid, $maxrank, $type == 'sample' ? 1 : 0, $md5in, $md5out, $testin, $testout, $description, $image_file, $image_thumb, $image_type);
            $maxrank++;
            $ncases++;
            echo "<li>Added {$type} testcase from: <tt>{$datafile}.{in,ans}</tt></li>\n";
        }
        echo "</ul>\n<p>Added {$ncases} {$type} testcase(s).</p>\n";
    }
    // submit reference solutions
    if ($cid == -1) {
        echo "<p>No jury solutions added: problem is not linked to a contest (yet).</p>\n";
    } else {
        if (empty($teamid)) {
            echo "<p>No jury solutions added: must associate team with your user first.</p>\n";
        } else {
            if ($DB->q('VALUE SELECT allow_submit FROM problem
	                    INNER JOIN contestproblem using (probid)
	                    WHERE probid = %i AND cid = %i', $probid, $cid)) {
                // First find all submittable languages:
                $langs = $DB->q('KEYVALUETABLE SELECT langid, extensions
		                 FROM language WHERE allow_submit = 1');
                $njurysols = 0;
                echo "<ul>\n";
                for ($j = 0; $j < $zip->numFiles; $j++) {
                    $filename = $zip->getNameIndex($j);
                    $filename_parts = explode(".", $filename);
                    $extension = end($filename_parts);
                    if (!starts_with($filename, 'submissions/') || ends_with($filename, '/')) {
                        // skipping non-submission files and directories silently
                        continue;
                    }
                    unset($langid);
                    foreach ($langs as $key => $exts) {
                        if (in_array($extension, json_decode($exts))) {
                            $langid = $key;
                            break;
                        }
                    }
                    if (empty($langid)) {
                        echo "<li>Could not add jury solution <tt>{$filename}</tt>: unknown language.</li>\n";
                    } else {
                        if (!($tmpfname = tempnam(TMPDIR, "ref_solution-"))) {
                            error("Could not create temporary file in directory " . TMPDIR);
                        }
                        $offset = mb_strlen('submissions/');
                        $expectedResult = normalizeExpectedResult(mb_substr($filename, $offset, mb_strpos($filename, '/', $offset) - $offset));
                        $source = $zip->getFromIndex($j);
                        $results = getExpectedResults($source);
                        if ($results === NULL) {
                            // annotate source code with expected result
                            $source = "// added by import: " . $matchstrings[0] . $expectedResult . "\n" . $source;
                        } else {
                            if (!in_array($expectedResult, $results)) {
                                warning("annotated result '" . implode(', ', $results) . "' does not match directory for {$filename}");
                            }
                        }
                        file_put_contents($tmpfname, $source);
                        if (filesize($tmpfname) <= dbconfig_get('sourcesize_limit') * 1024) {
                            submit_solution($teamid, $probid, $cid, $langid, array($tmpfname), array(basename($filename)));
                            echo "<li>Added jury solution from: <tt>{$filename}</tt></li>\n";
                            $njurysols++;
                        } else {
                            echo "<li>Could not add jury solution <tt>{$filename}</tt>: too large.</li>\n";
                        }
                        unlink($tmpfname);
                    }
                }
                echo "</ul>\n<p>Added {$njurysols} jury solution(s).</p>\n";
            } else {
                echo "<p>No jury solutions added: problem not submittable</p>\n";
            }
        }
    }
    if (!in_array($cid, array_keys($cdatas))) {
        echo "<p>The corresponding contest is not activated yet." . "To view the submissions in the submissions list, you have to activate the contest first.</p>\n";
    }
    return $probid;
}
Example #3
0
function check_add($probid, $rank, $FILES)
{
    global $DB;
    $result = '';
    if (!empty($_FILES['add_input']['name']) || !empty($_FILES['add_output']['name'])) {
        $content = array();
        foreach ($FILES as $file) {
            if (empty($_FILES['add_' . $file]['name'])) {
                warning("No {$file} file specified for new testcase, ignoring.");
            } else {
                checkFileUpload($_FILES['add_' . $file]['error']);
                $content[$file] = file_get_contents($_FILES['add_' . $file]['tmp_name']);
            }
        }
        $DB->q("INSERT INTO testcase\n\t\t        (probid,rank,md5sum_input,md5sum_output,input,output,description,sample)\n\t\t        VALUES (%i,%i,%s,%s,%s,%s,%s,%i)", $probid, $rank, md5(@$content['input']), md5(@$content['output']), @$content['input'], @$content['output'], @$_POST['add_desc'], isset($_POST['add_sample']));
        if (!empty($content['image'])) {
            list($thumb, $type) = get_image_thumb_type($content['image']);
            $DB->q('UPDATE testcase SET image = %s, image_thumb = %s, image_type = %s
			        WHERE probid = %i AND rank = %i', @$content['image'], $thumb, $type, $probid, $rank);
        }
        auditlog('testcase', $probid, 'added', "rank {$rank}");
        $result .= "<li>Added new testcase {$rank} from files " . specialchars($_FILES['add_input']['name']) . " (" . printsize($_FILES['add_input']['size']) . ") and " . specialchars($_FILES['add_output']['name']) . " (" . printsize($_FILES['add_output']['size']) . ").";
        if ($_FILES['add_output']['size'] > dbconfig_get('output_limit') * 1024) {
            $result .= "<br /><b>Warning: output file size exceeds " . "<code>output_limit</code> of " . dbconfig_get('output_limit') . " kB. This will always result in wrong answers!</b>";
        }
        if (empty($content['input']) || empty($content['output'])) {
            $result .= "<br /><b>Warning: empty testcase file(s)!</b>";
        }
        $result .= "</li>\n";
    }
    return $result;
}