Merge upstream branch
diff --git a/application/config/mimes.php b/application/config/mimes.php
index 8c34fd2..d69497a 100644
--- a/application/config/mimes.php
+++ b/application/config/mimes.php
@@ -120,9 +120,9 @@
 				'mov'	=>	'video/quicktime',
 				'avi'	=>	array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
 				'movie'	=>	'video/x-sgi-movie',
-				'doc'	=>	'application/msword',
-				'docx'	=>	'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
-				'xlsx'	=>	'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+				'doc'	=>	array('application/msword', 'application/vnd.ms-office'),
+				'docx'	=>	array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'),
+				'xlsx'	=>	array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'),
 				'word'	=>	array('application/msword', 'application/octet-stream'),
 				'xl'	=>	'application/excel',
 				'eml'	=>	'message/rfc822',
diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php
index 4247351..eaae23f 100644
--- a/system/database/DB_active_rec.php
+++ b/system/database/DB_active_rec.php
@@ -236,7 +236,8 @@
 	{
 		if (strpos($item, '.') !== FALSE)
 		{
-			return end(explode('.', $item));
+			$item = explode('.', $item);
+			return end($item);
 		}
 
 		return $item;
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index 271a70e..5f435e3 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -185,20 +185,19 @@
 	/**
 	 * Set client character set
 	 *
-	 * @access	public
 	 * @param	string
 	 * @param	string
-	 * @return	resource
+	 * @return	bool
 	 */
-	function db_set_charset($charset, $collation)
+	public function db_set_charset($charset, $collation = '')
 	{
-		if ( ! $this->_db_set_charset($this->char_set, $this->dbcollat))
+		if (method_exists($this, '_db_set_charset') && ! $this->_db_set_charset($charset, $collation))
 		{
-			log_message('error', 'Unable to set database connection charset: '.$this->char_set);
+			log_message('error', 'Unable to set database connection charset: '.$charset);
 
 			if ($this->db_debug)
 			{
-				$this->display_error('db_unable_to_set_charset', $this->char_set);
+				$this->display_error('db_unable_to_set_charset', $charset);
 			}
 
 			return FALSE;
@@ -645,17 +644,12 @@
 	/**
 	 * Determines if a query is a "write" type.
 	 *
-	 * @access	public
 	 * @param	string	An SQL query string
-	 * @return	boolean
+	 * @return	bool
 	 */
-	function is_write_type($sql)
+	public function is_write_type($sql)
 	{
-		if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql))
-		{
-			return FALSE;
-		}
-		return TRUE;
+		return (bool) preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|RENAME|GRANT|REVOKE|LOCK|UNLOCK|OPTIMIZE)\s+/i', $sql);
 	}
 
 	// --------------------------------------------------------------------
@@ -1424,6 +1418,22 @@
 
 		return $item.$alias;
 	}
+	
+	// --------------------------------------------------------------------
+
+	/**
+	 * Dummy method that allows Active Record class to be disabled
+	 *
+	 * This function is used extensively by every db driver.
+	 *
+	 * @access	private
+	 * @return	void
+	 */
+	protected function _reset_select()
+	{
+	
+	}
+
 }
 
 /* End of file DB_driver.php */
diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php
index cde719e..42f08fb 100644
--- a/system/database/drivers/cubrid/cubrid_driver.php
+++ b/system/database/drivers/cubrid/cubrid_driver.php
@@ -156,24 +156,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set client character set
-	 *
-	 * @access	public
-	 * @param	string
-	 * @param	string
-	 * @return	resource
-	 */
-	function db_set_charset($charset, $collation)
-	{
-		// In CUBRID, there is no need to set charset or collation.
-		// This is why returning true will allow the application continue
-		// its normal process.
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Version number query string
 	 *
 	 * @access	public
@@ -362,12 +344,11 @@
 	/**
 	 * Affected Rows
 	 *
-	 * @access	public
-	 * @return	integer
+	 * @return	int
 	 */
-	function affected_rows()
+	public function affected_rows()
 	{
-		return @cubrid_affected_rows($this->conn_id);
+		return @cubrid_affected_rows();
 	}
 
 	// --------------------------------------------------------------------
@@ -801,4 +782,4 @@
 
 
 /* End of file cubrid_driver.php */
-/* Location: ./system/database/drivers/cubrid/cubrid_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/cubrid/cubrid_driver.php */
diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php
index 25a32f3..2a4f2b5 100644
--- a/system/database/drivers/mssql/mssql_driver.php
+++ b/system/database/drivers/mssql/mssql_driver.php
@@ -138,22 +138,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set client character set
-	 *
-	 * @access	public
-	 * @param	string
-	 * @param	string
-	 * @return	resource
-	 */
-	function db_set_charset($charset, $collation)
-	{
-		// @todo - add support if needed
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Execute the query
 	 *
 	 * @access	private called by the base class
diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php
index c662190..d9acaae 100644
--- a/system/database/drivers/oci8/oci8_driver.php
+++ b/system/database/drivers/oci8/oci8_driver.php
@@ -139,22 +139,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set client character set
-	 *
-	 * @access	public
-	 * @param	string
-	 * @param	string
-	 * @return	resource
-	 */
-	public function db_set_charset($charset, $collation)
-	{
-		// this is done upon connect
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Version number query string
 	 *
 	 * @access  protected
@@ -398,10 +382,9 @@
 	/**
 	 * Escape String
 	 *
-	 * @access  public
-	 * @param   string
+	 * @param	string
 	 * @param	bool	whether or not the string will be used in a LIKE condition
-	 * @return  string
+	 * @return	string
 	 */
 	public function escape_str($str, $like = FALSE)
 	{
@@ -415,15 +398,14 @@
 			return $str;
 		}
 
-		$str = remove_invisible_characters($str);
-		$str = str_replace("'", "''", $str);
+		$str = str_replace("'", "''", remove_invisible_characters($str));
 
 		// escape LIKE condition wildcards
 		if ($like === TRUE)
 		{
-			$str = str_replace(	array('%', '_', $this->_like_escape_chr),
-								array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
-								$str);
+			return str_replace(array($this->_like_escape_chr, '%', '_'),
+						array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
+						$str);
 		}
 
 		return $str;
diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php
index 6ba39f0..abb6603 100644
--- a/system/database/drivers/odbc/odbc_driver.php
+++ b/system/database/drivers/odbc/odbc_driver.php
@@ -124,22 +124,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set client character set
-	 *
-	 * @access	public
-	 * @param	string
-	 * @param	string
-	 * @return	resource
-	 */
-	function db_set_charset($charset, $collation)
-	{
-		// @todo - add support if needed
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Version number query string
 	 *
 	 * @access	public
@@ -646,4 +630,4 @@
 
 
 /* End of file odbc_driver.php */
-/* Location: ./system/database/drivers/odbc/odbc_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/odbc/odbc_driver.php */
diff --git a/system/database/drivers/odbc/odbc_result.php b/system/database/drivers/odbc/odbc_result.php
index ba66085..572e110 100644
--- a/system/database/drivers/odbc/odbc_result.php
+++ b/system/database/drivers/odbc/odbc_result.php
@@ -38,15 +38,27 @@
  */
 class CI_DB_odbc_result extends CI_DB_result {
 
+	public $num_rows;
+
 	/**
 	 * Number of rows in the result set
 	 *
-	 * @access	public
-	 * @return	integer
+	 * @return	int
 	 */
-	function num_rows()
+	public function num_rows()
 	{
-		return @odbc_num_rows($this->result_id);
+		if (is_int($this->num_rows))
+		{
+			return $this->num_rows;
+		}
+
+		// Work-around for ODBC subdrivers that don't support num_rows()
+		if (($this->num_rows = @odbc_num_rows($this->result_id)) === -1)
+		{
+			$this->num_rows = count($this->result_array());
+		}
+
+		return $this->num_rows;
 	}
 
 	// --------------------------------------------------------------------
@@ -54,10 +66,9 @@
 	/**
 	 * Number of fields in the result set
 	 *
-	 * @access	public
-	 * @return	integer
+	 * @return	int
 	 */
-	function num_fields()
+	public function num_fields()
 	{
 		return @odbc_num_fields($this->result_id);
 	}
@@ -69,15 +80,19 @@
 	 *
 	 * Generates an array of column names
 	 *
-	 * @access	public
 	 * @return	array
 	 */
-	function list_fields()
+	public function list_fields()
 	{
 		$field_names = array();
-		for ($i = 0; $i < $this->num_fields(); $i++)
+		$num_fields = $this->num_fields();
+
+		if ($num_fields > 0)
 		{
-			$field_names[]	= odbc_field_name($this->result_id, $i);
+			for ($i = 1; $i <= $num_fields; $i++)
+			{
+				$field_names[] = odbc_field_name($this->result_id, $i);
+			}
 		}
 
 		return $field_names;
@@ -90,22 +105,19 @@
 	 *
 	 * Generates an array of objects containing field meta-data
 	 *
-	 * @access	public
 	 * @return	array
 	 */
-	function field_data()
+	public function field_data()
 	{
 		$retval = array();
-		for ($i = 0; $i < $this->num_fields(); $i++)
+		for ($i = 0, $odbc_index = 1, $c = $this->num_fields(); $i < $c; $i++, $odbc_index++)
 		{
-			$F				= new stdClass();
-			$F->name		= odbc_field_name($this->result_id, $i);
-			$F->type		= odbc_field_type($this->result_id, $i);
-			$F->max_length	= odbc_field_len($this->result_id, $i);
-			$F->primary_key = 0;
-			$F->default		= '';
-
-			$retval[] = $F;
+			$retval[$i]			= new stdClass();
+			$retval[$i]->name		= odbc_field_name($this->result_id, $odbc_index);
+			$retval[$i]->type		= odbc_field_type($this->result_id, $odbc_index);
+			$retval[$i]->max_length		= odbc_field_len($this->result_id, $odbc_index);
+			$retval[$i]->primary_key	= 0;
+			$retval[$i]->default		= '';
 		}
 
 		return $retval;
@@ -237,4 +249,4 @@
 
 
 /* End of file odbc_result.php */
-/* Location: ./system/database/drivers/odbc/odbc_result.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/odbc/odbc_result.php */
diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php
index de2b0ab..90f0fd7 100644
--- a/system/database/drivers/pdo/pdo_driver.php
+++ b/system/database/drivers/pdo/pdo_driver.php
@@ -289,29 +289,13 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set client character set
-	 *
-	 * @access	public
-	 * @param	string
-	 * @param	string
-	 * @return	resource
-	 */
-	function db_set_charset($charset, $collation)
-	{
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Version number query string
 	 *
-	 * @access	public
 	 * @return	string
 	 */
-	function _version()
+	protected function _version()
 	{
-		return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION);
+		return $this->conn_id->getAttribute(PDO::ATTR_SERVER_VERSION);
 	}
 
 	// --------------------------------------------------------------------
@@ -510,33 +494,19 @@
 
 	/**
 	 * Insert ID
-	 * 
-	 * @access	public
-	 * @return	integer
+	 *
+	 * @return	int
 	 */
-	function insert_id($name=NULL)
+	public function insert_id($name = NULL)
 	{
-		if ($this->pdodriver == 'pgsql')
+		if ($this->pdodriver === 'pgsql' && $name === NULL && $this->_version() >= '8.1')
 		{
-			//Convenience method for postgres insertid
-			$v = $this->_version();
-
-			$table	= func_num_args() > 0 ? func_get_arg(0) : NULL;
-
-			if ($table == NULL && $v >= '8.1')
-			{
-				$sql='SELECT LASTVAL() as ins_id';
-			}
-
-			$query = $this->query($sql);
-			$row   = $query->row();
-
-			return $row->ins_id;
+			$query = $this->query('SELECT LASTVAL() AS ins_id');
+			$query = $query->row();
+			return $query->ins_id;
 		}
-		else
-		{
-			return $this->conn_id->lastInsertId($name);
-		}
+
+		return $this->conn_id->lastInsertId($name);
 	}
 
 	// --------------------------------------------------------------------
@@ -950,4 +920,4 @@
 }
 
 /* End of file pdo_driver.php */
-/* Location: ./system/database/drivers/pdo/pdo_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/pdo/pdo_driver.php */
diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php
index 42329bd..89541e5 100644
--- a/system/database/drivers/postgre/postgre_driver.php
+++ b/system/database/drivers/postgre/postgre_driver.php
@@ -147,22 +147,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set client character set
-	 *
-	 * @access	public
-	 * @param	string
-	 * @param	string
-	 * @return	resource
-	 */
-	function db_set_charset($charset, $collation)
-	{
-		// @todo - add support if needed
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Version number query string
 	 *
 	 * @access	public
@@ -712,4 +696,4 @@
 
 
 /* End of file postgre_driver.php */
-/* Location: ./system/database/drivers/postgre/postgre_driver.php */
\ No newline at end of file
+/* Location: ./system/database/drivers/postgre/postgre_driver.php */
diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php
index 25e012b..20b05af 100644
--- a/system/database/drivers/sqlite/sqlite_driver.php
+++ b/system/database/drivers/sqlite/sqlite_driver.php
@@ -134,21 +134,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set client character set
-	 *
-	 * @param	string
-	 * @param	string
-	 * @return	bool
-	 */
-	public function db_set_charset($charset, $collation)
-	{
-		// Not supported
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Version number query string
 	 *
 	 * @return	string
diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php
index 9c50209..9b90381 100644
--- a/system/database/drivers/sqlsrv/sqlsrv_driver.php
+++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php
@@ -144,22 +144,6 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Set client character set
-	 *
-	 * @access	public
-	 * @param	string
-	 * @param	string
-	 * @return	resource
-	 */
-	function db_set_charset($charset, $collation)
-	{
-		// @todo - add support if needed
-		return TRUE;
-	}
-
-	// --------------------------------------------------------------------
-
-	/**
 	 * Execute the query
 	 *
 	 * @access	private called by the base class
@@ -424,13 +408,18 @@
 	/**
 	 * The error message string
 	 *
-	 * @access	private
 	 * @return	string
 	 */
-	function _error_message()
+	protected function _error_message()
 	{
-		$error = array_shift(sqlsrv_errors());
-		return !empty($error['message']) ? $error['message'] : null;
+		$error = sqlsrv_errors();
+		if ( ! is_array($error))
+		{
+			return '';
+		}
+
+		$error = array_shift($error);
+		return isset($error['message']) ? $error['message'] : '';
 	}
 
 	// --------------------------------------------------------------------
@@ -438,13 +427,25 @@
 	/**
 	 * The error message number
 	 *
-	 * @access	private
-	 * @return	integer
+	 * @return	string
 	 */
-	function _error_number()
+	protected function _error_number()
 	{
-		$error = array_shift(sqlsrv_errors());
-		return isset($error['SQLSTATE']) ? $error['SQLSTATE'] : null;
+		$error = sqlsrv_errors();
+		if ( ! is_array($error))
+		{
+			return '';
+		}
+		elseif (isset($error['SQLSTATE']))
+		{
+			return isset($error['code']) ? $error['SQLSTATE'].'/'.$error['code'] : $error['SQLSTATE'];
+		}
+		elseif (isset($error['code']))
+		{
+			return $error['code'];
+		}
+
+		return '';
 	}
 
 	// --------------------------------------------------------------------
diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php
index aea948d..a8c59c2 100644
--- a/system/helpers/download_helper.php
+++ b/system/helpers/download_helper.php
@@ -1,13 +1,13 @@
-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 /**
  * CodeIgniter
  *
  * An open source application development framework for PHP 5.1.6 or newer
  *
  * NOTICE OF LICENSE
- * 
+ *
  * Licensed under the Open Software License version 3.0
- * 
+ *
  * This source file is subject to the Open Software License (OSL 3.0) that is
  * bundled with this package in the files license.txt / license.rst.  It is
  * also available through the world wide web at this URL:
@@ -25,8 +25,6 @@
  * @filesource
  */
 
-// ------------------------------------------------------------------------
-
 /**
  * CodeIgniter Download Helpers
  *
@@ -47,67 +45,72 @@
  * @access	public
  * @param	string	filename
  * @param	mixed	the data to be downloaded
+ * @param	bool	wether to try and send the actual file MIME type
  * @return	void
  */
 if ( ! function_exists('force_download'))
 {
-	function force_download($filename = '', $data = '')
+	function force_download($filename = '', $data = '', $set_mime = FALSE)
 	{
 		if ($filename == '' OR $data == '')
 		{
 			return FALSE;
 		}
 
-		// Try to determine if the filename includes a file extension.
-		// We need it in order to set the MIME type
-		if (FALSE === strpos($filename, '.'))
+		// Set the default MIME type to send
+		$mime = 'application/octet-stream';
+
+		if ($set_mime === TRUE)
 		{
-			return FALSE;
+			/* If we're going to detect the MIME type,
+			 * we'll need a file extension.
+			 */
+			if (FALSE === strpos($filename, '.'))
+			{
+				return FALSE;
+			}
+
+			$extension = explode('.', $filename);
+			$extension = end($extension);
+
+			// Load the mime types
+			if (defined('ENVIRONMENT') && is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
+			{
+				include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
+			}
+			elseif (is_file(APPPATH.'config/mimes.php'))
+			{
+				include(APPPATH.'config/mimes.php');
+			}
+
+			// Only change the default MIME if we can find one
+			if (isset($mimes[$extension]))
+			{
+				$mime = is_array($mimes[$extension]) ? $mimes[$extension][0] : $mimes[$extension];
+			}
 		}
 
-		// Grab the file extension
-		$x = explode('.', $filename);
-		$extension = end($x);
-
-		// Load the mime types
-		if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
-		{
-			include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php');
-		}
-		elseif (is_file(APPPATH.'config/mimes.php'))
-		{
-			include(APPPATH.'config/mimes.php');
-		}
-
-		// Set a default mime if we can't find it
-		if ( ! isset($mimes[$extension]))
-		{
-			$mime = 'application/octet-stream';
-		}
-		else
-		{
-			$mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
-		}
-		
 		// Generate the server headers
 		header('Content-Type: "'.$mime.'"');
 		header('Content-Disposition: attachment; filename="'.$filename.'"');
 		header('Expires: 0');
-		header("Content-Transfer-Encoding: binary");
-		header("Content-Length: ".strlen($data));
-		header('Pragma: no-cache');
+		header('Content-Transfer-Encoding: binary');
+		header('Content-Length: '.strlen($data));
 
-		// Internet Explorer-specific headers.
-		if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], "MSIE") !== FALSE)
+		// Internet Explorer-specific headers
+		if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE)
 		{
 			header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
 			header('Pragma: public');
 		}
+		else
+		{
+			header('Pragma: no-cache');
+		}
 
 		exit($data);
 	}
 }
 
-
 /* End of file download_helper.php */
-/* Location: ./system/helpers/download_helper.php */
\ No newline at end of file
+/* Location: ./system/helpers/download_helper.php */
diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php
index 2069a19..02c425b 100644
--- a/system/helpers/inflector_helper.php
+++ b/system/helpers/inflector_helper.php
@@ -34,7 +34,7 @@
  * @subpackage	Helpers
  * @category	Helpers
  * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/helpers/directory_helper.html
+ * @link		http://codeigniter.com/user_guide/helpers/inflector_helper.html
  */
 
 
@@ -45,7 +45,6 @@
  *
  * Takes a plural word and makes it singular
  *
- * @access	public
  * @param	string
  * @return	str
  */
@@ -55,37 +54,51 @@
 	{
 		$result = strval($str);
 
+		if ( ! is_countable($result))
+		{
+			return $result;
+		}
+		
 		$singular_rules = array(
-			'/(matr)ices$/'			=> '\1ix',
-			'/(vert|ind)ices$/'		=> '\1ex',
-			'/^(ox)en/'				=> '\1',
-			'/(alias)es$/'			=> '\1',
-			'/([octop|vir])i$/'		=> '\1us',
-			'/(cris|ax|test)es$/'	=> '\1is',
-			'/(shoe)s$/'			=> '\1',
-			'/(o)es$/'				=> '\1',
-			'/(bus|campus)es$/'		=> '\1',
-			'/([m|l])ice$/'			=> '\1ouse',
-			'/(x|ch|ss|sh)es$/'		=> '\1',
-			'/(m)ovies$/'			=> '\1\2ovie',
-			'/(s)eries$/'			=> '\1\2eries',
-			'/([^aeiouy]|qu)ies$/'	=> '\1y',
-			'/([lr])ves$/'			=> '\1f',
-			'/(tive)s$/'			=> '\1',
-			'/(hive)s$/'			=> '\1',
-			'/([^f])ves$/'			=> '\1fe',
-			'/(^analy)ses$/'		=> '\1sis',
+			'/(matr)ices$/'         => '\1ix',
+			'/(vert|ind)ices$/'     => '\1ex',
+			'/^(ox)en/'             => '\1',
+			'/(alias)es$/'          => '\1',
+			'/([octop|vir])i$/'     => '\1us',
+			'/(cris|ax|test)es$/'   => '\1is',
+			'/(shoe)s$/'            => '\1',
+			'/(o)es$/'              => '\1',
+			'/(bus|campus)es$/'     => '\1',
+			'/([m|l])ice$/'         => '\1ouse',
+			'/(x|ch|ss|sh)es$/'     => '\1',
+			'/(m)ovies$/'           => '\1\2ovie',
+			'/(s)eries$/'           => '\1\2eries',
+			'/([^aeiouy]|qu)ies$/'  => '\1y',
+			'/([lr])ves$/'          => '\1f',
+			'/(tive)s$/'            => '\1',
+			'/(hive)s$/'            => '\1',
+			'/([^f])ves$/'          => '\1fe',
+			'/(^analy)ses$/'        => '\1sis',
 			'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/' => '\1\2sis',
-			'/([ti])a$/'			=> '\1um',
-			'/(p)eople$/'			=> '\1\2erson',
-			'/(m)en$/'				=> '\1an',
-			'/(s)tatuses$/'			=> '\1\2tatus',
-			'/(c)hildren$/'			=> '\1\2hild',
-			'/(n)ews$/'				=> '\1\2ews',
-			'/([^u])s$/'			=> '\1',
+			'/([ti])a$/'            => '\1um',
+			'/(p)eople$/'           => '\1\2erson',
+			'/(m)en$/'              => '\1an',
+			'/(s)tatuses$/'         => '\1\2tatus',
+			'/(c)hildren$/'         => '\1\2hild',
+			'/(n)ews$/'             => '\1\2ews',
+			'/([^us])s$/'           => '\1',
 		);
 
-		return preg_replace(array_keys($singular_rules), $singular_rules, $result);
+		foreach ($singular_rules as $rule => $replacement)
+		{
+			if (preg_match($rule, $result))
+			{
+				$result = preg_replace($rule, $replacement, $result);
+				break;
+			}
+		}
+
+		return $result;
 	}
 }
 
@@ -96,7 +109,6 @@
  *
  * Takes a singular word and makes it plural
  *
- * @access	public
  * @param	string
  * @param	bool
  * @return	str
@@ -104,32 +116,46 @@
 if ( ! function_exists('plural'))
 {
 	function plural($str, $force = FALSE)
-	{
+	{	
 		$result = strval($str);
 
-		$plural_rules = array(
-			'/^(ox)$/'					=> '\1\2en',	 // ox
-			'/([m|l])ouse$/'			=> '\1ice',	  // mouse, louse
-			'/(matr|vert|ind)ix|ex$/'	=> '\1ices',	 // matrix, vertex, index
-			'/(x|ch|ss|sh)$/'			=> '\1es',	   // search, switch, fix, box, process, address
-			'/([^aeiouy]|qu)y$/'		=> '\1ies',	  // query, ability, agency
-			'/(hive)$/'					=> '\1s',		// archive, hive
-			'/(?:([^f])fe|([lr])f)$/'	=> '\1\2ves',	// half, safe, wife
-			'/sis$/'					=> 'ses',		// basis, diagnosis
-			'/([ti])um$/'				=> '\1a',		// datum, medium
-			'/(p)erson$/'				=> '\1eople',	// person, salesperson
-			'/(m)an$/'					=> '\1en',	   // man, woman, spokesman
-			'/(c)hild$/'				=> '\1hildren',  // child
-			'/(buffal|tomat)o$/'		=> '\1\2oes',	// buffalo, tomato
-			'/(bu|campu)s$/'			=> '\1\2ses',	// bus, campus
-			'/(alias|status|virus)/'	=> '\1es',	   // alias
-			'/(octop)us$/'				=> '\1i',		// octopus
-			'/(ax|cris|test)is$/'		=> '\1es',	   // axis, crisis
-			'/s$/'						=> 's',		  // no change (compatibility)
-			'/$/'						=> 's',
-		);
+		if ( ! is_countable($result))
+		{
+			return $result;
+		}
 
-		return preg_replace(array_keys($plural_rules), $plural_rules, $result);
+		$plural_rules = array(
+			'/^(ox)$/'                 => '\1\2en',     // ox
+			'/([m|l])ouse$/'           => '\1ice',      // mouse, louse
+			'/(matr|vert|ind)ix|ex$/'  => '\1ices',     // matrix, vertex, index
+			'/(x|ch|ss|sh)$/'          => '\1es',       // search, switch, fix, box, process, address
+			'/([^aeiouy]|qu)y$/'       => '\1ies',      // query, ability, agency
+			'/(hive)$/'                => '\1s',        // archive, hive
+			'/(?:([^f])fe|([lr])f)$/'  => '\1\2ves',    // half, safe, wife
+			'/sis$/'                   => 'ses',        // basis, diagnosis
+			'/([ti])um$/'              => '\1a',        // datum, medium
+			'/(p)erson$/'              => '\1eople',    // person, salesperson
+			'/(m)an$/'                 => '\1en',       // man, woman, spokesman
+			'/(c)hild$/'               => '\1hildren',  // child
+			'/(buffal|tomat)o$/'       => '\1\2oes',    // buffalo, tomato
+			'/(bu|campu)s$/'           => '\1\2ses',    // bus, campus
+			'/(alias|status|virus)$/'  => '\1es',       // alias
+			'/(octop)us$/'             => '\1i',        // octopus
+			'/(ax|cris|test)is$/'      => '\1es',       // axis, crisis
+			'/s$/'                     => 's',          // no change (compatibility)
+			'/$/'                      => 's',
+		);
+		
+		foreach ($plural_rules as $rule => $replacement)
+		{
+			if (preg_match($rule, $result))
+			{
+				$result = preg_replace($rule, $replacement, $result);
+				break;
+			}
+		}
+
+		return $result;
 	}
 }
 
@@ -140,7 +166,6 @@
  *
  * Takes multiple words separated by spaces or underscores and camelizes them
  *
- * @access	public
  * @param	string
  * @return	str
  */
@@ -159,7 +184,6 @@
  *
  * Takes multiple words separated by spaces and underscores them
  *
- * @access	public
  * @param	string
  * @return	str
  */
@@ -178,7 +202,6 @@
  *
  * Takes multiple words separated by the separator and changes them to spaces
  *
- * @access	public
  * @param	string $str
  * @param 	string $separator
  * @return	str
@@ -191,5 +214,22 @@
 	}
 }
 
+/**
+ * Checks if the given word has a plural version.
+ *
+ * @param   string  the word to check
+ * @return  bool    if the word is countable
+ */
+if ( ! function_exists('is_countable'))
+{
+	function is_countable($word)
+	{
+		return ! (in_array(strtolower(strval($word)), array(
+			'equipment', 'information', 'rice', 'money',
+			'species', 'series', 'fish', 'meta'
+		)));
+	}
+}
+
 /* End of file inflector_helper.php */
-/* Location: ./system/helpers/inflector_helper.php */
+/* Location: ./system/helpers/inflector_helper.php */
\ No newline at end of file
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index 922107e..c8a5b41 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -25,8 +25,6 @@
  * @filesource
  */
 
-// ------------------------------------------------------------------------
-
 /**
  * CodeIgniter Email Class
  *
@@ -40,55 +38,55 @@
  */
 class CI_Email {
 
-	public $useragent		= "CodeIgniter";
-	public $mailpath		= "/usr/sbin/sendmail";	// Sendmail path
-	public $protocol		= "mail";	// mail/sendmail/smtp
-	public $smtp_host		= "";		// SMTP Server.  Example: mail.earthlink.net
-	public $smtp_user		= "";		// SMTP Username
-	public $smtp_pass		= "";		// SMTP Password
-	public $smtp_port		= "25";		// SMTP Port
-	public $smtp_timeout	= 5;		// SMTP Timeout in seconds
-	public $smtp_crypto	= "";		// SMTP Encryption. Can be null, tls or ssl.
-	public $wordwrap		= TRUE;		// TRUE/FALSE  Turns word-wrap on/off
-	public $wrapchars		= "76";		// Number of characters to wrap at.
-	public $mailtype		= "text";	// text/html  Defines email formatting
-	public $charset		= "utf-8";	// Default char set: iso-8859-1 or us-ascii
-	public $multipart		= "mixed";	// "mixed" (in the body) or "related" (separate)
-	public $alt_message	= '';		// Alternative message for HTML emails
-	public $validate		= FALSE;	// TRUE/FALSE.  Enables email validation
-	public $priority		= "3";		// Default priority (1 - 5)
-	public $newline		= "\n";		// Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
-	public $crlf			= "\n";		// The RFC 2045 compliant CRLF for quoted-printable is "\r\n".  Apparently some servers,
+	public $useragent	= 'CodeIgniter';
+	public $mailpath	= '/usr/sbin/sendmail';	// Sendmail path
+	public $protocol	= 'mail';		// mail/sendmail/smtp
+	public $smtp_host	= '';			// SMTP Server. Example: mail.earthlink.net
+	public $smtp_user	= '';			// SMTP Username
+	public $smtp_pass	= '';			// SMTP Password
+	public $smtp_port	= 25;			// SMTP Port
+	public $smtp_timeout	= 5;			// SMTP Timeout in seconds
+	public $smtp_crypto	= '';			// SMTP Encryption. Can be null, tls or ssl.
+	public $wordwrap	= TRUE;			// TRUE/FALSE  Turns word-wrap on/off
+	public $wrapchars	= 76;			// Number of characters to wrap at.
+	public $mailtype	= 'text';		// text/html  Defines email formatting
+	public $charset		= 'utf-8';		// Default char set: iso-8859-1 or us-ascii
+	public $multipart	= 'mixed';		// "mixed" (in the body) or "related" (separate)
+	public $alt_message	= '';			// Alternative message for HTML emails
+	public $validate	= FALSE;		// TRUE/FALSE.  Enables email validation
+	public $priority	= 3;			// Default priority (1 - 5)
+	public $newline		= "\n";			// Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
+	public $crlf		= "\n";			// The RFC 2045 compliant CRLF for quoted-printable is "\r\n".  Apparently some servers,
 									// even on the receiving end think they need to muck with CRLFs, so using "\n", while
 									// distasteful, is the only thing that seems to work for all environments.
 	public $send_multipart	= TRUE;		// TRUE/FALSE - Yahoo does not like multipart alternative, so this is an override.  Set to FALSE for Yahoo.
-	public $bcc_batch_mode	= FALSE;	// TRUE/FALSE  Turns on/off Bcc batch feature
+	public $bcc_batch_mode	= FALSE;	// TRUE/FALSE - Turns on/off Bcc batch feature
 	public $bcc_batch_size	= 200;		// If bcc_batch_mode = TRUE, sets max number of Bccs in each batch
-	private $_safe_mode		= FALSE;
-	private $_subject		= "";
-	private $_body			= "";
-	private $_finalbody		= "";
-	private $_alt_boundary	= "";
-	private $_atc_boundary	= "";
-	private $_header_str	= "";
-	private $_smtp_connect	= "";
-	private $_encoding		= "8bit";
-	private $_IP			= FALSE;
-	private $_smtp_auth		= FALSE;
-	private $_replyto_flag	= FALSE;
-	private $_debug_msg		= array();
-	private $_recipients	= array();
-	private $_cc_array		= array();
-	private $_bcc_array		= array();
-	private $_headers		= array();
-	private $_attach_name	= array();
-	private $_attach_type	= array();
-	private $_attach_disp	= array();
-	private $_protocols		= array('mail', 'sendmail', 'smtp');
-	private $_base_charsets	= array('us-ascii', 'iso-2022-');	// 7-bit charsets (excluding language suffix)
-	private $_bit_depths	= array('7bit', '8bit');
-	private $_priorities	= array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
 
+	protected $_safe_mode		= FALSE;
+	protected $_subject		= '';
+	protected $_body		= '';
+	protected $_finalbody		= '';
+	protected $_alt_boundary	= '';
+	protected $_atc_boundary	= '';
+	protected $_header_str		= '';
+	protected $_smtp_connect	= '';
+	protected $_encoding		= '8bit';
+	protected $_IP			= FALSE;
+	protected $_smtp_auth		= FALSE;
+	protected $_replyto_flag	= FALSE;
+	protected $_debug_msg		= array();
+	protected $_recipients		= array();
+	protected $_cc_array		= array();
+	protected $_bcc_array		= array();
+	protected $_headers		= array();
+	protected $_attach_name		= array();
+	protected $_attach_type		= array();
+	protected $_attach_disp		= array();
+	protected $_protocols		= array('mail', 'sendmail', 'smtp');
+	protected $_base_charsets	= array('us-ascii', 'iso-2022-');	// 7-bit charsets (excluding language suffix)
+	protected $_bit_depths		= array('7bit', '8bit');
+	protected $_priorities		= array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
 
 	/**
 	 * Constructor - Sets Email Preferences
@@ -103,11 +101,11 @@
 		}
 		else
 		{
-			$this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
-			$this->_safe_mode = (bool) @ini_get("safe_mode");
+			$this->_smtp_auth = ! ($this->smtp_user == '' && $this->smtp_pass == '');
+			$this->_safe_mode = (bool) @ini_get('safe_mode');
 		}
 
-		log_message('debug', "Email Class Initialized");
+		log_message('debug', 'Email Class Initialized');
 	}
 
 	// --------------------------------------------------------------------
@@ -115,7 +113,6 @@
 	/**
 	 * Initialize preferences
 	 *
-	 * @access	public
 	 * @param	array
 	 * @return	void
 	 */
@@ -139,8 +136,8 @@
 		}
 		$this->clear();
 
-		$this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
-		$this->_safe_mode = (bool) @ini_get("safe_mode");
+		$this->_smtp_auth = ! ($this->smtp_user == '' && $this->smtp_pass == '');
+		$this->_safe_mode = (bool) @ini_get('safe_mode');
 
 		return $this;
 	}
@@ -150,17 +147,16 @@
 	/**
 	 * Initialize the Email Data
 	 *
-	 * @access	public
 	 * @param	bool
-	 * @return	void
+	 * @return	object
 	 */
 	public function clear($clear_attachments = FALSE)
 	{
-		$this->_subject		= "";
-		$this->_body		= "";
-		$this->_finalbody	= "";
-		$this->_header_str	= "";
-		$this->_replyto_flag = FALSE;
+		$this->_subject		= '';
+		$this->_body		= '';
+		$this->_finalbody	= '';
+		$this->_header_str	= '';
+		$this->_replyto_flag	= FALSE;
 		$this->_recipients	= array();
 		$this->_cc_array	= array();
 		$this->_bcc_array	= array();
@@ -185,14 +181,13 @@
 	/**
 	 * Set FROM
 	 *
-	 * @access	public
 	 * @param	string
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function from($from, $name = '')
 	{
-		if (preg_match( '/\<(.*)\>/', $from, $match))
+		if (preg_match('/\<(.*)\>/', $from, $match))
 		{
 			$from = $match[1];
 		}
@@ -228,14 +223,13 @@
 	/**
 	 * Set Reply-to
 	 *
-	 * @access	public
 	 * @param	string
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function reply_to($replyto, $name = '')
 	{
-		if (preg_match( '/\<(.*)\>/', $replyto, $match))
+		if (preg_match('/\<(.*)\>/', $replyto, $match))
 		{
 			$replyto = $match[1];
 		}
@@ -266,9 +260,8 @@
 	/**
 	 * Set Recipients
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function to($to)
 	{
@@ -282,17 +275,17 @@
 
 		if ($this->_get_protocol() !== 'mail')
 		{
-			$this->_set_header('To', implode(", ", $to));
+			$this->_set_header('To', implode(', ', $to));
 		}
 
 		switch ($this->_get_protocol())
 		{
-			case 'smtp'		:
+			case 'smtp':
 				$this->_recipients = $to;
 			break;
-			case 'sendmail'	:
-			case 'mail'		:
-				$this->_recipients = implode(", ", $to);
+			case 'sendmail':
+			case 'mail':
+				$this->_recipients = implode(', ', $to);
 			break;
 		}
 
@@ -304,9 +297,8 @@
 	/**
 	 * Set CC
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function cc($cc)
 	{
@@ -318,7 +310,7 @@
 			$this->validate_email($cc);
 		}
 
-		$this->_set_header('Cc', implode(", ", $cc));
+		$this->_set_header('Cc', implode(', ', $cc));
 
 		if ($this->_get_protocol() === 'smtp')
 		{
@@ -333,10 +325,9 @@
 	/**
 	 * Set BCC
 	 *
-	 * @access	public
 	 * @param	string
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function bcc($bcc, $limit = '')
 	{
@@ -360,7 +351,7 @@
 		}
 		else
 		{
-			$this->_set_header('Bcc', implode(", ", $bcc));
+			$this->_set_header('Bcc', implode(', ', $bcc));
 		}
 
 		return $this;
@@ -371,9 +362,8 @@
 	/**
 	 * Set Email Subject
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function subject($subject)
 	{
@@ -387,13 +377,12 @@
 	/**
 	 * Set Body
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function message($body)
 	{
-		$this->_body = rtrim(str_replace("\r", "", $body));
+		$this->_body = rtrim(str_replace("\r", '', $body));
 
 		/* strip slashes only if magic quotes is ON
 		   if we do it with magic quotes OFF, it strips real, user-inputted chars.
@@ -414,9 +403,8 @@
 	/**
 	 * Assign file attachments
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function attach($filename, $disposition = '', $newname = NULL)
 	{
@@ -431,7 +419,6 @@
 	/**
 	 * Add a Header Item
 	 *
-	 * @access	protected
 	 * @param	string
 	 * @param	string
 	 * @return	void
@@ -446,7 +433,6 @@
 	/**
 	 * Convert a String to an Array
 	 *
-	 * @access	protected
 	 * @param	string
 	 * @return	array
 	 */
@@ -460,8 +446,7 @@
 			}
 			else
 			{
-				$email = trim($email);
-				settype($email, "array");
+				$email = (array) trim($email);
 			}
 		}
 		return $email;
@@ -472,9 +457,8 @@
 	/**
 	 * Set Multipart Value
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function set_alt_message($str = '')
 	{
@@ -487,9 +471,8 @@
 	/**
 	 * Set Mailtype
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function set_mailtype($type = 'text')
 	{
@@ -502,9 +485,8 @@
 	/**
 	 * Set Wordwrap
 	 *
-	 * @access	public
 	 * @param	bool
-	 * @return	void
+	 * @return	object
 	 */
 	public function set_wordwrap($wordwrap = TRUE)
 	{
@@ -517,13 +499,12 @@
 	/**
 	 * Set Protocol
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function set_protocol($protocol = 'mail')
 	{
-		$this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol);
+		$this->protocol = in_array($protocol, $this->_protocols, TRUE) ? strtolower($protocol) : 'mail';
 		return $this;
 	}
 
@@ -532,19 +513,12 @@
 	/**
 	 * Set Priority
 	 *
-	 * @access	public
-	 * @param	integer
-	 * @return	void
+	 * @param	int
+	 * @return	object
 	 */
 	public function set_priority($n = 3)
 	{
-		if ( ! is_numeric($n) OR $n < 1 OR $n > 5)
-		{
-			$this->priority = 3;
-			return;
-		}
-
-		$this->priority = (int) $n;
+		$this->priority = preg_match('/^[1-5]$/', $n) ? (int) $n : 3;
 		return $this;
 	}
 
@@ -553,9 +527,8 @@
 	/**
 	 * Set Newline Character
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function set_newline($newline = "\n")
 	{
@@ -568,13 +541,12 @@
 	/**
 	 * Set CRLF
 	 *
-	 * @access	public
 	 * @param	string
-	 * @return	void
+	 * @return	object
 	 */
 	public function set_crlf($crlf = "\n")
 	{
-		$this->crlf = ($crlf !== "\n" AND $crlf !== "\r\n" AND $crlf !== "\r") ? "\n" : $crlf;
+		$this->crlf = ($crlf !== "\n" && $crlf !== "\r\n" && $crlf !== "\r") ? "\n" : $crlf;
 		return $this;
 	}
 
@@ -583,13 +555,12 @@
 	/**
 	 * Set Message Boundary
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _set_boundaries()
 	{
-		$this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
-		$this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
+		$this->_alt_boundary = 'B_ALT_'.uniqid(''); // multipart/alternative
+		$this->_atc_boundary = 'B_ATC_'.uniqid(''); // attachment boundary
 	}
 
 	// --------------------------------------------------------------------
@@ -597,14 +568,12 @@
 	/**
 	 * Get the Message ID
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _get_message_id()
 	{
 		$from = str_replace(array('>', '<'), '', $this->_headers['Return-Path']);
-
-		return  "<".uniqid('').strstr($from, '@').">";
+		return  '<'.uniqid('').strstr($from, '@').'>';
 	}
 
 	// --------------------------------------------------------------------
@@ -612,14 +581,13 @@
 	/**
 	 * Get Mail Protocol
 	 *
-	 * @access	protected
 	 * @param	bool
-	 * @return	string
+	 * @return	mixed
 	 */
 	protected function _get_protocol($return = TRUE)
 	{
 		$this->protocol = strtolower($this->protocol);
-		$this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;
+		in_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail';
 
 		if ($return == TRUE)
 		{
@@ -632,13 +600,12 @@
 	/**
 	 * Get Mail Encoding
 	 *
-	 * @access	protected
 	 * @param	bool
 	 * @return	string
 	 */
 	protected function _get_encoding($return = TRUE)
 	{
-		$this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;
+		in_array($this->_encoding, $this->_bit_depths) OR $this->_encoding = '8bit';
 
 		foreach ($this->_base_charsets as $charset)
 		{
@@ -659,7 +626,6 @@
 	/**
 	 * Get content type (text/html/attachment)
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _get_content_type()
@@ -687,17 +653,16 @@
 	/**
 	 * Set RFC 822 Date
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _set_date()
 	{
-		$timezone = date("Z");
+		$timezone = date('Z');
 		$operator = (strncmp($timezone, '-', 1) === 0) ? '-' : '+';
 		$timezone = abs($timezone);
 		$timezone = floor($timezone/3600) * 100 + ($timezone % 3600) / 60;
 
-		return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);
+		return sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone);
 	}
 
 	// --------------------------------------------------------------------
@@ -705,12 +670,11 @@
 	/**
 	 * Mime message
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _get_mime_message()
 	{
-		return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
+		return 'This is a multi-part message in MIME format.'.$this->newline.'Your email application may not support this format.';
 	}
 
 	// --------------------------------------------------------------------
@@ -718,7 +682,6 @@
 	/**
 	 * Validate Email Address
 	 *
-	 * @access	public
 	 * @param	string
 	 * @return	bool
 	 */
@@ -747,13 +710,12 @@
 	/**
 	 * Email Validation
 	 *
-	 * @access	public
 	 * @param	string
 	 * @return	bool
 	 */
 	public function valid_email($address)
 	{
-		return (bool) preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address);
+		return (bool) preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix', $address);
 	}
 
 	// --------------------------------------------------------------------
@@ -761,7 +723,6 @@
 	/**
 	 * Clean Extended Email Address: Joe Smith <joe@smith.com>
 	 *
-	 * @access	public
 	 * @param	string
 	 * @return	string
 	 */
@@ -776,7 +737,7 @@
 
 		foreach ($email as $addy)
 		{
-			$clean_email[] = (preg_match( '/\<(.*)\>/', $addy, $match)) ? $match[1] : $addy;
+			$clean_email[] = preg_match('/\<(.*)\>/', $addy, $match) ? $match[1] : $addy;
 		}
 
 		return $clean_email;
@@ -792,12 +753,11 @@
 	 * If the user hasn't specified his own alternative message
 	 * it creates one by stripping the HTML
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _get_alt_message()
 	{
-		if ($this->alt_message != "")
+		if ($this->alt_message != '')
 		{
 			return $this->word_wrap($this->alt_message, '76');
 		}
@@ -818,9 +778,8 @@
 	/**
 	 * Word Wrap
 	 *
-	 * @access	public
 	 * @param	string
-	 * @param	integer
+	 * @param	int
 	 * @return	string
 	 */
 	public function word_wrap($str, $charlim = '')
@@ -911,8 +870,6 @@
 	/**
 	 * Build final headers
 	 *
-	 * @access	protected
-	 * @param	string
 	 * @return	string
 	 */
 	protected function _build_headers()
@@ -929,7 +886,6 @@
 	/**
 	 * Write Headers as a string
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _write_headers()
@@ -964,12 +920,11 @@
 	/**
 	 * Build Final Body and attachments
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _build_message()
 	{
-		if ($this->wordwrap === TRUE AND $this->mailtype !== 'html')
+		if ($this->wordwrap === TRUE && $this->mailtype !== 'html')
 		{
 			$this->_body = $this->word_wrap($this->_body);
 		}
@@ -1131,9 +1086,8 @@
 	 * Prepares string for Quoted-Printable Content-Transfer-Encoding
 	 * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
 	 *
-	 * @access	protected
 	 * @param	string
-	 * @param	integer
+	 * @param	int
 	 * @return	string
 	 */
 	protected function _prep_quoted_printable($str, $charlim = '')
@@ -1203,9 +1157,7 @@
 		}
 
 		// get rid of extra CRLF tacked onto the end
-		$output = substr($output, 0, strlen($this->crlf) * -1);
-
-		return $output;
+		return substr($output, 0, strlen($this->crlf) * -1);
 	}
 
 	// --------------------------------------------------------------------
@@ -1216,10 +1168,9 @@
 	 * Performs "Q Encoding" on a string for use in email headers.  It's related
 	 * but not identical to quoted-printable, so it has its own method
 	 *
-	 * @access	public
-	 * @param	str
-	 * @param	bool	// set to TRUE for processing From: headers
-	 * @return	str
+	 * @param	string
+	 * @param	bool	set to TRUE for processing From: headers
+	 * @return	string
 	 */
 	protected function _prep_q_encoding($str, $from = FALSE)
 	{
@@ -1275,9 +1226,7 @@
 
 		// wrap each line with the shebang, charset, and transfer encoding
 		// the preceding space on successive lines is required for header "folding"
-		$str = trim(preg_replace('/^(.*)$/m', ' =?'.$this->charset.'?Q?$1?=', $str));
-
-		return $str;
+		return trim(preg_replace('/^(.*)$/m', ' =?'.$this->charset.'?Q?$1?=', $str));
 	}
 
 	// --------------------------------------------------------------------
@@ -1285,7 +1234,6 @@
 	/**
 	 * Send Email
 	 *
-	 * @access	public
 	 * @return	bool
 	 */
 	public function send()
@@ -1295,9 +1243,9 @@
 			$this->reply_to($this->_headers['From']);
 		}
 
-		if (( ! isset($this->_recipients) AND ! isset($this->_headers['To']))  AND
-			( ! isset($this->_bcc_array) AND ! isset($this->_headers['Bcc'])) AND
-			( ! isset($this->_headers['Cc'])))
+		if ( ! isset($this->_recipients) && ! isset($this->_headers['To'])
+			&& ! isset($this->_bcc_array) && ! isset($this->_headers['Bcc'])
+			&& ! isset($this->_headers['Cc']))
 		{
 			$this->_set_error_message('lang:email_no_recipients');
 			return FALSE;
@@ -1305,44 +1253,40 @@
 
 		$this->_build_headers();
 
-		if ($this->bcc_batch_mode AND count($this->_bcc_array) > $this->bcc_batch_size)
+		if ($this->bcc_batch_mode && count($this->_bcc_array) > $this->bcc_batch_size)
 		{
 			return $this->batch_bcc_send();
 		}
 
 		$this->_build_message();
-
 		return $this->_spool_email();
 	}
 
 	// --------------------------------------------------------------------
 
 	/**
-	 * Batch Bcc Send.  Sends groups of BCCs in batches
+	 * Batch Bcc Send. Sends groups of BCCs in batches
 	 *
-	 * @access	public
-	 * @return	bool
+	 * @return	void
 	 */
 	public function batch_bcc_send()
 	{
-		$float = $this->bcc_batch_size -1;
-
-		$set = "";
-
+		$float = $this->bcc_batch_size - 1;
+		$set = '';
 		$chunk = array();
 
 		for ($i = 0, $c = count($this->_bcc_array); $i < $c; $i++)
 		{
 			if (isset($this->_bcc_array[$i]))
 			{
-				$set .= ", ".$this->_bcc_array[$i];
+				$set .= ', '.$this->_bcc_array[$i];
 			}
 
 			if ($i == $float)
 			{
 				$chunk[] = substr($set, 1);
 				$float += $this->bcc_batch_size;
-				$set = "";
+				$set = '';
 			}
 
 			if ($i === $c-1)
@@ -1359,7 +1303,7 @@
 
 			if ($this->protocol !== 'smtp')
 			{
-				$this->_set_header('Bcc', implode(", ", $bcc));
+				$this->_set_header('Bcc', implode(', ', $bcc));
 			}
 			else
 			{
@@ -1376,7 +1320,6 @@
 	/**
 	 * Unwrap special elements
 	 *
-	 * @access	protected
 	 * @return	void
 	 */
 	protected function _unwrap_specials()
@@ -1389,7 +1332,6 @@
 	/**
 	 * Strip line-breaks via callback
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _remove_nl_callback($matches)
@@ -1407,7 +1349,6 @@
 	/**
 	 * Spool mail to the mail server
 	 *
-	 * @access	protected
 	 * @return	bool
 	 */
 	protected function _spool_email()
@@ -1429,7 +1370,6 @@
 	/**
 	 * Send using mail()
 	 *
-	 * @access	protected
 	 * @return	bool
 	 */
 	protected function _send_with_mail()
@@ -1451,7 +1391,6 @@
 	/**
 	 * Send using Sendmail
 	 *
-	 * @access	protected
 	 * @return	bool
 	 */
 	protected function _send_with_sendmail()
@@ -1484,7 +1423,6 @@
 	/**
 	 * Send using SMTP
 	 *
-	 * @access	protected
 	 * @return	bool
 	 */
 	protected function _send_with_smtp()
@@ -1553,7 +1491,6 @@
 	/**
 	 * SMTP Connect
 	 *
-	 * @access	protected
 	 * @param	string
 	 * @return	string
 	 */
@@ -1597,7 +1534,6 @@
 	/**
 	 * Send SMTP command
 	 *
-	 * @access	protected
 	 * @param	string
 	 * @param	string
 	 * @return	string
@@ -1670,7 +1606,6 @@
 	/**
 	 *  SMTP Authenticate
 	 *
-	 * @access	protected
 	 * @return	bool
 	 */
 	protected function _smtp_authenticate()
@@ -1680,7 +1615,7 @@
 			return TRUE;
 		}
 
-		if ($this->smtp_user == ""  AND  $this->smtp_pass == "")
+		if ($this->smtp_user == '' && $this->smtp_pass == '')
 		{
 			$this->_set_error_message('lang:email_no_smtp_unpw');
 			return FALSE;
@@ -1724,7 +1659,6 @@
 	/**
 	 * Send SMTP data
 	 *
-	 * @access	protected
 	 * @return	bool
 	 */
 	protected function _send_data($data)
@@ -1743,7 +1677,6 @@
 	/**
 	 * Get SMTP data
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _get_smtp_data()
@@ -1768,12 +1701,11 @@
 	/**
 	 * Get Hostname
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _get_hostname()
 	{
-		return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
+		return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
 	}
 
 	// --------------------------------------------------------------------
@@ -1781,7 +1713,6 @@
 	/**
 	 * Get IP
 	 *
-	 * @access	protected
 	 * @return	string
 	 */
 	protected function _get_ip()
@@ -1791,13 +1722,13 @@
 			return $this->_IP;
 		}
 
-		$cip = (isset($_SERVER['HTTP_CLIENT_IP']) AND $_SERVER['HTTP_CLIENT_IP'] != "") ? $_SERVER['HTTP_CLIENT_IP'] : FALSE;
-		$rip = (isset($_SERVER['REMOTE_ADDR']) AND $_SERVER['REMOTE_ADDR'] != "") ? $_SERVER['REMOTE_ADDR'] : FALSE;
+		$cip = ( ! empty($_SERVER['HTTP_CLIENT_IP'])) ? $_SERVER['HTTP_CLIENT_IP'] : FALSE;
+		$rip = ( ! empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : FALSE;
 		if ($cip) $this->_IP = $cip;
 		elseif ($rip) $this->_IP = $rip;
 		else
 		{
-			$fip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND $_SERVER['HTTP_X_FORWARDED_FOR'] != "") ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE;
+			$fip = ( ! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE;
 			if ($fip)
 			{
 				$this->_IP = $fip;
@@ -1810,7 +1741,7 @@
 			$this->_IP = end($x);
 		}
 
-		if ( ! preg_match( "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $this->_IP))
+		if ( ! preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $this->_IP))
 		{
 			$this->_IP = '0.0.0.0';
 		}
@@ -1823,7 +1754,6 @@
 	/**
 	 * Get Debug Message
 	 *
-	 * @access	public
 	 * @return	string
 	 */
 	public function print_debugger()
@@ -1838,8 +1768,7 @@
 			}
 		}
 
-		$msg .= "<pre>".$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';
-		return $msg;
+		return $msg.'<pre>'.$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';
 	}
 
 	// --------------------------------------------------------------------
@@ -1847,16 +1776,15 @@
 	/**
 	 * Set Message
 	 *
-	 * @access	protected
 	 * @param	string
-	 * @return	string
+	 * @return	void
 	 */
 	protected function _set_error_message($msg, $val = '')
 	{
 		$CI =& get_instance();
 		$CI->lang->load('email');
 
-		if (substr($msg, 0, 5) !== 'lang:' || FALSE === ($line = $CI->lang->line(substr($msg, 5))))
+		if (substr($msg, 0, 5) !== 'lang:' OR FALSE === ($line = $CI->lang->line(substr($msg, 5))))
 		{
 			$this->_debug_msg[] = str_replace('%s', $val, $msg)."<br />";
 		}
@@ -1871,13 +1799,13 @@
 	/**
 	 * Mime Types
 	 *
-	 * @access	protected
 	 * @param	string
 	 * @return	string
 	 */
-	protected function _mime_types($ext = "")
+	protected function _mime_types($ext = '')
 	{
-		$mimes = array(	'hqx'	=>	'application/mac-binhex40',
+		$mimes = array(
+						'hqx'	=>	'application/mac-binhex40',
 						'cpt'	=>	'application/mac-compactpro',
 						'doc'	=>	'application/msword',
 						'bin'	=>	'application/macbinary',
@@ -1966,11 +1894,10 @@
 						'eml'	=>	'message/rfc822'
 					);
 
-		return ( ! isset($mimes[strtolower($ext)])) ? "application/x-unknown-content-type" : $mimes[strtolower($ext)];
+		return isset($mimes[strtolower($ext)]) ? $mimes[strtolower($ext)] : 'application/x-unknown-content-type';
 	}
 
 }
-// END CI_Email class
 
 /* End of file Email.php */
 /* Location: ./system/libraries/Email.php */
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 5ea830f..9826eab 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -251,7 +251,7 @@
 		}
 		else
 		{
-			if (function_exists('realpath') && @realpath($this->new_image) !== FALSE)
+			if (strpos($this->new_image, '/') === FALSE AND strpos($this->new_image, '\\') === FALSE)
 			{
 				$full_dest_path = str_replace('\\', '/', realpath($this->new_image));
 			}
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index 0c63886..ac29c1b 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -1,4 +1,4 @@
-<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
+<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 /**
  * CodeIgniter
  *
@@ -593,16 +593,17 @@
 	/**
 	 * Verify that the filetype is allowed
 	 *
+	 * @param	bool
 	 * @return	bool
 	 */
 	public function is_allowed_filetype($ignore_mime = FALSE)
 	{
-		if ($this->allowed_types == '*')
+		if ($this->allowed_types === '*')
 		{
 			return TRUE;
 		}
 
-		if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types))
+		if ( ! is_array($this->allowed_types) OR count($this->allowed_types) === 0)
 		{
 			$this->set_error('upload_no_file_types');
 			return FALSE;
@@ -618,12 +619,9 @@
 		// Images get some additional checks
 		$image_types = array('gif', 'jpg', 'jpeg', 'png', 'jpe');
 
-		if (in_array($ext, $image_types))
+		if (in_array($ext, $image_types) && @getimagesize($this->file_temp) === FALSE)
 		{
-			if (getimagesize($this->file_temp) === FALSE)
-			{
-				return FALSE;
-			}
+			return FALSE;
 		}
 
 		if ($ignore_mime === TRUE)
@@ -640,7 +638,7 @@
 				return TRUE;
 			}
 		}
-		elseif ($mime == $this->file_type)
+		elseif ($mime === $this->file_type)
 		{
 				return TRUE;
 		}
@@ -960,7 +958,7 @@
 			}
 			elseif (is_file(APPPATH.'config/mimes.php'))
 			{
-				include(APPPATH.'config//mimes.php');
+				include(APPPATH.'config/mimes.php');
 			}
 			else
 			{
@@ -1026,47 +1024,104 @@
 	 */
 	protected function _file_mime_type($file)
 	{
-		// Use if the Fileinfo extension, if available (only versions above 5.3 support the FILEINFO_MIME_TYPE flag)
-		if ( (float) substr(phpversion(), 0, 3) >= 5.3 && function_exists('finfo_file'))
+		// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
+		$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
+
+		/* Fileinfo extension - most reliable method
+		 *
+		 * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
+		 * more convenient FILEINFO_MIME_TYPE flag doesn't exist.
+		 */
+		if (function_exists('finfo_file'))
 		{
-			$finfo = new finfo(FILEINFO_MIME_TYPE);
-			if ($finfo !== FALSE) // This is possible, if there is no magic MIME database file found on the system
+			$finfo = finfo_open(FILEINFO_MIME);
+			if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
 			{
-				$file_type = $finfo->file($file['tmp_name']);
+				$mime = @finfo_file($finfo, $file['tmp_name']);
+				finfo_close($finfo);
 
 				/* According to the comments section of the PHP manual page,
 				 * it is possible that this function returns an empty string
 				 * for some files (e.g. if they don't exist in the magic MIME database)
 				 */
-				if (strlen($file_type) > 1)
+				if (is_string($mime) && preg_match($regexp, $mime, $matches))
 				{
-					$this->file_type = $file_type;
+					$this->file_type = $matches[1];
 					return;
 				}
 			}
 		}
 
-		// Fall back to the deprecated mime_content_type(), if available
+		/* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
+		 * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
+		 * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
+		 * than mime_content_type() as well, hence the attempts to try calling the command line with
+		 * three different functions.
+		 *
+		 * Notes:
+		 *	- the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
+		 *	- many system admins would disable the exec(), shell_exec(), popen() and similar functions
+		 *	  due to security concerns, hence the function_exists() checks
+		 */
+		if (DIRECTORY_SEPARATOR !== '\\')
+		{
+			$cmd = 'file --brief --mime ' . escapeshellarg($file['tmp_name']) . ' 2>&1';
+
+			if (function_exists('exec'))
+			{
+				/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
+				 * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
+				 * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
+				 * value, which is only put to allow us to get the return status code.
+				 */
+				$mime = @exec($cmd, $mime, $return_status);
+				if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))
+				{
+					$this->file_type = $matches[1];
+					return;
+				}
+			}
+
+			if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec'))
+			{
+				$mime = @shell_exec($cmd);
+				if (strlen($mime) > 0)
+				{
+					$mime = explode("\n", trim($mime));
+					if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
+					{
+						$this->file_type = $matches[1];
+						return;
+					}
+				}
+			}
+
+			if (function_exists('popen'))
+			{
+				$proc = @popen($cmd, 'r');
+				if (is_resource($proc))
+				{
+					$mime = @fread($test, 512);
+					@pclose($proc);
+					if ($mime !== FALSE)
+					{
+						$mime = explode("\n", trim($mime));
+						if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
+						{
+							$this->file_type = $matches[1];
+							return;
+						}
+					}
+				}
+			}
+		}
+
+		// Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
 		if (function_exists('mime_content_type'))
 		{
 			$this->file_type = @mime_content_type($file['tmp_name']);
-			return;
-		}
-
-		/* This is an ugly hack, but UNIX-type systems provide a native way to detect the file type,
-		 * which is still more secure than depending on the value of $_FILES[$field]['type'].
-		 *
-		 * Notes:
-		 *	- a 'W' in the substr() expression bellow, would mean that we're using Windows
-		 *	- many system admins would disable the exec() function due to security concerns, hence the function_exists() check
-		 */
-		if (DIRECTORY_SEPARATOR !== '\\' && function_exists('exec'))
-		{
-			$output = array();
-			@exec('file --brief --mime-type ' . escapeshellarg($file['tmp_path']), $output, $return_code);
-			if ($return_code === 0 && strlen($output[0]) > 0) // A return status code != 0 would mean failed execution
+			if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
 			{
-				$this->file_type = rtrim($output[0]);
 				return;
 			}
 		}
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 9ed53ba..ecf6afc 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -28,6 +28,7 @@
    -  Added support for 3gp, 3g2, mp4, wmv, f4v, vlc Video files to mimes.php.
    -  Added support for m4a, aac, m4u, xspf, au, ac3, flac, ogg Audio files to mimes.php.
    -  Added support for kmz and kml (Google Earth) files to mimes.php.
+   -  Updated support for doc files in mimes.php.
    -  Added application/xml for xml and application/xml, text/xsl for xsl in mimes.php.
    -  Changed logger to only chmod when file is first created.
    -  Removed previously deprecated SHA1 Library.
@@ -39,6 +40,8 @@
    -  url_title() will now trim extra dashes from beginning and end.
    -  Added XHTML Basic 1.1 doctype to :doc:`HTML Helper <helpers/html_helper>`.
    -  Changed humanize to include a second param for the separator.
+   -  Refactored ``plural()`` and ``singular()`` to avoid double pluralization and support more words.
+   -  Added an optional third parameter to ``force_download()`` that enables/disables sending the actual file MIME type in the Content-Type header (disabled by default).
 
 -  Database
 
@@ -109,11 +112,41 @@
 -  Fixed a bug (#1039) - MySQL's _backup() method failed due to a table name not being escaped.
 -  Fixed a bug (#1070) - CI_DB_driver::initialize() didn't set a character set if a database is not selected.
 -  Fixed a bug (#177) - CI_Form_validation::set_value() didn't set the default value if POST data is NULL.
+-  Fixed a bug (#68, #414) - Oracle's escape_str() didn't properly escape LIKE wild characters.
+-  Fixed a bug (#81) - ODBC's list_fields() and field_data() methods skipped the first column due to odbc_field_*() functions' index starting at 1 instead of 0.
+-  Fixed a bug (#129) - ODBC's num_rows() returned -1 in some cases, due to not all subdrivers supporting the odbc_num_rows() function.
+-  Fixed a bug (#153) - E_NOTICE being generated by getimagesize() in the :doc:`File Uploading Library <libraries/file_uploading>`.
+-  Fixed a bug (#611) - SQLSRV's _error_message() and _error_number() methods used to issue warnings when there's no actual error.
+-  Fixed a bug (#1036) - is_write_type() method in the :doc:`Database Library <database/index>` didn't return TRUE for RENAME and OPTIMIZE queries.
+-  Fixed a bug in PDO's _version() method where it used to return the client version as opposed to the server one.
+-  Fixed a bug in PDO's insert_id() method where it could've failed if it's used with Postgre versions prior to 8.1.
+-  Fixed a bug in CUBRID's affected_rows() method where a connection resource was passed to cubrid_affected_rows() instead of a result.
+-  Fixed a bug (#638) - db_set_charset() ignored its arguments and always used the configured charset and collation instead.
+
+Version 2.1.1
+=============
+
+Release Date: Not Released
+
+-  General Changes
+   -  Fixed support for docx, xlsx files in mimes.php.
+
+-  Libraries
+   -  Further improved MIME type detection in the :doc:`File Uploading Library <libraries/file_uploading>`.
+
+
+Bug fixes for 2.1.1
+-------------------
+
+-  Fixed a bug (#697) - A wrong array key was used in the Upload library to check for mime-types.
+-  Fixed a bug - form_open() compared $action against site_url() instead of base_url().
+-  Fixed a bug - CI_Upload::_file_mime_type() could've failed if mime_content_type() is used for the detection and returns FALSE.
+-  Fixed a bug (#538) - Windows paths were ignored when using the :doc:`Image Manipulation Library <libraries/image_lib>` to create a new file.
 
 Version 2.1.0
 =============
 
-Release Date: Not Released
+Release Date: November 14, 2011
 
 -  General Changes