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