These two files need to be swapped
[isso.git] / Input.php
1 <?php
2 /*=====================================================================*\
3 || ###################################################################
4 || # Blue Static ISSO Framework
5 || # Copyright (c)2005-2008 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 * @author Blue Static
78 * @copyright Copyright (c)2005 - 2008, Blue Static
79 * @package ISSO
80 *
81 */
82 class BSInput
83 {
84 /**
85 * An array of sanitized variables that have been cleaned for HTML tag openers and double quotes
86 * @var array
87 */
88 public $in = array();
89
90 /**
91 * If we are running with magic_quotes_gpc on or off
92 * @var int
93 */
94 private $magicquotes = 0;
95
96 // ###################################################################
97 /**
98 * Constructor: set instance variables and execute input cleaning
99 */
100 public function __construct()
101 {
102 // magic quotes
103 $this->magicquotes = get_magic_quotes_gpc();
104 set_magic_quotes_runtime(0);
105
106 // some debug info that's always useful
107 BSApp::debug('magic_quotes_gpc = ' . $this->magicquotes);
108 BSApp::debug('register_globals = ' . ini_get('register_globals'));
109
110 $this->_sanitizeInputData();
111 }
112
113 // ###################################################################
114 /**
115 * Recursive XSS cleaner
116 *
117 * @param mixed Unsanitized REQUEST data
118 *
119 * @return mixed Sanitized data
120 */
121 private function _sanitizeDataRecursive($data)
122 {
123 foreach ($data as $key => $value)
124 {
125 if (is_array($value))
126 {
127 $data["$key"] = $this->_sanitizeDataRecursive($value);
128 }
129 else
130 {
131 if ($this->magicquotes)
132 {
133 $value = str_replace("\'", "'", $value);
134 }
135 $data["$key"] = $this->sanitize($value);
136 }
137 }
138 return $data;
139 }
140
141 // ###################################################################
142 /**
143 * Simple way to protect against HTML attacks with Unicode support
144 *
145 * @param string Unsanitzed text
146 *
147 * @return string Properly protected text that only encodes potential threats
148 */
149 public function sanitize($text)
150 {
151 if ($this->magicquotes)
152 {
153 return str_replace(array('<', '>', '\"', '"'), array('&lt;', '&gt;', '&quot;', '&quot;'), $text);
154 }
155 else
156 {
157 return str_replace(array('<', '>', '"'), array('&lt;', '&gt;', '&quot;'), $text);
158 }
159 }
160
161 // ###################################################################
162 /**
163 * Unicode-safe entity encoding system; similar to sanitize()
164 *
165 * @param string Unsanitized text
166 *
167 * @return string Unicode-safe sanitized text with entities preserved
168 */
169 public function entityEncode($text)
170 {
171 $text = str_replace('&', '&amp;', $text);
172 $text = $this->sanitize($text);
173 return $text;
174 }
175
176 // ###################################################################
177 /**
178 * Takes text that has been processed for HTML and unsanitizes it
179 *
180 * @param string Text that needs to be turned back into HTML
181 *
182 * @return string Unsanitized text
183 */
184 public function unsanitize($text)
185 {
186 return str_replace(array('&lt;', '&gt;', '&quot;'), array('<', '>', '"'), $text);
187 }
188
189 // ###################################################################
190 /**
191 * Smart addslashes() that only applies itself it the Magic Quotes GPC
192 * is off. This should only be run on database query values that come
193 * from ISSO->in[] input; data that needs sanitization should be run
194 * through Db->escapeString()
195 *
196 * @param string Some string
197 * @param bool Force magic quotes to be off
198 *
199 * @return string String that has slashes added
200 */
201 public function escape($str, $force = true)
202 {
203 $db = BSApp::$db;
204 if ($this->magicquotes && !$force)
205 {
206 if ($db)
207 {
208 return $db->escapeString(str_replace(array("\'", '\"'), array("'", '"'), $str));
209 }
210 return $str;
211 }
212 else
213 {
214 if ($db)
215 {
216 return $db->escapeString($str);
217 }
218 return addslashes($str);
219 }
220 }
221
222 // ###################################################################
223 /**
224 * Runs through all of the input data and sanitizes it.
225 */
226 private function _sanitizeInputData()
227 {
228 $this->in = $this->_sanitizeDataRecursive(array_merge($_GET, $_POST, $_COOKIE));
229 }
230
231 // ###################################################################
232 /**
233 * Sanitize function for something other than a string (which
234 * everything is sanitized for if you use _sanitizeInputData(). Cleaned
235 * data is placed back into Input->in; this makes it so you don't have
236 * to constantly intval() [etc.] data.
237 *
238 * @param array Array of elements to clean as varname => type
239 */
240 public function inputCleanArray($vars)
241 {
242 foreach ($vars as $varname => $type)
243 {
244 $this->inputClean($varname, $type);
245 }
246 }
247
248 // ###################################################################
249 /**
250 * Sanitize function that does a single variable as oppoesd to an array
251 * (see inputCleanArray() for more details)
252 *
253 * @param string Variable name in ISSO->in[]
254 * @param integer Sanitization type constant
255 */
256 public function inputClean($varname, $type)
257 {
258 if (isset($this->in["$varname"]))
259 {
260 $this->in["$varname"] = $this->clean($this->in["$varname"], $type);
261 }
262 else
263 {
264 $this->in["$varname"] = $this->clean(null, $type);
265 }
266
267 return $this->in["$varname"];
268 }
269
270 // ###################################################################
271 /**
272 * Runs Input->escape() on a variable on Input->in[]. This is just a
273 * short-hand wrapper so that queries can be shortened. When this is used,
274 * the actual value in Input->in[] is not changed, only the return value
275 * is escaped.
276 *
277 * @param string Input variable
278 *
279 * @return string Escaped input
280 */
281 public function inputEscape($varname)
282 {
283 if (isset($this->in["$varname"]))
284 {
285 return $this->escape($this->in["$varname"]);
286 }
287 else
288 {
289 return $this->escape(null);
290 }
291 }
292
293 // ###################################################################
294 /**
295 * Cleaning function that does the work for inputClean(); this is
296 * moved here so it can be used to clean things that aren't in
297 * Input->in[]
298 *
299 * @param mixed Data
300 * @param integer Sanitization type constant
301 *
302 * @return mixed Cleaned data
303 */
304 public function clean($value, $type)
305 {
306 if (is_array($value))
307 {
308 return $this->_cleanArray($value, $type);
309 }
310
311 if ($type == TYPE_INT)
312 {
313 $value = intval($value);
314 }
315 else if ($type == TYPE_UINT)
316 {
317 $value = (($val = intval($value)) < 0 ? 0 : $val);
318 }
319 else if ($type == TYPE_FLOAT)
320 {
321 $value = floatval($value);
322 }
323 else if ($type == TYPE_BOOL)
324 {
325 $value = (bool)$value;
326 }
327 else if ($type == TYPE_STR)
328 {
329 $value = $value;
330 }
331 else if ($type == TYPE_STRUN)
332 {
333 $value = $this->unsanitize($value);
334 }
335 else if ($type == TYPE_NONE)
336 {
337 if ($this->magicquotes)
338 {
339 $value = str_replace(array('\"', "\'"), array('"', "'"), $value);
340 }
341 else
342 {
343 $value = $value;
344 }
345 }
346 else if ($type == TYPE_BIN)
347 {
348 $value = $value;
349 }
350 else
351 {
352 throw new Exception('Invalid clean type specified in BSInput::clean()');
353 return;
354 }
355
356 return $value;
357 }
358
359 // ###################################################################
360 /**
361 * Recursion function for Input->clean()
362 *
363 * @param array Uncleaned array
364 * @param integer Sanitization type constant
365 *
366 * @return array Cleaned array of data
367 */
368 private function _cleanArray($array, $type)
369 {
370 foreach ($array as $key => $value)
371 {
372 $array["$key"] = $this->clean($value, $type);
373 }
374
375 return $array;
376 }
377
378 // ###################################################################
379 /**
380 * Returns the lowercase version of the HTTP method (post or get)
381 *
382 * @return string HTTP method
383 */
384 public function getHttpMethod()
385 {
386 $method = strtolower($_SERVER['REQUEST_METHOD']);
387 if (!in_array($method, array('get', 'post')))
388 {
389 throw new Exception('Invalid server request method: ' . $method);
390 }
391 return $method;
392 }
393
394 // ###################################################################
395 /**
396 * Checks to see if a POST refer is actually from us
397 */
398 public function checkPostReferer()
399 {
400 if ($this->getHttpMethod() == 'post')
401 {
402 $host = ($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_ENV['HTTP_HOST'];
403
404 if ($host && $_SERVER['HTTP_REFERER'])
405 {
406 $parts = parse_url($_SERVER['HTTP_REFERER']);
407 $ourhost = $parts['host'] . (isset($parts['port']) ? ":$parts[port]" : '');
408
409 if ($ourhost != $host)
410 {
411 throw new Exception('No external hosts are allowed to POST to this application');
412 exit;
413 }
414 BSApp::debug('remote post check = ok');
415 }
416 else
417 {
418 BSApp::debug('remote post check = FAILED');
419 }
420 }
421 }
422 }
423
424 ?>