Elgg  Version master
ServeFileHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Application;
4 
5 use DateTime;
6 use Elgg\Config;
13 
20 
28  public function __construct(
29  protected HmacFactory $hmac,
30  protected Config $config,
31  protected MimeTypeService $mimetype
32  ) {
33  }
34 
42  public function getResponse(Request $request) {
43 
44  $response = new Response();
45  $response->prepare($request);
46 
47  $path = implode('/', $request->getUrlSegments(true));
48  if (!preg_match('~serve-file/e(\d+)/l(\d+)/d([ia])/c([01])/([a-zA-Z0-9\-_]+)/(.*)$~', $path, $m)) {
49  return $response->setStatusCode(400)->setContent('Malformatted request URL');
50  }
51 
52  list(, $expires, $last_updated, $disposition, $use_cookie, $mac, $path_from_dataroot) = $m;
53 
54  if ($expires && $expires < time()) {
55  return $response->setStatusCode(403)->setContent('URL has expired');
56  }
57 
58  // revert spaces from elgg_normalize_url()
59  // not using urldecode because it could replace to much
60  $path_from_dataroot = str_replace('%20', ' ', $path_from_dataroot);
61 
62  $hmac_data = [
63  'expires' => (int) $expires,
64  'last_updated' => (int) $last_updated,
65  'disposition' => $disposition,
66  'path' => $path_from_dataroot,
67  'use_cookie' => (int) $use_cookie,
68  ];
69  if ((bool) $use_cookie) {
70  $hmac_data['cookie'] = $this->getCookieValue($request);
71  }
72 
73  ksort($hmac_data);
74 
75  $hmac = $this->hmac->getHmac($hmac_data);
76  if (!$hmac->matchesToken($mac)) {
77  return $response->setStatusCode(403)->setContent('HMAC mismatch');
78  }
79 
80  // Path may have been encoded to avoid problems with special chars in URLs
81  if (str_starts_with($path_from_dataroot, ':')) {
82  $path_from_dataroot = Base64Url::decode(substr($path_from_dataroot, 1));
83  }
84 
85  $filenameonfilestore = "{$this->config->dataroot}{$path_from_dataroot}";
86 
87  if (!is_readable($filenameonfilestore)) {
88  return $response->setStatusCode(404)->setContent('File not found');
89  }
90 
91  $actual_last_updated = filemtime($filenameonfilestore);
92  if ($actual_last_updated != $last_updated) {
93  return $response->setStatusCode(403)->setContent('URL has expired');
94  }
95 
96  $if_none_match = $request->headers->get('if_none_match');
97  if (!empty($if_none_match)) {
98  // strip mod_deflate suffixes
99  $request->headers->set('if_none_match', str_replace('-gzip', '', $if_none_match));
100  }
101 
102  $etag = '"' . $actual_last_updated . '"';
103  $response->setPublic()->setEtag($etag);
104  if ($response->isNotModified($request)) {
105  return $response;
106  }
107 
108  $public = !(bool) $use_cookie;
109  $content_disposition = $disposition == 'i' ? 'inline' : 'attachment';
110 
111  $headers = [
112  'Content-Type' => $this->mimetype->getMimeType($filenameonfilestore),
113  'X-Content-Type-Options' => 'nosniff',
114  ];
115  $response = new BinaryFileResponse($filenameonfilestore, 200, $headers, $public, $content_disposition);
116 
117  $sendfile_type = $this->config->x_sendfile_type;
118  if ($sendfile_type) {
119  $request->headers->set('X-Sendfile-Type', $sendfile_type);
120 
121  $mapping = (string) $this->config->x_accel_mapping;
122  $request->headers->set('X-Accel-Mapping', $mapping);
123 
124  $response->trustXSendfileTypeHeader();
125  }
126 
127  $response->prepare($request);
128 
129  if (empty($expires)) {
130  $expires = strtotime('+1 year');
131  }
132 
133  $expires_dt = (new DateTime())->setTimestamp($expires);
134  $response->setExpires($expires_dt);
135 
136  $response->setEtag($etag);
137  return $response;
138  }
139 
147  private function getCookieValue(Request $request) {
148  $config = $this->config->getCookieConfig();
149  $session_name = $config['session']['name'];
150  return $request->cookies->get($session_name, '');
151  }
152 }
Elgg HTTP request.
Definition: Request.php:17
$response
Definition: content.php:10
$request
Definition: livesearch.php:12
getResponse(Request $request)
Handle a request for a file.
$config
Advanced site settings, debugging section.
Definition: debugging.php:6
$path
Definition: details.php:70
if(empty($entity_guid)||empty($recipient)||empty($muted_settings)||empty($hmac_token)) $hmac
Definition: mute.php:18
getUrlSegments(bool $raw=false)
Get the Elgg URL segments.
Definition: Request.php:241
$expires
__construct(protected HmacFactory $hmac, protected Config $config, protected MimeTypeService $mimetype)
Constructor.
Public service related to MIME type detection.
Provides a factory for HMAC objects.
Definition: HmacFactory.php:10