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