ILIAS  trunk Revision v11.0_alpha-1689-g66c127b4ae8
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
StreamDelivery.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
33 
37 final class StreamDelivery extends BaseDelivery
38 {
42  public const SUBREQUEST_SEPARATOR = '/-/';
43 
44  public function __construct(
45  private DataSigner $data_signer,
47  ResponseBuilder $response_builder,
48  ResponseBuilder $fallback_response_builder,
49  ) {
50  parent::__construct($http, $response_builder, $fallback_response_builder);
51  }
52 
58  protected function notFound(ResponseInterface $r): void
59  {
60  $this->http->saveResponse($r->withStatus(404));
61  $this->http->sendResponse();
62  $this->http->close();
63  }
64 
65  public function attached(
66  FileStream $stream,
67  string $download_file_name,
68  ?string $mime_type = null
69  ): never {
70  $this->deliver(
71  $stream,
72  $download_file_name,
73  $mime_type,
74  Disposition::ATTACHMENT
75  );
76  }
77 
78  public function inline(
79  FileStream $stream,
80  string $download_file_name,
81  ?string $mime_type = null
82  ): never {
83  $this->deliver(
84  $stream,
85  $download_file_name,
86  $mime_type,
87  Disposition::INLINE
88  );
89  }
90 
91  public function deliver(
92  FileStream $stream,
93  string $download_file_name,
94  ?string $mime_type = null,
95  Disposition $disposition = Disposition::INLINE
96  ): never {
97  $r = $this->http->response();
98  $uri = $stream->getMetadata()['uri'];
99 
100  if ($stream instanceof ZIPStream || $stream->getMetadata()['uri'] === 'php://memory') {
101  $this->response_builder = $this->fallback_response_builder;
102  }
103 
104  $r = $this->setGeneralHeaders(
105  $r,
106  $uri,
107  $mime_type ?? mime_content_type($uri),
108  $download_file_name,
110  );
111 
112  $r = $this->response_builder->buildForStream(
113  $this->http->request(),
114  $r,
115  $stream
116  );
117  $this->saveAndClose($r);
118  }
119 
120  public function deliverFromToken(string $token): never
121  {
122  // check if $token has a sub-request, such as .../index.html
123  $parts = explode(self::SUBREQUEST_SEPARATOR, $token);
124  $sub_request = null;
125  if (count($parts) > 1) {
126  $token = $parts[0];
127  $sub_request = implode('/', array_slice($parts, 1));
128  }
129 
130  $r = $this->http->response();
131  $payload = $this->data_signer->verifyStreamToken($token);
132 
133  switch (true) {
134  case $payload instanceof FilePayload:
135  $uri = $payload->getUri();
136  $mime_type = $payload->getMimeType();
137  $file_name = $payload->getFilename();
138  $disposition = Disposition::tryFrom($payload->getDisposition()) ?? Disposition::INLINE;
139  break;
140  case $payload instanceof ShortFilePayload:
141  $uri = $payload->getUri();
142  $mime_type = $this->determineMimeType($uri);
143  $file_name = $payload->getFilename();
144  $disposition = Disposition::INLINE;
145  break;
146  default:
147  $this->notFound($r);
148  }
149  unset($payload);
150 
151  // handle direct access to file
152 
153  if ($sub_request === null) {
154  $r = $this->setGeneralHeaders(
155  $r,
156  $uri,
157  $mime_type,
158  $file_name,
160  );
161 
162  $this->http->saveResponse(
163  $this->response_builder->buildForStream(
164  $this->http->request(),
165  $r,
166  Streams::ofResource(fopen($uri, 'rb'))
167  )
168  );
169  } else { // handle subrequest, aka file in a ZIP
170  $requested_zip = $uri;
171  $sub_request = urldecode($sub_request);
172  // remove query
173  $sub_request = explode('?', $sub_request)[0];
174 
175  try {
176  $file_inside_ZIP = Streams::ofFileInsideZIP($requested_zip, $sub_request);
177  } catch (\Throwable) {
178  $this->notFound($r);
179  }
180  $file_inside_zip_uri = $file_inside_ZIP->getMetadata()['uri'];
181  $file_inside_zip_stream = fopen($file_inside_zip_uri, 'rb');
182 
183  if ($file_inside_zip_stream === false) {
184  $this->notFound($r);
185  }
186 
187  // we must use PHPResponseBuilder here, because the streams inside zips cant be delivered using XSendFile or others
188  $this->response_builder = $this->fallback_response_builder;
189 
190  $mime_type = $this->determineMimeType($file_inside_zip_uri);
191  $r = $this->setGeneralHeaders(
192  $r,
193  $file_inside_zip_uri,
194  $mime_type,
195  basename($sub_request),
196  Disposition::INLINE // subrequests are always inline per default, browsers may change this to download
197  );
198 
199 
200  $this->http->saveResponse(
201  $this->response_builder->buildForStream(
202  $this->http->request(),
203  $r,
204  $file_inside_ZIP
205  )
206  );
207  }
208  $this->http->sendResponse();
209  $this->http->close();
210  }
211 
212  private function determineMimeType(string $filename): string
213  {
214  $suffix = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
215  if (isset($this->mime_type_map[$suffix])) {
216  if (is_array($this->mime_type_map[$suffix]) && isset($this->mime_type_map[$suffix][0])) {
217  return $this->mime_type_map[$suffix][0];
218  }
219 
220  return $this->mime_type_map[$suffix];
221  }
222 
223  $mime_type = mime_content_type($filename);
224  if ($mime_type === 'application/octet-stream') {
225  $mime_type = mime_content_type(substr($filename, 64));
226  }
227  return $mime_type ?: 'application/octet-stream';
228  }
229 }
setGeneralHeaders(ResponseInterface $r, string $uri, string $mime_type, string $file_name, Disposition $disposition=Disposition::INLINE)
deliver(FileStream $stream, string $download_file_name, ?string $mime_type=null, Disposition $disposition=Disposition::INLINE)
attached(FileStream $stream, string $download_file_name, ?string $mime_type=null)
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:61
if(count($parts) !=3) $payload
Definition: ltitoken.php:67
static ofFileInsideZIP(string $path_to_zip, string $path_inside_zip)
Definition: Streams.php:84
saveAndClose(ResponseInterface $r, ?string $path_to_delete=null)
$http
Definition: deliver.php:30
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
static ofResource($resource)
Wraps an already created resource with the stream abstraction.
Definition: Streams.php:64
static http()
Fetches the global http state from ILIAS.
__construct(private DataSigner $data_signer, Services $http, ResponseBuilder $response_builder, ResponseBuilder $fallback_response_builder,)
$token
Definition: xapitoken.php:70
$filename
Definition: buildRTE.php:78
__construct(Container $dic, ilPlugin $plugin)
The base interface for all filesystem streams.
Definition: FileStream.php:31
$r