2024-02-10 17:41:07 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace app\controllers;
|
|
|
|
|
|
2024-02-11 16:04:21 +08:00
|
|
|
|
use app\models\RenameForm;
|
2024-02-11 12:39:05 +08:00
|
|
|
|
use app\utils\FileTypeDetector;
|
2024-02-10 17:41:07 +08:00
|
|
|
|
use Yii;
|
2024-02-11 16:04:21 +08:00
|
|
|
|
use yii\filters\VerbFilter;
|
|
|
|
|
use yii\helpers\ArrayHelper;
|
|
|
|
|
use yii\web\Controller;
|
2024-02-10 17:41:07 +08:00
|
|
|
|
use yii\web\NotFoundHttpException;
|
|
|
|
|
use yii\web\Response;
|
|
|
|
|
|
2024-02-11 16:04:21 +08:00
|
|
|
|
class HomeController extends Controller
|
2024-02-10 17:41:07 +08:00
|
|
|
|
{
|
2024-02-11 16:04:21 +08:00
|
|
|
|
public function behaviors()
|
|
|
|
|
{
|
|
|
|
|
return array_merge(
|
|
|
|
|
parent::behaviors(),
|
|
|
|
|
[
|
|
|
|
|
'verbs' => [
|
|
|
|
|
'class' => VerbFilter::class,
|
|
|
|
|
'actions' => [
|
|
|
|
|
'index' => ['GET'],
|
|
|
|
|
'download' => ['GET'],
|
|
|
|
|
'rename' => ['POST'],
|
|
|
|
|
'delete' => ['POST'],
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-10 17:41:07 +08:00
|
|
|
|
/**
|
2024-02-11 12:39:05 +08:00
|
|
|
|
* diplay the page of the file manager (accepts a directory parameter like cd command)
|
|
|
|
|
* visit it via https://devs.chenx221.cyou:8081/index.php?r=home
|
|
|
|
|
*
|
2024-02-10 17:41:07 +08:00
|
|
|
|
* @return string|Response
|
|
|
|
|
* @throws NotFoundHttpException
|
|
|
|
|
*/
|
|
|
|
|
public function actionIndex($directory = null)
|
|
|
|
|
{
|
2024-02-11 12:39:05 +08:00
|
|
|
|
//Warning: Security Vulnerability: access via $directory parameter = ../ will display the internal files of the server
|
2024-02-10 17:41:07 +08:00
|
|
|
|
if (Yii::$app->user->isGuest) {
|
|
|
|
|
return $this->redirect(Yii::$app->user->loginUrl);
|
|
|
|
|
}
|
|
|
|
|
$rootDataDirectory = Yii::getAlias(Yii::$app->params['dataDirectory']);
|
|
|
|
|
$userId = Yii::$app->user->id;
|
|
|
|
|
|
2024-02-12 12:49:18 +08:00
|
|
|
|
if ($directory === '.' ||$directory == null) {
|
|
|
|
|
$directory = null;
|
2024-02-10 17:41:07 +08:00
|
|
|
|
$parentDirectory = null;
|
2024-02-12 12:49:18 +08:00
|
|
|
|
} elseif ($directory === '..' || str_contains($directory, '../')) {
|
|
|
|
|
throw new NotFoundHttpException('Invalid directory.');
|
2024-02-10 17:41:07 +08:00
|
|
|
|
} else {
|
|
|
|
|
$parentDirectory = dirname($directory);
|
|
|
|
|
}
|
|
|
|
|
$directoryContents = $this->getDirectoryContents(join(DIRECTORY_SEPARATOR, [$rootDataDirectory, $userId, $directory ?: '.']));
|
2024-02-11 12:39:05 +08:00
|
|
|
|
foreach ($directoryContents as $key => $item) {
|
|
|
|
|
$relativePath = $directory ? $directory . '/' . $item : $item;
|
|
|
|
|
$absolutePath = Yii::getAlias('@app') . '/data/' . Yii::$app->user->id . '/' . $relativePath;
|
|
|
|
|
$type = FileTypeDetector::detect($absolutePath);
|
|
|
|
|
$directoryContents[$key] = ['name' => $item, 'type' => $type];
|
|
|
|
|
}
|
2024-02-10 17:41:07 +08:00
|
|
|
|
return $this->render('index', [
|
|
|
|
|
'directoryContents' => $directoryContents,
|
|
|
|
|
'parentDirectory' => $parentDirectory,
|
|
|
|
|
'directory' => $directory, // 将$directory传递给视图
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取指定路径下的文件和文件夹内容
|
|
|
|
|
* @param string $path 路径
|
|
|
|
|
* @return array 文件和文件夹内容数组
|
|
|
|
|
* @throws NotFoundHttpException 如果路径不存在
|
|
|
|
|
*/
|
|
|
|
|
protected function getDirectoryContents($path)
|
|
|
|
|
{
|
|
|
|
|
// 确定路径是否存在
|
|
|
|
|
if (!is_dir($path)) {
|
|
|
|
|
throw new NotFoundHttpException('Directory not found.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取路径下的所有文件和文件夹
|
|
|
|
|
$directoryContents = scandir($path);
|
|
|
|
|
|
2024-02-12 12:49:18 +08:00
|
|
|
|
// 移除 '.' 和 '..'
|
|
|
|
|
$directoryContents = array_diff($directoryContents, ['.', '..']);
|
|
|
|
|
|
|
|
|
|
// 使用 usort 对目录内容进行排序,使文件夹始终在文件之前
|
|
|
|
|
usort($directoryContents, function ($a, $b) use ($path) {
|
|
|
|
|
$aIsDir = is_dir($path . '/' . $a);
|
|
|
|
|
$bIsDir = is_dir($path . '/' . $b);
|
|
|
|
|
if ($aIsDir === $bIsDir) {
|
|
|
|
|
return strnatcasecmp($a, $b); // 如果两者都是文件夹或都是文件,按名称排序
|
|
|
|
|
}
|
|
|
|
|
return $aIsDir ? -1 : 1; // 文件夹始终在文件之前
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $directoryContents;
|
2024-02-10 17:41:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 下载指定路径下的文件
|
2024-02-11 12:39:05 +08:00
|
|
|
|
* download link:https://devs.chenx221.cyou:8081/index.php?r=home%2Fdownload&relativePath={the relative path of the file}
|
|
|
|
|
*
|
2024-02-10 17:41:07 +08:00
|
|
|
|
* @param string $relativePath 文件的相对路径
|
|
|
|
|
* @throws NotFoundHttpException 如果文件不存在
|
|
|
|
|
*/
|
|
|
|
|
public function actionDownload($relativePath)
|
|
|
|
|
{
|
|
|
|
|
// 对相对路径进行解码
|
|
|
|
|
$relativePath = rawurldecode($relativePath);
|
|
|
|
|
|
|
|
|
|
// 检查相对路径是否只包含允许的字符
|
2024-02-12 13:02:28 +08:00
|
|
|
|
if (!preg_match('/^[\w\-.\/\s]+$/u', $relativePath) || $relativePath === '.' || $relativePath === '..' || str_contains($relativePath, '../')) {
|
2024-02-10 17:41:07 +08:00
|
|
|
|
throw new NotFoundHttpException('Invalid file path.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确定文件的绝对路径
|
|
|
|
|
$absolutePath = Yii::getAlias(Yii::$app->params['dataDirectory']) . '/' . Yii::$app->user->id . '/' . $relativePath;
|
|
|
|
|
|
|
|
|
|
// 使用realpath函数解析路径,并检查解析后的路径是否在预期的目录中
|
|
|
|
|
$realPath = realpath($absolutePath);
|
|
|
|
|
$dataDirectory = str_replace('/', '\\', Yii::getAlias(Yii::$app->params['dataDirectory']));
|
|
|
|
|
if (!$realPath || !str_starts_with($realPath, $dataDirectory)) {
|
|
|
|
|
throw new NotFoundHttpException('File not found.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查文件是否存在
|
|
|
|
|
if (!file_exists($realPath)) {
|
|
|
|
|
throw new NotFoundHttpException('File not found.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将文件发送给用户进行下载
|
|
|
|
|
Yii::$app->response->sendFile($realPath)->send();
|
|
|
|
|
}
|
2024-02-11 16:04:21 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 重命名文件或文件夹
|
|
|
|
|
* @param string $relativePath 文件或文件夹的相对路径
|
|
|
|
|
* @param string $newName 新名称
|
|
|
|
|
* @throws NotFoundHttpException 如果文件或文件夹不存在
|
|
|
|
|
*/
|
|
|
|
|
public function actionRename()
|
|
|
|
|
{
|
|
|
|
|
$relativePath = Yii::$app->request->post('relativePath');
|
|
|
|
|
|
|
|
|
|
// 对相对路径进行解码
|
|
|
|
|
$relativePath = rawurldecode($relativePath);
|
|
|
|
|
|
|
|
|
|
// 检查相对路径是否只包含允许的字符
|
2024-02-12 13:02:28 +08:00
|
|
|
|
if (!preg_match('/^[\w\-.\/\s]+$/u', $relativePath) || $relativePath === '.' || $relativePath === '..' || str_contains($relativePath, '../')) {
|
2024-02-11 16:04:21 +08:00
|
|
|
|
throw new NotFoundHttpException('Invalid file path.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确定文件的绝对路径
|
|
|
|
|
$absolutePath = Yii::getAlias(Yii::$app->params['dataDirectory']) . '/' . Yii::$app->user->id . '/' . $relativePath;
|
|
|
|
|
|
|
|
|
|
// 检查文件或文件夹是否存在
|
|
|
|
|
if (!file_exists($absolutePath)) {
|
|
|
|
|
throw new NotFoundHttpException('File or directory not found.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$model = new RenameForm();
|
|
|
|
|
|
|
|
|
|
$model->newName = ArrayHelper::getValue(Yii::$app->request->post('RenameForm'), 'newName');
|
|
|
|
|
|
|
|
|
|
if (!$model->validate()) {
|
|
|
|
|
Yii::$app->response->statusCode = 400;
|
|
|
|
|
return $model->getFirstError('newName');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查新名称是否和现有的文件或文件夹重名
|
|
|
|
|
if (file_exists(dirname($absolutePath) . '/' . $model->newName)) {
|
|
|
|
|
Yii::$app->session->setFlash('error', 'Failed to rename file or directory.');
|
|
|
|
|
return $this->redirect(['index', 'directory' => dirname($relativePath)]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重命名文件或文件夹
|
|
|
|
|
if (!rename($absolutePath, dirname($absolutePath) . '/' . $model->newName)) {
|
|
|
|
|
Yii::$app->session->setFlash('error', 'Failed to rename file or directory.');
|
|
|
|
|
} else {
|
|
|
|
|
Yii::$app->session->setFlash('success', 'File or directory renamed successfully.');
|
|
|
|
|
}
|
|
|
|
|
return $this->redirect(['index', 'directory' => dirname($relativePath)]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 删除文件或文件夹
|
|
|
|
|
* @throws NotFoundHttpException 如果文件或文件夹不存在
|
|
|
|
|
*/
|
2024-02-12 11:59:55 +08:00
|
|
|
|
public function actionDelete()
|
2024-02-11 16:04:21 +08:00
|
|
|
|
{
|
2024-02-12 11:59:55 +08:00
|
|
|
|
$relativePath = Yii::$app->request->post('relativePath');
|
2024-02-11 16:04:21 +08:00
|
|
|
|
$relativePath = rawurldecode($relativePath);
|
2024-02-12 13:02:28 +08:00
|
|
|
|
if (!preg_match('/^[\w\-.\/\s]+$/u', $relativePath) || $relativePath === '.' || $relativePath === '..' || str_contains($relativePath, '../')) {
|
2024-02-11 16:04:21 +08:00
|
|
|
|
throw new NotFoundHttpException('Invalid file path.');
|
|
|
|
|
}
|
|
|
|
|
$absolutePath = Yii::getAlias(Yii::$app->params['dataDirectory']) . '/' . Yii::$app->user->id . '/' . $relativePath;
|
|
|
|
|
if (!file_exists($absolutePath)) {
|
|
|
|
|
throw new NotFoundHttpException('File or directory not found.');
|
2024-02-12 12:49:18 +08:00
|
|
|
|
} else {
|
|
|
|
|
$realPath = realpath($absolutePath);
|
|
|
|
|
$expectedPathPrefix = realpath(Yii::getAlias(Yii::$app->params['dataDirectory']) . '/' . Yii::$app->user->id);
|
|
|
|
|
if (!str_starts_with($realPath, $expectedPathPrefix)) {
|
|
|
|
|
throw new NotFoundHttpException('File or directory not found.');
|
|
|
|
|
}
|
2024-02-11 16:04:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_dir($absolutePath)) {
|
2024-02-12 12:49:18 +08:00
|
|
|
|
if (!$this->deleteDirectory($absolutePath)) {
|
|
|
|
|
Yii::$app->session->setFlash('error', 'Failed to delete directory.');
|
|
|
|
|
} else {
|
|
|
|
|
Yii::$app->session->setFlash('success', 'Directory deleted successfully.');
|
|
|
|
|
}
|
2024-02-11 16:04:21 +08:00
|
|
|
|
} else {
|
2024-02-12 12:49:18 +08:00
|
|
|
|
if (!unlink($absolutePath)) {
|
|
|
|
|
Yii::$app->session->setFlash('error', 'Failed to delete file.');
|
|
|
|
|
} else {
|
|
|
|
|
Yii::$app->session->setFlash('success', 'File deleted successfully.');
|
|
|
|
|
}
|
2024-02-11 16:04:21 +08:00
|
|
|
|
}
|
2024-02-12 12:49:18 +08:00
|
|
|
|
return $this->redirect(['index', 'directory' => dirname($relativePath)]);
|
2024-02-11 16:04:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 递归删除目录及其内容
|
|
|
|
|
* @param string $dir 目录路径
|
|
|
|
|
*/
|
2024-02-12 12:49:18 +08:00
|
|
|
|
protected function deleteDirectory($dir): bool
|
2024-02-11 16:04:21 +08:00
|
|
|
|
{
|
|
|
|
|
if (!is_dir($dir)) {
|
2024-02-12 12:49:18 +08:00
|
|
|
|
return false;
|
2024-02-11 16:04:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$files = array_diff(scandir($dir), ['.', '..']);
|
|
|
|
|
foreach ($files as $file) {
|
2024-02-12 12:49:18 +08:00
|
|
|
|
if (is_dir("$dir/$file")) {
|
|
|
|
|
if (!$this->deleteDirectory("$dir/$file")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!unlink("$dir/$file")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!rmdir($dir)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2024-02-11 16:04:21 +08:00
|
|
|
|
}
|
2024-02-10 17:41:07 +08:00
|
|
|
|
}
|