Elgg  Version 2.2
 All Classes Namespaces Files Functions Variables Pages
ServeFileHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Application;
4 
5 use DateTime;
7 use Elgg\Config;
10 use Symfony\Component\HttpFoundation\BinaryFileResponse;
11 use Symfony\Component\HttpFoundation\Response;
13 
22 
26  private $crypto;
27 
31  private $config;
32 
39  public function __construct(\ElggCrypto $crypto, Config $config) {
40  $this->crypto = $crypto;
41  $this->config = $config;
42  }
43 
50  public function getResponse(Request $request) {
51 
52  $response = new Response();
53  $response->prepare($request);
54 
55  $path = implode('/', $request->getUrlSegments(true));
56  if (!preg_match('~serve-file/e(\d+)/l(\d+)/d([ia])/c([01])/([a-zA-Z0-9\-_]+)/(.*)$~', $path, $m)) {
57  return $response->setStatusCode(400)->setContent('Malformatted request URL');
58  }
59 
60  list(, $expires, $last_updated, $disposition, $use_cookie, $mac, $path_from_dataroot) = $m;
61 
62  if ($expires && $expires < time()) {
63  return $response->setStatusCode(403)->setContent('URL has expired');
64  }
65 
66  $hmac_data = array(
67  'expires' => (int) $expires,
68  'last_updated' => (int) $last_updated,
69  'disposition' => $disposition,
70  'path' => $path_from_dataroot,
71  'use_cookie' => (int) $use_cookie,
72  );
73  if ((bool) $use_cookie) {
74  $hmac_data['cookie'] = $this->getCookieValue($request);
75  }
76  ksort($hmac_data);
77 
78  $hmac = $this->crypto->getHmac($hmac_data);
79  if (!$hmac->matchesToken($mac)) {
80  return $response->setStatusCode(403)->setContent('HMAC mistmatch');
81  }
82 
83  // Path may have been encoded to avoid problems with special chars in URLs
84  if (0 === strpos($path_from_dataroot, ':')) {
85  $path_from_dataroot = Base64Url::decode(substr($path_from_dataroot, 1));
86  }
87 
88  $dataroot = $this->config->getDataPath();
89  $filenameonfilestore = "{$dataroot}{$path_from_dataroot}";
90 
91  if (!is_readable($filenameonfilestore)) {
92  return $response->setStatusCode(404)->setContent('File not found');
93  }
94 
95  $actual_last_updated = filemtime($filenameonfilestore);
96  if ($actual_last_updated != $last_updated) {
97  return $response->setStatusCode(403)->setContent('URL has expired');
98  }
99 
100  $if_none_match = $request->headers->get('if_none_match');
101  if (!empty($if_none_match)) {
102  // strip mod_deflate suffixes
103  $request->headers->set('if_none_match', str_replace('-gzip', '', $if_none_match));
104  }
105 
106  $etag = '"' . $actual_last_updated . '"';
107  $response->setPublic()->setEtag($etag);
108  if ($response->isNotModified($request)) {
109  return $response;
110  }
111 
112  $public = $use_cookie ? false : true;
113  $content_disposition = $disposition == 'i' ? 'inline' : 'attachment';
114 
115  $headers = [
116  'Content-Type' => (new MimeTypeDetector())->getType($filenameonfilestore),
117  ];
118  $response = new BinaryFileResponse($filenameonfilestore, 200, $headers, $public, $content_disposition);
119 
120  $sendfile_type = $this->config->getVolatile('X-Sendfile-Type');
121  if ($sendfile_type) {
122  $request->headers->set('X-Sendfile-Type', $sendfile_type);
123 
124  $mapping = (string)$this->config->getVolatile('X-Accel-Mapping');
125  $request->headers->set('X-Accel-Mapping', $mapping);
126 
127  $response->trustXSendfileTypeHeader();
128  }
129 
130  $response->prepare($request);
131 
132  if (empty($expires)) {
133  $expires = strtotime('+1 year');
134  }
135  $expires_dt = (new DateTime())->setTimestamp($expires);
136  $response->setExpires($expires_dt);
137 
138  $response->setEtag($etag);
139  return $response;
140  }
141 
148  private function getCookieValue(Request $request) {
149  $config = $this->config->getCookieConfig();
150  $session_name = $config['session']['name'];
151  return $request->cookies->get($session_name, '');
152  }
153 
154 }
Encode and decode Base 64 URL.
Definition: Base64Url.php:9
Elgg HTTP request.
Definition: Request.php:12
$m
Definition: metadata.php:11
__construct(\ElggCrypto $crypto, Config $config)
Constructor.
getResponse(Request $request)
Handle a request for a file.
$headers
Definition: default.php:14
$path
Definition: details.php:88
Access to configuration values.
Definition: Config.php:11
getUrlSegments($raw=false)
Get the Elgg URL segments.
Definition: Request.php:21
$mac
Definition: contents.php:14
$dataroot
Detect the MIME type of a file.