ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
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_element_handler($a_xml_parser, $this->handlerBeginTag(...), $this->handlerEndTag(...));
111  xml_set_character_data_handler($a_xml_parser, $this->handlerCharacterData(...));
112  }
113 
124  public function handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs): void
125  {
126  global $DIC;
127 
128  global $DIC;
129 
130  switch ($a_name) {
131  case 'File':
132  if (isset($a_attribs["obj_id"])) {
133  $read_obj_id = ilUtil::__extractId($a_attribs["obj_id"], IL_INST_ID);
134  if ($this->obj_id != -1 && (int) $read_obj_id != -1 && $this->obj_id !== (int) $read_obj_id) {
135  throw new ilFileException(
136  "Object IDs (xml $read_obj_id and argument " . $this->obj_id . ") do not match!",
138  );
139  }
140  }
141 
142  break;
143  case 'Content': // Old import files
144  case 'Version':
145  if ($a_name === "Version" && !isset($a_attribs["mode"])) {
146  // Old import files
147  $this->version = null;
148  if ($this->date === null) {
149  // Version tag comes after Content tag. Take only first (= Should be latest)
150  $this->date = $a_attribs["date"];
151  $this->usr_id = (int) $a_attribs["usr_id"];
152  $this->versions[0]["date"] = $this->date;
153  $this->versions[0]["usr_id"] = $this->usr_id;
154  }
155  break;
156  }
157 
159  #echo $a_attribs["mode"];
160  if (isset($a_attribs["mode"])) {
161  if ($a_attribs["mode"] == "GZIP") {
162  if (!function_exists("gzread")) {
163  throw new ilFileException(
164  "Deflating with gzip is not supported",
166  );
167  }
168 
170  } elseif ($a_attribs["mode"] == "ZLIB") {
171  if (!function_exists("gzuncompress")) {
172  throw new ilFileException(
173  "Deflating with zlib (compress/uncompress) is not supported",
175  );
176  }
177 
179  } elseif ($a_attribs["mode"] == "COPY") {
180  $this->mode = ilFileXMLParser::$CONTENT_COPY;
181  } // begin-patch fm
182  elseif ($a_attribs['mode'] == 'REST') {
183  $this->mode = ilFileXMLParser::$CONTENT_REST;
184  }
185  // end-patch fm
186  }
187 
188  if ($a_name === "Version") {
189  $this->version = (int) $a_attribs["version"];
190  $this->max_version = (int) $a_attribs["max_version"];
191  $this->date = (int) $a_attribs["date"];
192  $this->usr_id = (int) $a_attribs["usr_id"];
193  $this->action = (string) $a_attribs["action"];
194  }
195  }
196  }
197 
204  public function handlerEndTag($a_xml_parser, string $a_name): void
205  {
206  $this->cdata = trim($this->cdata ?? '');
207 
208  $GLOBALS['DIC']['ilLog']->write(__METHOD__ . ': ' . $this->cdata);
209 
210  switch ($a_name) {
211  case 'File':
212  $this->result = true;
213  break;
214  case 'Filename':
215  $this->file->setFilename($this->cdata ?? 'unkown');
216  $this->file->setTitle($this->cdata ?? 'unkown');
217 
218  break;
219  case 'Title':
220  $this->file->setTitle(trim($this->cdata));
221  break;
222  case 'Description':
223  $this->file->setDescription(trim($this->cdata));
224  break;
225  case 'Rating':
226  $this->file->setRating((bool) $this->cdata);
227  break;
228  case 'Content': // Old import files
229  case 'Version':
230  if ($a_name === "Version" && $this->version === null) {
231  // Old import files
232  break;
233  }
234 
235  $baseDecodedFilename = ilFileUtils::ilTempnam();
236  if ($this->mode === ilFileXMLParser::$CONTENT_COPY) {
237  $this->tmpFilename = $this->getImportDirectory() . "/" . self::normalizeRelativePath($this->cdata);
238  } // begin-patch fm
239  elseif ($this->mode === ilFileXMLParser::$CONTENT_REST) {
240  $storage = new ilRestFileStorage();
241  $this->tmpFilename = $storage->getStoredFilePath(self::normalizeRelativePath($this->cdata));
242  if (!$this->fastBase64Decode($this->tmpFilename, $baseDecodedFilename)) {
243  throw new ilFileException("Base64-Decoding failed", ilFileException::$DECOMPRESSION_FAILED);
244  }
245  $this->tmpFilename = $baseDecodedFilename;
246  } // end-patch fm
247  else {
248  if (!$this->fastBase64Decode($this->tmpFilename, $baseDecodedFilename)) {
249  throw new ilFileException("Base64-Decoding failed", ilFileException::$DECOMPRESSION_FAILED);
250  }
251  if ($this->mode === ilFileXMLParser::$CONTENT_GZ_COMPRESSED) {
252  if (!$this->fastGunzip($baseDecodedFilename, $this->tmpFilename)) {
253  throw new ilFileException(
254  "Deflating with fastzunzip failed",
256  );
257  }
258  unlink($baseDecodedFilename);
259  } elseif ($this->mode === ilFileXMLParser::$CONTENT_ZLIB_COMPRESSED) {
260  if (!$this->fastGunzip($baseDecodedFilename, $this->tmpFilename)) {
261  throw new ilFileException(
262  "Deflating with fastDecompress failed",
264  );
265  }
266  unlink($baseDecodedFilename);
267  } else {
268  $this->tmpFilename = $baseDecodedFilename;
269  }
270  }
271 
272  //$this->content = $content;
273  // see #17211
274 
275  if ($this->version == $this->file->getVersion()) {
276  if (is_file($this->tmpFilename)) {
277  $this->file->setFileSize(filesize($this->tmpFilename)); // strlen($this->content));
278  }
279 
280  // if no file type is given => lookup mime type
281  if ($this->file->getFileType() === '' || $this->file->getFileType() === '0') {
282  global $DIC;
283  $this->file->setFileType(MimeType::getMimeType($this->tmpFilename));
284  }
285  }
286 
287  $this->versions[] = [
288  "version" => $this->version,
289  "max_version" => $this->max_version,
290  "tmpFilename" => $this->tmpFilename,
291  "date" => $this->date,
292  "usr_id" => $this->usr_id,
293  "action" => $this->action,
294  ];
295  $this->version = null;
296  $this->date = null;
297  $this->usr_id = null;
298  break;
299  }
300 
301  $this->cdata = '';
302  }
303 
310  public function handlerCharacterData($a_xml_parser, string $a_data): void
311  {
312  if ($a_data !== "\n") {
313  // begin-patch fm
314  if ($this->mode !== ilFileXMLParser::$CONTENT_COPY
315  && $this->mode !== ilFileXMLParser::$CONTENT_REST
316  ) { // begin-patch fm
317  $this->cdata .= $a_data;
318  } else {
319  $this->cdata .= $a_data;
320  }
321  }
322  }
323 
329  public function setFileContents(): void
330  {
331  // Delete exists version 1 history
332  ilHistory::_removeEntriesForObject($this->file->getId());
333 
334  foreach ($this->versions as $version) {
335  if (!file_exists($version["tmpFilename"])) {
336  if (!isset($version["tmpFilename"])) {
337  continue;
338  }
339  // try to get first file of directory
340  $files = scandir(dirname((string) $version["tmpFilename"]));
341  $version["tmpFilename"] = rtrim(
342  dirname((string) $version["tmpFilename"]),
343  "/"
344  ) . "/" . $files[2];// because [0] = "." [1] = ".."
345  if (!file_exists($version["tmpFilename"])) {
346  ilLoggerFactory::getLogger('file')->error(
347  __METHOD__ . ' "' . ($version["tmpFilename"]) . '" file not found.'
348  );
349 
350  continue;
351  }
352  }
353 
354  if (filesize($version["tmpFilename"]) == 0) {
355  continue;
356  }
357 
358  // imported file version
359  $import_file_version_path = $version["tmpFilename"];
360 
361  $stream = Streams::ofResource(fopen($import_file_version_path, 'rb'));
362  $this->file->appendStream($stream, $this->file->getTitle());
363  }
364  }
365 
371  public function updateFileContents(): void
372  {
373  // removed
374  }
375 
383  public function start(): bool
384  {
385  $this->startParsing();
386 
387  return $this->result > 0;
388  }
389 
400  public static function normalizeRelativePath(string $path): string
401  {
402  $path = str_replace('\\', '/', $path);
403 
404  while (preg_match('#\p{C}+|^\./#u', (string) $path)) {
405  $path = preg_replace('#\p{C}+|^\./#u', '', (string) $path);
406  }
407 
408  $parts = [];
409  foreach (explode('/', (string) $path) as $part) {
410  switch ($part) {
411  case '':
412  case '.':
413  break;
414  case '..':
415  array_pop($parts);
416  break;
417  default:
418  $parts[] = $part;
419  break;
420  }
421  }
422 
423  return implode('/', $parts);
424  }
425 
426  private function fastBase64Decode(string $filein, string $fileout): bool
427  {
428  $fh = fopen($filein, 'rb');
429  $fh2 = fopen($fileout, 'wb');
430  stream_filter_append($fh2, 'convert.base64-decode');
431 
432  while (!feof($fh)) {
433  $chunk = fgets($fh);
434  if ($chunk === false) {
435  break;
436  }
437  fwrite($fh2, $chunk);
438  }
439  fclose($fh);
440  fclose($fh2);
441 
442  return true;
443  }
444 
445  private function fastGunzip(string $in, string $out): bool
446  {
447  if (!file_exists($in)) {
448  return false;
449  }
450  if (!is_readable($in)) {
451  return false;
452  }
453  if (!file_exists($out) && !is_writable(dirname($out))) {
454  return false;
455  }
456  if (file_exists($out) && !is_writable($out)) {
457  return false;
458  }
459  $in_file = gzopen($in, "rb");
460  $out_file = fopen($out, "wb");
461 
462  while (!gzeof($in_file)) {
463  $buffer = gzread($in_file, 4096);
464  fwrite($out_file, $buffer, 4096);
465  }
466 
467  gzclose($in_file);
468  fclose($out_file);
469 
470  return true;
471  }
472 }
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:29
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.
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
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:53
static int $CONTENT_GZ_COMPRESSED
Class ilObjFile.
$out
Definition: buildRTE.php:24
global $DIC
Definition: shib_login.php:26
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...