Browse Source

Fixed a bug with Database::prepare and ? in user input (#38 #214 #196)

Tobias Reich 9 years ago
parent
commit
85c18290e6
1 changed files with 46 additions and 3 deletions
  1. 46 3
      php/modules/Database.php

+ 46 - 3
php/modules/Database.php

@@ -258,18 +258,61 @@ if(!defined('LYCHEE')) exit('Error: Direct access is not allowed!');
 		# Check dependencies
 		Module::dependencies(isset($database, $query, $data));
 
+		# Count the number of placeholders and compare it with the number of arguments
+		# If it doesn't match, calculate the difference and skip this number of placeholders before starting the replacement
+		# This avoids problems with placeholders in user-input
+		# $skip = Number of placeholders which need to be skipped
+		$skip	= 0;
+		$num	= array(
+			'placeholder'	=> substr_count($query, '?'),
+			'data'			=> count($data)
+		);
+
+		if (($num['data']-$num['placeholder'])<0) Log::notice($database, __METHOD__, __LINE__, 'Could not completely prepare query. Query has more placeholders than values.');
+
 		foreach ($data as $value) {
 
 			# Escape
 			$value = mysqli_real_escape_string($database, $value);
 
+			# Recalculate number of placeholders
+			$num['placeholder'] = substr_count($query, '?');
+
+			# Calculate number of skips
+			if ($num['placeholder']>$num['data']) $skip = $num['placeholder'] - $num['data'];
+
+			if ($skip>0) {
+
+				# Need to skip $skip placeholders, because the user input contained placeholders
+				# Calculate a substring which does not contain the user placeholders
+				# 1 or -1 is the length of the placeholder (placeholder = ?)
+
+				$pos = -1;
+				for ($i=$skip; $i>0; $i--) $pos = strpos($query, '?', $pos + 1);
+				$pos++;
+
+				$temp	= substr($query, 0, $pos); # First part of $query
+				$query	= substr($query, $pos); # Last part of $query
+
+			}
+
 			# Replace
 			$query = preg_replace('/\?/', $value, $query, 1);
 
-		}
+			if ($skip>0) {
 
-		# Add semicolon at the end
-		$query .= ';';
+				# Reassemble the parts of $query
+				$query = $temp . $query;
+
+			}
+
+			# Reset skip
+			$skip = 0;
+
+			# Decrease number of data elements
+			$num['data']--;
+
+		}
 
 		return $query;