Completed
Push — 4.0 ( 87d096...bcc1be )
by Kiyotaka
05:44 queued 11s
created

Eccube/Controller/Admin/Content/FileController.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.ec-cube.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Controller\Admin\Content;
15
16
use Eccube\Controller\AbstractController;
17
use Eccube\Util\FilesystemUtil;
18
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
19
use Symfony\Component\Filesystem\Exception\IOException;
20
use Symfony\Component\Filesystem\Filesystem;
21
use Symfony\Component\Finder\Finder;
22
use Symfony\Component\Form\Extension\Core\Type\FileType;
23
use Symfony\Component\Form\Extension\Core\Type\FormType;
24
use Symfony\Component\Form\Extension\Core\Type\TextType;
25
use Symfony\Component\HttpFoundation\BinaryFileResponse;
26
use Symfony\Component\HttpFoundation\File\Exception\FileException;
27
use Symfony\Component\HttpFoundation\Request;
28
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
29
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
30
use Symfony\Component\Routing\Annotation\Route;
31
use Symfony\Component\Validator\Constraints as Assert;
32
33
class FileController extends AbstractController
34
{
35
    const SJIS = 'sjis-win';
36
    const UTF = 'UTF-8';
37
    private $errors = [];
38
    private $encode = '';
39
40
    /**
41
     * FileController constructor.
42
     */
43
    public function __construct()
44 6
    {
45
        $this->encode = self::UTF;
46 6
        if ('\\' === DIRECTORY_SEPARATOR) {
47 6
            $this->encode = self::SJIS;
48
        }
49
    }
50
51
    /**
52
     * @Route("/%eccube_admin_route%/content/file_manager", name="admin_content_file")
53
     * @Template("@admin/Content/file.twig")
54
     */
55
    public function index(Request $request)
56 3
    {
57
        $form = $this->formFactory->createBuilder(FormType::class)
58 3
            ->add('file', FileType::class)
59 3
            ->add('create_file', TextType::class)
60 3
            ->getForm();
61 3
62
        // user_data_dir
63
        $userDataDir = $this->getUserDataDir();
64 3
        $topDir = $this->normalizePath($userDataDir);
65 3
        //        $topDir = '/';
66
        // user_data_dirの親ディレクトリ
67
        $htmlDir = $this->normalizePath($this->getUserDataDir().'/../');
68 3
69
        // カレントディレクトリ
70
        $nowDir = $this->checkDir($this->getUserDataDir($request->get('tree_select_file')), $this->getUserDataDir())
71 3
            ? $this->normalizePath($this->getUserDataDir($request->get('tree_select_file')))
72 3
            : $topDir;
73 3
74
        // パンくず表示用データ
75
        $nowDirList = json_encode(explode('/', trim(str_replace($htmlDir, '', $nowDir), '/')));
76 3
        $jailNowDir = $this->getJailDir($nowDir);
77 3
        $isTopDir = ($topDir === $jailNowDir);
78 3
        $parentDir = substr($nowDir, 0, strrpos($nowDir, '/'));
79 3
80
        if ('POST' === $request->getMethod()) {
81 3
            switch ($request->get('mode')) {
82 2
                case 'create':
83
                    $this->create($request);
84 1
                    break;
85 1
                case 'upload':
86
                    $this->upload($request);
87 1
                    break;
88 1
                default:
89
                    break;
90
            }
91
        }
92
        $tree = $this->getTree($this->getUserDataDir(), $request);
93 3
        $arrFileList = $this->getFileList($nowDir);
94 3
        $paths = $this->getPathsToArray($tree);
95 3
        $tree = $this->getTreeToArray($tree);
96 3
97
        return [
98
            'form' => $form->createView(),
99 3
            'tpl_javascript' => json_encode($tree),
100 3
            'top_dir' => $this->getJailDir($topDir),
101 3
            'tpl_is_top_dir' => $isTopDir,
102 3
            'tpl_now_dir' => $jailNowDir,
103 3
            'html_dir' => $this->getJailDir($htmlDir),
104 3
            'now_dir_list' => $nowDirList,
105 3
            'tpl_parent_dir' => $this->getJailDir($parentDir),
106 3
            'arrFileList' => $arrFileList,
107 3
            'errors' => $this->errors,
108 3
            'paths' => json_encode($paths),
109 3
        ];
110
    }
111
112
    /**
113
     * @Route("/%eccube_admin_route%/content/file_view", name="admin_content_file_view")
114
     */
115
    public function view(Request $request)
116 1
    {
117
        $file = $this->convertStrToServer($this->getUserDataDir($request->get('file')));
118 1
        if ($this->checkDir($file, $this->getUserDataDir())) {
119 1
            setlocale(LC_ALL, 'ja_JP.UTF-8');
120 1
121
            return new BinaryFileResponse($file);
122 1
        }
123
124
        throw new NotFoundHttpException();
125
    }
126
127
    /**
128
     * Create directory
129
     *
130
     * @param Request $request
131
     */
132
    public function create(Request $request)
133 1
    {
134
        $form = $this->formFactory->createBuilder(FormType::class)
135 1
            ->add('file', FileType::class)
136 1
            ->add('create_file', TextType::class, [
137 1
                'constraints' => [
138
                    new Assert\NotBlank(),
139 1
                    new Assert\Regex([
140 1
                        'pattern' => '/[^[:alnum:]_.\\-]/',
141
                        'match' => false,
142 1
                        'message' => 'file.text.error.folder_symbol',
143 1
                    ]),
144
                    new Assert\Regex([
145
                        'pattern' => "/^\.(.*)$/",
146
                        'match' => false,
147 1
                        'message' => 'file.text.error.folder_period',
148 1
                    ]),
149
                ],
150
            ])
151
            ->getForm();
152
153
        $form->handleRequest($request);
154 1 View Code Duplication
        if (!$form->isValid()) {
155
            foreach ($form->getErrors(true) as $error) {
156 1
                $this->errors[] = ['message' => $error->getMessage()];
157 1
            }
158
159
            return;
160
        }
161
162
        $fs = new Filesystem();
163
        $filename = $form->get('create_file')->getData();
164
165 1
        try {
166 1
            $topDir = $this->getUserDataDir();
167
            $nowDir = $this->getUserDataDir($request->get('now_dir'));
168
            $nowDir = $this->checkDir($nowDir, $topDir)
169 1
                ? $this->normalizePath($nowDir)
170 1
                : $topDir;
171 1
            $fs->mkdir($nowDir.'/'.$filename);
172
173 1
            $this->addSuccess('admin.common.create_complete', 'admin');
174 1
        } catch (IOException $e) {
175
            $this->errors[] = ['message' => $e->getMessage()];
176 1
        }
177
    }
178
179
    /**
180
     * @Route("/%eccube_admin_route%/content/file_delete", name="admin_content_file_delete", methods={"DELETE"})
181
     */
182
    public function delete(Request $request)
183
    {
184
        $this->isTokenValid();
185
186 1
        $selectFile = $request->get('select_file');
187
        if (is_null($selectFile) || $selectFile == '/') {
188 1
            return $this->redirectToRoute('admin_content_file');
189
        }
190 1
191 1
        $topDir = $this->getUserDataDir();
192 1
        $file = $this->convertStrToServer($this->getUserDataDir($selectFile));
193 1
        if ($this->checkDir($file, $topDir)) {
194 1
            $fs = new Filesystem();
195 1
            if ($fs->exists($file)) {
196 1
                $fs->remove($file);
197
                $this->addSuccess('admin.common.delete_complete', 'admin');
198
            }
199
        }
200 1
201
        // 削除実行時のカレントディレクトリを表示させる
202
        return $this->redirectToRoute('admin_content_file', array('tree_select_file' => dirname($selectFile)));
203
    }
204
205
    /**
206 1
     * @Route("/%eccube_admin_route%/content/file_download", name="admin_content_file_download")
207
     */
208 1
    public function download(Request $request)
209 1
    {
210 1
        $topDir = $this->getUserDataDir();
211 1
        $file = $this->convertStrToServer($this->getUserDataDir($request->get('select_file')));
212 1
        if ($this->checkDir($file, $topDir)) {
213 1
            if (!is_dir($file)) {
214
                setlocale(LC_ALL, 'ja_JP.UTF-8');
215
                $pathParts = pathinfo($file);
216 1
217
                $patterns = [
218
                    '/[a-zA-Z0-9!"#$%&()=~^|@`:*;+{}]/',
219
                    '/[- ,.<>?_[\]\/\\\\]/',
220
                    "/['\r\n\t\v\f]/",
221 1
                ];
222 1
223 1
                $str = preg_replace($patterns, '', $pathParts['basename']);
224
                if (strlen($str) === 0) {
225
                    return (new BinaryFileResponse($file))->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT);
226
                } else {
227
                    return new BinaryFileResponse($file, 200, [
228
                        'Content-Type' => 'aplication/octet-stream;',
229
                        'Content-Disposition' => "attachment; filename*=UTF-8\'\'".rawurlencode($this->convertStrFromServer($pathParts['basename'])),
230
                    ]);
231
                }
232
            }
233
        }
234
        throw new NotFoundHttpException();
235 1
    }
236
237 1
    public function upload(Request $request)
238 1
    {
239
        $form = $this->formFactory->createBuilder(FormType::class)
240 1
            ->add('file', FileType::class, [
241 1
                'constraints' => [
242
                    new Assert\NotBlank([
243
                        'message' => 'admin.common.file_select_empty',
244
                    ]),
245 1
                ],
246 1
            ])
247
            ->add('create_file', TextType::class)
248 1
            ->getForm();
249
250 1
        $form->handleRequest($request);
251
252 View Code Duplication
        if (!$form->isValid()) {
253
            foreach ($form->getErrors(true) as $error) {
254
                $this->errors[] = ['message' => $error->getMessage()];
255
            }
256
257
            return;
258 1
        }
259 1
260 1
        $data = $form->getData();
261
        $topDir = $this->getUserDataDir();
262 1
        $nowDir = $this->getUserDataDir($request->get('now_dir'));
263
264
        if (!$this->checkDir($nowDir, $topDir)) {
265
            $this->errors[] = ['message' => 'file.text.error.invalid_upload_folder'];
266
267
            return;
268 1
        }
269
270 1
        $filename = $this->convertStrToServer($data['file']->getClientOriginalName());
271 1
        try {
272
            $data['file']->move($nowDir, $filename);
273
            $this->addSuccess('admin.common.upload_complete', 'admin');
274
        } catch (FileException $e) {
275
            $this->errors[] = ['message' => $e->getMessage()];
276
        }
277 3
    }
278
279 3
    private function getTreeToArray($tree)
280 3
    {
281 3
        $arrTree = [];
282 3
        foreach ($tree as $key => $val) {
283 3
            $path = $this->getJailDir($val['path']);
284 3
            $arrTree[$key] = [
285 3
                $key,
286 3
                $val['type'],
287 3
                $path,
288
                $val['depth'],
289
                $val['open'] ? 'true' : 'false',
290
            ];
291 3
        }
292
293
        return $arrTree;
294 3
    }
295
296 3
    private function getPathsToArray($tree)
297 3
    {
298 3
        $paths = [];
299
        foreach ($tree as $val) {
300
            $paths[] = $this->getJailDir($val['path']);
301 3
        }
302
303
        return $paths;
304
    }
305
306
    /**
307
     * @param string $topDir
308 3
     * @param Request $request
309
     */
310 3
    private function getTree($topDir, $request)
311 3
    {
312 3
        $finder = Finder::create()->in($topDir)
313
            ->directories()
314 3
            ->sortByName();
315 3
316 3
        $tree = [];
317 3
        $tree[] = [
318 3
            'path' => $topDir,
319
            'type' => '_parent',
320
            'depth' => 0,
321
            'open' => true,
322 3
        ];
323
324 3
        $defaultDepth = count(explode('/', $topDir));
325 3
326
        $openDirs = [];
327
        if ($request->get('tree_status')) {
328
            $openDirs = explode('|', $request->get('tree_status'));
329 3
        }
330 1
331 1
        foreach ($finder as $dirs) {
332 1
            $path = $this->normalizePath($dirs->getRealPath());
333 1
            $type = (iterator_count(Finder::create()->in($path)->directories())) ? '_parent' : '_child';
334 1
            $depth = count(explode('/', $path)) - $defaultDepth;
335 1
            $tree[] = [
336 1
                'path' => $path,
337 1
                'type' => $type,
338
                'depth' => $depth,
339
                'open' => (in_array($path, $openDirs)) ? true : false,
340
            ];
341 3
        }
342
343
        return $tree;
344
    }
345
346
    /**
347 3
     * @param string $nowDir
348
     */
349 3
    private function getFileList($nowDir)
350 3
    {
351 3
        $topDir = $this->getuserDataDir();
352 3
        $filter = function (\SplFileInfo $file) use ($topDir) {
353
            $acceptPath = realpath($topDir);
354 3
            $targetPath = $file->getRealPath();
355 3
356
            return strpos($targetPath, $acceptPath) === 0;
357 3
        };
358 3
359 3
        $finder = Finder::create()
360 3
            ->filter($filter)
361 3
            ->in($nowDir)
362 3
            ->ignoreDotFiles(false)
363 3
            ->sortByName()
364
            ->depth(0);
365 3
        $dirFinder = $finder->directories();
366
        try {
367
            $dirs = $dirFinder->getIterator();
368
        } catch (\Exception $e) {
369
            $dirs = [];
370 3
        }
371
372 3
        $fileFinder = $finder->files();
373
        try {
374
            $files = $fileFinder->getIterator();
375
        } catch (\Exception $e) {
376
            $files = [];
377 3
        }
378 3
379 1
        $arrFileList = [];
380 1
        foreach ($dirs as $dir) {
381 1
            $dirPath = $this->normalizePath($dir->getRealPath());
382 1
            $childDir = Finder::create()
383 1
                ->in($dirPath)
384 1
                ->ignoreDotFiles(false)
385 1
                ->directories()
386 1
                ->depth(0);
387 1
            $childFile = Finder::create()
388 1
                ->in($dirPath)
389 1
                ->ignoreDotFiles(false)
390 1
                ->files()
391 1
                ->depth(0);
392 1
            $countNumber = $childDir->count() + $childFile->count();
393 1
            $arrFileList[] = [
394 1
                'file_name' => $this->convertStrFromServer($dir->getFilename()),
395 1
                'file_path' => $this->convertStrFromServer($this->getJailDir($dirPath)),
396
                'file_size' => FilesystemUtil::sizeToHumanReadable($dir->getSize()),
397 1
                'file_time' => $dir->getmTime(),
398
                'is_dir' => true,
399
                'is_empty' => $countNumber == 0 ? true : false,
400 3
            ];
401 3
        }
402 3
        foreach ($files as $file) {
403 3
            $arrFileList[] = [
404 3
                'file_name' => $this->convertStrFromServer($file->getFilename()),
405 3
                'file_path' => $this->convertStrFromServer($this->getJailDir($this->normalizePath($file->getRealPath()))),
406
                'file_size' => FilesystemUtil::sizeToHumanReadable($file->getSize()),
407
                'file_time' => $file->getmTime(),
408 3
                'is_dir' => false,
409
                'is_empty' => false,
410
                'extension' => $file->getExtension(),
411
            ];
412 3
        }
413
414
        return $arrFileList;
415 3
    }
416
417 3
    protected function normalizePath($path)
418
    {
419
        return str_replace('\\', '/', realpath($path));
420
    }
421
422
    /**
423 6
     * @param string $topDir
424
     */
425 6
    protected function checkDir($targetDir, $topDir)
426 6
    {
427
        $targetDir = realpath($targetDir);
428 6
        $topDir = realpath($topDir);
429
430
        return strpos($targetDir, $topDir) === 0;
431
    }
432
433
    /**
434 3
     * @return string
435
     */
436 3 View Code Duplication
    private function convertStrFromServer($target)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
437
    {
438
        if ($this->encode == self::SJIS) {
439
            return mb_convert_encoding($target, self::UTF, self::SJIS);
440 3
        }
441
442
        return $target;
443 4
    }
444
445 4 View Code Duplication
    private function convertStrToServer($target)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
446
    {
447
        if ($this->encode == self::SJIS) {
448
            return mb_convert_encoding($target, self::SJIS, self::UTF);
449 4
        }
450
451
        return $target;
452 6
    }
453
454 6
    private function getUserDataDir($nowDir = null)
455
    {
456
        return rtrim($this->getParameter('kernel.project_dir').'/html/user_data'.$nowDir, '/');
457 3
    }
458
459 3
    private function getJailDir($path)
460 3
    {
461
        $realpath = realpath($path);
462 3
        $jailPath = str_replace(realpath($this->getUserDataDir()), '', $realpath);
463
464
        return $jailPath ? $jailPath : '/';
465
    }
466
}
467