ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
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';
32 const EXPIRES_IN = '+5 days';
36 private static $delivery_type_static = null;
44 private $mime_type = '';
48 private $path_to_file = '';
52 private $download_file_name = '';
60 private $send_mime_type = true;
64 private $exit_after = true;
72 private $etag = '';
76 private $show_last_modified = true;
80 private $has_context = true;
84 private $cache = false;
88 private $hash_filename = false;
92 private $delete_file = false;
96 private static $DEBUG = false;
105
106
111 public function __construct($path_to_file, GlobalHttpState $httpState)
112 {
113 assert(is_string($path_to_file));
114 $this->httpService = $httpState;
115 if ($path_to_file == self::DIRECT_PHP_OUTPUT) {
116 $this->setPathToFile(self::DIRECT_PHP_OUTPUT);
117 } else {
119 $this->detemineDeliveryType();
120 $this->determineMimeType();
122 }
123 $this->setHasContext(\ilContext::getType() !== null);
124 $this->fileDeliveryTypeFactory = new FileDeliveryTypeFactory($httpState);
125 }
126
127
128 public function stream()
129 {
130 if (!$this->delivery()->supportsStreaming()) {
132 }
133 $this->deliver();
134 }
135
136
137 private function delivery()
138 {
139 return $this->fileDeliveryTypeFactory->getInstance($this->getDeliveryType());
140 }
141
142
143 public function deliver()
144 {
145 $response = $this->httpService->response()->withHeader('X-ILIAS-FileDelivery-Method', $this->getDeliveryType());
146 if (!$this->delivery()->doesFileExists($this->path_to_file)) {
147 $response = $this->httpService->response()->withStatus(404);
148 $this->httpService->saveResponse($response);
149 $this->httpService->sendResponse();
150 $this->close();
151 }
152 $this->httpService->saveResponse($response);
153
154 $this->clearBuffer();
155 $this->checkCache();
156 $this->setGeneralHeaders();
157 $this->delivery()->prepare($this->getPathToFile());
158 $this->delivery()->deliver($this->getPathToFile(), $this->isDeleteFile());
159 if ($this->isDeleteFile()) {
160 $this->delivery()->handleFileDeletion($this->getPathToFile());
161 }
162 if ($this->isExitAfter()) {
163 $this->close();
164 }
165 }
166
167
168 public function setGeneralHeaders()
169 {
170 $this->checkExisting();
171 if ($this->isSendMimeType()) {
172 $response = $this->httpService->response()->withHeader(ResponseHeader::CONTENT_TYPE, $this->getMimeType());
173 $this->httpService->saveResponse($response);
174 }
175 if ($this->isConvertFileNameToAsci()) {
176 $this->cleanDownloadFileName();
177 }
178 if ($this->hasHashFilename()) {
179 $this->setDownloadFileName(md5($this->getDownloadFileName()));
180 }
181 $this->setDispositionHeaders();
182 $response = $this->httpService->response()->withHeader(ResponseHeader::ACCEPT_RANGES, 'bytes');
183 $this->httpService->saveResponse($response);
185 && $this->getPathToFile() != self::DIRECT_PHP_OUTPUT
186 ) {
187 $response = $this->httpService->response()->withHeader(ResponseHeader::CONTENT_LENGTH, (string) filesize($this->getPathToFile()));
188 $this->httpService->saveResponse($response);
189 }
190 $response = $this->httpService->response()->withHeader(ResponseHeader::CONNECTION, "close");
191 $this->httpService->saveResponse($response);
192 }
193
194
195 public function setCachingHeaders()
196 {
197 $response = $this->httpService->response()->withHeader(ResponseHeader::CACHE_CONTROL, 'must-revalidate, post-check=0, pre-check=0')->withHeader(ResponseHeader::PRAGMA, 'public');
198
199 $this->httpService->saveResponse($response->withHeader(ResponseHeader::EXPIRES, date("D, j M Y H:i:s", strtotime(self::EXPIRES_IN)) . " GMT"));
200 $this->sendEtagHeader();
201 $this->sendLastModified();
202 }
203
204
205 public function generateEtag()
206 {
207 $this->setEtag(md5(filemtime($this->getPathToFile()) . filesize($this->getPathToFile())));
208 }
209
210
211 public function close()
212 {
213 exit;
214 }
215
216
220 private function determineMimeType()
221 {
223 if ($info) {
224 $this->setMimeType($info);
225
226 return true;
227 }
228 $finfo = finfo_open(FILEINFO_MIME_TYPE);
229 $info = finfo_file($finfo, $this->getPathToFile());
230 finfo_close($finfo);
231 if ($info) {
232 $this->setMimeType($info);
233
234 return true;
235 }
236
237 return false;
238 }
239
240
244 private function determineDownloadFileName()
245 {
246 if (!$this->getDownloadFileName()) {
247 $download_file_name = basename($this->getPathToFile());
249 }
250 }
251
252
256 private function detemineDeliveryType()
257 {
258 if (self::$delivery_type_static) {
259 \ilWACLog::getInstance()->write('used cached delivery type');
260 $this->setDeliveryType(self::$delivery_type_static);
261
262 return true;
263 }
264
265 if (function_exists('apache_get_modules')
266 && in_array('mod_xsendfile', apache_get_modules())
267 ) {
269 }
270
271 if (is_file('./Services/FileDelivery/classes/override.php')) {
272 $override_delivery_type = false;
273 require_once('./Services/FileDelivery/classes/override.php');
274 if ($override_delivery_type) {
275 $this->setDeliveryType($override_delivery_type);
276 }
277 }
278
279 require_once('./Services/Environment/classes/class.ilRuntime.php');
280 $ilRuntime = \ilRuntime::getInstance();
281 if ((!$ilRuntime->isFPM() && !$ilRuntime->isHHVM())
282 && $this->getDeliveryType() == DeliveryMethod::XACCEL
283 ) {
285 }
286
288 && strpos($this->getPathToFile(), './data') !== 0
289 ) {
291 }
292
293 self::$delivery_type_static = $this->getDeliveryType();
294
295 return true;
296 }
297
298
302 public function getDeliveryType()
303 {
305 }
306
307
312 {
313 $this->delivery_type = $delivery_type;
314 }
315
316
320 public function getMimeType()
321 {
322 return $this->mime_type;
323 }
324
325
329 public function setMimeType($mime_type)
330 {
331 $this->mime_type = $mime_type;
332 }
333
334
338 public function getPathToFile()
339 {
340 return $this->path_to_file;
341 }
342
343
348 {
349 $this->path_to_file = $path_to_file;
350 }
351
352
356 public function getDownloadFileName()
357 {
359 }
360
361
366 {
367 $this->download_file_name = $download_file_name;
368 }
369
370
374 public function getDisposition()
375 {
376 return $this->disposition;
377 }
378
379
384 {
385 $this->disposition = $disposition;
386 }
387
388
392 public function isSendMimeType()
393 {
395 }
396
397
402 {
403 $this->send_mime_type = $send_mime_type;
404 }
405
406
410 public function isExitAfter()
411 {
412 return $this->exit_after;
413 }
414
415
419 public function setExitAfter($exit_after)
420 {
421 $this->exit_after = $exit_after;
422 }
423
424
428 public function isConvertFileNameToAsci()
429 {
431 }
432
433
438 {
439 $this->convert_file_name_to_asci = $convert_file_name_to_asci;
440 }
441
442
446 public function getEtag()
447 {
448 return $this->etag;
449 }
450
451
455 public function setEtag($etag)
456 {
457 $this->etag = $etag;
458 }
459
460
464 public function getShowLastModified()
465 {
467 }
468
469
474 {
475 $this->show_last_modified = $show_last_modified;
476 }
477
478
482 public function isHasContext()
483 {
484 return $this->has_context;
485 }
486
487
492 {
493 $this->has_context = $has_context;
494 }
495
496
500 public function hasCache()
501 {
502 return $this->cache;
503 }
504
505
509 public function setCache($cache)
510 {
511 $this->cache = $cache;
512 }
513
514
518 public function hasHashFilename()
519 {
521 }
522
523
528 {
529 $this->hash_filename = $hash_filename;
530 }
531
532
533 private function sendEtagHeader()
534 {
535 if ($this->getEtag()) {
536 $response = $this->httpService->response()->withHeader('ETag', $this->getEtag());
537 $this->httpService->saveResponse($response);
538 }
539 }
540
541
542 private function sendLastModified()
543 {
544 if ($this->getShowLastModified()) {
545 $response = $this->httpService->response()->withHeader(
546 'Last-Modified',
547 date("D, j M Y H:i:s", filemtime($this->getPathToFile()))
548 . " GMT"
549 );
550 $this->httpService->saveResponse($response);
551 }
552 }
553
554 // /**
555 // * @return bool
556 // */
557 // private function isNonModified() {
558 // if (self::$DEBUG) {
559 // return false;
560 // }
561 //
562 // if (!isset($_SERVER['HTTP_IF_NONE_MATCH']) || !isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
563 // return false;
564 // }
565 //
566 // $http_if_none_match = $_SERVER['HTTP_IF_NONE_MATCH'];
567 // $http_if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
568 //
569 // switch (true) {
570 // case ($http_if_none_match != $this->getEtag()):
571 // return false;
572 // case (@strtotime($http_if_modified_since) <= filemtime($this->getPathToFile())):
573 // return false;
574 // }
575 //
576 // return true;
577 // }
578
582 public static function isDEBUG()
583 {
584 return (bool) self::$DEBUG;
585 }
586
587
591 public static function setDEBUG($DEBUG)
592 {
593 assert(is_bool($DEBUG));
594 self::$DEBUG = $DEBUG;
595 }
596
597
601 public function checkCache()
602 {
603 if ($this->hasCache()) {
604 $this->generateEtag();
605 $this->sendEtagHeader();
606 $this->setShowLastModified(true);
607 $this->setCachingHeaders();
608 }
609 }
610
611
615 public function clearBuffer()
616 {
617 $ob_get_contents = ob_get_contents();
618 if ($ob_get_contents) {
619 // \ilWACLog::getInstance()->write(__CLASS__ . ' had output before file delivery: '
620 // . $ob_get_contents);
621 }
622 ob_end_clean(); // fixed 0016469, 0016467, 0016468
623 }
624
625
629 private function checkExisting()
630 {
631 if ($this->getPathToFile() != self::DIRECT_PHP_OUTPUT
632 && !file_exists($this->getPathToFile())
633 ) {
634 $this->close();
635 }
636 }
637
638
644 private function cleanDownloadFileName()
645 {
648 }
649
650
658 public static function returnASCIIFileName($original_filename)
659 {
660 // The filename must be converted to ASCII, as of RFC 2183,
661 // section 2.3.
662
674
677
678 // #15914 - try to fix german umlauts
679 $umlauts = array(
680 "Ä" => "Ae",
681 "Ö" => "Oe",
682 "Ü" => "Ue",
683 "ä" => "ae",
684 "ö" => "oe",
685 "ü" => "ue",
686 "ß" => "ss",
687 );
688 foreach ($umlauts as $src => $tgt) {
689 $original_filename = str_replace($src, $tgt, $original_filename);
690 }
691
692 $ascii_filename = htmlentities($original_filename, ENT_NOQUOTES, 'UTF-8');
693 $ascii_filename = preg_replace('/\&(.)[^;]*;/', '\\1', $ascii_filename);
694 $ascii_filename = preg_replace('/[\x7f-\xff]/', '_', $ascii_filename);
695
696 // OS do not allow the following characters in filenames: \/:*?"<>|
697 $ascii_filename = preg_replace('/[:\x5c\/\*\?\"<>\|]/', '_', $ascii_filename);
698
699 return (string) $ascii_filename;
700 // return iconv("UTF-8", "ASCII//TRANSLIT", $original_name); // proposal
701 }
702
703
707 public function isDeleteFile()
708 {
709 return (bool) $this->delete_file;
710 }
711
712
719 {
720 assert(is_bool($delete_file));
721 $this->delete_file = $delete_file;
722 }
723
724
725 private function setDispositionHeaders()
726 {
727 $response = $this->httpService->response();
728 $response = $response->withHeader(
730 $this->getDisposition()
731 . '; filename="'
732 . $this->getDownloadFileName()
733 . '"'
734 );
735 $response = $response->withHeader('Content-Description', $this->getDownloadFileName());
736 $this->httpService->saveResponse($response);
737 }
738}
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:311
setShowLastModified($show_last_modified)
Definition: Delivery.php:473
setDeleteFile($delete_file)
Definition: Delivery.php:718
setConvertFileNameToAsci($convert_file_name_to_asci)
Definition: Delivery.php:437
cleanDownloadFileName()
Converts the filename to ASCII.
Definition: Delivery.php:644
setSendMimeType($send_mime_type)
Definition: Delivery.php:401
static returnASCIIFileName($original_filename)
Converts a UTF-8 filename to ASCII.
Definition: Delivery.php:658
__construct($path_to_file, GlobalHttpState $httpState)
Definition: Delivery.php:111
setDownloadFileName($download_file_name)
Definition: Delivery.php:365
static setDEBUG($DEBUG)
Definition: Delivery.php:591
setPathToFile($path_to_file)
Definition: Delivery.php:347
setDisposition($disposition)
Definition: Delivery.php:383
setHasContext($has_context)
Definition: Delivery.php:491
setHashFilename($hash_filename)
Definition: Delivery.php:527
static getType()
Get context type.
static lookupMimeType($path_to_file, $fallback=self::APPLICATION__OCTET_STREAM, $a_external=null)
static getInstance()
Interface GlobalHttpState.
Interface ResponseHeader.
exit
Definition: login.php:29
$ascii_filename
Definition: metadata.php:361
$response