Update CodeIgniter system 3.1.0 => 3.1.16

This commit is contained in:
2020-08-02 02:23:47 -04:00
parent 3523bc5f57
commit 083b534f0a
176 changed files with 2351 additions and 1889 deletions
+168 -69
View File
@@ -6,7 +6,7 @@
*
* This content is released under the MIT License (MIT)
*
* Copyright (c) 2014 - 2016, British Columbia Institute of Technology
* Copyright (c) 2014 - 2019, British Columbia Institute of Technology
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -29,8 +29,8 @@
* @package CodeIgniter
* @author EllisLab Dev Team
* @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
* @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
* @license http://opensource.org/licenses/MIT MIT License
* @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
* @license https://opensource.org/licenses/MIT MIT License
* @link https://codeigniter.com
* @since Version 1.0.0
* @filesource
@@ -374,6 +374,13 @@ class CI_Email {
5 => '5 (Lowest)'
);
/**
* mbstring.func_overload flag
*
* @var bool
*/
protected static $func_overload;
// --------------------------------------------------------------------
/**
@@ -390,6 +397,8 @@ class CI_Email {
$this->initialize($config);
$this->_safe_mode = ( ! is_php('5.4') && ini_get('safe_mode'));
isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));
log_message('info', 'Email Class Initialized');
}
@@ -449,7 +458,6 @@ class CI_Email {
$this->_headers = array();
$this->_debug_msg = array();
$this->set_header('User-Agent', $this->useragent);
$this->set_header('Date', $this->_set_date());
if ($clear_attachments !== FALSE)
@@ -905,18 +913,13 @@ class CI_Email {
/**
* Get Mail Protocol
*
* @param bool
* @return mixed
*/
protected function _get_protocol($return = TRUE)
protected function _get_protocol()
{
$this->protocol = strtolower($this->protocol);
in_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail';
if ($return === TRUE)
{
return $this->protocol;
}
return $this->protocol;
}
// --------------------------------------------------------------------
@@ -924,25 +927,21 @@ class CI_Email {
/**
* Get Mail Encoding
*
* @param bool
* @return string
*/
protected function _get_encoding($return = TRUE)
protected function _get_encoding()
{
in_array($this->_encoding, $this->_bit_depths) OR $this->_encoding = '8bit';
foreach ($this->_base_charsets as $charset)
{
if (strpos($charset, $this->charset) === 0)
if (strpos($this->charset, $charset) === 0)
{
$this->_encoding = '7bit';
}
}
if ($return === TRUE)
{
return $this->_encoding;
}
return $this->_encoding;
}
// --------------------------------------------------------------------
@@ -962,10 +961,8 @@ class CI_Email {
{
return 'plain-attach';
}
else
{
return 'plain';
}
return 'plain';
}
// --------------------------------------------------------------------
@@ -1035,9 +1032,17 @@ class CI_Email {
*/
public function valid_email($email)
{
if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@'))
if (function_exists('idn_to_ascii') && strpos($email, '@'))
{
$email = substr($email, 0, ++$atpos).idn_to_ascii(substr($email, $atpos));
list($account, $domain) = explode('@', $email, 2);
$domain = defined('INTL_IDNA_VARIANT_UTS46')
? idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46)
: idn_to_ascii($domain);
if ($domain !== FALSE)
{
$email = $account.'@'.$domain;
}
}
return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
@@ -1154,7 +1159,7 @@ class CI_Email {
{
// Is the line within the allowed character count?
// If so we'll join it to the output and continue
if (mb_strlen($line) <= $charlim)
if (self::strlen($line) <= $charlim)
{
$output .= $line.$this->newline;
continue;
@@ -1170,10 +1175,10 @@ class CI_Email {
}
// Trim the word down
$temp .= mb_substr($line, 0, $charlim - 1);
$line = mb_substr($line, $charlim - 1);
$temp .= self::substr($line, 0, $charlim - 1);
$line = self::substr($line, $charlim - 1);
}
while (mb_strlen($line) > $charlim);
while (self::strlen($line) > $charlim);
// If $temp contains data it means we had to split up an over-length
// word into smaller chunks so we'll add it back to our current line
@@ -1202,10 +1207,11 @@ class CI_Email {
/**
* Build final headers
*
* @return string
* @return void
*/
protected function _build_headers()
{
$this->set_header('User-Agent', $this->useragent);
$this->set_header('X-Sender', $this->clean_email($this->_headers['From']));
$this->set_header('X-Mailer', $this->useragent);
$this->set_header('X-Priority', $this->_priorities[$this->priority]);
@@ -1385,7 +1391,7 @@ class CI_Email {
$this->_header_str .= $hdr;
}
strlen($body) && $body .= $this->newline.$this->newline;
self::strlen($body) && $body .= $this->newline.$this->newline;
$body .= $this->_get_mime_message().$this->newline.$this->newline
.'--'.$last_boundary.$this->newline
@@ -1468,7 +1474,8 @@ class CI_Email {
.'Content-Type: '.$this->_attachments[$i]['type'].'; name="'.$name.'"'.$this->newline
.'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline
.'Content-Transfer-Encoding: base64'.$this->newline
.(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline.$this->newline)
.(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline)
.$this->newline
.$this->_attachments[$i]['content'].$this->newline;
}
@@ -1514,14 +1521,7 @@ class CI_Email {
// which only works with "\n".
if ($this->crlf === "\r\n")
{
if (is_php('5.3'))
{
return quoted_printable_encode($str);
}
elseif (function_exists('imap_8bit'))
{
return imap_8bit($str);
}
return quoted_printable_encode($str);
}
// Reduce multiple spaces & remove nulls
@@ -1538,7 +1538,7 @@ class CI_Email {
foreach (explode("\n", $str) as $line)
{
$length = strlen($line);
$length = self::strlen($line);
$temp = '';
// Loop through each character in the line to add soft-wrap
@@ -1573,7 +1573,7 @@ class CI_Email {
// 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)) >= 76)
if ((self::strlen($temp) + self::strlen($char)) >= 76)
{
$output .= $temp.$escape.$this->crlf;
$temp = '';
@@ -1588,7 +1588,7 @@ class CI_Email {
}
// get rid of extra CRLF tacked onto the end
return substr($output, 0, strlen($this->crlf) * -1);
return self::substr($output, 0, self::strlen($this->crlf) * -1);
}
// --------------------------------------------------------------------
@@ -1630,7 +1630,7 @@ class CI_Email {
// 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);
return self::substr($output, 2);
}
$chars = iconv_strlen($str, 'UTF-8');
@@ -1642,10 +1642,10 @@ class CI_Email {
}
// We might already have this set for UTF-8
isset($chars) OR $chars = strlen($str);
isset($chars) OR $chars = self::strlen($str);
$output = '=?'.$this->charset.'?Q?';
for ($i = 0, $length = strlen($output); $i < $chars; $i++)
for ($i = 0, $length = self::strlen($output); $i < $chars; $i++)
{
$chr = ($this->charset === 'UTF-8' && ICONV_ENABLED === TRUE)
? '='.implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2))
@@ -1653,11 +1653,11 @@ class CI_Email {
// 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)
if ($length + ($l = self::strlen($chr)) > 74)
{
$output .= '?='.$this->crlf // EOL
.' =?'.$this->charset.'?Q?'.$chr; // New line
$length = 6 + strlen($this->charset) + $l; // Reset the length for the new line
$length = 6 + self::strlen($this->charset) + $l; // Reset the length for the new line
}
else
{
@@ -1750,14 +1750,14 @@ class CI_Email {
if ($i === $float)
{
$chunk[] = substr($set, 1);
$chunk[] = self::substr($set, 1);
$float += $this->bcc_batch_size;
$set = '';
}
if ($i === $c-1)
{
$chunk[] = substr($set, 1);
$chunk[] = self::substr($set, 1);
}
}
@@ -1826,19 +1826,55 @@ class CI_Email {
{
$this->_unwrap_specials();
$method = '_send_with_'.$this->_get_protocol();
$protocol = $this->_get_protocol();
$method = '_send_with_'.$protocol;
if ( ! $this->$method())
{
$this->_set_error_message('lang:email_send_failure_'.($this->_get_protocol() === 'mail' ? 'phpmail' : $this->_get_protocol()));
$this->_set_error_message('lang:email_send_failure_'.($protocol === 'mail' ? 'phpmail' : $protocol));
return FALSE;
}
$this->_set_error_message('lang:email_sent', $this->_get_protocol());
$this->_set_error_message('lang:email_sent', $protocol);
return TRUE;
}
// --------------------------------------------------------------------
/**
* Validate email for shell
*
* Applies stricter, shell-safe validation to email addresses.
* Introduced to prevent RCE via sendmail's -f option.
*
* @see https://github.com/bcit-ci/CodeIgniter/issues/4963
* @see https://gist.github.com/Zenexer/40d02da5e07f151adeaeeaa11af9ab36
* @license https://creativecommons.org/publicdomain/zero/1.0/ CC0 1.0, Public Domain
*
* Credits for the base concept go to Paul Buonopane <paul@namepros.com>
*
* @param string $email
* @return bool
*/
protected function _validate_email_for_shell(&$email)
{
if (function_exists('idn_to_ascii') && strpos($email, '@'))
{
list($account, $domain) = explode('@', $email, 2);
$domain = defined('INTL_IDNA_VARIANT_UTS46')
? idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46)
: idn_to_ascii($domain);
if ($domain !== FALSE)
{
$email = $account.'@'.$domain;
}
}
return (filter_var($email, FILTER_VALIDATE_EMAIL) === $email && preg_match('#\A[a-z0-9._+-]+@[a-z0-9.-]{1,253}\z#i', $email));
}
// --------------------------------------------------------------------
/**
* Send using mail()
*
@@ -1851,7 +1887,11 @@ class CI_Email {
$this->_recipients = implode(', ', $this->_recipients);
}
if ($this->_safe_mode === TRUE)
// _validate_email_for_shell() below accepts by reference,
// so this needs to be assigned to a variable
$from = $this->clean_email($this->_headers['Return-Path']);
if ($this->_safe_mode === TRUE || ! $this->_validate_email_for_shell($from))
{
return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str);
}
@@ -1859,7 +1899,7 @@ class CI_Email {
{
// most documentation of sendmail using the "-f" flag lacks a space after it, however
// we've encountered servers that seem to require it to be in place.
return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$this->clean_email($this->_headers['Return-Path']));
return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$from);
}
}
@@ -1872,13 +1912,22 @@ class CI_Email {
*/
protected function _send_with_sendmail()
{
// is popen() enabled?
if ( ! function_usable('popen')
OR FALSE === ($fp = @popen(
$this->mailpath.' -oi -f '.$this->clean_email($this->_headers['From']).' -t'
, 'w'))
) // server probably has popen disabled, so nothing we can do to get a verbose error.
// _validate_email_for_shell() below accepts by reference,
// so this needs to be assigned to a variable
$from = $this->clean_email($this->_headers['From']);
if ($this->_validate_email_for_shell($from))
{
$from = '-f '.$from;
}
else
{
$from = '';
}
// is popen() enabled?
if ( ! function_usable('popen') OR FALSE === ($fp = @popen($this->mailpath.' -oi '.$from.' -t', 'w')))
{
// server probably has popen disabled, so nothing we can do to get a verbose error.
return FALSE;
}
@@ -2033,7 +2082,19 @@ class CI_Email {
$this->_send_command('hello');
$this->_send_command('starttls');
$crypto = stream_socket_enable_crypto($this->_smtp_connect, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT);
/**
* STREAM_CRYPTO_METHOD_TLS_CLIENT is quite the mess ...
*
* - On PHP <5.6 it doesn't even mean TLS, but SSL 2.0, and there's no option to use actual TLS
* - On PHP 5.6.0-5.6.6, >=7.2 it means negotiation with any of TLS 1.0, 1.1, 1.2
* - On PHP 5.6.7-7.1.* it means only TLS 1.0
*
* We want the negotiation, so we'll force it below ...
*/
$method = is_php('5.6')
? STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
: STREAM_CRYPTO_METHOD_TLS_CLIENT;
$crypto = stream_socket_enable_crypto($this->_smtp_connect, TRUE, $method);
if ($crypto !== TRUE)
{
@@ -2052,7 +2113,7 @@ class CI_Email {
*
* @param string
* @param string
* @return string
* @return bool
*/
protected function _send_command($cmd, $data = '')
{
@@ -2115,7 +2176,7 @@ class CI_Email {
$this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>';
if ((int) substr($reply, 0, 3) !== $resp)
if ((int) self::substr($reply, 0, 3) !== $resp)
{
$this->_set_error_message('lang:email_smtp_error', $reply);
return FALSE;
@@ -2202,9 +2263,9 @@ class CI_Email {
protected function _send_data($data)
{
$data .= $this->newline;
for ($written = $timestamp = 0, $length = strlen($data); $written < $length; $written += $result)
for ($written = $timestamp = 0, $length = self::strlen($data); $written < $length; $written += $result)
{
if (($result = fwrite($this->_smtp_connect, substr($data, $written))) === FALSE)
if (($result = fwrite($this->_smtp_connect, self::substr($data, $written))) === FALSE)
{
break;
}
@@ -2224,10 +2285,8 @@ class CI_Email {
usleep(250000);
continue;
}
else
{
$timestamp = 0;
}
$timestamp = 0;
}
if ($result === FALSE)
@@ -2388,4 +2447,44 @@ class CI_Email {
{
is_resource($this->_smtp_connect) && $this->_send_command('quit');
}
// --------------------------------------------------------------------
/**
* Byte-safe strlen()
*
* @param string $str
* @return int
*/
protected static function strlen($str)
{
return (self::$func_overload)
? mb_strlen($str, '8bit')
: strlen($str);
}
// --------------------------------------------------------------------
/**
* Byte-safe substr()
*
* @param string $str
* @param int $start
* @param int $length
* @return string
*/
protected static function substr($str, $start, $length = NULL)
{
if (self::$func_overload)
{
// mb_substr($str, $start, null, '8bit') returns an empty
// string on PHP 5.3
isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start);
return mb_substr($str, $start, $length, '8bit');
}
return isset($length)
? substr($str, $start, $length)
: substr($str, $start);
}
}