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