ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Delivery.php
Go to the documentation of this file.
1<?php
2
4
5require_once('./Services/Utilities/classes/class.ilMimeTypeUtil.php');
6require_once('./Services/Utilities/classes/class.ilUtil.php'); // This include is needed since WAC can use ilFileDelivery without Initialisation
7require_once('./Services/Context/classes/class.ilContext.php');
8require_once('./Services/Http/classes/class.ilHTTPS.php');
9require_once('./Services/FileDelivery/classes/FileDeliveryTypes/FileDeliveryTypeFactory.php');
10require_once './Services/FileDelivery/classes/FileDeliveryTypes/DeliveryMethod.php';
11
17
27final class Delivery
28{
29 const DIRECT_PHP_OUTPUT = 'php://output';
30 const DISP_ATTACHMENT = 'attachment';
31 const DISP_INLINE = 'inline';
35 private static $delivery_type_static = null;
43 private $mime_type = '';
47 private $path_to_file = '';
51 private $download_file_name = '';
59 private $send_mime_type = true;
63 private $exit_after = true;
71 private $etag = '';
75 private $show_last_modified = true;
79 private $has_context = true;
83 private $cache = false;
87 private $hash_filename = false;
91 private $delete_file = false;
95 private static $DEBUG = false;
99 private $httpService;
104
105
110 public function __construct($path_to_file, GlobalHttpState $httpState)
111 {
112 assert(is_string($path_to_file));
113 $this->httpService = $httpState;
114 if ($path_to_file == self::DIRECT_PHP_OUTPUT) {
115 $this->setPathToFile(self::DIRECT_PHP_OUTPUT);
116 } else {
118 $this->detemineDeliveryType();
119 $this->determineMimeType();
121 }
122 $this->setHasContext(\ilContext::getType() !== null);
123 $this->fileDeliveryTypeFactory = new FileDeliveryTypeFactory($httpState);
124 }
125
126
127 public function stream()
128 {
129 if (!$this->delivery()->supportsStreaming()) {
131 }
132 $this->deliver();
133 }
134
135
136 private function delivery()
137 {
138 return $this->fileDeliveryTypeFactory->getInstance($this->getDeliveryType());
139 }
140
141
142 public function deliver()
143 {
144 $response = $this->httpService->response()->withHeader('X-ILIAS-FileDelivery-Method', $this->getDeliveryType());
145 if (!$this->delivery()->doesFileExists($this->path_to_file)) {
146 $response = $this->httpService->response()->withStatus(404);
147 $this->httpService->saveResponse($response);
148 $this->httpService->sendResponse();
149 $this->close();
150 }
151 $this->httpService->saveResponse($response);
152
153 $this->clearBuffer();
154 $this->checkCache();
155 $this->setGeneralHeaders();
156 $this->delivery()->prepare($this->getPathToFile());
157 $this->delivery()->deliver($this->getPathToFile(), $this->isDeleteFile());
158 if ($this->isDeleteFile()) {
159 $this->delivery()->handleFileDeletion($this->getPathToFile());
160 }
161 if ($this->isExitAfter()) {
162 $this->close();
163 }
164 }
165
166
167 public function setGeneralHeaders()
168 {
169 $this->checkExisting();
170 if ($this->isSendMimeType()) {
171 $response = $this->httpService->response()->withHeader(ResponseHeader::CONTENT_TYPE, $this->getMimeType());
172 $this->httpService->saveResponse($response);
173 }
174 if ($this->isConvertFileNameToAsci()) {
175 $this->cleanDownloadFileName();
176 }
177 if ($this->hasHashFilename()) {
178 $this->setDownloadFileName(md5($this->getDownloadFileName()));
179 }
180 $this->setDispositionHeaders();
181 $response = $this->httpService->response()->withHeader(ResponseHeader::ACCEPT_RANGES, 'bytes');
182 $this->httpService->saveResponse($response);
184 && $this->getPathToFile() != self::DIRECT_PHP_OUTPUT
185 ) {
186 $response = $this->httpService->response()->withHeader(ResponseHeader::CONTENT_LENGTH, (string) filesize($this->getPathToFile()));
187 $this->httpService->saveResponse($response);
188 }
189 $response = $this->httpService->response()->withHeader(ResponseHeader::CONNECTION, "close");
190 $this->httpService->saveResponse($response);
191 }
192
193
194 public function setCachingHeaders()
195 {
196 $response = $this->httpService->response()->withHeader(ResponseHeader::CACHE_CONTROL, 'must-revalidate, post-check=0, pre-check=0')->withHeader(ResponseHeader::PRAGMA, 'public');
197
198 $this->httpService->saveResponse($response);
199 $this->sendEtagHeader();
200 $this->sendLastModified();
201 }
202
203
204 public function generateEtag()
205 {
206 $this->setEtag(md5(filemtime($this->getPathToFile()) . filesize($this->getPathToFile())));
207 }
208
209
210 public function close()
211 {
212 exit;
213 }
214
215
219 private function determineMimeType()
220 {
222 if ($info) {
223 $this->setMimeType($info);
224
225 return true;
226 }
227 $finfo = finfo_open(FILEINFO_MIME_TYPE);
228 $info = finfo_file($finfo, $this->getPathToFile());
229 finfo_close($finfo);
230 if ($info) {
231 $this->setMimeType($info);
232
233 return true;
234 }
235
236 return false;
237 }
238
239
243 private function determineDownloadFileName()
244 {
245 if (!$this->getDownloadFileName()) {
246 $download_file_name = basename($this->getPathToFile());
248 }
249 }
250
251
255 private function detemineDeliveryType()
256 {
257 if (self::$delivery_type_static) {
258 \ilWACLog::getInstance()->write('used cached delivery type');
259 $this->setDeliveryType(self::$delivery_type_static);
260
261 return true;
262 }
263
264 if (function_exists('apache_get_modules')
265 && in_array('mod_xsendfile', apache_get_modules())
266 ) {
268 }
269
270 if (is_file('./Services/FileDelivery/classes/override.php')) {
271 $override_delivery_type = false;
272 require_once('./Services/FileDelivery/classes/override.php');
273 if ($override_delivery_type) {
274 $this->setDeliveryType($override_delivery_type);
275 }
276 }
277
278 require_once('./Services/Environment/classes/class.ilRuntime.php');
279 $ilRuntime = \ilRuntime::getInstance();
280 if ((!$ilRuntime->isFPM() && !$ilRuntime->isHHVM())
281 && $this->getDeliveryType() == DeliveryMethod::XACCEL
282 ) {
284 }
285
287 && strpos($this->getPathToFile(), './data') !== 0
288 ) {
290 }
291
292 self::$delivery_type_static = $this->getDeliveryType();
293
294 return true;
295 }
296
297
301 public function getDeliveryType()
302 {
304 }
305
306
311 {
312 $this->delivery_type = $delivery_type;
313 }
314
315
319 public function getMimeType()
320 {
321 return $this->mime_type;
322 }
323
324
328 public function setMimeType($mime_type)
329 {
330 $this->mime_type = $mime_type;
331 }
332
333
337 public function getPathToFile()
338 {
339 return $this->path_to_file;
340 }
341
342
347 {
348 $this->path_to_file = $path_to_file;
349 }
350
351
355 public function getDownloadFileName()
356 {
358 }
359
360
365 {
366 $this->download_file_name = $download_file_name;
367 }
368
369
373 public function getDisposition()
374 {
375 return $this->disposition;
376 }
377
378
383 {
384 $this->disposition = $disposition;
385 }
386
387
391 public function isSendMimeType()
392 {
394 }
395
396
401 {
402 $this->send_mime_type = $send_mime_type;
403 }
404
405
409 public function isExitAfter()
410 {
411 return $this->exit_after;
412 }
413
414
418 public function setExitAfter($exit_after)
419 {
420 $this->exit_after = $exit_after;
421 }
422
423
427 public function isConvertFileNameToAsci()
428 {
430 }
431
432
437 {
438 $this->convert_file_name_to_asci = $convert_file_name_to_asci;
439 }
440
441
445 public function getEtag()
446 {
447 return $this->etag;
448 }
449
450
454 public function setEtag($etag)
455 {
456 $this->etag = $etag;
457 }
458
459
463 public function getShowLastModified()
464 {
466 }
467
468
473 {
474 $this->show_last_modified = $show_last_modified;
475 }
476
477
481 public function isHasContext()
482 {
483 return $this->has_context;
484 }
485
486
491 {
492 $this->has_context = $has_context;
493 }
494
495
499 public function hasCache()
500 {
501 return $this->cache;
502 }
503
504
508 public function setCache($cache)
509 {
510 $this->cache = $cache;
511 }
512
513
517 public function hasHashFilename()
518 {
520 }
521
522
527 {
528 $this->hash_filename = $hash_filename;
529 }
530
531
532 private function sendEtagHeader()
533 {
534 if ($this->getEtag()) {
535 $response = $this->httpService->response()->withHeader('ETag', $this->getEtag());
536 $this->httpService->saveResponse($response);
537 }
538 }
539
540
541 private function sendLastModified()
542 {
543 if ($this->getShowLastModified()) {
544 $response = $this->httpService->response()->withHeader(
545 'Last-Modified',
546 date("D, j M Y H:i:s", filemtime($this->getPathToFile()))
547 . " GMT"
548 );
549 $this->httpService->saveResponse($response);
550 }
551 }
552
553 // /**
554 // * @return bool
555 // */
556 // private function isNonModified() {
557 // if (self::$DEBUG) {
558 // return false;
559 // }
560 //
561 // if (!isset($_SERVER['HTTP_IF_NONE_MATCH']) || !isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
562 // return false;
563 // }
564 //
565 // $http_if_none_match = $_SERVER['HTTP_IF_NONE_MATCH'];
566 // $http_if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
567 //
568 // switch (true) {
569 // case ($http_if_none_match != $this->getEtag()):
570 // return false;
571 // case (@strtotime($http_if_modified_since) <= filemtime($this->getPathToFile())):
572 // return false;
573 // }
574 //
575 // return true;
576 // }
577
581 public static function isDEBUG()
582 {
583 return (bool) self::$DEBUG;
584 }
585
586
590 public static function setDEBUG($DEBUG)
591 {
592 assert(is_bool($DEBUG));
593 self::$DEBUG = $DEBUG;
594 }
595
596
600 public function checkCache()
601 {
602 if ($this->hasCache()) {
603 $this->generateEtag();
604 $this->sendEtagHeader();
605 $this->setShowLastModified(true);
606 $this->setCachingHeaders();
607 }
608 }
609
610
614 public function clearBuffer()
615 {
616 $ob_get_contents = ob_get_contents();
617 if ($ob_get_contents) {
618 // \ilWACLog::getInstance()->write(__CLASS__ . ' had output before file delivery: '
619 // . $ob_get_contents);
620 }
621 ob_end_clean(); // fixed 0016469, 0016467, 0016468
622 }
623
624
628 private function checkExisting()
629 {
630 if ($this->getPathToFile() != self::DIRECT_PHP_OUTPUT
631 && !file_exists($this->getPathToFile())
632 ) {
633 $this->close();
634 }
635 }
636
637
643 private function cleanDownloadFileName()
644 {
647 }
648
649
657 public static function returnASCIIFileName($original_filename)
658 {
659 // The filename must be converted to ASCII, as of RFC 2183,
660 // section 2.3.
661
673
676
677 // #15914 - try to fix german umlauts
678 $umlauts = array(
679 "Ä" => "Ae",
680 "Ö" => "Oe",
681 "Ü" => "Ue",
682 "ä" => "ae",
683 "ö" => "oe",
684 "ü" => "ue",
685 "ß" => "ss",
686 );
687 foreach ($umlauts as $src => $tgt) {
688 $original_filename = str_replace($src, $tgt, $original_filename);
689 }
690
691 $ascii_filename = htmlentities($original_filename, ENT_NOQUOTES, 'UTF-8');
692 $ascii_filename = preg_replace('/\&(.)[^;]*;/', '\\1', $ascii_filename);
693 $ascii_filename = preg_replace('/[\x7f-\xff]/', '_', $ascii_filename);
694
695 // OS do not allow the following characters in filenames: \/:*?"<>|
696 $ascii_filename = preg_replace('/[:\x5c\/\*\?\"<>\|]/', '_', $ascii_filename);
697
698 return (string) $ascii_filename;
699 // return iconv("UTF-8", "ASCII//TRANSLIT", $original_name); // proposal
700 }
701
702
706 public function isDeleteFile()
707 {
708 return (bool) $this->delete_file;
709 }
710
711
718 {
719 assert(is_bool($delete_file));
720 $this->delete_file = $delete_file;
721 }
722
723
724 private function setDispositionHeaders()
725 {
726 $response = $this->httpService->response();
727 $response = $response->withHeader(
729 $this->getDisposition()
730 . '; filename="'
731 . $this->getDownloadFileName()
732 . '"'
733 );
734 $response = $response->withHeader('Content-Description', $this->getDownloadFileName());
735 $this->httpService->saveResponse($response);
736 }
737}
$ascii_filename
Definition: metadata.php:311
exit
Definition: backend.php:16
An exception for terminatinating execution or to throw for unit testing.
Provides an interface to the ILIAS HTTP services.
setDeliveryType($delivery_type)
Definition: Delivery.php:310
setShowLastModified($show_last_modified)
Definition: Delivery.php:472
setDeleteFile($delete_file)
Definition: Delivery.php:717
setConvertFileNameToAsci($convert_file_name_to_asci)
Definition: Delivery.php:436
cleanDownloadFileName()
Converts the filename to ASCII.
Definition: Delivery.php:643
setSendMimeType($send_mime_type)
Definition: Delivery.php:400
static returnASCIIFileName($original_filename)
Converts a UTF-8 filename to ASCII.
Definition: Delivery.php:657
__construct($path_to_file, GlobalHttpState $httpState)
Definition: Delivery.php:110
setDownloadFileName($download_file_name)
Definition: Delivery.php:364
static setDEBUG($DEBUG)
Definition: Delivery.php:590
setPathToFile($path_to_file)
Definition: Delivery.php:346
setDisposition($disposition)
Definition: Delivery.php:382
setHasContext($has_context)
Definition: Delivery.php:490
setHashFilename($hash_filename)
Definition: Delivery.php:526
static getType()
Get context type.
static lookupMimeType($path_to_file, $fallback=self::APPLICATION__OCTET_STREAM, $a_external=null)
static getInstance()
Interface GlobalHttpState.
Interface ResponseHeader.
$info
Definition: index.php:5
$response