@@ -207,7 +207,7 @@ public function getHandler($queries)
207207 private function downloadAction ($ path )
208208 {
209209 $ file_name = basename ($ path );
210- $ path = $ this ->basePath . $ path ;
210+ $ path = $ this ->canonicalizePath ( $ this -> basePath . $ path) ;
211211
212212 if (!file_exists ($ path )) {
213213 return false ;
@@ -261,12 +261,12 @@ private function downloadMultipleAction($items, $archiveName)
261261
262262 private function uploadAction ($ path , $ files )
263263 {
264- $ path = $ this ->basePath . $ path ;
264+ $ path = $ this ->canonicalizePath ( $ this -> basePath . $ path) ;
265265
266266 foreach ($ _FILES as $ file ) {
267267 $ uploaded = move_uploaded_file (
268268 $ file ['tmp_name ' ],
269- rtrim ( $ path, ' / ' ) . ' / ' . $ file ['name ' ]
269+ $ path . DIRECTORY_SEPARATOR . $ this -> normalizeName ( $ file ['name ' ])
270270 );
271271 if ($ uploaded === false ) {
272272 return false ;
@@ -278,10 +278,19 @@ private function uploadAction($path, $files)
278278
279279 private function listAction ($ path )
280280 {
281- $ files = glob ($ this ->basePath . rtrim ($ path , '/ ' ) . '/* ' );
281+ $ files = array_values (array_filter (
282+ scandir ($ this ->basePath . $ path ),
283+ function ($ path ) {
284+ return !($ path === '. ' || $ path === '.. ' );
285+ }
286+ ));
282287
283- $ files = array_map (function ($ file ) {
288+ $ files = array_map (function ($ file ) use ($ path ) {
289+ $ file = $ this ->canonicalizePath (
290+ $ this ->basePath . $ path . DIRECTORY_SEPARATOR . $ file
291+ );
284292 $ date = new \DateTime ('@ ' . filemtime ($ file ));
293+
285294 return [
286295 'name ' => basename ($ file ),
287296 'rights ' => $ this ->parsePerms (fileperms ($ file )),
@@ -308,10 +317,10 @@ private function renameAction($oldPath, $newPath)
308317
309318 private function moveAction ($ oldPaths , $ newPath )
310319 {
311- $ newPath = $ this ->basePath . rtrim ($ newPath, ' / ' ) . ' / ' ;
320+ $ newPath = $ this ->basePath . $ this -> canonicalizePath ($ newPath ) . DIRECTORY_SEPARATOR ;
312321
313322 foreach ($ oldPaths as $ oldPath ) {
314- if (! file_exists ($ this ->basePath . $ oldPath )) {
323+ if (!file_exists ($ this ->basePath . $ oldPath )) {
315324 return false ;
316325 }
317326
@@ -326,10 +335,10 @@ private function moveAction($oldPaths, $newPath)
326335
327336 private function copyAction ($ oldPaths , $ newPath )
328337 {
329- $ newPath = $ this ->basePath . rtrim ($ newPath, ' / ' ) . ' / ' ;
338+ $ newPath = $ this ->basePath . $ this -> canonicalizePath ($ newPath ) . DIRECTORY_SEPARATOR ;
330339
331340 foreach ($ oldPaths as $ oldPath ) {
332- if (! file_exists ($ this ->basePath . $ oldPath )) {
341+ if (!file_exists ($ this ->basePath . $ oldPath )) {
333342 return false ;
334343 }
335344
@@ -348,7 +357,7 @@ private function copyAction($oldPaths, $newPath)
348357 private function removeAction ($ paths )
349358 {
350359 foreach ($ paths as $ path ) {
351- $ path = $ this ->basePath . $ path ;
360+ $ path = $ this ->canonicalizePath ( $ this -> basePath . $ path) ;
352361
353362 if (is_dir ($ path )) {
354363 $ dirEmpty = (new \FilesystemIterator ($ path ))->valid ();
@@ -434,7 +443,43 @@ private function compressAction($paths, $destination, $archiveName)
434443 }
435444
436445 foreach ($ paths as $ path ) {
437- $ zip ->addFile ($ this ->basePath . $ path , basename ($ path ));
446+ $ fullPath = $ this ->basePath . $ path ;
447+
448+ if (is_dir ($ fullPath )) {
449+ $ dirs = [
450+ [
451+ 'dir ' => basename ($ path ),
452+ 'path ' => $ this ->canonicalizePath ($ this ->basePath . $ path ),
453+ ]
454+ ];
455+
456+ while (count ($ dirs )) {
457+ $ dir = current ($ dirs );
458+ $ zip ->addEmptyDir ($ dir ['dir ' ]);
459+
460+ $ dh = opendir ($ dir ['path ' ]);
461+ while ($ file = readdir ($ dh )) {
462+ if ($ file != '. ' && $ file != '.. ' ) {
463+ $ filePath = $ dir ['path ' ] . DIRECTORY_SEPARATOR . $ file ;
464+ if (is_file ($ filePath )) {
465+ $ zip ->addFile (
466+ $ dir ['path ' ] . DIRECTORY_SEPARATOR . $ file ,
467+ $ dir ['dir ' ] . '/ ' . basename ($ file )
468+ );
469+ } elseif (is_dir ($ filePath )) {
470+ $ dirs [] = [
471+ 'dir ' => $ dir ['dir ' ] . '/ ' . $ file ,
472+ 'path ' => $ dir ['path ' ] . DIRECTORY_SEPARATOR . $ file
473+ ];
474+ }
475+ }
476+ }
477+ closedir ($ dh );
478+ array_shift ($ dirs );
479+ }
480+ } else {
481+ $ zip ->addFile ($ path , basename ($ path ));
482+ }
438483 }
439484
440485 return $ zip ->close ();
@@ -443,7 +488,7 @@ private function compressAction($paths, $destination, $archiveName)
443488 private function extractAction ($ destination , $ archivePath , $ folderName )
444489 {
445490 $ archivePath = $ this ->basePath . $ archivePath ;
446- $ folderPath = $ this ->basePath . rtrim ($ destination, ' / ' ) . ' / ' . $ folderName ;
491+ $ folderPath = $ this ->basePath . $ this -> canonicalizePath ($ destination ) . DIRECTORY_SEPARATOR . $ folderName ;
447492
448493 $ zip = new \ZipArchive ;
449494 if ($ zip ->open ($ archivePath ) === false ) {
@@ -533,4 +578,53 @@ private function parsePerms($perms)
533578
534579 return $ info ;
535580 }
581+
582+ private function canonicalizePath ($ path )
583+ {
584+ $ dirSep = DIRECTORY_SEPARATOR ;
585+ $ wrongDirSep = DIRECTORY_SEPARATOR === '/ ' ? '\\' : '/ ' ;
586+
587+ // Replace incorrect dir separators
588+ $ path = str_replace ($ wrongDirSep , $ dirSep , $ path );
589+
590+ $ path = explode ($ dirSep , $ path );
591+ $ stack = array ();
592+ foreach ($ path as $ seg ) {
593+ if ($ seg == '.. ' ) {
594+ // Ignore this segment, remove last segment from stack
595+ array_pop ($ stack );
596+ continue ;
597+ }
598+
599+ if ($ seg == '. ' ) {
600+ // Ignore this segment
601+ continue ;
602+ }
603+
604+ $ stack [] = $ seg ;
605+ }
606+
607+ // Remove last /
608+ if (empty ($ stack [count ($ stack ) - 1 ])) {
609+ array_pop ($ stack );
610+ }
611+
612+ return implode ($ dirSep , $ stack );
613+ }
614+
615+ /**
616+ * Creates ASCII name
617+ *
618+ * @param string name encoded in UTF-8
619+ * @return string name containing only numbers, chars without diacritics, underscore and dash
620+ * @copyright Jakub Vrána, https://php.vrana.cz/
621+ */
622+ private function normalizeName ($ name )
623+ {
624+ $ name = preg_replace ('~[^ \\pL0-9_]+~u ' , '- ' , $ name );
625+ $ name = trim ($ name , "- " );
626+ $ name = iconv ("utf-8 " , "us-ascii//TRANSLIT " , $ name );
627+ $ name = preg_replace ('~[^-a-z0-9_]+~ ' , '' , $ name );
628+ return $ name ;
629+ }
536630}
0 commit comments