summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--composer.json2
-rw-r--r--system/helpers/url_helper.php21
-rw-r--r--system/libraries/Email.php158
-rwxr-xr-xsystem/libraries/Session/drivers/Session_cookie.php47
-rwxr-xr-xsystem/libraries/Session/drivers/Session_native.php20
-rw-r--r--tests/codeigniter/core/Config_test.php197
-rw-r--r--tests/codeigniter/helpers/captcha_helper_test.php10
-rw-r--r--tests/codeigniter/helpers/cookie_helper_test.php59
-rw-r--r--tests/codeigniter/helpers/download_helper_test.php10
-rw-r--r--tests/codeigniter/helpers/language_helper_test.php14
-rw-r--r--tests/codeigniter/helpers/security_helper_test.php64
-rw-r--r--tests/codeigniter/helpers/url_helper_test.php2
-rw-r--r--tests/codeigniter/libraries/Upload_test.php10
-rw-r--r--tests/phpunit.xml7
-rw-r--r--user_guide_src/source/changelog.rst26
-rw-r--r--user_guide_src/source/libraries/javascript.rst2
-rw-r--r--user_guide_src/source/libraries/output.rst2
17 files changed, 482 insertions, 169 deletions
diff --git a/composer.json b/composer.json
index dc098acc3..7d60020c3 100644
--- a/composer.json
+++ b/composer.json
@@ -3,6 +3,6 @@
"mikey179/vfsStream": "*"
},
"require-dev": {
- "EHER/PHPUnit": "*"
+ "phpunit/phpunit": "*"
}
} \ No newline at end of file
diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php
index b1f5eccf1..de5bdec31 100644
--- a/system/helpers/url_helper.php
+++ b/system/helpers/url_helper.php
@@ -388,40 +388,43 @@ if ( ! function_exists('auto_link'))
for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
{
- if (preg_match('|\.$|', $matches[6][$i]))
+ if (preg_match('/(\.|\,)$/i', $matches[6][$i], $m))
{
- $period = '.';
+ $punct = $m[1];
$matches[6][$i] = substr($matches[6][$i], 0, -1);
}
else
{
- $period = '';
+ $punct = '';
}
$str = str_replace($matches[0][$i],
$matches[1][$i].'<a href="http'.$matches[4][$i].'://'
.$matches[5][$i].$matches[6][$i].'"'.$pop.'>http'
.$matches[4][$i].'://'.$matches[5][$i]
- .$matches[6][$i].'</a>'.$period,
+ .$matches[6][$i].'</a>'.$punct,
$str);
}
}
- if ($type !== 'url' && preg_match_all('/([a-zA-Z0-9_\.\-\+]+)@([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-\.]*)/i', $str, $matches))
+ if ($type !== 'url' && preg_match_all('/([a-zA-Z0-9_\.\-\+]+)@([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-\.]+)/i', $str, $matches))
{
for ($i = 0, $c = count($matches); $i < $c; $i++)
{
- if (preg_match('|\.$|', $matches[3][$i]))
+ if (preg_match('/(\.|\,)$/i', $matches[3][$i], $m))
{
- $period = '.';
+ $punct = $m[1];
$matches[3][$i] = substr($matches[3][$i], 0, -1);
}
else
{
- $period = '';
+ $punct = '';
}
- $str = str_replace($matches[0][$i], safe_mailto($matches[1][$i].'@'.$matches[2][$i].'.'.$matches[3][$i]).$period, $str);
+ if (filter_var(($m = $matches[1][$i].'@'.$matches[2][$i].'.'.$matches[3][$i]), FILTER_VALIDATE_EMAIL) !== FALSE)
+ {
+ $str = str_replace($matches[0][$i], safe_mailto($m).$punct, $str);
+ }
}
}
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index 08057f2f7..c1130e915 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -98,7 +98,7 @@ class CI_Email {
*/
public function __construct($config = array())
{
- $this->charset = strtoupper(config_item('charset'));
+ $this->charset = config_item('charset');
if (count($config) > 0)
{
@@ -110,6 +110,8 @@ class CI_Email {
$this->_safe_mode = (bool) @ini_get('safe_mode');
}
+ $this->charset = strtoupper($this->charset);
+
log_message('debug', 'Email Class Initialized');
}
@@ -186,11 +188,11 @@ class CI_Email {
/**
* Set FROM
*
- * @param string
- * @param string
+ * @param string From
+ * @param string Return-Path
* @return object
*/
- public function from($from, $name = '', $return_path = '')
+ public function from($from, $name = '', $return_path = NULL)
{
if (preg_match('/\<(.*)\>/', $from, $match))
{
@@ -217,16 +219,13 @@ class CI_Email {
}
else
{
- $name = $this->_prep_q_encoding($name, TRUE);
+ $name = $this->_prep_q_encoding($name);
}
}
$this->set_header('From', $name.' <'.$from.'>');
- if( ! $return_path)
- {
- $return_path = $from;
- }
+ isset($return_path) OR $return_path = $from;
$this->set_header('Return-Path', '<'.$return_path.'>');
return $this;
@@ -292,16 +291,7 @@ class CI_Email {
$this->set_header('To', implode(', ', $to));
}
- switch ($this->_get_protocol())
- {
- case 'smtp':
- $this->_recipients = $to;
- break;
- case 'sendmail':
- case 'mail':
- $this->_recipients = implode(', ', $to);
- break;
- }
+ $this->_recipients = $to;
return $this;
}
@@ -752,8 +742,8 @@ class CI_Email {
/**
* Build alternative plain text message
*
- * This public function provides the raw message for use
- * in plain-text headers of HTML-formatted emails.
+ * Provides the raw message for use in plain-text headers of
+ * HTML-formatted emails.
* If the user hasn't specified his own alternative message
* it creates one by stripping the HTML
*
@@ -761,9 +751,11 @@ class CI_Email {
*/
protected function _get_alt_message()
{
- if ($this->alt_message !== '')
+ if ( ! empty($this->alt_message))
{
- return $this->word_wrap($this->alt_message, '76');
+ return ($this->wordwrap)
+ ? $this->word_wrap($this->alt_message, 76)
+ : $this->alt_message;
}
$body = preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match) ? $match[1] : $this->_body;
@@ -774,7 +766,9 @@ class CI_Email {
$body = str_replace(str_repeat("\n", $i), "\n\n", $body);
}
- return $this->word_wrap($body, 76);
+ return ($this->wordwrap)
+ ? $this->word_wrap($body, 76)
+ : $body;
}
// --------------------------------------------------------------------
@@ -783,15 +777,15 @@ class CI_Email {
* Word Wrap
*
* @param string
- * @param int
+ * @param int line-length limit
* @return string
*/
- public function word_wrap($str, $charlim = '')
+ public function word_wrap($str, $charlim = NULL)
{
- // Se the character limit
- if ($charlim === '')
+ // Set the character limit, if not already present
+ if (empty($charlim))
{
- $charlim = ($this->wrapchars === '') ? 76 : $this->wrapchars;
+ $charlim = empty($this->wrapchars) ? 76 : $this->wrapchars;
}
// Reduce multiple spaces
@@ -1105,6 +1099,10 @@ class CI_Email {
*/
protected function _prep_quoted_printable($str)
{
+ // We are intentionally wrapping so mail servers will encode characters
+ // properly and MUAs will behave, so {unwrap} must go!
+ $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
+
// RFC 2045 specifies CRLF as "\r\n".
// However, many developers choose to override that and violate
// the RFC rules due to (apparently) a bug in MS Exchange,
@@ -1130,10 +1128,6 @@ class CI_Email {
$str = str_replace(array("\r\n", "\r"), "\n", $str);
}
- // We are intentionally wrapping so mail servers will encode characters
- // properly and MUAs will behave, so {unwrap} must go!
- $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
-
$escape = '=';
$output = '';
@@ -1186,66 +1180,75 @@ class CI_Email {
/**
* Prep Q Encoding
*
- * 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
+ * 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.
*
* @param string
- * @param bool set to TRUE for processing From: headers
* @return string
*/
- protected function _prep_q_encoding($str, $from = FALSE)
+ protected function _prep_q_encoding($str)
{
- $str = str_replace(array("\r", "\n"), array('', ''), $str);
-
- // Line length must not exceed 76 characters, so we adjust for
- // a space, 7 extra characters =??Q??=, and the charset that we will add to each line
- $limit = 75 - 7 - strlen($this->charset);
+ $str = str_replace(array("\r", "\n"), '', $str);
- // these special characters must be converted too
- $convert = array('_', '=', '?');
-
- if ($from === TRUE)
+ if ($this->charset === 'UTF-8')
{
- $convert[] = ',';
- $convert[] = ';';
+ if (MB_ENABLED === TRUE)
+ {
+ return mb_encode_mimeheader($str, $this->charset, 'Q', $this->crlf);
+ }
+ elseif (extension_loaded('iconv'))
+ {
+ $output = @iconv_mime_encode('', $str,
+ array(
+ 'scheme' => 'Q',
+ 'line-length' => 76,
+ 'input-charset' => $this->charset,
+ 'output-charset' => $this->charset,
+ 'line-break-chars' => $this->crlf
+ )
+ );
+
+ // There are reports that iconv_mime_encode() might fail and return FALSE
+ if ($output !== FALSE)
+ {
+ // iconv_mime_encode() will always put a header field name.
+ // We've passed it an empty one, but it still prepends our
+ // encoded string with ': ', so we need to strip it.
+ return substr($output, 2);
+ }
+
+ $chars = iconv_strlen($str, 'UTF-8');
+ }
}
- $output = '';
- $temp = '';
+ // We might already have this set for UTF-8
+ isset($chars) OR $chars = strlen($str);
- for ($i = 0, $length = strlen($str); $i < $length; $i++)
+ $output = '=?'.$this->charset.'?Q?';
+ for ($i = 0, $length = strlen($output), $iconv = extension_loaded('iconv'); $i < $chars; $i++)
{
- // Grab the next character
- $char = $str[$i];
- $ascii = ord($char);
+ $chr = ($this->charset === 'UTF-8' && $iconv === TRUE)
+ ? '='.implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2))
+ : '='.strtoupper(bin2hex($str[$i]));
- // convert ALL non-printable ASCII characters and our specials
- if ($ascii < 32 OR $ascii > 126 OR in_array($char, $convert))
+ // RFC 2045 sets a limit of 76 characters per line.
+ // We'll append ?= to the end of each line though.
+ if ($length + ($l = strlen($chr)) > 74)
{
- $char = '='.dechex($ascii);
+ $output .= '?='.$this->crlf // EOL
+ .' =?'.$this->charset.'?Q?'.$chr; // New line
+ $length = 6 + strlen($this->charset) + $l; // Reset the length for the new line
}
-
- // handle regular spaces a bit more compactly than =20
- if ($ascii === 32)
- {
- $char = '_';
- }
-
- // If we're at the character limit, add the line to the output,
- // reset our temp variable, and keep on chuggin'
- if ((strlen($temp) + strlen($char)) >= $limit)
+ else
{
- $output .= $temp.$this->crlf;
- $temp = '';
+ $output .= $chr;
+ $length += $l;
}
-
- // Add the character to our temporary line
- $temp .= $char;
}
- // wrap each line with the shebang, charset, and transfer encoding
- // the preceding space on successive lines is required for header "folding"
- return trim(preg_replace('/^(.*?)(\r*)$/m', ' =?'.$this->charset.'?Q?$1?=$2', $output.$temp));
+ // End the header
+ return $output.'?=';
}
// --------------------------------------------------------------------
@@ -1408,6 +1411,11 @@ class CI_Email {
*/
protected function _send_with_mail()
{
+ if (is_array($this->_recipients))
+ {
+ $this->_recipients = implode(', ', $this->_recipients);
+ }
+
if ($this->_safe_mode === TRUE)
{
return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str);
diff --git a/system/libraries/Session/drivers/Session_cookie.php b/system/libraries/Session/drivers/Session_cookie.php
index 5bb1f7aa6..8617aec2d 100755
--- a/system/libraries/Session/drivers/Session_cookie.php
+++ b/system/libraries/Session/drivers/Session_cookie.php
@@ -308,7 +308,7 @@ class CI_Session_cookie extends CI_Session_driver {
}
// Kill the cookie
- $this->_setcookie($this->sess_cookie_name, addslashes(serialize(array())), ($this->now - 31500000),
+ $this->_setcookie($this->sess_cookie_name, '', ($this->now - 31500000),
$this->cookie_path, $this->cookie_domain, 0);
// Kill session data
@@ -372,27 +372,31 @@ class CI_Session_cookie extends CI_Session_driver {
return FALSE;
}
+ $len = strlen($session) - 40;
+
+ if ($len < 0)
+ {
+ log_message('debug', 'The session cookie was not signed.');
+ return FALSE;
+ }
+
+ // Check cookie authentication
+ $hmac = substr($session, $len);
+ $session = substr($session, 0, $len);
+
+ if ($hmac !== hash_hmac('sha1', $session, $this->encryption_key))
+ {
+ log_message('error', 'The session cookie data did not match what was expected.');
+ $this->sess_destroy();
+ return FALSE;
+ }
+
// Check for encryption
if ($this->sess_encrypt_cookie === TRUE)
{
// Decrypt the cookie data
$session = $this->CI->encrypt->decode($session);
}
- else
- {
- // Encryption was not used, so we need to check the md5 hash in the last 32 chars
- $len = strlen($session)-32;
- $hash = substr($session, $len);
- $session = substr($session, 0, $len);
-
- // Does the md5 hash match? This is to prevent manipulation of session data in userspace
- if ($hash !== md5($session.$this->encryption_key))
- {
- log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.');
- $this->sess_destroy();
- return FALSE;
- }
- }
// Unserialize the session array
$session = $this->_unserialize($session);
@@ -658,10 +662,13 @@ class CI_Session_cookie extends CI_Session_driver {
// Serialize the userdata for the cookie
$cookie_data = $this->_serialize($cookie_data);
- $cookie_data = ($this->sess_encrypt_cookie === TRUE)
- ? $this->CI->encrypt->encode($cookie_data)
- // if encryption is not used, we provide an md5 hash to prevent userside tampering
- : $cookie_data.md5($cookie_data.$this->encryption_key);
+ if ($this->sess_encrypt_cookie === TRUE)
+ {
+ $cookie_data = $this->CI->encrypt->encode($cookie_data);
+ }
+
+ // Require message authentication
+ $cookie_data .= hash_hmac('sha1', $cookie_data, $this->encryption_key);
$expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
diff --git a/system/libraries/Session/drivers/Session_native.php b/system/libraries/Session/drivers/Session_native.php
index 6529d4c36..da744f39b 100755
--- a/system/libraries/Session/drivers/Session_native.php
+++ b/system/libraries/Session/drivers/Session_native.php
@@ -55,7 +55,9 @@ class CI_Session_native extends CI_Session_driver {
'sess_time_to_update',
'cookie_prefix',
'cookie_path',
- 'cookie_domain'
+ 'cookie_domain',
+ 'cookie_secure',
+ 'cookie_httponly'
);
foreach ($prefs as $key)
@@ -82,6 +84,9 @@ class CI_Session_native extends CI_Session_driver {
$expire = 7200;
$path = '/';
$domain = '';
+ $secure = (bool) $config['cookie_secure'];
+ $http_only = (bool) $config['cookie_httponly'];
+
if ($config['sess_expiration'] !== FALSE)
{
// Default to 2 years if expiration is "0"
@@ -99,7 +104,8 @@ class CI_Session_native extends CI_Session_driver {
// Use specified domain
$domain = $config['cookie_domain'];
}
- session_set_cookie_params($config['sess_expire_on_close'] ? 0 : $expire, $path, $domain);
+
+ session_set_cookie_params($config['sess_expire_on_close'] ? 0 : $expire, $path, $domain, $secure, $http_only);
// Start session
session_start();
@@ -137,8 +143,12 @@ class CI_Session_native extends CI_Session_driver {
if ($config['sess_time_to_update'] && isset($_SESSION['last_activity'])
&& ($_SESSION['last_activity'] + $config['sess_time_to_update']) < $now)
{
- // Regenerate ID, but don't destroy session
- $this->sess_regenerate(FALSE);
+ // Changing the session ID amidst a series of AJAX calls causes problems
+ if( ! $this->CI->input->is_ajax_request())
+ {
+ // Regenerate ID, but don't destroy session
+ $this->sess_regenerate(FALSE);
+ }
}
// Set activity time
@@ -189,7 +199,7 @@ class CI_Session_native extends CI_Session_driver {
{
// Clear session cookie
$params = session_get_cookie_params();
- setcookie($name, '', time() - 42000, $params['path'], $params['domain']);
+ setcookie($name, '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
unset($_COOKIE[$name]);
}
session_destroy();
diff --git a/tests/codeigniter/core/Config_test.php b/tests/codeigniter/core/Config_test.php
index 80e0862ff..d652a625e 100644
--- a/tests/codeigniter/core/Config_test.php
+++ b/tests/codeigniter/core/Config_test.php
@@ -7,11 +7,12 @@ class Config_test extends CI_TestCase {
$cls =& $this->ci_core_class('cfg');
// set predictable config values
- $this->ci_set_config(array(
+ $this->cfg = array(
'index_page' => 'index.php',
'base_url' => 'http://example.com/',
'subclass_prefix' => 'MY_'
- ));
+ );
+ $this->ci_set_config($this->cfg);
$this->config = new $cls;
}
@@ -20,7 +21,7 @@ class Config_test extends CI_TestCase {
public function test_item()
{
- $this->assertEquals('http://example.com/', $this->config->item('base_url'));
+ $this->assertEquals($this->cfg['base_url'], $this->config->item('base_url'));
// Bad Config value
$this->assertFalse($this->config->item('no_good_item'));
@@ -48,36 +49,103 @@ class Config_test extends CI_TestCase {
// Bad Config value
$this->assertFalse($this->config->slash_item('no_good_item'));
- $this->assertEquals('http://example.com/', $this->config->slash_item('base_url'));
+ $this->assertEquals($this->cfg['base_url'], $this->config->slash_item('base_url'));
- $this->assertEquals('MY_/', $this->config->slash_item('subclass_prefix'));
+ $this->assertEquals($this->cfg['subclass_prefix'].'/', $this->config->slash_item('subclass_prefix'));
}
// --------------------------------------------------------------------
- public function test_site_url()
+ public function test_base_url()
{
- $this->assertEquals('http://example.com/index.php', $this->config->site_url());
+ // Test regular base URL
+ $base_url = $this->cfg['base_url'];
+ $this->assertEquals($base_url, $this->config->base_url());
+
+ // Test with URI
+ $uri = 'test';
+ $this->assertEquals($base_url.$uri, $this->config->base_url($uri));
+
+ // Clear base_url
+ $this->ci_set_config('base_url', '');
+
+ // Rerun constructor
+ $cls =& $this->ci_core_class('cfg');
+ $this->config = new $cls;
- $base_url = $this->config->item('base_url');
+ // Test default base
+ $this->assertEquals('http://localhost/', $this->config->base_url());
+ // Capture server vars
+ $old_host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : NULL;
+ $old_script = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL;
+ $old_https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : NULL;
+
+ // Setup server vars for detection
+ $host = 'test.com';
+ $path = '/path/';
+ $script = 'base_test.php';
+ $_SERVER['HTTP_HOST'] = $host;
+ $_SERVER['SCRIPT_NAME'] = $path.$script;
+
+ // Rerun constructor
+ $this->config = new $cls;
+
+ // Test plain detected
+ $this->assertEquals('http://'.$host.$path, $this->config->base_url());
+
+ // Rerun constructor
+ $_SERVER['HTTPS'] = 'on';
+ $this->config = new $cls;
+
+ // Test secure detected
+ $this->assertEquals('https://'.$host.$path, $this->config->base_url());
+
+ // Restore server vars
+ if ($old_host === NULL) unset($_SERVER['HTTP_HOST']);
+ else $_SERVER['HTTP_HOST'] = $old_host;
+ if ($old_script === NULL) unset($_SERVER['SCRIPT_NAME']);
+ else $_SERVER['SCRIPT_NAME'] = $old_script;
+ if ($old_https === NULL) unset($_SERVER['HTTPS']);
+ else $_SERVER['HTTPS'] = $old_https;
+ }
+
+ // --------------------------------------------------------------------
+
+ public function test_site_url()
+ {
+ $base_url = $this->cfg['base_url'];
+ $index_page = $this->cfg['index_page'];
+ $this->assertEquals($base_url.$index_page, $this->config->site_url());
+
+ $old_base = $this->config->item('base_url');
$this->config->set_item('base_url', '');
$q_string = $this->config->item('enable_query_strings');
-
$this->config->set_item('enable_query_strings', FALSE);
- $this->assertEquals('index.php/test', $this->config->site_url('test'));
- $this->assertEquals('index.php/test/1', $this->config->site_url(array('test', '1')));
+ $uri= 'test';
+ $uri2 = '1';
+ $this->assertEquals($index_page.'/'.$uri, $this->config->site_url($uri));
+ $this->assertEquals($index_page.'/'.$uri.'/'.$uri2, $this->config->site_url(array($uri, $uri2)));
+
+ $suffix = 'ing';
+ $this->config->set_item('url_suffix', $suffix);
+
+ $arg = 'pass';
+ $this->assertEquals($index_page.'/'.$uri.$suffix, $this->config->site_url($uri));
+ $this->assertEquals($index_page.'/'.$uri.$suffix.'?'.$arg, $this->config->site_url($uri.'?'.$arg));
+
+ $this->config->set_item('url_suffix', FALSE);
$this->config->set_item('enable_query_strings', TRUE);
- $this->assertEquals('index.php?test', $this->config->site_url('test'));
- $this->assertEquals('index.php?0=test&1=1', $this->config->site_url(array('test', '1')));
+ $this->assertEquals($index_page.'?'.$uri, $this->config->site_url($uri));
+ $this->assertEquals($index_page.'?0='.$uri.'&1='.$uri2, $this->config->site_url(array($uri, $uri2)));
- $this->config->set_item('base_url', $base_url);
+ $this->config->set_item('base_url', $old_base);
- $this->assertEquals('http://example.com/index.php?test', $this->config->site_url('test'));
+ $this->assertEquals($base_url.$index_page.'?'.$uri, $this->config->site_url($uri));
// back to home base
$this->config->set_item('enable_query_strings', $q_string);
@@ -87,49 +155,100 @@ class Config_test extends CI_TestCase {
public function test_system_url()
{
- $this->assertEquals('http://example.com/system/', $this->config->system_url());
+ $this->assertEquals($this->cfg['base_url'].'system/', $this->config->system_url());
}
// --------------------------------------------------------------------
public function test_load()
{
- // Create config files in VFS
- $file1 = 'test.php';
- $file2 = 'secttest';
- $key1 = 'testconfig';
- $val1 = 'my_value';
- $cfg1 = array(
- $key1 => $val1
- );
- $cfg2 = array(
+ // Test regular load
+ $file = 'test.php';
+ $key = 'testconfig';
+ $val = 'my_value';
+ $cfg = array($key => $val);
+ $this->ci_vfs_create($file, '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');
+ $this->assertTrue($this->config->load($file));
+ $this->assertEquals($val, $this->config->item($key));
+
+ // Test reload - value should not change
+ $val2 = 'new_value';
+ $cfg = array($key => $val2);
+ $this->ci_vfs_create($file, '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');
+ $this->assertTrue($this->config->load($file));
+ $this->assertEquals($val, $this->config->item($key));
+
+ // Test section load
+ $file = 'secttest';
+ $cfg = array(
'one' => 'prime',
'two' => 2,
'three' => true
);
- $this->ci_vfs_create(array(
- $file1 => '<?php $config = '.var_export($cfg1, TRUE).';',
- $file2.'.php' => '<?php $config = '.var_export($cfg2, TRUE).';'
- ), '', $this->ci_app_root, 'config');
+ $this->ci_vfs_create($file.'.php', '<?php $config = '.var_export($cfg, TRUE).';', $this->ci_app_root, 'config');
+ $this->assertTrue($this->config->load($file, TRUE));
+ $this->assertEquals($cfg, $this->config->item($file));
- // Test regular load
- $this->assertTrue($this->config->load($file1));
- $this->assertEquals($val1, $this->config->item($key1));
+ // Test section merge
+ $cfg2 = array(
+ 'three' => 'tres',
+ 'number' => 42,
+ 'letter' => 'Z'
+ );
+ $pkg_dir = 'package';
+ $this->ci_vfs_create($file.'.php', '<?php $config = '.var_export($cfg2, TRUE).';', $this->ci_app_root,
+ array($pkg_dir, 'config'));
+ array_push($this->config->_config_paths, $this->ci_vfs_path($pkg_dir.'/', APPPATH));
+ $this->assertTrue($this->config->load($file, TRUE));
+ $this->assertEquals(array_merge($cfg, $cfg2), $this->config->item($file));
+ array_pop($this->config->_config_paths);
+
+ // Test graceful fail of invalid file
+ $file = 'badfile';
+ $this->ci_vfs_create($file, '', $this->ci_app_root, 'config');
+ $this->assertFalse($this->config->load($file, FALSE, TRUE));
+
+ // Test regular fail of invalid file
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'CI Error: Your '.$this->ci_vfs_path('config/'.$file.'.php', APPPATH).
+ ' file does not appear to contain a valid configuration array.'
+ );
+ $this->assertNull($this->config->load($file));
+ }
- // Test section load
- $this->assertTrue($this->config->load($file2, TRUE));
- $this->assertEquals($cfg2, $this->config->item($file2));
+ // --------------------------------------------------------------------
- // Test graceful fail
+ public function test_load_nonexistent()
+ {
+ // Test graceful fail of nonexistent file
$this->assertFalse($this->config->load('not_config_file', FALSE, TRUE));
// Test regular fail
- $file3 = 'absentia';
+ $file = 'absentia';
$this->setExpectedException(
'RuntimeException',
- 'CI Error: The configuration file '.$file3.'.php does not exist.'
+ 'CI Error: The configuration file '.$file.'.php does not exist.'
+ );
+ $this->assertNull($this->config->load($file));
+ }
+
+ // --------------------------------------------------------------------
+
+ public function test_assign_to_config()
+ {
+ $key1 = 'test';
+ $key2 = '1';
+ $val1 = 'foo';
+ $val2 = 'bar';
+ $cfg = array(
+ $key1 => $val1,
+ $key2 => $val2
);
- $this->assertNull($this->config->load($file3));
+
+ $this->assertNull($this->config->_assign_to_config($cfg));
+ $this->assertEquals($val1, $this->config->item($key1));
+ $this->assertEquals($val2, $this->config->item($key2));
}
} \ No newline at end of file
diff --git a/tests/codeigniter/helpers/captcha_helper_test.php b/tests/codeigniter/helpers/captcha_helper_test.php
new file mode 100644
index 000000000..fc86305e3
--- /dev/null
+++ b/tests/codeigniter/helpers/captcha_helper_test.php
@@ -0,0 +1,10 @@
+<?php
+
+class Captcha_helper_test extends CI_TestCase {
+
+ public function test_create_captcha()
+ {
+ $this->markTestSkipped('Cant easily test');
+ }
+
+} \ No newline at end of file
diff --git a/tests/codeigniter/helpers/cookie_helper_test.php b/tests/codeigniter/helpers/cookie_helper_test.php
new file mode 100644
index 000000000..fba68f20f
--- /dev/null
+++ b/tests/codeigniter/helpers/cookie_helper_test.php
@@ -0,0 +1,59 @@
+<?php
+
+class Cookie_helper_test extends CI_TestCase {
+
+ public function set_up()
+ {
+ $this->helper('cookie');
+ }
+
+ // ------------------------------------------------------------------------
+
+ function test_set_cookie()
+ {
+ /*$input_cls = $this->ci_core_class('input');
+ $this->ci_instance_var('input', new $input_cls);
+
+ $this->assertTrue(set_cookie(
+ 'my_cookie',
+ 'foobar'
+ ));*/
+
+ $this->markTestSkipped('Need to find a way to overcome a headers already set exception');
+ }
+
+ // ------------------------------------------------------------------------
+
+ function test_get_cookie()
+ {
+ $_COOKIE['foo'] = 'bar';
+
+ $security = new Mock_Core_Security();
+ $utf8 = new Mock_Core_Utf8();
+ $input_cls = $this->ci_core_class('input');
+ $this->ci_instance_var('input', new Mock_Core_Input($security, $utf8));
+
+ $this->assertEquals('bar', get_cookie('foo', FALSE));
+ $this->assertEquals('bar', get_cookie('foo', TRUE));
+
+ $_COOKIE['bar'] = "Hello, i try to <script>alert('Hack');</script> your site";
+
+ $this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", get_cookie('bar', TRUE));
+ $this->assertEquals("Hello, i try to <script>alert('Hack');</script> your site", get_cookie('bar', FALSE));
+ }
+
+ // ------------------------------------------------------------------------
+
+ function test_delete_cookie()
+ {
+ /*$input_cls = $this->ci_core_class('input');
+ $this->ci_instance_var('input', new $input_cls);
+
+ $this->assertTrue(delete_cookie(
+ 'my_cookie'
+ ));*/
+
+ $this->markTestSkipped('Need to find a way to overcome a headers already set exception');
+ }
+
+} \ No newline at end of file
diff --git a/tests/codeigniter/helpers/download_helper_test.php b/tests/codeigniter/helpers/download_helper_test.php
new file mode 100644
index 000000000..d2b42e46b
--- /dev/null
+++ b/tests/codeigniter/helpers/download_helper_test.php
@@ -0,0 +1,10 @@
+<?php
+
+class Download_helper_test extends CI_TestCase {
+
+ public function test_force_download()
+ {
+ $this->markTestSkipped('Cant easily test');
+ }
+
+} \ No newline at end of file
diff --git a/tests/codeigniter/helpers/language_helper_test.php b/tests/codeigniter/helpers/language_helper_test.php
new file mode 100644
index 000000000..06932b9fd
--- /dev/null
+++ b/tests/codeigniter/helpers/language_helper_test.php
@@ -0,0 +1,14 @@
+<?php
+
+class Language_helper_test extends CI_TestCase {
+
+ public function test_lang()
+ {
+ $this->helper('language');
+ $this->ci_instance_var('lang', new Mock_Core_Lang());
+
+ $this->assertFalse(lang(1));
+ $this->assertEquals('<label for="foo"></label>', lang(1, 'foo'));
+ }
+
+} \ No newline at end of file
diff --git a/tests/codeigniter/helpers/security_helper_test.php b/tests/codeigniter/helpers/security_helper_test.php
new file mode 100644
index 000000000..effd3ec02
--- /dev/null
+++ b/tests/codeigniter/helpers/security_helper_test.php
@@ -0,0 +1,64 @@
+<?php
+
+class Security_helper_tests extends CI_TestCase {
+
+ function setUp()
+ {
+ $this->helper('security');
+ $obj = new stdClass;
+ $obj->security = new Mock_Core_Security();
+ $this->ci_instance($obj);
+ }
+
+ function test_xss_clean()
+ {
+ $this->assertEquals('foo', xss_clean('foo'));
+
+ $this->assertEquals("Hello, i try to [removed]alert&#40;'Hack'&#41;;[removed] your site", xss_clean("Hello, i try to <script>alert('Hack');</script> your site"));
+ }
+
+ function test_sanitize_filename()
+ {
+ $this->assertEquals('hello.doc', sanitize_filename('hello.doc'));
+
+ $filename = './<!--foo-->';
+ $this->assertEquals('foo', sanitize_filename($filename));
+ }
+
+ function test_do_hash()
+ {
+ $md5 = md5('foo');
+ $sha1 = sha1('foo');
+
+ $algos = hash_algos();
+ $algo_results = array();
+ foreach ($algos as $k => $v)
+ {
+ $algo_results[$v] = hash($v, 'foo');
+ }
+
+ $this->assertEquals($sha1, do_hash('foo'));
+ $this->assertEquals($sha1, do_hash('foo', 'sha1'));
+ $this->assertEquals($md5, do_hash('foo', 'md5'));
+ $this->assertEquals($md5, do_hash('foo', 'foobar'));
+
+ // Test each algorithm available to PHP
+ foreach ($algo_results as $algo => $result)
+ {
+ $this->assertEquals($result, do_hash('foo', $algo));
+ }
+ }
+
+ function test_strip_image_tags()
+ {
+ $this->assertEquals('http://example.com/spacer.gif', strip_image_tags('http://example.com/spacer.gif'));
+
+ $this->assertEquals('http://example.com/spacer.gif', strip_image_tags('<img src="http://example.com/spacer.gif" alt="Who needs CSS when you have a spacer.gif?" />'));
+ }
+
+ function test_encode_php_tags()
+ {
+ $this->assertEquals('&lt;? echo $foo; ?&gt;', encode_php_tags('<? echo $foo; ?>'));
+ }
+
+} \ No newline at end of file
diff --git a/tests/codeigniter/helpers/url_helper_test.php b/tests/codeigniter/helpers/url_helper_test.php
index c81c5f1b8..5fc364238 100644
--- a/tests/codeigniter/helpers/url_helper_test.php
+++ b/tests/codeigniter/helpers/url_helper_test.php
@@ -51,6 +51,8 @@ class Url_helper_test extends CI_TestCase {
'www.codeigniter.com test' => '<a href="http://www.codeigniter.com">http://www.codeigniter.com</a> test',
'This is my noreply@codeigniter.com test' => 'This is my noreply@codeigniter.com test',
'<br />www.google.com' => '<br /><a href="http://www.google.com">http://www.google.com</a>',
+ 'Download CodeIgniter at www.codeigniter.com. Period test.' => 'Download CodeIgniter at <a href="http://www.codeigniter.com">http://www.codeigniter.com</a>. Period test.',
+ 'Download CodeIgniter at www.codeigniter.com, comma test' => 'Download CodeIgniter at <a href="http://www.codeigniter.com">http://www.codeigniter.com</a>, comma test'
);
foreach ($strings as $in => $out)
diff --git a/tests/codeigniter/libraries/Upload_test.php b/tests/codeigniter/libraries/Upload_test.php
index 827942773..546cebc59 100644
--- a/tests/codeigniter/libraries/Upload_test.php
+++ b/tests/codeigniter/libraries/Upload_test.php
@@ -11,9 +11,9 @@ class Upload_test extends CI_TestCase {
$this->upload = $ci->upload;
}
- function test_do_upload()
+ function test_do_upload()
{
- $this->markTestIncomplete('We can\'t really test this at the moment because of the call to `is_uploaded_file` in do_upload which isn\'t supported by vfsStream');
+ $this->markTestSkipped('We can\'t really test this at the moment because of the call to `is_uploaded_file` in do_upload which isn\'t supported by vfsStream');
}
function test_data()
@@ -72,7 +72,7 @@ class Upload_test extends CI_TestCase {
{
$this->upload->set_max_filesize(100);
$this->assertEquals(100, $this->upload->max_size);
- }
+ }
function test_set_max_filename()
{
@@ -84,7 +84,7 @@ class Upload_test extends CI_TestCase {
{
$this->upload->set_max_width(100);
$this->assertEquals(100, $this->upload->max_width);
- }
+ }
function test_set_max_height()
{
@@ -178,7 +178,7 @@ class Upload_test extends CI_TestCase {
$this->upload->file_type = 'image/gif';
$this->upload->file_temp = realpath(PROJECT_BASE.'tests/mocks/uploads/ci_logo.gif');
- $this->upload->max_width = 10;
+ $this->upload->max_width = 10;
$this->assertFalse($this->upload->is_allowed_dimensions());
$this->upload->max_width = 170;
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index 56cb8841c..96c3af9bb 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<phpunit
+<phpunit
bootstrap="./Bootstrap.php"
colors="true"
convertNoticesToExceptions="true"
@@ -16,10 +16,11 @@
<directory suffix="test.php">./codeigniter/libraries</directory>
</testsuite>
</testsuites>
- <filters>
+ <filter>
<blacklist>
<directory suffix=".php">PEAR_INSTALL_DIR</directory>
<directory suffix=".php">PHP_LIBDIR</directory>
+ <directory suffix=".php">../vendor</directory>
</blacklist>
- </filters>
+ </filter>
</phpunit> \ No newline at end of file
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index f31781d5c..4ee382fce 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -65,7 +65,7 @@ Release Date: Not Released
- ``create_captcha()`` accepts additional colors parameter, allowing for color customization.
- :doc:`URL Helper <helpers/url_helper>` changes include:
- ``url_title()`` will now trim extra dashes from beginning and end.
- - ``anchor_popup()`` will now fill the "href" attribute with the URL and its JS code will return false instead.
+ - ``anchor_popup()`` will now fill the *href* attribute with the URL and its JS code will return FALSE instead.
- Added JS window name support to ``anchor_popup()`` function.
- Added support (auto-detection) for HTTP/1.1 response code 303 in ``redirect()``.
- "auto" method in ``redirect()`` now chooses the "refresh" method only on IIS servers, instead of all servers on Windows.
@@ -153,12 +153,14 @@ Release Date: Not Released
- :doc:`Session Library <libraries/sessions>` changes include:
- Library changed to :doc:`Driver <general/drivers>` with classic Cookie driver as default.
- - Added Native PHP Session driver to work with $_SESSION.
- - Custom session drivers can be added anywhere in package paths and loaded with Session library.
- - Session drivers interchangeable on the fly.
- - New tempdata feature allows setting user data items with an expiration time.
- - Added default $config['sess_driver'] and $config['sess_valid_drivers'] items to config.php file.
- - Cookie driver now respects php.ini's session.gc_probability and session.gc_divisor
+ - Added Native PHP Session driver to work with ``$_SESSION``.
+ - Custom drivers can be added anywhere in package paths and be loaded with the library.
+ - Drivers interchangeable on the fly.
+ - New **tempdata** feature allows setting user data items with an expiration time.
+ - Added default ``$config['sess_driver']`` and ``$config['sess_valid_drivers']`` items to *config.php* file.
+ - Cookie driver now respects php.ini's *session.gc_probability* and *session.gc_divisor* settings.
+ - Cookie driver now uses HMAC authentication instead of the simple md5 checksum.
+ - The Cookie driver now also checks authentication on encrypted session data.
- Changed the Cookie driver to select only one row when using database sessions.
- Cookie driver now only writes to database at end of request when using database.
- Cookie driver now uses PHP functions for faster array manipulation when using database.
@@ -204,6 +206,7 @@ Release Date: Not Released
- Internal method ``_prep_quoted_printable()`` will now utilize the native ``quoted_printable_encode()``, ``imap_8bit()`` functions (if available) when CRLF is set to "\r\n".
- Default charset now relies on the global ``$config['charset']`` setting.
- Removed unused protected method ``_get_ip()`` (:doc:`Input Library <libraries/input>`'s ``ip_address()`` should be used anyway).
+ - Internal method ``_prep_q_encoding()`` now utilizes PHP's *mbstring* and *iconv* extensions (when available) and no longer has a second (``$from``) argument.
- :doc:`Pagination Library <libraries/pagination>` changes include:
- Added support for the anchor "rel" attribute.
- Added support for setting custom attributes.
@@ -341,7 +344,7 @@ Bug fixes for 3.0
- Fixed a bug (#318) - :doc:`Profiling <general/profiling>` setting *query_toggle_count* was not settable as described in the manual.
- Fixed a bug (#938) - :doc:`Config Library <libraries/config>` method ``site_url()`` added a question mark to the URL string when query strings are enabled even if it already existed.
- Fixed a bug (#999) - :doc:`Config Library <libraries/config>` method ``site_url()`` always appended ``$config['url_suffix']`` to the end of the URL string, regardless of whether a query string exists in it.
-- Fixed a bug where :doc:`URL Helper <helpers/url_helper>` function anchor_popup() ignored the attributes argument if it is not an array.
+- Fixed a bug where :doc:`URL Helper <helpers/url_helper>` function ``anchor_popup()`` ignored the attributes argument if it is not an array.
- Fixed a bug (#1328) - :doc:`Form Validation Library <libraries/form_validation>` didn't properly check the type of the form fields before processing them.
- Fixed a bug (#79) - :doc:`Form Validation Library <libraries/form_validation>` didn't properly validate array fields that use associative keys or have custom indexes.
- Fixed a bug (#427) - :doc:`Form Validation Library <libraries/form_validation>` method ``strip_image_tags()`` was an alias to a non-existent method.
@@ -362,7 +365,10 @@ Bug fixes for 3.0
- Fixed a bug (#1257) - :doc:`Query Builder <database/query_builder>` used to (unnecessarily) group FROM clause contents, which breaks certain queries and is invalid for some databases.
- Fixed a bug (#1709) - :doc:`Email <libraries/email>` headers were broken when using long email subjects and \r\n as CRLF.
- Fixed a bug where ``MB_ENABLED`` was only declared if ``UTF8_ENABLED`` was set to TRUE.
-- Fixed a bug where the :doc:`Session Library <libraries/session>` accepted cookies with *last_activity* values being in the future.
+- Fixed a bug where the :doc:`Session Library <libraries/sessions>` accepted cookies with *last_activity* values being in the future.
+- Fixed a bug (#1897) - :doc:`Email Library <libraries/email>` triggered PHP E_WARNING errors when *mail* protocol used and ``to()`` is never called.
+- Fixed a bug (#1409) - :doc:`Email Library <libraries/email>` didn't properly handle multibyte characters when applying Q-encoding to headers.
+- Fixed a bug where :doc:`Email Library <libraries/email>` didn't honor it's *wordwrap* setting while handling alternative messages.
Version 2.1.3
=============
@@ -376,7 +382,7 @@ Bug fixes for 2.1.3
-------------------
- Fixed a bug (#1543) - File-based :doc:`Caching <libraries/caching>` method ``get_metadata()`` used a non-existent array key to look for the TTL value.
-- Fixed a bug (#1314) - :doc:`Session Library <libraries/session>` method ``sess_destroy()`` didn't destroy the userdata array.
+- Fixed a bug (#1314) - :doc:`Session Library <libraries/sessions>` method ``sess_destroy()`` didn't destroy the userdata array.
- Fixed a bug (#804) - Profiler library was trying to handle objects as strings in some cases, resulting in *E_WARNING* messages being issued by ``htmlspecialchars()``.
- Fixed a bug (#1699) - :doc:`Migration Library <libraries/migration>` ignored the ``$config['migration_path']`` setting.
- Fixed a bug (#227) - :doc:`Input Library <libraries/input>` allowed unconditional spoofing of HTTP clients' IP addresses through the *HTTP_CLIENT_IP* header.
diff --git a/user_guide_src/source/libraries/javascript.rst b/user_guide_src/source/libraries/javascript.rst
index d5e09c314..393d4e321 100644
--- a/user_guide_src/source/libraries/javascript.rst
+++ b/user_guide_src/source/libraries/javascript.rst
@@ -192,7 +192,7 @@ and triggered by a click using the jQuery library's click() event.
'width' => '50%',
'marginLeft' => 125
);
- $this->jquery->click('#trigger', $this->jquery->animate('#note', $params, normal));
+ $this->jquery->click('#trigger', $this->jquery->animate('#note', $params, 'normal'));
fadeIn() / fadeOut()
--------------------
diff --git a/user_guide_src/source/libraries/output.rst b/user_guide_src/source/libraries/output.rst
index 3289a241f..2b72ba777 100644
--- a/user_guide_src/source/libraries/output.rst
+++ b/user_guide_src/source/libraries/output.rst
@@ -105,7 +105,7 @@ Permits you to manually set a server status header. Example::
`See here <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>`_ for
a full list of headers.
-.. note:: This method is an alias for :doc:`Common function <../general/common_funtions.rst>`
+.. note:: This method is an alias for :doc:`Common function <../general/common_functions.rst>`
``set_status_header()``.
$this->output->enable_profiler();