ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
FilePathSanitizer.php
Go to the documentation of this file.
1<?php
2
4
5use DirectoryIterator;
6use Exception;
9use ilObjFile;
10
17{
18
22 private $file_object;
30 private $fs;
35
36
43 {
44 $this->file_object = $file_object;
45 $this->absolute_path = $this->file_object->getDirectory($this->file_object->getVersion()) . "/" . $this->file_object->getFileName();
46 $this->relative_path = LegacyPathHelper::createRelativePath($this->absolute_path);
47 $this->fs = LegacyPathHelper::deriveFilesystemFrom($this->absolute_path);
48 }
49
50
54 public function needsSanitation() /* : bool*/
55 {
56 try {
57 $fs_relative_path_existing = $this->fs->has($this->relative_path);
58 $fs_valid_relative_path_existing = $this->fs->has(ilFileUtils::getValidFilename($this->relative_path));
59 $native_absolute_path_exists = file_exists($this->absolute_path);
60 $native_valid_absolute_path_existing = file_exists(ilFileUtils::getValidFilename($this->absolute_path));
61
62 return (
63 !$fs_relative_path_existing
64 || !$fs_valid_relative_path_existing
65 || !$native_absolute_path_exists
66 || !$native_valid_absolute_path_existing
67 );
68 } catch (Exception $e) {
69 return false;
70 }
71 }
72
73
77 private function log(/*string*/ $message)
78 {
79 global $DIC;
80 $DIC->logger()->root()->debug("FilePathSanitizer: " . $message);
81 }
82
83
88 public function sanitizeIfNeeded() /* : void */
89 {
90 if ($this->needsSanitation()) {
91 // First Try: using FileSystemService
92 $dirname = dirname($this->relative_path);
93 if (!$this->fs->has($dirname)) {
94 $this->log("FAILED: Sanitizing File Path: {$this->file_object->getFile()}. Message: Directory not found");
95
96 return false;
97 }
98 $first_file = reset($this->fs->listContents($dirname));
99 if ($first_file instanceof \ILIAS\Filesystem\DTO\Metadata) {
100 try {
101 $valid_filename = $this->santitizeFilename($first_file->getPath());
102 // rename file in filesystem
103 if (!$this->fs->has($valid_filename)) {
104 $this->fs->rename($first_file->getPath(), $valid_filename);
105 // rename file object
106
107 $this->log("Sanitized File Path: {$valid_filename}");
108 }
109 $this->saveNewNameForFileObject($valid_filename);
110
111 return true;
112 } catch (Exception $e) {
113 $this->log("FAILED: Sanitizing File Path: {$this->file_object->getFile()}. Message: {$e->getMessage()}. Will try using native PHP");
114
115 try {
116 // Second try: use native php
117 $scandir = scandir(dirname($this->absolute_path));
118 if (isset($scandir[2])) {
119 $first_file = $scandir[2];
120 if (is_file($first_file)) {
121 $valid_filename = $this->santitizeFilename($first_file);
122 if (rename($first_file, $valid_filename)) {
123 $this->saveNewNameForFileObject($valid_filename);
124 $this->log("Sanitized File Path: {$valid_filename}");
125 }
126 } else {
127 throw new Exception("is not a file: " . $first_file);
128 }
129 } else {
130 throw new Exception("no File found in " . dirname($this->absolute_path));
131 }
132 } catch (Exception $e) {
133 $this->log("FAILED AGAIN: Sanitizing File Path: {$this->file_object->getFile()}. Message: {$e->getMessage()}");
134
135 try {
136 foreach (new DirectoryIterator(dirname($this->absolute_path)) as $item) {
137 if ($item->isDot()) {
138 continue;
139 }
140 if ($item->isFile()) {
141 $valid_filename = $this->santitizeFilename($item->getPathname());
142 if (rename($item->getPathname(), $valid_filename)) {
143 $this->saveNewNameForFileObject($valid_filename);
144 $this->log("Sanitized File Path: {$valid_filename}");
145 }
146 break;
147 }
148 }
149 } catch (Exception $e) {
150 $this->log("FAILED AGAIN and AGAIN: Sanitizing File Path: {$this->file_object->getFile()}. Message: {$e->getMessage()}");
151 }
152 }
153
154 return false;
155 }
156 }
157
158 return false;
159 }
160
161 return true;
162 }
163
164
171 private function santitizeFilename($first_file)
172 {
173 $valid_filename = $first_file;
174
175 while (preg_match('#\p{C}+|^\./#u', $valid_filename)) {
176 $valid_filename = preg_replace('#\p{C}+|^\./#u', '', $valid_filename);
177 }
178
179 $valid_filename = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $valid_filename); // removes all non printable characters (ASCII 7 Bit)
180
181 // $valid_filename = \League\Flysystem\Util::normalizeRelativePath($valid_filename);
182 // $valid_filename = preg_replace('/[\x00-\x1F\x7F-\xA0\xAD]/u', '', $valid_filename);
183 // $valid_filename = iconv(mb_detect_encoding($valid_filename, mb_detect_order(), true), "UTF-8", $valid_filename);
184 // $valid_filename = utf8_encode($valid_filename);
185
186 $valid_filename = ilFileUtils::getValidFilename($valid_filename);
187
188 return $valid_filename;
189 }
190
191
195 private function saveNewNameForFileObject($valid_filename)
196 {
197 $sanitized_filename = basename($valid_filename);
198 $this->file_object->setFileName($sanitized_filename);
199 $this->file_object->update();
200 }
201}
An exception for terminatinating execution or to throw for unit testing.
__construct(ilObjFile $file_object)
FilePathSanitizer constructor.
static deriveFilesystemFrom($absolute_path)
Tries to fetch the filesystem responsible for the absolute path.
static createRelativePath($absolute_path)
Creates a relative path from an absolute path which starts with a valid storage location.
Class ilFileUtils.
static getValidFilename($a_filename)
Get valid filename.
Class ilObjFile.
catch(Exception $e) $message
Class FlySystemFileAccessTest.
Class BaseForm.
global $DIC
Definition: saml.php:7