1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Http;
9:
10: use Nette;
11: use Nette\Utils\Strings;
12:
13:
14: 15: 16:
17: class RequestFactory
18: {
19: use Nette\SmartObject;
20:
21:
22: const CHARS = '\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}';
23:
24:
25: public $urlFilters = [
26: 'path' => ['#/{2,}#' => '/'],
27: 'url' => [],
28: ];
29:
30:
31: private $binary = false;
32:
33:
34: private $proxies = [];
35:
36:
37: 38: 39: 40:
41: public function setBinary($binary = true)
42: {
43: $this->binary = (bool) $binary;
44: return $this;
45: }
46:
47:
48: 49: 50: 51:
52: public function setProxy($proxy)
53: {
54: $this->proxies = (array) $proxy;
55: return $this;
56: }
57:
58:
59: 60: 61: 62:
63: public function createHttpRequest()
64: {
65:
66: $url = new UrlScript;
67: $url->setScheme(!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http');
68: $url->setUser(isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '');
69: $url->setPassword(isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '');
70:
71:
72: if (
73: (isset($_SERVER[$tmp = 'HTTP_HOST']) || isset($_SERVER[$tmp = 'SERVER_NAME']))
74: && preg_match('#^([a-z0-9_.-]+|\[[a-f0-9:]+\])(:\d+)?\z#i', $_SERVER[$tmp], $pair)
75: ) {
76: $url->setHost(strtolower($pair[1]));
77: if (isset($pair[2])) {
78: $url->setPort((int) substr($pair[2], 1));
79: } elseif (isset($_SERVER['SERVER_PORT'])) {
80: $url->setPort((int) $_SERVER['SERVER_PORT']);
81: }
82: }
83:
84:
85: $requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
86: $requestUrl = preg_replace('#^\w++://[^/]++#', '', $requestUrl);
87: $requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']);
88: $tmp = explode('?', $requestUrl, 2);
89: $path = Url::unescape($tmp[0], '%/?#');
90: $path = Strings::fixEncoding(Strings::replace($path, $this->urlFilters['path']));
91: $url->setPath($path);
92: $url->setQuery(isset($tmp[1]) ? $tmp[1] : '');
93:
94:
95: $lpath = strtolower($path);
96: $script = isset($_SERVER['SCRIPT_NAME']) ? strtolower($_SERVER['SCRIPT_NAME']) : '';
97: if ($lpath !== $script) {
98: $max = min(strlen($lpath), strlen($script));
99: for ($i = 0; $i < $max && $lpath[$i] === $script[$i]; $i++);
100: $path = $i ? substr($path, 0, strrpos($path, '/', $i - strlen($path) - 1) + 1) : '/';
101: }
102: $url->setScriptPath($path);
103:
104:
105: $useFilter = (!in_array(ini_get('filter.default'), ['', 'unsafe_raw'], true) || ini_get('filter.default_flags'));
106:
107: $query = $url->getQueryParameters();
108: $post = $useFilter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? [] : $_POST);
109: $cookies = $useFilter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? [] : $_COOKIE);
110:
111:
112: $reChars = '#^[' . self::CHARS . ']*+\z#u';
113: if (!$this->binary) {
114: $list = [&$query, &$post, &$cookies];
115: while (list($key, $val) = @each($list)) {
116: foreach ($val as $k => $v) {
117: if (is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
118: unset($list[$key][$k]);
119:
120: } elseif (is_array($v)) {
121: $list[$key][$k] = $v;
122: $list[] = &$list[$key][$k];
123:
124: } else {
125: $list[$key][$k] = (string) preg_replace('#[^' . self::CHARS . ']+#u', '', $v);
126: }
127: }
128: }
129: unset($list, $key, $val, $k, $v);
130: }
131: $url->setQuery($query);
132:
133:
134:
135: $files = [];
136: $list = [];
137: if (!empty($_FILES)) {
138: foreach ($_FILES as $k => $v) {
139: if (
140: !is_array($v)
141: || !isset($v['name'], $v['type'], $v['size'], $v['tmp_name'], $v['error'])
142: || (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error()))
143: ) {
144: continue;
145: }
146: $v['@'] = &$files[$k];
147: $list[] = $v;
148: }
149: }
150:
151: while (list(, $v) = @each($list)) {
152: if (!isset($v['name'])) {
153: continue;
154:
155: } elseif (!is_array($v['name'])) {
156: if (!$this->binary && (!preg_match($reChars, $v['name']) || preg_last_error())) {
157: $v['name'] = '';
158: }
159: if ($v['error'] !== UPLOAD_ERR_NO_FILE) {
160: $v['@'] = new FileUpload($v);
161: }
162: continue;
163: }
164:
165: foreach ($v['name'] as $k => $foo) {
166: if (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
167: continue;
168: }
169: $list[] = [
170: 'name' => $v['name'][$k],
171: 'type' => $v['type'][$k],
172: 'size' => $v['size'][$k],
173: 'tmp_name' => $v['tmp_name'][$k],
174: 'error' => $v['error'][$k],
175: '@' => &$v['@'][$k],
176: ];
177: }
178: }
179:
180:
181:
182: if (function_exists('apache_request_headers')) {
183: $headers = apache_request_headers();
184: } else {
185: $headers = [];
186: foreach ($_SERVER as $k => $v) {
187: if (strncmp($k, 'HTTP_', 5) == 0) {
188: $k = substr($k, 5);
189: } elseif (strncmp($k, 'CONTENT_', 8)) {
190: continue;
191: }
192: $headers[strtr($k, '_', '-')] = $v;
193: }
194: }
195:
196: $remoteAddr = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
197: $remoteHost = !empty($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
198:
199:
200: $usingTrustedProxy = $remoteAddr && array_filter($this->proxies, function ($proxy) use ($remoteAddr) {
201: return Helpers::ipMatch($remoteAddr, $proxy);
202: });
203: if ($usingTrustedProxy) {
204: if (!empty($_SERVER['HTTP_FORWARDED'])) {
205: $forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']);
206: foreach ($forwardParams as $forwardParam) {
207: list($key, $value) = explode('=', $forwardParam, 2) + [1 => null];
208: $proxyParams[strtolower(trim($key))][] = trim($value, " \t\"");
209: }
210:
211: if (isset($proxyParams['for'])) {
212: $address = $proxyParams['for'][0];
213: if (strpos($address, '[') === false) {
214: $remoteAddr = explode(':', $address)[0];
215: } else {
216: $remoteAddr = substr($address, 1, strpos($address, ']') - 1);
217: }
218: }
219:
220: if (isset($proxyParams['host']) && count($proxyParams['host']) === 1) {
221: $host = $proxyParams['host'][0];
222: $startingDelimiterPosition = strpos($host, '[');
223: if ($startingDelimiterPosition === false) {
224: $remoteHostArr = explode(':', $host);
225: $remoteHost = $remoteHostArr[0];
226: if (isset($remoteHostArr[1])) {
227: $url->setPort((int) $remoteHostArr[1]);
228: }
229: } else {
230: $endingDelimiterPosition = strpos($host, ']');
231: $remoteHost = substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1);
232: $remoteHostArr = explode(':', substr($host, $endingDelimiterPosition));
233: if (isset($remoteHostArr[1])) {
234: $url->setPort((int) $remoteHostArr[1]);
235: }
236: }
237: }
238:
239: $scheme = (isset($proxyParams['proto']) && count($proxyParams['proto']) === 1) ? $proxyParams['proto'][0] : 'http';
240: $url->setScheme(strcasecmp($scheme, 'https') === 0 ? 'https' : 'http');
241: } else {
242: if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
243: $url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
244: $url->setPort($url->getScheme() === 'https' ? 443 : 80);
245: }
246:
247: if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
248: $url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
249: }
250:
251: if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
252: $xForwardedForWithoutProxies = array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function ($ip) {
253: return !array_filter($this->proxies, function ($proxy) use ($ip) {
254: return filter_var(trim($ip), FILTER_VALIDATE_IP) !== false && Helpers::ipMatch(trim($ip), $proxy);
255: });
256: });
257: $remoteAddr = trim(end($xForwardedForWithoutProxies));
258: $xForwardedForRealIpKey = key($xForwardedForWithoutProxies);
259: }
260:
261: if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
262: $xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
263: if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
264: $remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]);
265: }
266: }
267: }
268: }
269:
270:
271: $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null;
272: if (
273: $method === 'POST'
274: && isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
275: && preg_match('#^[A-Z]+\z#', $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
276: ) {
277: $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
278: }
279:
280:
281: $rawBodyCallback = function () {
282: return file_get_contents('php://input');
283: };
284:
285: return new Request($url, null, $post, $files, $cookies, $headers, $method, $remoteAddr, $remoteHost, $rawBodyCallback);
286: }
287: }
288: