1<?php if (!defined('BASEPATH')) {
2    exit('No direct script access allowed');
3}
4/*
5 * LimeSurvey
6 * Copyright (C) 2007-2011 The LimeSurvey Project Team / Carsten Schmitz
7 * All rights reserved.
8 * License: GNU/GPL License v2 or later, see LICENSE.php
9 * LimeSurvey is free software. This version may have been modified pursuant
10 * to the GNU General Public License, and as distributed it includes or
11 * is derivative of works licensed under the GNU General Public License or
12 * other free or open source software licenses.
13 * See COPYRIGHT.php for copyright notices and details.
14 *
15 */
16/*
17  *
18 * Copyright (c) 2002,2003 Free Software Foundation
19 * developed under the custody of the
20 * Open Web Application Security Project
21 * (http://www.owasp.org)
22 *
23 * This file is part of the PHP Filters.
24 * PHP Filters is free software; you can redistribute it and/or modify it
25 * under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 2 of the License, or
27 * (at your option) any later version.
28 *
29 * PHP Filters is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
32 * See the GNU General Public License for more details.
33 *
34 * If you are not able to view the LICENSE, which should
35 * always be possible within a valid and working PHP Filters release,
36 * please write to the Free Software Foundation, Inc.,
37 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
38 * to get a copy of the GNU General Public License or to report a
39 * possible license violation.
40 */
41///////////////////////////////////////
42// sanitize.inc.php
43// Sanitization functions for PHP
44// by: Gavin Zuchlinski, Jamie Pratt, Hokkaido
45// webpage: http://libox.net
46// Last modified: December 21, 2003
47//
48// Many thanks to those on the webappsec list for helping me improve these functions
49///////////////////////////////////////
50// Function list:
51// sanitize_paranoid_string($string) -- input string, returns string stripped of all non
52//           alphanumeric
53// sanitize_system_string($string) -- input string, returns string stripped of special
54//           characters
55// sanitize_html_string($string) -- input string, returns string with html replacements
56//           for special characters
57// sanitize_int($integer) -- input integer, returns ONLY the integer (no extraneous
58//           characters
59// sanitize_float($float) -- input float, returns ONLY the float (no extraneous
60//           characters)
61// sanitize($input, $flags) -- input any variable, performs sanitization
62//           functions specified in flags. flags can be bitwise
63//           combination of PARANOID, SQL, SYSTEM, HTML, INT, FLOAT, LDAP,
64//           UTF8
65// sanitize_user($string) -- total length check (and more ??)
66// sanitize_userfullname($string) -- total length check (and more ??)
67//
68//
69///////////////////////////////////////
70//
71// 20031121 jp - added defines for magic_quotes and register_globals, added ; to replacements
72//               in sanitize_sql_string() function, created rudimentary testing pages
73// 20031221 gz - added nice_addslashes and changed sanitize_sql_string to use it
74// 20070213 lemeur - marked sanitize_sql_string as obsolete, should use db_quote instead
75// 20071032 lemeur - added sanitize_user and sanitize_userfullname
76//
77/////////////////////////////////////////
78
79define("PARANOID", 1);
80//define("SQL", 2);
81define("SYSTEM", 4);
82define("HTML", 8);
83define("INT", 16);
84define("FLOAT", 32);
85define("LDAP", 64);
86define("UTF8", 128);
87
88// get magic_quotes_gpc ini setting - jp
89$magic_quotes = (bool) @ini_get('magic_quotes_gpc');
90if ($magic_quotes == true) { define("MAGIC_QUOTES", 1); } else { define("MAGIC_QUOTES", 0); }
91
92// addslashes wrapper to check for gpc_magic_quotes - gz
93function nice_addslashes($string)
94{
95    // if magic quotes is on the string is already quoted, just return it
96    if (MAGIC_QUOTES) {
97        return $string;
98    } else {
99        return addslashes($string);
100    }
101    }
102
103
104/**
105 * Function: sanitize_filename
106 * Returns a sanitized string, typically for URLs.
107 *
108 * Parameters:
109 *     $string - The string to sanitize.
110 *     $force_lowercase - Force the string to lowercase?
111 *     $alphanumeric - If set to *true*, will remove all non-alphanumeric characters.
112 */
113function sanitize_filename($filename, $force_lowercase = true, $alphanumeric = false, $beautify = true)
114{
115    // sanitize filename
116    $filename = preg_replace(
117        '~
118        [<>:"/\\|?*]|
119        [\x00-\x1F]|
120        [\x7F\xA0\xAD]|
121        [#\[\]@!$&\'()+,;=]|
122        [{}^\~`]
123        ~x',
124        '-', $filename);
125    // Removes smart quotes
126    $filename = str_replace(array("\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c", "\xe2\x80\x9d", "\xe2\x80\x93", "\xe2\x80\x94", "\xe2\x80\xa6"), array('','', '', '', '-', '--','...'), $filename);
127    // avoids ".", ".." or ".hiddenFiles"
128    $filename = ltrim($filename, '.-');
129    // optional beautification
130    if ($beautify) {
131        $filename = beautify_filename($filename);
132    }
133    // maximise filename length to 255 bytes http://serverfault.com/a/9548/44086
134    $ext = pathinfo($filename, PATHINFO_EXTENSION);
135    $filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)).($ext ? '.'.$ext : '');
136    $filename = ($alphanumeric) ? preg_replace("/[^a-zA-Z0-9]/", "", $filename) : $filename;
137
138    if ($force_lowercase) {
139        $filename=mb_strtolower($filename, 'UTF-8');
140    }
141    // At the end of the process there are sometimes question marks left from non-UTF-8 characters
142    $filename = str_replace('?', '', $filename);
143    return $filename;
144}
145
146/**
147 * @param string $filename
148 */
149function beautify_filename($filename)
150{
151    // reduce consecutive characters
152    $filename = preg_replace(array(
153        // "file   name.zip" becomes "file-name.zip"
154        '/ +/',
155        // "file___name.zip" becomes "file-name.zip"
156        '/_+/',
157        // "file---name.zip" becomes "file-name.zip"
158        '/-+/'
159    ), '-', $filename);
160    $filename = preg_replace(array(
161        // "file--.--.-.--name.zip" becomes "file.name.zip"
162        '/-*\.-*/',
163        // "file...name..zip" becomes "file.name.zip"
164        '/\.{2,}/'
165    ), '.', $filename);
166    // lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
167    $filename = mb_strtolower($filename, mb_detect_encoding($filename));
168    // ".file-name.-" becomes "file-name"
169    $filename = trim($filename, '.-');
170    return $filename;
171}
172
173
174
175/**
176 * Function: sanitize_dirname
177 * sanitizes a string that will be used as a directory name
178 *
179 * Parameters:
180 *     $string - The string to sanitize.
181 *     $force_lowercase - Force the string to lowercase?
182 *     $alphanumeric - If set to *true*, will remove all non-alphanumeric characters.
183 */
184
185function sanitize_dirname($string, $force_lowercase = false, $alphanumeric = false)
186{
187    $string = str_replace(".", "", $string);
188    return sanitize_filename($string, $force_lowercase, $alphanumeric, false);
189}
190
191
192// paranoid sanitization -- only let the alphanumeric set through
193function sanitize_paranoid_string($string, $min = '', $max = '')
194{
195    if (isset($string)) {
196        $string = preg_replace("/[^_.a-zA-Z0-9]/", "", $string);
197        $len = strlen($string);
198        if ((($min != '') && ($len < $min)) || (($max != '') && ($len > $max))) {
199                return false;
200        }
201        return $string;
202    }
203}
204
205function sanitize_cquestions($string, $min = '', $max = '')
206{
207    if (isset($string)) {
208        $string = preg_replace("/[^_.a-zA-Z0-9+#]/", "", $string);
209        $len = strlen($string);
210        if ((($min != '') && ($len < $min)) || (($max != '') && ($len > $max))) {
211                return false;
212        }
213        return $string;
214    }
215}
216
217// sanitize a string in prep for passing a single argument to system() (or similar)
218function sanitize_system_string($string, $min = '', $max = '')
219{
220    if (isset($string)) {
221        $pattern = '/(;|\||`|>|<|&|^|"|'."\n|\r|'".'|{|}|[|]|\)|\()/i'; // no piping, passing possible environment variables ($),
222        // separate commands, nested execution, file redirection,
223        // background processing, special commands (backspace, etc.), quotes
224        // newlines, or some other special characters
225        $string = preg_replace($pattern, '', $string);
226        $string = '"'.preg_replace('/\$/', '\\\$', $string).'"'; //make sure this is only interpretted as ONE argument
227        $len = strlen($string);
228        if ((($min != '') && ($len < $min)) || (($max != '') && ($len > $max))) {
229            return false;
230        }
231        return $string;
232    }
233}
234
235function sanitize_xss_string($string)
236{
237    if (isset($string)) {
238        $bad = array('*', '^', '&', ';', '\"', '(', ')', '%', '$', '?');
239        return str_replace($bad, '', $string);
240    }
241}
242
243
244
245// sanitize a string for SQL input (simple slash out quotes and slashes)
246function sanitize_sql_db_tablename($string)
247{
248    $bad = array('*', '^', '&', '\'', '-', ';', '\"', '(', ')', '%', '$', '?');
249    return str_replace($bad, "", $string);
250}
251
252// sanitize a string for SQL input (simple slash out quotes and slashes)
253function sanitize_ldap_string($string, $min = '', $max = '')
254{
255    $pattern = '/(\)|\(|\||&)/';
256    $len = strlen($string);
257    if ((($min != '') && ($len < $min)) || (($max != '') && ($len > $max))) {
258        return false;
259    }
260    return preg_replace($pattern, '', $string);
261}
262
263
264// sanitize a string for HTML (make sure nothing gets interpretted!)
265function sanitize_html_string($string)
266{
267    $pattern[0] = '/\&/';
268    $pattern[1] = '/</';
269    $pattern[2] = "/>/";
270    $pattern[3] = '/\n/';
271    $pattern[4] = '/"/';
272    $pattern[5] = "/'/";
273    $pattern[6] = "/%/";
274    $pattern[7] = '/\(/';
275    $pattern[8] = '/\)/';
276    $pattern[9] = '/\+/';
277    $pattern[10] = '/-/';
278    $replacement[0] = '&amp;';
279    $replacement[1] = '&lt;';
280    $replacement[2] = '&gt;';
281    $replacement[3] = '<br />';
282    $replacement[4] = '&quot;';
283    $replacement[5] = '&#39;';
284    $replacement[6] = '&#37;';
285    $replacement[7] = '&#40;';
286    $replacement[8] = '&#41;';
287    $replacement[9] = '&#43;';
288    $replacement[10] = '&#45;';
289    return preg_replace($pattern, $replacement, $string);
290}
291
292// make int int!
293function sanitize_int($integer, $min = '', $max = '')
294{
295    $int = preg_replace("#[^0-9]#", "", $integer);
296    if ((($min != '') && ($int < $min)) || (($max != '') && ($int > $max))) {
297        return false;
298    }
299    if ($int == '') {
300        return null;
301    }
302    return $int;
303}
304
305// sanitize a username
306// TODO: define the exact format of the username
307// allow for instance 0-9a-zA-Z@_-.
308/**
309 * @param string $string
310 */
311function sanitize_user($string)
312{
313    $username_length = 64;
314    $string = mb_substr($string, 0, $username_length);
315    return $string;
316}
317
318// sanitize a username
319// TODO: define the exact format of the username
320// allow for instance 0-9a-zA-Z@_-.
321function sanitize_userfullname($string)
322{
323    $username_length = 50;
324    $string = mb_substr($string, 0, $username_length);
325    return $string;
326}
327
328function sanitize_labelname($string)
329{
330    $labelname_length = 100;
331    $string = mb_substr($string, 0, $labelname_length);
332    return $string;
333}
334
335// make float float!
336function sanitize_float($float, $min = '', $max = '')
337{
338    $float = str_replace(',', '.', $float);
339    // GMP library allows for high precision and high value numbers
340    if (function_exists('gmp_init') && defined('GMP_VERSION') && version_compare(GMP_VERSION, '4.3.2') == 1) {
341        $gNumber = gmp_init($float);
342        if (($min != '' && gmp_cmp($gNumber, $min) < 0) || ($max != '' && gmp_cmp($gNumber, $max) > 0)) {
343            return false;
344        } else {
345            return gmp_strval($gNumber);
346        }
347    } else {
348        $fNumber = str_replace(',', '.', $float);
349        $fNumber = floatval($fNumber);
350        if ((($min != '') && ($fNumber < $min)) || (($max != '') && ($fNumber > $max))) {
351                    return false;
352        }
353        return $fNumber;
354    }
355}
356
357
358// glue together all the other functions
359function sanitize($input, $flags, $min = '', $max = '')
360{
361    if ($flags & PARANOID) {
362        $input = sanitize_paranoid_string($input, $min, $max);
363    }
364    if ($flags & INT) {
365        $input = sanitize_int($input, $min, $max);
366    }
367    if ($flags & FLOAT) {
368        $input = sanitize_float($input, $min, $max);
369    }
370    if ($flags & HTML) {
371        $input = sanitize_html_string($input);
372    }
373    if ($flags & LDAP) {
374        $input = sanitize_ldap_string($input, $min, $max);
375    }
376    if ($flags & SYSTEM) {
377        $input = sanitize_system_string($input, $min, $max);
378    }
379    return $input;
380}
381
382function check_paranoid_string($input, $min = '', $max = '')
383{
384    if ($input != sanitize_paranoid_string($input, $min, $max)) {
385        return false;
386    }
387    return true;
388}
389
390function check_int($input, $min = '', $max = '')
391{
392    if ($input != sanitize_int($input, $min, $max)) {
393        return false;
394    }
395    return true;
396}
397
398function check_float($input, $min = '', $max = '')
399{
400    if ($input != sanitize_float($input, $min, $max)) {
401        return false;
402    }
403    return true;
404}
405
406function check_html_string($input, $min = '', $max = '')
407{
408    if ($input != sanitize_html_string($input)) {
409            return false;
410    }
411    return true;
412}
413
414
415function check_ldap_string($input, $min = '', $max = '')
416{
417    if ($input != sanitize_string($input, $min, $max)) {
418        return false;
419    }
420    return true;
421}
422
423function check_system_string($input, $min = '', $max = '')
424{
425    if ($input != sanitize_system_string($input, $min, $max)) {
426            return false;
427    }
428    return true;
429}
430
431// glue together all the other functions
432function check($input, $flags, $min = '', $max = '')
433{
434    $oldput = $input;
435    if ($flags & UTF8) {
436        $input = my_utf8_decode($input);
437    }
438    if ($flags & PARANOID) {
439        $input = sanitize_paranoid_string($input, $min, $max);
440    }
441    if ($flags & INT) {
442        $input = sanitize_int($input, $min, $max);
443    }
444    if ($flags & FLOAT) {
445        $input = sanitize_float($input, $min, $max);
446    }
447    if ($flags & HTML) {
448        $input = sanitize_html_string($input);
449    }
450    if ($flags & LDAP) {
451        $input = sanitize_ldap_string($input, $min, $max);
452    }
453    if ($flags & SYSTEM) {
454        $input = sanitize_system_string($input, $min, $max);
455    }
456    if ($input != $oldput) {
457        return false;
458    }
459    return true;
460}
461
462function sanitize_languagecode($codetosanitize)
463{
464    return preg_replace('/[^a-z0-9-]/i', '', $codetosanitize);
465}
466
467/**
468 * @param string $codestringtosanitize
469 */
470function sanitize_languagecodeS($codestringtosanitize)
471{
472    $codearray = explode(" ", trim($codestringtosanitize));
473    $codearray = array_map("sanitize_languagecode", $codearray);
474    return implode(" ", $codearray);
475}
476
477
478function sanitize_signedint($integer, $min = '', $max = '')
479{
480    $int = (int) $integer;
481
482    if ((($min != '') && ($int < $min)) || (($max != '') && ($int > $max))) {
483        return false; // Oops! Outside limits.
484    }
485
486    return $int;
487};
488