Merge pull request #2409 from chernjie/develop

apache_request_headers need not go through recapitalization of incoming headers
diff --git a/application/config/user_agents.php b/application/config/user_agents.php
index 35c36cb..0aae987 100644
--- a/application/config/user_agents.php
+++ b/application/config/user_agents.php
@@ -208,13 +208,15 @@
 $robots = array(
 	'googlebot'		=> 'Googlebot',
 	'msnbot'		=> 'MSNBot',
+	'baiduspider'	=> 'Baiduspider',
 	'bingbot'		=> 'Bing',
 	'slurp'			=> 'Inktomi Slurp',
 	'yahoo'			=> 'Yahoo',
 	'askjeeves'		=> 'AskJeeves',
 	'fastcrawler'	=> 'FastCrawler',
 	'infoseek'		=> 'InfoSeek Robot 1.0',
-	'lycos'			=> 'Lycos'
+	'lycos'			=> 'Lycos',
+	'yandex'		=> 'YandexBot'
 );
 
 /* End of file user_agents.php */
diff --git a/composer.json b/composer.json
index e21aaed..2971576 100644
--- a/composer.json
+++ b/composer.json
@@ -1,9 +1,10 @@
 {
-    "name" : "ellislab/codeigniter",
-    "require": {
-        "php": ">=5.2.4"
-    },
-    "require-dev": {
-      "mikey179/vfsStream": "*"
-		}
+	"description" : "Dependencies for CodeIgniter's testing environment",
+	"name" : "ellislab/codeigniter",
+	"require": {
+		"php": ">=5.2.4"
+	},
+	"require-dev": {
+		"mikey179/vfsStream": "*"
+	}
 }
\ No newline at end of file
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
index 7f76977..3fe5c06 100644
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -241,12 +241,12 @@
 	// Load the local application controller
 	// Note: The Router class automatically validates the controller path using the router->_validate_request().
 	// If this include fails it means that the default controller in the Routes.php file is not resolving to something valid.
-	if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'))
+	if ( ! file_exists(APPPATH.'controllers/'.$RTR->directory.$RTR->class.'.php'))
 	{
 		show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
 	}
 
-	include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');
+	include(APPPATH.'controllers/'.$RTR->directory.$RTR->class.'.php');
 
 	// Set a mark point for benchmarking
 	$BM->mark('loading_time:_base_classes_end');
@@ -260,8 +260,8 @@
  *  loader class can be called via the URI, nor can
  *  controller functions that begin with an underscore.
  */
-	$class  = $RTR->fetch_class();
-	$method = $RTR->fetch_method();
+	$class	= $RTR->class;
+	$method	= $RTR->method;
 
 	if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
 	{
diff --git a/system/core/Output.php b/system/core/Output.php
index 3320ae1..06d7a86 100644
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -841,9 +841,8 @@
 			$output = substr_replace($output, '', 0, $pos);
 
 			// Remove closing tag and save it for later
-			$end_pos = strlen($output);
 			$pos = strpos($output, '</');
-			$closing_tag = substr($output, $pos, $end_pos);
+			$closing_tag = substr($output, $pos, strlen($output));
 			$output = substr_replace($output, '', $pos);
 		}
 
@@ -852,7 +851,16 @@
 
 		// Remove spaces around curly brackets, colons,
 		// semi-colons, parenthesis, commas
-		$output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!i', '$1', $output);
+		$chunks = preg_split('/([\'|"]).+(?![^\\\]\\1)\\1/iU', $output, -1, PREG_SPLIT_OFFSET_CAPTURE);
+		for ($i = count($chunks) - 1; $i >= 0; $i--)
+		{
+			$output = substr_replace(
+				$output,
+				preg_replace('/\s*(:|;|,|}|{|\(|\))\s*/i', '$1', $chunks[$i][0]),
+				$chunks[$i][1],
+				strlen($chunks[$i][0])
+			);
+		}
 
 		// Replace tabs with spaces
 		// Replace carriage returns & multiple new lines with single new line
diff --git a/system/core/Router.php b/system/core/Router.php
index bb0ce16..c86ab9c 100644
--- a/system/core/Router.php
+++ b/system/core/Router.php
@@ -119,16 +119,16 @@
 			if (isset($_GET[$this->config->item('directory_trigger')]) && is_string($_GET[$this->config->item('directory_trigger')]))
 			{
 				$this->set_directory(trim($this->uri->_filter_uri($_GET[$this->config->item('directory_trigger')])));
-				$segments[] = $this->fetch_directory();
+				$segments[] = $this->directory;
 			}
 
 			$this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')])));
-			$segments[] = $this->fetch_class();
+			$segments[] = $this->class;
 
 			if ( ! empty($_GET[$this->config->item('function_trigger')]) && is_string($_GET[$this->config->item('function_trigger')]))
 			{
 				$this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')])));
-				$segments[] = $this->fetch_method();
+				$segments[] = $this->method;
 			}
 		}
 
@@ -270,7 +270,7 @@
 				empty($segments[1]) OR $segments[1] = str_replace('-', '_', $segments[1]);
 
 				// Does the requested controller exist in the sub-folder?
-				if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php'))
+				if ( ! file_exists(APPPATH.'controllers/'.$this->directory.$segments[0].'.php'))
 				{
 					if ( ! empty($this->routes['404_override']))
 					{
@@ -279,7 +279,7 @@
 					}
 					else
 					{
-						show_404($this->fetch_directory().$segments[0]);
+						show_404($this->directory.$segments[0]);
 					}
 				}
 			}
@@ -287,7 +287,7 @@
 			{
 				// Is the method being specified in the route?
 				$segments = explode('/', $this->default_controller);
-				if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php'))
+				if ( ! file_exists(APPPATH.'controllers/'.$this->directory.$segments[0].'.php'))
 				{
 					$this->directory = '';
 				}
@@ -413,6 +413,7 @@
 	/**
 	 * Fetch the current class
 	 *
+	 * @deprecated	3.0.0	Read the 'class' property instead
 	 * @return	string
 	 */
 	public function fetch_class()
@@ -438,11 +439,12 @@
 	/**
 	 * Fetch the current method
 	 *
+	 * @deprecated	3.0.0	Read the 'method' property instead
 	 * @return	string
 	 */
 	public function fetch_method()
 	{
-		return ($this->method === $this->fetch_class()) ? 'index' : $this->method;
+		return $this->method;
 	}
 
 	// --------------------------------------------------------------------
@@ -466,6 +468,7 @@
 	 * Feches the sub-directory (if any) that contains the requested
 	 * controller class.
 	 *
+	 * @deprecated	3.0.0	Read the 'directory' property instead
 	 * @return	string
 	 */
 	public function fetch_directory()
diff --git a/system/core/URI.php b/system/core/URI.php
index b2286f0..bc086d2 100644
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -720,7 +720,7 @@
 	{
 		global $RTR;
 
-		if (($dir = $RTR->fetch_directory()) === '/')
+		if (($dir = $RTR->directory) === '/')
 		{
 			$dir = '';
 		}
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index 97021f1..9239dc1 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -816,7 +816,7 @@
 		}
 
 		// The query() function will set this flag to FALSE in the event that a query failed
-		if ($this->_trans_status === FALSE)
+		if ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE)
 		{
 			$this->trans_rollback();
 
diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php
index 78e255a..f3b9c6c 100644
--- a/system/helpers/captcha_helper.php
+++ b/system/helpers/captcha_helper.php
@@ -51,7 +51,7 @@
 	 */
 	function create_captcha($data = '', $img_path = '', $img_url = '', $font_path = '')
 	{
-		$defaults = array('word' => '', 'img_path' => '', 'img_url' => '', 'img_width' => '150', 'img_height' => '30', 'font_path' => '', 'expiration' => 7200);
+		$defaults = array('word' => '', 'img_path' => '', 'img_url' => '', 'img_width' => '150', 'img_height' => '30', 'font_path' => '', 'expiration' => 7200, 'word_length' => 8, 'pool' => '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
 
 		foreach ($defaults as $key => $val)
 		{
@@ -95,9 +95,8 @@
 
 		if (empty($word))
 		{
-			$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 			$word = '';
-			for ($i = 0, $mt_rand_max = strlen($pool) - 1; $i < 8; $i++)
+			for ($i = 0, $mt_rand_max = strlen($pool) - 1; $i < $word_length; $i++)
 			{
 				$word .= $pool[mt_rand(0, $mt_rand_max)];
 			}
diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php
index 470688f..3c7ce54 100644
--- a/system/libraries/Profiler.php
+++ b/system/libraries/Profiler.php
@@ -405,7 +405,7 @@
 			.'<fieldset id="ci_profiler_controller_info" style="border:1px solid #995300;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">'
 			."\n"
 			.'<legend style="color:#995300;">&nbsp;&nbsp;'.$this->CI->lang->line('profiler_controller_info')."&nbsp;&nbsp;</legend>\n"
-			.'<div style="color:#995300;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->router->fetch_class().'/'.$this->CI->router->fetch_method()
+			.'<div style="color:#995300;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->router->class.'/'.$this->CI->router->method
 			.'</div></fieldset>';
 	}
 
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 21d0bde..742dacb 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -105,6 +105,7 @@
    -  :doc:`Directory Helper <helpers/directory_helper>` :php:func:`directory_map()` will now append ``DIRECTORY_SEPARATOR`` to directory names in the returned array.
    -  :doc:`Language Helper <helpers/language_helper>` :php:func:`lang()` now accepts an optional list of additional HTML attributes.
    -  Deprecated the :doc:`Email Helper <helpers/email_helper>` as its ``valid_email()``, ``send_email()`` functions are now only aliases for PHP native functions ``filter_var()`` and ``mail()`` respectively.
+   -  :doc:`CAPTCHA Helper <helpers/captcha_helper>` :php:func:`create_captcha` added word_length and pool options for setting length of randomly generated captcha word, and what characters to select from.
 
 -  Database
 
@@ -221,6 +222,7 @@
 	 -  *Product Name* strictness can be disabled by switching the ``$product_name_safe`` property to FALSE.
 	 -  Added method ``remove()`` to remove a cart item, updating with quantity of 0 seemed like a hack but has remained to retain compatibility.
 	 -  Added method ``get_item()`` to enable retrieving data for a single cart item.
+	 -  Added unicode support for product names.
    -  :doc:`Image Manipulation library <libraries/image_lib>` changes include:
 	 -  The ``initialize()`` method now only sets existing class properties.
 	 -  Added support for 3-length hex color values for *wm_font_color* and *wm_shadow_color* properties, as well as validation for them.
@@ -329,6 +331,7 @@
    -  :doc:`URI Routing <general/routing>` changes include:
 	 -  Added possibility to route requests using callbacks.
 	 -  Added possibility to use dashes in the controller and method URI segments (translated to underscores).
+	 -  Deprecated methods ``fetch_directory()``, ``fetch_class()`` and ``fetch_method()`` in favor of their respective public properties.
    -  :doc:`Language Library <libraries/language>` changes include:
 	 -  Changed method ``load()`` to filter the language name with ``ctype_digit()``.
 	 -  Added an optional second parameter to method ``line()`` to disable error login for line keys that were not found.
@@ -488,9 +491,13 @@
 -  Fixed a bug (#2255) - :doc:`Email Library <libraries/email>` didn't apply ``smtp_timeout`` to socket reads and writes.
 -  Fixed a bug (#2239) - :doc:`Email Library <libraries/email>` improperly handled the Subject when used with ``bcc_batch_mode`` resulting in E_WARNING messages and an empty Subject.
 -  Fixed a bug (#2234) - :doc:`Query Builder <database/query_builder>` didn't reset JOIN cache for write-type queries.
--  Fixed a bug (#2298) - :doc:`Database Results <database/results>` method `next_row()` kept returning the last row, allowing for infinite loops.
+-  Fixed a bug (#2298) - :doc:`Database Results <database/results>` method ``next_row()`` kept returning the last row, allowing for infinite loops.
 -  Fixed a bug (#2236) - :doc:`Form Helper <helpers/form_helper>` function ``set_value()`` didn't parse array notation for keys if the rule was not present in the :doc:`Form Validation Library <libraries/form_validation>`.
 -  Fixed a bug (#2353) - :doc:`Query Builder <database/query_builder>` erroneously prefixed literal strings with **dbprefix**.
+-  Fixed a bug (#78) - :doc:`Cart Library <libraries/cart>` didn't allow non-English letters in product names.
+-  Fixed a bug (#77) - :doc:`Database Class <database/index>` didn't properly handle the transaction "test mode" flag.
+-  Fixed a bug (#2380) - :doc:`URI Routing <general/routing>` method ``fetch_method()`` returned 'index' if the requested method name matches its controller name.
+
 
 Version 2.1.3
 =============
diff --git a/user_guide_src/source/helpers/captcha_helper.rst b/user_guide_src/source/helpers/captcha_helper.rst
index 17462a8..ca24e01 100644
--- a/user_guide_src/source/helpers/captcha_helper.rst
+++ b/user_guide_src/source/helpers/captcha_helper.rst
@@ -62,7 +62,9 @@
 		'font_path'	=> './path/to/fonts/texb.ttf',
 		'img_width'	=> '150',
 		'img_height'	=> 30,
-		'expiration'	=> 7200
+		'expiration'	=> 7200,
+		'word_length'	=> 8,
+		'pool'	=> '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
 	);
 
 	$cap = create_captcha($vals);
@@ -79,6 +81,7 @@
 -  The **expiration** (in seconds) signifies how long an image will remain
    in the captcha folder before it will be deleted. The default is two
    hours.
+-  **word_length** defaults to 8, **pool** defaults to '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
 
 Adding a Database
 -----------------
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index 02841ab..926af31 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -383,4 +383,22 @@
 	sooner rather than later.
 
 .. note:: This is for MySQL and CUBRID databases only! Other drivers don't support this
-	clause and will silently ignore it.
\ No newline at end of file
+	clause and will silently ignore it.
+
+URI Routing methods fetch_directory(), fetch_class(), fetch_method()
+====================================================================
+
+With properties ``CI_Router::$directory``, ``CI_Router::$class`` and ``CI_Router::$method``
+being public and their respective ``fetch_*()`` no longer doing anything else to just return
+the properties - it doesn't make sense to keep them.
+
+Those are all internal, undocumented methods, but we've opted to deprecate them for now
+in order to maintain backwards-compatibility just in case. If some of you have utilized them,
+then you can now just access the properties instead::
+
+	$this->router->directory;
+	$this->router->class;
+	$this->router->method;
+
+.. note:: Those methods are still available, but you're strongly encouraged to remove their usage
+	sooner rather than later.
\ No newline at end of file