Clear insurance debt from collections report (#4989)
[openemr.git] / library / formdata.inc.php
blob1a7868755f8604ca8c4916325cd0fb9b38120890
1 <?php
3 /**
4 * Functions to globally validate and prepare data for sql database insertion.
6 * @package OpenEMR
7 * @link https://www.open-emr.org
8 * @author Rod Roark <rod@sunsetsystems.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @copyright Copyright (c) 2009 Rod Roark <rod@sunsetsystems.com>
11 * @copyright Copyright (c) 2018 Brady Miller <brady.g.miller@gmail.com>
12 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
16 /**
17 * Escape a parameter to prepare for a sql query.
19 * @param string $s Parameter to be escaped.
20 * @return string Escaped parameter.
22 function add_escape_custom($s)
24 //prepare for safe mysql insertion
25 $s = mysqli_real_escape_string($GLOBALS['dbh'], ($s ?? ''));
26 return $s;
29 /**
30 * Escape a sql limit variable to prepare for a sql query.
32 * This will escape integers within the LIMIT ?, ? part of a sql query.
33 * Note that there is a maximum value to these numbers, which is why
34 * should only use for the LIMIT ? , ? part of the sql query and why
35 * this is centralized to a function (in case need to upgrade this
36 * function to support larger numbers in the future).
38 * @param string $s Limit variable to be escaped.
39 * @return string Escaped limit variable.
41 function escape_limit($s)
43 //prepare for safe mysql insertion
44 $s = (int)$s;
45 return $s;
48 /**
49 * Escape/sanitize a sql sort order keyword variable to prepare for a sql query.
51 * This will escape/sanitize the sort order keyword. It is done by whitelisting
52 * only certain keywords(asc,desc). If the keyword is illegal, then will default
53 * to asc.
55 * @param string $s Sort order keyword variable to be escaped/sanitized.
56 * @return string Escaped sort order keyword variable.
58 function escape_sort_order($s)
60 return escape_identifier(strtolower($s), array("asc","desc"));
63 /**
64 * If parameter string contains comma(,) delimeter
65 * Splits parameter string into an array, using comma(,) as delimeter
66 * else it returns original string
68 * @param string $s string to be processed
69 * @return array $columns an array formed by spliting $s with comma(,) delimeter
72 function process_cols_escape($s)
74 //returns an array of columns
75 $columns = explode(",", $s);
76 if (count($columns) > 1) {
77 return $columns;
80 return $s;
83 /**
84 * Escape/sanitize a table sql column name for a sql query..
86 * This will escape/sanitize the sql column name for a sql query. It is done by whitelisting
87 * all of the current sql column names in the openemr database from a table(s). Note that if
88 * there is no match, then it will die() and a error message will be sent to the screen and
89 * the error log. This function should not be used for escaping tables outside the openemr
90 * database (should use escape_identifier() function below for that scenario)
92 * @param string|array $s sql column name(s) variable to be escaped/sanitized.
93 * @param array $tables The table(s) that the sql columns is from (in an array).
94 * @param boolean $long Use long form (ie. table.colname) vs short form (ie. colname).
95 * @param boolean $throwException Whether to throw a SQL exception instead of dieing
96 * @return string Escaped table name variable.
98 function escape_sql_column_name($s, $tables, $long = false, $throwException = false)
100 // If $s is asterisk return asterisk to select all columns
101 if ($s === "*") {
102 return "*";
105 // If $s is an array process then use recursion to check each column
106 if (is_array($s)) {
107 $multiple_columns = [];
108 foreach ($s as $column) {
109 $multiple_columns[] = escape_sql_column_name(trim($column), $tables);
111 return implode(", ", $multiple_columns);
114 // If the $tables is empty, then process them all
115 if (empty($tables)) {
116 $res = sqlStatementNoLog("SHOW TABLES");
117 $tables = array();
118 while ($row = sqlFetchArray($res)) {
119 $keys_return = array_keys($row);
120 $tables[] = $row[$keys_return[0]];
124 // First need to escape the $tables
125 $tables_escaped = array();
126 foreach ($tables as $table) {
127 $tables_escaped[] = escape_table_name($table);
130 // Collect all the possible sql columns from the tables
131 $columns_options = array();
132 foreach ($tables_escaped as $table_escaped) {
133 $res = sqlStatementNoLog("SHOW COLUMNS FROM " . $table_escaped);
134 while ($row = sqlFetchArray($res)) {
135 if ($long) {
136 $columns_options[] = $table_escaped . "." . $row['Field'];
137 } else {
138 $columns_options[] = $row['Field'];
143 // Now can escape(via whitelisting) the sql column name
144 $dieIfNoMatch = !$throwException;
145 return escape_identifier($s, $columns_options, $dieIfNoMatch, true, $throwException);
149 * Escape/sanitize a table name for a sql query. This function can also can be used to
150 * process tables that contain any upper case letters.
152 * This will escape/sanitize the table name for a sql query. It is done by whitelisting
153 * all of the current tables in the openemr database. The matching is not case sensitive,
154 * although it will attempt a case sensitive match before proceeding to a case insensitive
155 * match (see below escape_identifier() function for more details on this). Note that if
156 * there is no match, then it will die() and a error message will be sent to the screen
157 * and the error log. This function should not be used for escaping tables outside the
158 * openemr database (should use escape_identifier() function below for that scenario).
159 * Another use of this function is to deal with casing issues that arise in tables that
160 * contain upper case letter(s) (these tables can be huge issues when transferring databases
161 * from Windows to Linux and vice versa); this function can avoid this issues if run the
162 * table name through this function (To avoid confusion, there is a wrapper function
163 * entitled mitigateSqlTableUpperCase() that is used when just need to mitigate casing
164 * for table names that contain any uppercase letters).
165 * @param string $s sql table name variable to be escaped/sanitized.
166 * @return string Escaped table name variable.
168 function escape_table_name($s)
170 $res = sqlStatementNoLog("SHOW TABLES");
171 $tables_array = array();
172 while ($row = sqlFetchArray($res)) {
173 $keys_return = array_keys($row);
174 $tables_array[] = $row[$keys_return[0]];
177 // Now can escape(via whitelisting) the sql table name
178 return escape_identifier($s, $tables_array, true, false);
182 * Process tables that contain any upper case letters; this is simple a wrapper function of
183 * escape_table_name() above when using it for the sole purpose of mitigating sql table names
184 * that contain upper case letters.
186 * @param string $s sql table name variable to be escaped/sanitized.
187 * @return string Escaped table name variable.
189 function mitigateSqlTableUpperCase($s)
191 return escape_table_name($s);
195 * Escape/sanitize a sql identifier variable to prepare for a sql query.
197 * This will escape/sanitize a sql identifier. There are two options provided by this
198 * function.
199 * The first option is done by whitelisting ($whitelist_items is array) and in this case
200 * only certain identifiers (listed in the $whitelist_items array) can be used; if
201 * there is no match, then it will either default to the first item in the $whitelist_items
202 * (if $die_if_no_match is FALSE) or it will die() and send an error message to the screen
203 * and log (if $die_if_no_match is TRUE). Note there is an option to allow case insensitive
204 * matching; if this option is chosen, it will first attempt a case sensitive match and if this
205 * fails, then attempt a case insensitive match.
206 * The second option is done by checking against a regex expression, which would use as a string
207 * in $whitelist_items (for example, 'a-zA-Z0-9_'). If $die_if_no_match is true, then will die
208 * if any illegal characters are found. If $die_if_no_match is false, then will remove the illegal
209 * characters and send back string of only the legal characters.
210 * The first option is ideal if all the possible identifiers are known, however we realize this
211 * may not always be the case.
213 * @param string $s Sql identifier variable to be escaped/sanitized.
214 * @param array/string $whitelist_items Items used in whitelisting method (See function description for details of whitelisting method).
215 * Standard use is to use a array. If use a string, then should be regex expression of allowed
216 * characters (for example 'a-zA-Z0-9_').
217 * @param boolean $die_if_no_match If there is no match in the whitelist, then die and echo an error to screen and log.
218 * @param boolean $case_sens_match Use case sensitive match (this is default).
219 * @param boolean $throw_exception_if_no_match If there is no match in the whitelist then throw an exception
220 * @return string Escaped/sanitized sql identifier variable.
222 function escape_identifier($s, $whitelist_items, $die_if_no_match = false, $case_sens_match = true, $throw_exception_if_no_match = false)
224 if (is_array($whitelist_items)) {
225 // Only return an item within the whitelist_items
226 $ok = $whitelist_items;
227 // First, search for case sensitive match
228 $key = array_search($s, $ok);
229 if ($key === false) {
230 // No match
231 if (!$case_sens_match) {
232 // Attempt a case insensitive match
233 $ok_UPPER = array_map("strtoupper", $ok);
234 $key = array_search(strtoupper($s), $ok_UPPER);
237 if ($key === false) {
238 // Still no match
239 if ($die_if_no_match) {
240 // No match and $die_if_no_match is set, so die() and send error messages to screen and log
241 error_Log("ERROR: OpenEMR SQL Escaping ERROR of the following string: " . errorLogEscape($s), 0);
242 die("<br /><span style='color:red;font-weight:bold;'>" . xlt("There was an OpenEMR SQL Escaping ERROR of the following string") . " " . text($s) . "</span><br />");
243 } else if ($throw_exception_if_no_match) {
244 throw new \OpenEMR\Common\Database\SqlQueryException("", "ERROR: OpenEMR SQL Escaping ERROR of the following string: " . errorLogEscape($s));
245 } else {
246 // Return first token since no match
247 $key = 0;
252 return $ok[$key];
253 } else {
254 if ($die_if_no_match) {
255 if (preg_match('/[^' . $whitelist_items . ']/', $s)) {
256 // Contains illegal character and $die_if_no_match is set, so die() and send error messages to screen and log
257 error_Log("ERROR: OpenEMR SQL Escaping ERROR of the following string: " . errorLogEscape($s), 0);
258 die("<br /><span style='color:red;font-weight:bold;'>" . xlt("There was an OpenEMR SQL Escaping ERROR of the following string") . " " . text($s) . "</span><br />");
259 } else if ($throw_exception_if_no_match) {
260 throw new \OpenEMR\Common\Database\SqlQueryException("", "ERROR: OpenEMR SQL Escaping ERROR of the following string: " . errorLogEscape($s));
261 } else {
262 // Contains all legal characters, so return the legal string
263 return $s;
265 } else {
266 // Since not using $die_if_no_match, then will remove the illegal characters and send back a legal string
267 return preg_replace('/[^' . $whitelist_items . ']/', '', $s);
273 * (Note this function is deprecated for new scripts and is only utilized to support legacy scripts)
274 * Function to manage POST, GET, and REQUEST variables.
276 * @param string $name name of the variable requested.
277 * @param string $type 'P', 'G' for post or get data, otherwise uses request.
278 * @param bool $istrim whether to use trim() on the data.
279 * @return string variable requested, or empty string
281 function formData($name, $type = 'P', $isTrim = false)
283 if ($type == 'P') {
284 $s = isset($_POST[$name]) ? $_POST[$name] : '';
285 } elseif ($type == 'G') {
286 $s = isset($_GET[$name]) ? $_GET[$name] : '';
287 } else {
288 $s = isset($_REQUEST[$name]) ? $_REQUEST[$name] : '';
291 return formDataCore($s, $isTrim);
295 * (Note this function is deprecated for new scripts and is only utilized to support legacy scripts)
296 * NEED TO KEEP THIS FUNCTION TO ENSURE LEGACY FORMS ARE SUPPORTED
297 * Core function that will be called by formData.
298 * Note it can also be called directly if preparing
299 * normal variables (not GET,POST, or REQUEST)
301 * @param string $s
302 * @param bool $istrim whether to use trim() on the data.
303 * @return string
305 function formDataCore($s, $isTrim = false)
307 //trim if selected
308 if ($isTrim) {
309 $s = trim($s);
312 //add escapes for safe database insertion
313 $s = add_escape_custom($s);
314 return $s;