146 lines
6.6 KiB
PHP
146 lines
6.6 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../web/include/courseware.inc.php';
|
|
|
|
function cw_assert($condition, $message) {
|
|
if (!$condition) {
|
|
fwrite(STDERR, "FAIL: " . $message . PHP_EOL);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
function cw_rmdir_tree($dir) {
|
|
if (!is_dir($dir)) {
|
|
return;
|
|
}
|
|
$items = scandir($dir);
|
|
if ($items === false) {
|
|
return;
|
|
}
|
|
foreach ($items as $name) {
|
|
if ($name === '.' || $name === '..') {
|
|
continue;
|
|
}
|
|
$path = $dir . DIRECTORY_SEPARATOR . $name;
|
|
if (is_dir($path) && !is_link($path)) {
|
|
cw_rmdir_tree($path);
|
|
} else {
|
|
@unlink($path);
|
|
}
|
|
}
|
|
@rmdir($dir);
|
|
}
|
|
|
|
$src = file_get_contents(__DIR__ . '/../web/include/courseware.inc.php');
|
|
cw_assert($src !== false, 'courseware.inc.php is readable');
|
|
cw_assert(stripos($src, 'ZipArchive') === false, 'courseware.inc.php does not use ZipArchive');
|
|
cw_assert(!function_exists('courseware_count_slides'), 'slide counting helper is gone');
|
|
|
|
$fixture = realpath(__DIR__ . '/../courseware/示例课件.pptx');
|
|
cw_assert($fixture !== false && is_file($fixture), 'example pptx exists in repo-root courseware/');
|
|
|
|
$tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'hustoj-cw-' . bin2hex(random_bytes(4));
|
|
cw_assert(mkdir($tmp), 'create temp root');
|
|
|
|
cw_assert(copy($fixture, $tmp . DIRECTORY_SEPARATOR . '数组 插入.pptx'), 'copy chinese spaced pptx');
|
|
cw_assert(copy($fixture, $tmp . DIRECTORY_SEPARATOR . '2 item.pptx'), 'copy 2 item pptx');
|
|
cw_assert(copy($fixture, $tmp . DIRECTORY_SEPARATOR . '10 item.pptx'), 'copy 10 item pptx');
|
|
cw_assert(copy($fixture, $tmp . DIRECTORY_SEPARATOR . '3 item.pptx'), 'copy 3 item pptx');
|
|
cw_assert(copy($fixture, $tmp . DIRECTORY_SEPARATOR . 'Upper.PPTX'), 'copy uppercase extension');
|
|
cw_assert(copy($fixture, $tmp . DIRECTORY_SEPARATOR . 'Mixed.Pptx'), 'copy mixed-case extension');
|
|
file_put_contents($tmp . DIRECTORY_SEPARATOR . 'notes.txt', 'ignore');
|
|
file_put_contents($tmp . DIRECTORY_SEPARATOR . 'old.ppt', 'ignore');
|
|
|
|
$GLOBALS['_courseware_root'] = $tmp;
|
|
|
|
cw_assert(!courseware_is_slug(''), 'reject empty slug');
|
|
cw_assert(!courseware_is_slug('.'), 'reject dot slug');
|
|
cw_assert(!courseware_is_slug('..'), 'reject parent slug');
|
|
cw_assert(!courseware_is_slug('../etc'), 'reject parent path slug');
|
|
cw_assert(!courseware_is_slug('a/b'), 'reject slash slug');
|
|
cw_assert(!courseware_is_slug('a\\b'), 'reject backslash slug');
|
|
cw_assert(!courseware_is_slug("a\0b"), 'reject NUL slug');
|
|
cw_assert(courseware_is_slug('2.2-array'), 'accept dotted slug');
|
|
cw_assert(courseware_is_slug('数组 插入'), 'accept unicode spaced slug');
|
|
|
|
cw_assert(courseware_build_deck('') === null, 'empty slug is null');
|
|
cw_assert(courseware_build_deck("a\0b") === null, 'NUL slug is null');
|
|
cw_assert(courseware_build_deck('../etc') === null, 'traversal slug is null');
|
|
cw_assert(courseware_build_deck('a/b') === null, 'slash slug is null');
|
|
cw_assert(courseware_build_deck('missing') === null, 'missing deck is null');
|
|
cw_assert(courseware_build_deck('notes') === null, 'non-pptx is ignored');
|
|
cw_assert(courseware_build_deck('old') === null, 'ppt without x is ignored');
|
|
|
|
$zh = courseware_build_deck('数组 插入');
|
|
cw_assert($zh !== null, 'chinese spaced pptx loads');
|
|
cw_assert($zh['title'] === '数组 插入', 'title keeps spaces and unicode');
|
|
cw_assert($zh['filename'] === '数组 插入.pptx', 'filename is actual directory entry');
|
|
cw_assert($zh['url'] === 'courseware-file.php?deck=' . rawurlencode('数组 插入'), 'url uses file endpoint');
|
|
cw_assert(!array_key_exists('page_count', $zh), 'does not fake page count');
|
|
|
|
$scan_names = array();
|
|
foreach (scandir($tmp) as $entry_name) {
|
|
if ($entry_name === '.' || $entry_name === '..') {
|
|
continue;
|
|
}
|
|
$scan_names[$entry_name] = true;
|
|
}
|
|
$upper = courseware_build_deck('Upper');
|
|
cw_assert($upper !== null, 'uppercase PPTX extension is accepted');
|
|
cw_assert(isset($scan_names[$upper['filename']]), 'uppercase deck uses an actual directory entry');
|
|
cw_assert(strtolower($upper['filename']) === 'upper.pptx', 'uppercase slug maps without concatenating .pptx');
|
|
|
|
$mixed = courseware_build_deck('Mixed');
|
|
cw_assert($mixed !== null, 'mixed-case Pptx extension is accepted');
|
|
cw_assert(isset($scan_names[$mixed['filename']]), 'mixed-case deck uses an actual directory entry');
|
|
cw_assert(strtolower($mixed['filename']) === 'mixed.pptx', 'mixed-case slug maps without concatenating .pptx');
|
|
|
|
$decks = courseware_list_decks();
|
|
$titles = array();
|
|
foreach ($decks as $deck) {
|
|
$titles[] = $deck['title'];
|
|
cw_assert(!array_key_exists('page_count', $deck), 'listed decks do not fake page count');
|
|
}
|
|
cw_assert(count($decks) === 6, 'only pptx files are listed');
|
|
cw_assert($titles === array('2 item', '3 item', '10 item', 'Mixed', 'Upper', '数组 插入'), 'natural sort of titles');
|
|
|
|
cw_assert(courseware_clamp_page($zh, 0) === 1, 'clamp low without page count');
|
|
cw_assert(courseware_clamp_page($zh, 9) === 9, 'unknown total keeps requested page');
|
|
cw_assert(courseware_clamp_page(array('page_count' => 3), 99) === 3, 'clamp high when count is known');
|
|
|
|
$outside = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'hustoj-cw-out-' . bin2hex(random_bytes(4));
|
|
cw_assert(mkdir($outside), 'create outside dir');
|
|
$secret = $outside . DIRECTORY_SEPARATOR . 'secret.pptx';
|
|
cw_assert(copy($fixture, $secret), 'copy secret pptx outside root');
|
|
cw_assert(!courseware_inside_root(realpath($secret), realpath($tmp)), 'outside realpath is rejected');
|
|
|
|
$escape = $tmp . DIRECTORY_SEPARATOR . 'escape.pptx';
|
|
$link_ok = @symlink($secret, $escape);
|
|
if ($link_ok) {
|
|
cw_assert(courseware_build_deck('escape') === null, 'out-of-root symlink is rejected');
|
|
} else {
|
|
fwrite(STDOUT, "SKIP symlink escape test (cannot create symlink)\n");
|
|
}
|
|
|
|
$rel_link = $tmp . DIRECTORY_SEPARATOR . 'relescape.pptx';
|
|
$rel_ok = @symlink('..' . DIRECTORY_SEPARATOR . basename($outside) . DIRECTORY_SEPARATOR . 'secret.pptx', $rel_link);
|
|
if (!$rel_ok) {
|
|
$rel_ok = @symlink(realpath($secret), $rel_link);
|
|
}
|
|
if ($rel_ok) {
|
|
cw_assert(courseware_build_deck('relescape') === null, 'relative out-of-root symlink is rejected');
|
|
} else {
|
|
fwrite(STDOUT, "SKIP relative symlink test (cannot create symlink)\n");
|
|
}
|
|
|
|
unset($GLOBALS['_courseware_root']);
|
|
$example = courseware_build_deck('示例课件');
|
|
cw_assert($example !== null, 'example pptx is present in default root');
|
|
cw_assert($example['filename'] === '示例课件.pptx', 'example uses actual filename');
|
|
cw_assert($example['url'] === 'courseware-file.php?deck=' . rawurlencode('示例课件'), 'example url uses file endpoint');
|
|
cw_assert(!array_key_exists('page_count', $example), 'example does not fake page count');
|
|
|
|
cw_rmdir_tree($tmp);
|
|
cw_rmdir_tree($outside);
|
|
|
|
fwrite(STDOUT, "OK courseware_test.php\n");
|