]> src.bluestatic.org Git - isso.git/blob - Input.php
Fix a failing unit test to work around the file system being slow
[isso.git] / Input.php
1 <?php
2 /*=====================================================================*\
3 || ###################################################################
4 || # Blue Static ISSO Framework
5 || # Copyright (c)2002-2007 Iris Studios, Inc.
6 || #
7 || # This program is free software; you can redistribute it and/or modify
8 || # it under the terms of the GNU General Public License as published by
9 || # the Free Software Foundation; version 2 of the License.
10 || #
11 || # This program is distributed in the hope that it will be useful, but
12 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 || # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 || # more details.
15 || #
16 || # You should have received a copy of the GNU General Public License along
17 || # with this program; if not, write to the Free Software Foundation, Inc.,
18 || # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 || ###################################################################
20 \*=====================================================================*/
21
22 /**
23 * Input sanitizer (Input.php)
24 *
25 * @package ISSO
26 */
27
28 /**#@+
29 * Input cleaning type constant
30 */
31 /**
32 * Integer type
33 */
34 define('TYPE_INT', 1);
35
36 /**
37 * Unsigned integer
38 */
39 define('TYPE_UINT', 2);
40
41 /**
42 * Float type
43 */
44 define('TYPE_FLOAT', 4);
45
46 /**
47 * Boolean type
48 */
49 define('TYPE_BOOL', 8);
50
51 /**
52 * String - cleaned
53 */
54 define('TYPE_STR', 16);
55
56 /**
57 * String - deliberate unclean
58 */
59 define('TYPE_STRUN', 32);
60
61 /**
62 * No cleaning - here for use in API
63 */
64 define('TYPE_NONE', 64);
65
66 /**
67 * Macro for using DB->escape_binary() without cleaning - used in API
68 */
69 define('TYPE_BIN', 128);
70 /**#@-*/
71
72 /**
73 * Input Sanitizer
74 *
75 * This class is responsible for cleaning input.
76 *
77 * Constants:
78 * ISSO_CHECK_POST_REFERER - Will check to make sure that on POSTed
79 * data, the referer matches the host
80 *
81 * @author Blue Static
82 * @copyright Copyright (c)2002 - 2007, Blue Static
83 * @package ISSO
84 *
85 */
86 class BSInput
87 {
88 /**
89 * An array of sanitized variables that have been cleaned for HTML tag openers and double quotes
90 * @var array
91 */
92 public $in = array();
93
94 /**
95 * If we are running with magic_quotes_gpc on or off
96 * @var int
97 */
98 private $magicquotes = 0;
99
100 // ###################################################################
101 /**
102 * Constructor: set instance variables and execute input cleaning
103 */
104 public function __construct()
105 {
106 // magic quotes
107 $this->magicquotes = get_magic_quotes_gpc();
108 set_magic_quotes_runtime(0);
109
110 // some debug info that's always useful
111 BSApp::Debug('magic_quotes_gpc = ' . $this->magicquotes);
112 BSApp::Debug('register_globals = ' . ini_get('register_globals'));
113
114 $this->_sanitizeInputData();
115
116 if (defined('ISSO_CHECK_POST_REFERER'))
117 {
118 $this->_checkPostReferer();
119 }
120 }
121
122 // ###################################################################
123 /**
124 * Recursive XSS cleaner
125 *
126 * @param mixed Unsanitized REQUEST data
127 *
128 * @return mixed Sanitized data
129 */
130 private function _sanitizeDataRecursive($data)
131 {
132 foreach ($data AS $key => $value)
133 {
134 if (is_array($value))
135 {
136 $data["$key"] = $this->_sanitizeDataRecursive($value);
137 }
138 else
139 {
140 if ($this->magicquotes)
141 {
142 $value = str_replace("\'", "'", $value);
143 }
144 $data["$key"] = $this->sanitize($value);
145 }
146 }
147 return $data;
148 }
149
150 // ###################################################################
151 /**
152 * Simple way to protect against HTML attacks with Unicode support
153 *
154 * @param string Unsanitzed text
155 *
156 * @return string Properly protected text that only encodes potential threats
157 */
158 public function sanitize($text)
159 {
160 if ($this->magicquotes)
161 {
162 return str_replace(array('<', '>', '\"', '"'), array('&lt;', '&gt;', '&quot;', '&quot;'), $text);
163 }
164 else
165 {
166 return str_replace(array('<', '>', '"'), array('&lt;', '&gt;', '&quot;'), $text);
167 }
168 }
169
170 // ###################################################################
171 /**
172 * Unicode-safe entity encoding system; similar to sanitize()
173 *
174 * @param string Unsanitized text
175 *
176 * @return string Unicode-safe sanitized text with entities preserved
177 */
178 public function entityEncode($text)
179 {
180 $text = str_replace('&', '&amp;', $text);
181 $text = $this->sanitize($text);
182 return $text;
183 }
184
185 // ###################################################################
186 /**
187 * Takes text that has been processed for HTML and unsanitizes it
188 *
189 * @param string Text that needs to be turned back into HTML
190 *
191 * @return string Unsanitized text
192 */
193 public function unsanitize($text)
194 {
195 return str_replace(array('&lt;', '&gt;', '&quot;'), array('<', '>', '"'), $text);
196 }
197
198 // ###################################################################
199 /**
200 * Smart addslashes() that only applies itself it the Magic Quotes GPC
201 * is off. This should only be run on database query values that come
202 * from ISSO->in[] input; data that needs sanitization should be run
203 * through Db->escapeString()
204 *
205 * @param string Some string
206 * @param bool Force magic quotes to be off
207 *
208 * @return string String that has slashes added
209 */
210 public function escape($str, $force = true)
211 {
212 $db = BSApp::Registry()->getType('Db');
213 if ($this->magicquotes AND !$force)
214 {
215 if ($db)
216 {
217 return $db->escapeString(str_replace(array("\'", '\"'), array("'", '"'), $str));
218 }
219 return $str;
220 }
221 else
222 {
223 if ($db)
224 {
225 return $db->escapeString($str);
226 }
227 return addslashes($str);
228 }
229 }
230
231 // ###################################################################
232 /**
233 * Runs through all of the input data and sanitizes it.
234 */
235 private function _sanitizeInputData()
236 {
237 $this->in = $this->_sanitizeDataRecursive(array_merge($_GET, $_POST, $_COOKIE));
238 }
239
240 // ###################################################################
241 /**
242 * Sanitize function for something other than a string (which
243 * everything is sanitized for if you use _sanitizeInputData(). Cleaned
244 * data is placed back into Input->in; this makes it so you don't have
245 * to constantly intval() [etc.] data.
246 *
247 * @param array Array of elements to clean as varname => type
248 */
249 public function inputCleanArray($vars)
250 {
251 foreach ($vars AS $varname => $type)
252 {
253 $this->inputClean($varname, $type);
254 }
255 }
256
257 // ###################################################################
258 /**
259 * Sanitize function that does a single variable as oppoesd to an array
260 * (see inputCleanArray() for more details)
261 *
262 * @param string Variable name in ISSO->in[]
263 * @param integer Sanitization type constant
264 */
265 public function inputClean($varname, $type)
266 {
267 if (isset($this->in["$varname"]))
268 {
269 $this->in["$varname"] = $this->clean($this->in["$varname"], $type);
270 }
271 else
272 {
273 $this->in["$varname"] = $this->clean(null, $type);
274 }
275
276 return $this->in["$varname"];
277 }
278
279 // ###################################################################
280 /**
281 * Runs Input->escape() on a variable on Input->in[]. This is just a
282 * short-hand wrapper so that queries can be shortened. When this is used,
283 * the actual value in Input->in[] is not changed, only the return value
284 * is escaped.
285 *
286 * @param string Input variable
287 *
288 * @return string Escaped input
289 */
290 public function inputEscape($varname)
291 {
292 if (isset($this->in["$varname"]))
293 {
294 return $this->escape($this->in["$varname"]);
295 }
296 else
297 {
298 return $this->escape(null);
299 }
300 }
301
302 // ###################################################################
303 /**
304 * Cleaning function that does the work for inputClean(); this is
305 * moved here so it can be used to clean things that aren't in
306 * Input->in[]
307 *
308 * @param mixed Data
309 * @param integer Sanitization type constant
310 *
311 * @return mixed Cleaned data
312 */
313 public function clean($value, $type)
314 {
315 if (is_array($value))
316 {
317 return $this->_cleanArray($value, $type);
318 }
319
320 if ($type == TYPE_INT)
321 {
322 $value = intval($value);
323 }
324 else if ($type == TYPE_UINT)
325 {
326 $value = (($val = intval($value)) < 0 ? 0 : $val);
327 }
328 else if ($type == TYPE_FLOAT)
329 {
330 $value = floatval($value);
331 }
332 else if ($type == TYPE_BOOL)
333 {
334 $value = (bool)$value;
335 }
336 else if ($type == TYPE_STR)
337 {
338 $value = $value;
339 }
340 else if ($type == TYPE_STRUN)
341 {
342 $value = $this->unsanitize($value);
343 }
344 else if ($type == TYPE_NONE)
345 {
346 if ($this->magicquotes)
347 {
348 $value = str_replace(array('\"', "\'"), array('"', "'"), $value);
349 }
350 else
351 {
352 $value = $value;
353 }
354 }
355 else if ($type == TYPE_BIN)
356 {
357 $value = $value;
358 }
359 else
360 {
361 throw new Exception('Invalid clean type specified in BSInput::clean()');
362 return;
363 }
364
365 return $value;
366 }
367
368 // ###################################################################
369 /**
370 * Recursion function for Input->clean()
371 *
372 * @param array Uncleaned array
373 * @param integer Sanitization type constant
374 *
375 * @return array Cleaned array of data
376 */
377 private function _cleanArray($array, $type)
378 {
379 foreach ($array AS $key => $value)
380 {
381 $array["$key"] = $this->clean($value, $type);
382 }
383
384 return $array;
385 }
386
387 // ###################################################################
388 /**
389 * Returns the lowercase version of the HTTP method (post or get)
390 *
391 * @return string HTTP method
392 */
393 public function getHttpMethod()
394 {
395 $method = strtolower($_SERVER['REQUEST_METHOD']);
396 if (!in_array($method, array('get', 'post')))
397 {
398 throw new Exception('Invalid server request method: ' . $method);
399 }
400 return $method;
401 }
402
403 // ###################################################################
404 /**
405 * Checks to see if a POST refer is actually from us
406 */
407 private function _checkPostReferer()
408 {
409 if ($this->getHttpMethod() == 'post')
410 {
411 $host = ($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_ENV['HTTP_HOST'];
412
413 if ($host AND $_SERVER['HTTP_REFERER'])
414 {
415 $parts = parse_url($_SERVER['HTTP_REFERER']);
416 $ourhost = $parts['host'] . (isset($parts['port']) ? ":$parts[port]" : '');
417
418 if ($ourhost != $host)
419 {
420 throw new Exception('No external hosts are allowed to POST to this application');
421 exit;
422 }
423 BSApp::Debug('remote post check = ok');
424 }
425 else
426 {
427 BSApp::Debug('remote post check = FAILED');
428 }
429 }
430 }
431 }
432
433 ?>