1
|
|
|
<?php |
2
|
|
|
namespace Elgg\Http; |
3
|
|
|
|
4
|
|
|
use Symfony\Component\HttpFoundation\Request as SymfonyRequest; |
5
|
|
|
use Elgg\Application; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Elgg HTTP request. |
9
|
|
|
* |
10
|
|
|
* @access private |
11
|
|
|
*/ |
12
|
|
|
class Request extends SymfonyRequest { |
13
|
|
|
|
14
|
|
|
const REWRITE_TEST_TOKEN = '__testing_rewrite'; |
15
|
|
|
const REWRITE_TEST_OUTPUT = 'success'; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Get the Elgg URL segments |
19
|
|
|
* |
20
|
|
|
* @param bool $raw If true, the segments will not be HTML escaped |
21
|
|
|
* |
22
|
|
|
* @return string[] |
23
|
|
|
*/ |
24
|
146 |
|
public function getUrlSegments($raw = false) { |
25
|
146 |
|
$path = trim($this->getElggPath(), '/'); |
26
|
146 |
|
if (!$raw) { |
27
|
137 |
|
$path = htmlspecialchars($path, ENT_QUOTES, 'UTF-8'); |
28
|
|
|
} |
29
|
146 |
|
if (!$path) { |
30
|
44 |
|
return []; |
31
|
|
|
} |
32
|
|
|
|
33
|
123 |
|
return explode('/', $path); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Get a cloned request with new Elgg URL segments |
38
|
|
|
* |
39
|
|
|
* @param string[] $segments URL segments |
40
|
|
|
* |
41
|
|
|
* @return Request |
42
|
|
|
*/ |
43
|
1 |
|
public function setUrlSegments(array $segments) { |
44
|
1 |
|
$base_path = trim($this->getBasePath(), '/'); |
45
|
1 |
|
$server = $this->server->all(); |
46
|
1 |
|
$server['REQUEST_URI'] = "$base_path/" . implode('/', $segments); |
47
|
|
|
|
48
|
1 |
|
return $this->duplicate(null, null, null, null, null, $server); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Get first Elgg URL segment |
53
|
|
|
* |
54
|
|
|
* @see \Elgg\Http\Request::getUrlSegments() |
55
|
|
|
* |
56
|
|
|
* @return string |
57
|
|
|
*/ |
58
|
12 |
|
public function getFirstUrlSegment() { |
59
|
12 |
|
$segments = $this->getUrlSegments(); |
60
|
12 |
|
if ($segments) { |
|
|
|
|
61
|
5 |
|
return array_shift($segments); |
62
|
|
|
} else { |
63
|
7 |
|
return ''; |
64
|
|
|
} |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Get the Request URI minus querystring |
69
|
|
|
* |
70
|
|
|
* @return string |
71
|
|
|
*/ |
72
|
156 |
|
public function getElggPath() { |
73
|
156 |
|
if (php_sapi_name() === 'cli-server') { |
74
|
|
|
$path = $this->getRequestUri(); |
75
|
|
|
} else { |
76
|
156 |
|
$path = $this->getPathInfo(); |
77
|
|
|
} |
78
|
156 |
|
return preg_replace('~(\?.*)$~', '', $path); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* {@inheritdoc} |
83
|
|
|
*/ |
84
|
427 |
|
public function getClientIp() { |
85
|
427 |
|
$ip = parent::getClientIp(); |
86
|
|
|
|
87
|
427 |
|
if ($ip == $this->server->get('REMOTE_ADDR')) { |
88
|
|
|
// try one more |
89
|
427 |
|
$ip_addresses = $this->server->get('HTTP_X_REAL_IP'); |
90
|
427 |
|
if ($ip_addresses) { |
91
|
1 |
|
$ip_addresses = explode(',', $ip_addresses); |
92
|
1 |
|
return array_pop($ip_addresses); |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
|
96
|
426 |
|
return $ip; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* {@inheritdoc} |
101
|
|
|
*/ |
102
|
102 |
|
public function isXmlHttpRequest() { |
103
|
102 |
|
return (strtolower($this->headers->get('X-Requested-With')) === 'xmlhttprequest' |
104
|
50 |
|
|| $this->query->get('X-Requested-With') === 'XMLHttpRequest' |
105
|
102 |
|
|| $this->request->get('X-Requested-With') === 'XMLHttpRequest'); |
106
|
|
|
// GET/POST check is necessary for jQuery.form and other iframe-based "ajax". #8735 |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Sniff the Elgg site URL with trailing slash |
111
|
|
|
* |
112
|
|
|
* @return string |
113
|
|
|
*/ |
114
|
9 |
|
public function sniffElggUrl() { |
115
|
9 |
|
$base_url = $this->getBaseUrl(); |
116
|
|
|
|
117
|
|
|
// baseURL may end with the PHP script |
118
|
9 |
|
if ('.php' === substr($base_url, -4)) { |
119
|
|
|
$base_url = dirname($base_url); |
120
|
|
|
} |
121
|
|
|
|
122
|
9 |
|
$base_url = str_replace('\\', '/', $base_url); |
123
|
|
|
|
124
|
9 |
|
return rtrim($this->getSchemeAndHttpHost() . $base_url, '/') . '/'; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Is the request for checking URL rewriting? |
129
|
|
|
* |
130
|
|
|
* @return bool |
131
|
|
|
*/ |
132
|
|
|
public function isRewriteCheck() { |
133
|
|
|
if ($this->getPathInfo() !== ('/' . self::REWRITE_TEST_TOKEN)) { |
134
|
|
|
return false; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
if (!$this->get(self::REWRITE_TEST_TOKEN)) { |
138
|
|
|
return false; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
return true; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Is PHP running the CLI server front controller |
146
|
|
|
* |
147
|
|
|
* @return bool |
148
|
|
|
*/ |
149
|
|
|
public function isCliServer() { |
150
|
|
|
return php_sapi_name() === 'cli-server'; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Is the request pointing to a file that the CLI server can handle? |
155
|
|
|
* |
156
|
|
|
* @param string $root Root directory |
157
|
|
|
* |
158
|
|
|
* @return bool |
159
|
|
|
*/ |
160
|
|
|
public function isCliServable($root) { |
161
|
|
|
$file = rtrim($root, '\\/') . $this->getElggPath(); |
162
|
|
|
if (!is_file($file)) { |
163
|
|
|
return false; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// http://php.net/manual/en/features.commandline.webserver.php |
167
|
|
|
$extensions = ".3gp, .apk, .avi, .bmp, .css, .csv, .doc, .docx, .flac, .gif, .gz, .gzip, .htm, .html, .ics, .jpe, .jpeg, .jpg, .js, .kml, .kmz, .m4a, .mov, .mp3, .mp4, .mpeg, .mpg, .odp, .ods, .odt, .oga, .ogg, .ogv, .pdf, .pdf, .png, .pps, .pptx, .qt, .svg, .swf, .tar, .text, .tif, .txt, .wav, .webm, .wmv, .xls, .xlsx, .xml, .xsl, .xsd, and .zip"; |
168
|
|
|
|
169
|
|
|
// The CLI server routes ALL requests here (even existing files), so we have to check for these. |
170
|
|
|
$ext = pathinfo($file, PATHINFO_EXTENSION); |
171
|
|
|
if (!$ext) { |
172
|
|
|
return false; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$ext = preg_quote($ext, '~'); |
176
|
|
|
return (bool) preg_match("~\\.{$ext}[,$]~", $extensions); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Returns an array of uploaded file objects regardless of upload status/errors |
181
|
|
|
* |
182
|
|
|
* @param string $input_name Form input name |
183
|
|
|
* @return UploadedFile[] |
184
|
|
|
*/ |
185
|
6 |
|
public function getFiles($input_name) { |
186
|
6 |
|
$files = $this->files->get($input_name, []); |
187
|
6 |
|
if (!is_array($files)) { |
188
|
2 |
|
$files = [$files]; |
189
|
|
|
} |
190
|
6 |
|
return $files; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Returns the first file found based on the input name |
195
|
|
|
* |
196
|
|
|
* @param string $input_name Form input name |
197
|
|
|
* @param bool $check_for_validity If there is an uploaded file, is it required to be valid |
198
|
|
|
* |
199
|
|
|
* @return UploadedFile|false |
|
|
|
|
200
|
|
|
*/ |
201
|
1 |
|
public function getFile($input_name, $check_for_validity = true) { |
202
|
1 |
|
$files = $this->getFiles($input_name); |
203
|
1 |
|
if (empty($files)) { |
204
|
|
|
return false; |
205
|
|
|
} |
206
|
|
|
|
207
|
1 |
|
$file = $files[0]; |
208
|
1 |
|
if (empty($file)) { |
209
|
|
|
return false; |
210
|
|
|
} |
211
|
|
|
|
212
|
1 |
|
if ($check_for_validity && !$file->isValid()) { |
213
|
|
|
return false; |
214
|
|
|
} |
215
|
|
|
|
216
|
1 |
|
return $file; |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.