ILIAS  release_7 Revision v7.30-3-g800a261c036
All Data Structures Namespaces Files Functions Variables Modules Pages
FilePathSanitizer.php
Go to the documentation of this file.
1 <?php
2 
3 namespace ILIAS\File\Sanitation;
4 
6 use Exception;
7 use ilFileUtils;
10 use ilObjFile;
11 
18 {
19 
23  private $file_object;
27  private $relative_path;
31  private $fs;
35  private $absolute_path;
39  private $version = 1;
40 
41 
48  {
49  $this->version = (int) $file_object->getVersion();
50  $this->file_object = $file_object;
51  $this->absolute_path = $this->file_object->getDirectory($this->version) . "/" . $this->file_object->getFileName();
52  $this->relative_path = LegacyPathHelper::createRelativePath($this->absolute_path);
53  $this->fs = LegacyPathHelper::deriveFilesystemFrom($this->absolute_path);
54  }
55 
56 
60  public function needsSanitation() /* : bool*/
61  {
62  try {
63  $fs_relative_path_existing = $this->fs->has($this->relative_path);
64  $fs_valid_relative_path_existing = $this->fs->has(ilFileUtils::getValidFilename($this->relative_path));
65  $native_absolute_path_exists = file_exists($this->absolute_path);
66  $native_valid_absolute_path_existing = file_exists(ilFileUtils::getValidFilename($this->absolute_path));
67 
68  return (
69  !$fs_relative_path_existing
70  || !$fs_valid_relative_path_existing
71  || !$native_absolute_path_exists
72  || !$native_valid_absolute_path_existing
73  );
74  } catch (Exception $e) {
75  return false;
76  }
77  }
78 
79 
83  private function log(/*string*/ $message)
84  {
85  global $DIC;
86  $DIC->logger()->root()->debug("FilePathSanitizer: " . $message);
87  }
88 
89 
93  public function sanitizeIfNeeded() /* : void */
94  {
95  if ($this->needsSanitation()) {
96  // First Try: using FileSystemService
97  $dirname = dirname($this->relative_path);
98  if (!$this->fs->has($dirname)) {
99  $this->log("FAILED: Sanitizing File Path: {$this->file_object->getFile()}. Message: Directory not found");
100 
101  return false;
102  }
103  try {
104  $first_file = reset($this->fs->listContents($dirname));
105  } catch (DirectoryNotFoundException $e) {
106  $this->log("FAILED AGAIN and AGAIN: Sanitizing File Path: {$this->file_object->getFile()}. Message: {$e->getMessage()}");
107 
108  return false;
109  }
110  if ($first_file instanceof \ILIAS\Filesystem\DTO\Metadata) {
111  try {
112  $valid_filename = $this->santitizeFilename($first_file->getPath());
113  // rename file in filesystem
114  if (!$this->fs->has($valid_filename)) {
115  $this->fs->rename($first_file->getPath(), $valid_filename);
116  // rename file object
117 
118  $this->log("Sanitized File Path: {$valid_filename}");
119  }
120  $this->saveNewNameForFileObject($valid_filename);
121 
122  return true;
123  } catch (Exception $e) {
124  $this->log("FAILED: Sanitizing File Path: {$this->file_object->getFile()}. Message: {$e->getMessage()}. Will try using native PHP");
125 
126  try {
127  // Second try: use native php
128  $scandir = scandir(dirname($this->absolute_path));
129  if (isset($scandir[2])) {
130  $first_file = $scandir[2];
131  if (is_file($first_file)) {
132  $valid_filename = $this->santitizeFilename($first_file);
133  if (rename($first_file, $valid_filename)) {
134  $this->saveNewNameForFileObject($valid_filename);
135  $this->log("Sanitized File Path: {$valid_filename}");
136  }
137  } else {
138  throw new Exception("is not a file: " . $first_file);
139  }
140  } else {
141  throw new Exception("no File found in " . dirname($this->absolute_path));
142  }
143  } catch (Exception $e) {
144  $this->log("FAILED AGAIN: Sanitizing File Path: {$this->file_object->getFile()}. Message: {$e->getMessage()}");
145 
146  try {
147  foreach (new DirectoryIterator(dirname($this->absolute_path)) as $item) {
148  if ($item->isDot()) {
149  continue;
150  }
151  if ($item->isFile()) {
152  $valid_filename = $this->santitizeFilename($item->getPathname());
153  if (rename($item->getPathname(), $valid_filename)) {
154  $this->saveNewNameForFileObject($valid_filename);
155  $this->log("Sanitized File Path: {$valid_filename}");
156  }
157  break;
158  }
159  }
160  } catch (Exception $e) {
161  $this->log("FAILED AGAIN and AGAIN: Sanitizing File Path: {$this->file_object->getFile()}. Message: {$e->getMessage()}");
162  }
163  }
164 
165  return false;
166  }
167  }
168 
169  return false;
170  }
171 
172  return true;
173  }
174 
175 
182  private function santitizeFilename($first_file)
183  {
184  $valid_filename = $first_file;
185 
186  while (preg_match('#\p{C}+|^\./#u', $valid_filename)) {
187  $valid_filename = preg_replace('#\p{C}+|^\./#u', '', $valid_filename);
188  }
189 
190  $valid_filename = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $valid_filename); // removes all non printable characters (ASCII 7 Bit)
191 
192  // $valid_filename = \League\Flysystem\Util::normalizeRelativePath($valid_filename);
193  // $valid_filename = preg_replace('/[\x00-\x1F\x7F-\xA0\xAD]/u', '', $valid_filename);
194  // $valid_filename = iconv(mb_detect_encoding($valid_filename, mb_detect_order(), true), "UTF-8", $valid_filename);
195  // $valid_filename = utf8_encode($valid_filename);
196 
197  $valid_filename = ilFileUtils::getValidFilename($valid_filename);
198 
199  return $valid_filename;
200  }
201 
202 
206  private function saveNewNameForFileObject($valid_filename)
207  {
208  $sanitized_filename = basename($valid_filename);
209  $this->file_object->setFileName($sanitized_filename);
210  $this->file_object->update();
211  }
212 }
static createRelativePath(string $absolute_path)
Creates a relative path from an absolute path which starts with a valid storage location.
__construct(ilObjFile $file_object)
FilePathSanitizer constructor.
Class ChatMainBarProvider .
Class DirectoryNotFoundException Indicates that the directory is missing or not found.
global $DIC
Definition: goto.php:24
static deriveFilesystemFrom(string $absolute_path)
Tries to fetch the filesystem responsible for the absolute path.
$message
Definition: xapiexit.php:14
Class FlySystemFileAccessTest disabled disabled disabled.
static getValidFilename($a_filename)
Get valid filename.