ILIAS  release_8 Revision v8.24
Delivery.php
Go to the documentation of this file.
1<?php
2
3declare(strict_types=1);
4
22
27
37final class Delivery
38{
39 public const DIRECT_PHP_OUTPUT = 'php://output';
40 public const DISP_ATTACHMENT = 'attachment';
41 public const DISP_INLINE = 'inline';
42 public const EXPIRES_IN = '+5 days';
43 private static ?string $delivery_type_static = null;
45 private string $mime_type = '';
46 private string $path_to_file = '';
47 private string $download_file_name = '';
49 private bool $send_mime_type = true;
50 private bool $exit_after = true;
51 private bool $convert_file_name_to_asci = true;
52 private string $etag = '';
53 private bool $show_last_modified = true;
54 private bool $has_context = true;
55 private bool $cache = false;
56 private bool $hash_filename = false;
57 private bool $delete_file = false;
58 private static bool $DEBUG = false;
59 private Services $http;
61
62
67 public function __construct(string $path_to_file, Services $http)
68 {
69 $this->http = $http;
70 if ($path_to_file === self::DIRECT_PHP_OUTPUT) {
71 $this->setPathToFile(self::DIRECT_PHP_OUTPUT);
72 } else {
73 $this->setPathToFile($path_to_file);
74 $this->detemineDeliveryType();
75 $this->determineMimeType();
77 }
78 $this->setHasContext(\ilContext::getType() !== null);
79 $this->factory = new FileDeliveryTypeFactory($http);
80 }
81
82
83 public function stream(): void
84 {
85 if (!$this->delivery()->supportsStreaming()) {
87 }
88 $this->deliver();
89 }
90
91
92 private function delivery(): ilFileDeliveryType
93 {
94 return $this->factory->getInstance($this->getDeliveryType());
95 }
96
97
98 public function deliver(): void
99 {
100 $response = $this->http->response()->withHeader('X-ILIAS-FileDelivery-Method', $this->getDeliveryType());
101 if (
102 !$this->delivery()->doesFileExists($this->path_to_file)
103 && $this->path_to_file !== self::DIRECT_PHP_OUTPUT
104 ) {
105 $response = $this->http->response()->withStatus(404);
106 $this->http->saveResponse($response);
107 $this->http->sendResponse();
108 $this->close();
109 }
110 $this->http->saveResponse($response);
111
112 $this->clearBuffer();
113 $this->checkCache();
114 $this->setGeneralHeaders();
115 $this->delivery()->prepare($this->getPathToFile());
116 $this->delivery()->deliver($this->getPathToFile(), $this->isDeleteFile());
117 if ($this->isDeleteFile()) {
118 $this->delivery()->handleFileDeletion($this->getPathToFile());
119 }
120 if ($this->isExitAfter()) {
121 $this->close();
122 }
123 }
124
125
126 public function setGeneralHeaders(): void
127 {
128 $this->checkExisting();
129 if ($this->isSendMimeType()) {
130 $response = $this->http->response()->withHeader(ResponseHeader::CONTENT_TYPE, $this->getMimeType());
131 $this->http->saveResponse($response);
132 }
133 if ($this->isConvertFileNameToAsci()) {
134 $this->cleanDownloadFileName();
135 }
136 if ($this->hasHashFilename()) {
137 $this->setDownloadFileName(md5($this->getDownloadFileName()));
138 }
139 $this->setDispositionHeaders();
140 $response = $this->http->response()->withHeader(ResponseHeader::ACCEPT_RANGES, 'bytes');
141 $this->http->saveResponse($response);
142 if ($this->getDeliveryType() === DeliveryMethod::PHP
143 && $this->getPathToFile() !== self::DIRECT_PHP_OUTPUT
144 ) {
145 $response = $this->http->response()->withHeader(ResponseHeader::CONTENT_LENGTH, (string) filesize($this->getPathToFile()));
146 $this->http->saveResponse($response);
147 }
148 $response = $this->http->response()->withHeader(ResponseHeader::CONNECTION, "close");
149 $this->http->saveResponse($response);
150 }
151
152
153 public function setCachingHeaders(): void
154 {
155 $response = $this->http->response()->withHeader(ResponseHeader::CACHE_CONTROL, 'must-revalidate, post-check=0, pre-check=0')->withHeader(ResponseHeader::PRAGMA, 'public');
156
157 $this->http->saveResponse($response->withHeader(ResponseHeader::EXPIRES, date("D, j M Y H:i:s", strtotime(self::EXPIRES_IN)) . " GMT"));
158 $this->sendEtagHeader();
159 $this->sendLastModified();
160 }
161
162
163 public function generateEtag(): void
164 {
165 $this->setEtag(md5(filemtime($this->getPathToFile()) . filesize($this->getPathToFile())));
166 }
167
168
169 public function close(): void
170 {
171 $this->http->close();
172 }
173
174
175 private function determineMimeType(): void
176 {
177 $info = \ILIAS\FileUpload\MimeType::lookupMimeType($this->getPathToFile(), \ILIAS\FileUpload\MimeType::APPLICATION__OCTET_STREAM);
178 if ($info) {
179 $this->setMimeType($info);
180
181 return;
182 }
183 $finfo = finfo_open(FILEINFO_MIME_TYPE);
184 $info = finfo_file($finfo, $this->getPathToFile());
185 finfo_close($finfo);
186 if ($info) {
187 $this->setMimeType($info);
188
189 return;
190 }
191 }
192
193
194 private function determineDownloadFileName(): void
195 {
196 if (!$this->getDownloadFileName()) {
197 $download_file_name = basename($this->getPathToFile());
198 $this->setDownloadFileName($download_file_name);
199 }
200 }
201
202
203 private function detemineDeliveryType(): void
204 {
205 if (self::$delivery_type_static) {
206 $this->setDeliveryType(self::$delivery_type_static);
207
208 return;
209 }
210
211 if (function_exists('apache_get_modules')
212 && in_array('mod_xsendfile', apache_get_modules(), true)
213 ) {
215 }
216
217 if (is_file('./Services/FileDelivery/classes/override.php')) {
218 $override_delivery_type = false;
220 require_once('./Services/FileDelivery/classes/override.php');
221 if ($override_delivery_type) {
222 $this->setDeliveryType($override_delivery_type);
223 }
224 }
225 $ilRuntime = \ilRuntime::getInstance();
226 if ((!$ilRuntime->isFPM() && !$ilRuntime->isHHVM())
227 && $this->getDeliveryType() === DeliveryMethod::XACCEL
228 ) {
230 }
231
233 && strpos($this->getPathToFile(), './data') !== 0
234 ) {
236 }
237
238 self::$delivery_type_static = $this->getDeliveryType();
239 }
240
241
242 public function getDeliveryType(): string
243 {
245 }
246
247
248 public function setDeliveryType(string $delivery_type): void
249 {
250 $this->delivery_type = $delivery_type;
251 }
252
253
254 public function getMimeType(): string
255 {
256 return $this->mime_type;
257 }
258
259
260 public function setMimeType(string $mime_type): void
261 {
262 $this->mime_type = $mime_type;
263 }
264
265
266 public function getPathToFile(): string
267 {
268 return $this->path_to_file;
269 }
270
271
272 public function setPathToFile(string $path_to_file): void
273 {
274 $this->path_to_file = $path_to_file;
275 }
276
277
278 public function getDownloadFileName(): string
279 {
281 }
282
283
284 public function setDownloadFileName(string $download_file_name): void
285 {
286 $this->download_file_name = $download_file_name;
287 }
288
289
290 public function getDisposition(): string
291 {
292 return $this->disposition;
293 }
294
295
296 public function setDisposition(string $disposition): void
297 {
298 $this->disposition = $disposition;
299 }
300
301
302 public function isSendMimeType(): bool
303 {
305 }
306
307
308 public function setSendMimeType(bool $send_mime_type): void
309 {
310 $this->send_mime_type = $send_mime_type;
311 }
312
313
314 public function isExitAfter(): bool
315 {
316 return $this->exit_after;
317 }
318
319
320 public function setExitAfter(bool $exit_after): void
321 {
322 $this->exit_after = $exit_after;
323 }
324
325
326 public function isConvertFileNameToAsci(): bool
327 {
329 }
330
331
333 {
334 $this->convert_file_name_to_asci = $convert_file_name_to_asci;
335 }
336
337
338 public function getEtag(): string
339 {
340 return $this->etag;
341 }
342
343
344 public function setEtag(string $etag): void
345 {
346 $this->etag = $etag;
347 }
348
349
350 public function getShowLastModified(): bool
351 {
353 }
354
355
356 public function setShowLastModified(bool $show_last_modified): void
357 {
358 $this->show_last_modified = $show_last_modified;
359 }
360
361
362 public function isHasContext(): bool
363 {
364 return $this->has_context;
365 }
366
367
368 public function setHasContext(bool $has_context): void
369 {
370 $this->has_context = $has_context;
371 }
372
373
374 public function hasCache(): bool
375 {
376 return $this->cache;
377 }
378
379
380 public function setCache(bool $cache): void
381 {
382 $this->cache = $cache;
383 }
384
385
386 public function hasHashFilename(): bool
387 {
389 }
390
391
392 public function setHashFilename(bool $hash_filename): void
393 {
394 $this->hash_filename = $hash_filename;
395 }
396
397
398 private function sendEtagHeader(): void
399 {
400 if ($this->getEtag()) {
401 $response = $this->http->response()->withHeader('ETag', $this->getEtag());
402 $this->http->saveResponse($response);
403 }
404 }
405
406
407 private function sendLastModified(): void
408 {
409 if ($this->getShowLastModified()) {
410 $response = $this->http->response()->withHeader(
411 'Last-Modified',
412 date("D, j M Y H:i:s", filemtime($this->getPathToFile()))
413 . " GMT"
414 );
415 $this->http->saveResponse($response);
416 }
417 }
418
419 public static function isDEBUG(): bool
420 {
421 return self::$DEBUG;
422 }
423
424
425 public static function setDEBUG(bool $DEBUG): void
426 {
427 self::$DEBUG = $DEBUG;
428 }
429
430
431 public function checkCache(): void
432 {
433 if ($this->hasCache()) {
434 $this->generateEtag();
435 $this->sendEtagHeader();
436 $this->setShowLastModified(true);
437 $this->setCachingHeaders();
438 }
439 }
440
441
445 public function clearBuffer(): bool
446 {
447 try {
448 $ob_get_contents = ob_get_contents();
449 if ($ob_get_contents) {
450 // \ilWACLog::getInstance()->write(__CLASS__ . ' had output before file delivery: '
451 // . $ob_get_contents);
452 }
453 ob_end_clean(); // fixed 0016469, 0016467, 0016468
454 return true;
455 } catch (\Throwable $t) {
456 return false;
457 }
458 }
459
460
461 private function checkExisting(): void
462 {
463 if ($this->getPathToFile() !== self::DIRECT_PHP_OUTPUT
464 && !file_exists($this->getPathToFile())
465 ) {
466 $this->close();
467 }
468 }
469
470
474 private function cleanDownloadFileName(): void
475 {
477 $this->setDownloadFileName($download_file_name);
478 }
479
480
488 public static function returnASCIIFileName(string $original_filename): string
489 {
490 $umlaut_mapping = [
491 "Ä" => "Ae",
492 "Ö" => "Oe",
493 "Ü" => "Ue",
494 "ä" => "ae",
495 "ö" => "oe",
496 "ü" => "ue",
497 "ß" => "ss"
498 ];
499 foreach ($umlaut_mapping as $src => $tgt) {
500 $original_filename = str_replace($src, $tgt, $original_filename);
501 }
502
503 $ascii_filename = htmlentities($original_filename, ENT_NOQUOTES, 'UTF-8');
504 $ascii_filename = preg_replace('/\&(.)[^;]*;/', '\\1', $ascii_filename);
505 $ascii_filename = preg_replace('/[\x7f-\xff]/', '_', $ascii_filename);
506
507 // OS do not allow the following characters in filenames: \/:*?"<>|
508 $ascii_filename = preg_replace(
509 '/[:\x5c\/\*\?\"<>\|]/',
510 '_',
512 );
513 return $ascii_filename;
514 }
515
516
517 public function isDeleteFile(): bool
518 {
519 return $this->delete_file;
520 }
521
522
523 public function setDeleteFile(bool $delete_file): void
524 {
525 $this->delete_file = $delete_file;
526 }
527
528
529 private function setDispositionHeaders(): void
530 {
531 $response = $this->http->response();
532 $response = $response->withHeader(
534 $this->getDisposition()
535 . '; filename="'
536 . $this->getDownloadFileName()
537 . '"'
538 );
539 $response = $response->withHeader('Content-Description', $this->getDownloadFileName());
540 $this->http->saveResponse($response);
541 }
542}
static string $delivery_type_static
Definition: Delivery.php:43
setSendMimeType(bool $send_mime_type)
Definition: Delivery.php:308
FileDeliveryTypeFactory $factory
Definition: Delivery.php:60
cleanDownloadFileName()
Converts the filename to ASCII.
Definition: Delivery.php:474
setExitAfter(bool $exit_after)
Definition: Delivery.php:320
setConvertFileNameToAsci(bool $convert_file_name_to_asci)
Definition: Delivery.php:332
setShowLastModified(bool $show_last_modified)
Definition: Delivery.php:356
static setDEBUG(bool $DEBUG)
Definition: Delivery.php:425
setMimeType(string $mime_type)
Definition: Delivery.php:260
setDisposition(string $disposition)
Definition: Delivery.php:296
__construct(string $path_to_file, Services $http)
Definition: Delivery.php:67
setHasContext(bool $has_context)
Definition: Delivery.php:368
setHashFilename(bool $hash_filename)
Definition: Delivery.php:392
setDeliveryType(string $delivery_type)
Definition: Delivery.php:248
setPathToFile(string $path_to_file)
Definition: Delivery.php:272
static returnASCIIFileName(string $original_filename)
Converts a UTF-8 filename to ASCII.
Definition: Delivery.php:488
setDeleteFile(bool $delete_file)
Definition: Delivery.php:523
setDownloadFileName(string $download_file_name)
Definition: Delivery.php:284
static lookupMimeType(string $path_to_file, string $fallback=self::APPLICATION__OCTET_STREAM, bool $a_external=false)
Definition: MimeType.php:542
Class Services.
Definition: Services.php:38
static getType()
Get context type.
static getInstance()
Interface ResponseHeader.
$ascii_filename
Definition: metadata.php:378
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Delivery.php:21
static http()
Fetches the global http state from ILIAS.
Class ChatMainBarProvider \MainMenu\Provider.
$response