ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
class.ilFileXMLParser.php
Go to the documentation of this file.
1 <?php
2 
31 
33 {
34  public static int $CONTENT_NOT_COMPRESSED = 0;
35  public static int $CONTENT_GZ_COMPRESSED = 1;
36  public static int $CONTENT_ZLIB_COMPRESSED = 2;
37  public static int $CONTENT_COPY = 4;
38  // begin-patch fm
39  public static int $CONTENT_REST = 5;
43  public bool $result = false;
47  public ?string $tmpFilename = null;
48 
49  protected ?int $version = null;
50  protected ?string $action = null;
51  protected ?int $max_version = null;
52  protected ?int $date = null;
53  protected ?int $usr_id = null;
54  protected array $versions = [];
55  protected ?string $import_directory = null;
56  protected ?string $cdata = null;
57 
61  public function __construct(// end-patch fm
65  public ilObjFile $file,
66  string $a_xml_data,
71  public int $obj_id = -1,
75  public int $mode = 0
76  ) {
78  $this->setXMLContent($a_xml_data);
79  }
80 
86  public function setImportDirectory(?string $a_val): void
87  {
88  $this->import_directory = $a_val;
89  }
90 
96  public function getImportDirectory(): ?string
97  {
99  }
100 
108  public function setHandlers($a_xml_parser): void
109  {
110  xml_set_object($a_xml_parser, $this);
111  xml_set_element_handler($a_xml_parser, 'handlerBeginTag', 'handlerEndTag');
112  xml_set_character_data_handler($a_xml_parser, 'handlerCharacterData');
113  }
114 
125  public function handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs): void
126  {
127  global $DIC;
128 
129  global $DIC;
130 
131  switch ($a_name) {
132  case 'File':
133  if (isset($a_attribs["obj_id"])) {
134  $read_obj_id = ilUtil::__extractId($a_attribs["obj_id"], IL_INST_ID);
135  if ($this->obj_id != -1 && (int) $read_obj_id != -1 && $this->obj_id != (int) $read_obj_id) {
136  throw new ilFileException(
137  "Object IDs (xml $read_obj_id and argument " . $this->obj_id . ") do not match!",
139  );
140  }
141  }
142 
143  break;
144  case 'Content': // Old import files
145  case 'Version':
146  if ($a_name === "Version" && !isset($a_attribs["mode"])) {
147  // Old import files
148  $this->version = null;
149  if ($this->date === null) {
150  // Version tag comes after Content tag. Take only first (= Should be latest)
151  $this->date = $a_attribs["date"];
152  $this->usr_id = (int) $a_attribs["usr_id"];
153  $this->versions[0]["date"] = $this->date;
154  $this->versions[0]["usr_id"] = $this->usr_id;
155  }
156  break;
157  }
158 
160  #echo $a_attribs["mode"];
161  if (isset($a_attribs["mode"])) {
162  if ($a_attribs["mode"] == "GZIP") {
163  if (!function_exists("gzread")) {
164  throw new ilFileException(
165  "Deflating with gzip is not supported",
167  );
168  }
169 
171  } elseif ($a_attribs["mode"] == "ZLIB") {
172  if (!function_exists("gzuncompress")) {
173  throw new ilFileException(
174  "Deflating with zlib (compress/uncompress) is not supported",
176  );
177  }
178 
180  } elseif ($a_attribs["mode"] == "COPY") {
181  $this->mode = ilFileXMLParser::$CONTENT_COPY;
182  } // begin-patch fm
183  elseif ($a_attribs['mode'] == 'REST') {
184  $this->mode = ilFileXMLParser::$CONTENT_REST;
185  }
186  // end-patch fm
187  }
188 
189  if ($a_name === "Version") {
190  $this->version = (int) $a_attribs["version"];
191  $this->max_version = (int) $a_attribs["max_version"];
192  $this->date = (int) $a_attribs["date"];
193  $this->usr_id = (int) $a_attribs["usr_id"];
194  $this->action = (string) $a_attribs["action"];
195  }
196  }
197  }
198 
205  public function handlerEndTag($a_xml_parser, string $a_name): void
206  {
207  $this->cdata = trim($this->cdata ?? '');
208 
209  $GLOBALS['DIC']['ilLog']->write(__METHOD__ . ': ' . $this->cdata);
210 
211  switch ($a_name) {
212  case 'File':
213  $this->result = true;
214  break;
215  case 'Filename':
216  $this->file->setFilename($this->cdata ?? 'unkown');
217  $this->file->setTitle($this->cdata ?? 'unkown');
218 
219  break;
220  case 'Title':
221  $this->file->setTitle(trim($this->cdata));
222  break;
223  case 'Description':
224  $this->file->setDescription(trim($this->cdata));
225  break;
226  case 'Rating':
227  $this->file->setRating((bool) $this->cdata);
228  break;
229  case 'Content': // Old import files
230  case 'Version':
231  if ($a_name === "Version" && $this->version === null) {
232  // Old import files
233  break;
234  }
235 
236  $baseDecodedFilename = ilFileUtils::ilTempnam();
237  if ($this->mode === ilFileXMLParser::$CONTENT_COPY) {
238  $this->tmpFilename = $this->getImportDirectory() . "/" . self::normalizeRelativePath($this->cdata);
239  } // begin-patch fm
240  elseif ($this->mode === ilFileXMLParser::$CONTENT_REST) {
241  $storage = new ilRestFileStorage();
242  $this->tmpFilename = $storage->getStoredFilePath(self::normalizeRelativePath($this->cdata));
243  if (!$this->fastBase64Decode($this->tmpFilename, $baseDecodedFilename)) {
244  throw new ilFileException("Base64-Decoding failed", ilFileException::$DECOMPRESSION_FAILED);
245  }
246  $this->tmpFilename = $baseDecodedFilename;
247  } // end-patch fm
248  else {
249  if (!$this->fastBase64Decode($this->tmpFilename, $baseDecodedFilename)) {
250  throw new ilFileException("Base64-Decoding failed", ilFileException::$DECOMPRESSION_FAILED);
251  }
252  if ($this->mode === ilFileXMLParser::$CONTENT_GZ_COMPRESSED) {
253  if (!$this->fastGunzip($baseDecodedFilename, $this->tmpFilename)) {
254  throw new ilFileException(
255  "Deflating with fastzunzip failed",
257  );
258  }
259  unlink($baseDecodedFilename);
260  } elseif ($this->mode === ilFileXMLParser::$CONTENT_ZLIB_COMPRESSED) {
261  if (!$this->fastGunzip($baseDecodedFilename, $this->tmpFilename)) {
262  throw new ilFileException(
263  "Deflating with fastDecompress failed",
265  );
266  }
267  unlink($baseDecodedFilename);
268  } else {
269  $this->tmpFilename = $baseDecodedFilename;
270  }
271  }
272 
273  //$this->content = $content;
274  // see #17211
275 
276  if ($this->version == $this->file->getVersion()) {
277  if (is_file($this->tmpFilename)) {
278  $this->file->setFileSize(filesize($this->tmpFilename)); // strlen($this->content));
279  }
280 
281  // if no file type is given => lookup mime type
282  if (!$this->file->getFileType()) {
283  global $DIC;
284  $this->file->setFileType(MimeType::getMimeType($this->tmpFilename));
285  }
286  }
287 
288  $this->versions[] = [
289  "version" => $this->version,
290  "max_version" => $this->max_version,
291  "tmpFilename" => $this->tmpFilename,
292  "date" => $this->date,
293  "usr_id" => $this->usr_id,
294  "action" => $this->action,
295  ];
296  $this->version = null;
297  $this->date = null;
298  $this->usr_id = null;
299  break;
300  }
301 
302  $this->cdata = '';
303  }
304 
311  public function handlerCharacterData($a_xml_parser, string $a_data): void
312  {
313  if ($a_data != "\n") {
314  // begin-patch fm
315  if ($this->mode !== ilFileXMLParser::$CONTENT_COPY
316  && $this->mode !== ilFileXMLParser::$CONTENT_REST
317  ) { // begin-patch fm
318  $this->cdata .= $a_data;
319  } else {
320  $this->cdata .= $a_data;
321  }
322  }
323  }
324 
330  public function setFileContents(): void
331  {
332  // Delete exists version 1 history
333  ilHistory::_removeEntriesForObject($this->file->getId());
334 
335  foreach ($this->versions as $version) {
336  if (!file_exists($version["tmpFilename"])) {
337  if (!isset($version["tmpFilename"])) {
338  continue;
339  }
340  // try to get first file of directory
341  $files = scandir(dirname($version["tmpFilename"]));
342  $version["tmpFilename"] = rtrim(
343  dirname($version["tmpFilename"]),
344  "/"
345  ) . "/" . $files[2];// because [0] = "." [1] = ".."
346  if (!file_exists($version["tmpFilename"])) {
347  ilLoggerFactory::getLogger('file')->error(
348  __METHOD__ . ' "' . ($version["tmpFilename"]) . '" file not found.'
349  );
350 
351  continue;
352  }
353  }
354 
355  if (filesize($version["tmpFilename"]) == 0) {
356  continue;
357  }
358 
359  // imported file version
360  $import_file_version_path = $version["tmpFilename"];
361 
362  $stream = Streams::ofResource(fopen($import_file_version_path, 'rb'));
363  $this->file->appendStream($stream, $this->file->getTitle());
364  }
365  }
366 
372  public function updateFileContents(): void
373  {
374  // removed
375  }
376 
384  public function start(): bool
385  {
386  $this->startParsing();
387 
388  return $this->result > 0;
389  }
390 
401  public static function normalizeRelativePath(string $path): string
402  {
403  $path = str_replace('\\', '/', $path);
404 
405  while (preg_match('#\p{C}+|^\./#u', $path)) {
406  $path = preg_replace('#\p{C}+|^\./#u', '', $path);
407  }
408 
409  $parts = [];
410  foreach (explode('/', $path) as $part) {
411  switch ($part) {
412  case '':
413  case '.':
414  break;
415  case '..':
416  array_pop($parts);
417  break;
418  default:
419  $parts[] = $part;
420  break;
421  }
422  }
423 
424  return implode('/', $parts);
425  }
426 
427  private function fastBase64Decode(string $filein, string $fileout): bool
428  {
429  $fh = fopen($filein, 'rb');
430  $fh2 = fopen($fileout, 'wb');
431  stream_filter_append($fh2, 'convert.base64-decode');
432 
433  while (!feof($fh)) {
434  $chunk = fgets($fh);
435  if ($chunk === false) {
436  break;
437  }
438  fwrite($fh2, $chunk);
439  }
440  fclose($fh);
441  fclose($fh2);
442 
443  return true;
444  }
445 
446  private function fastGunzip(string $in, string $out): bool
447  {
448  if (!file_exists($in)) {
449  return false;
450  }
451  if (!is_readable($in)) {
452  return false;
453  }
454  if (!file_exists($out) && !is_writable(dirname($out))) {
455  return false;
456  }
457  if (file_exists($out) && !is_writable($out)) {
458  return false;
459  }
460  $in_file = gzopen($in, "rb");
461  $out_file = fopen($out, "wb");
462 
463  while (!gzeof($in_file)) {
464  $buffer = gzread($in_file, 4096);
465  fwrite($out_file, $buffer, 4096);
466  }
467 
468  gzclose($in_file);
469  fclose($out_file);
470 
471  return true;
472  }
473 }
fastGunzip(string $in, string $out)
handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs)
handler for begin of element
const IL_INST_ID
Definition: constants.php:40
static getLogger(string $a_component_id)
Get component logger.
startParsing()
stores xml data in array
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:61
fastBase64Decode(string $filein, string $fileout)
$path
Definition: ltiservices.php:30
handlerEndTag($a_xml_parser, string $a_name)
handler for end of element
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
start()
starts parsing an changes object by side effect.
static int $DECOMPRESSION_FAILED
__construct(public ilObjFile $file, string $a_xml_data, public int $obj_id=-1, public int $mode=0)
Constructor.
static _removeEntriesForObject(int $a_obj_id)
remove all history entries for an object
string $tmpFilename
file of temporary file where we store the file content instead of in memory
$GLOBALS["DIC"]
Definition: wac.php:30
static int $CONTENT_GZ_COMPRESSED
Class ilObjFile.
$out
Definition: buildRTE.php:24
global $DIC
Definition: shib_login.php:25
handlerCharacterData($a_xml_parser, string $a_data)
handler for character data
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static int $CONTENT_NOT_COMPRESSED
static normalizeRelativePath(string $path)
Normalize relative directories in a path.
static int $CONTENT_ZLIB_COMPRESSED
setHandlers($a_xml_parser)
set event handlers
static ilTempnam(?string $a_temp_path=null)
Returns a unique and non existing Path for e temporary file or directory.
setImportDirectory(?string $a_val)
Set import directory.
File storage handling.
updateFileContents()
update file according to filename and version and create history entry has to be called after (!) fil...
static int $ID_DEFLATE_METHOD_MISMATCH
static __extractId(string $ilias_id, int $inst_id)
extract ref id from role title, e.g.
__construct(Container $dic, ilPlugin $plugin)
getImportDirectory()
Get import directory.
bool $result
result of parsing and updating
setXMLContent(string $a_xml_content)
setFileContents()
update file according to filename and version, does not update history has to be called after (!) fil...