Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/Actions/Admin/Themes.php
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ public function setSettings(): void

foreach (Theme::$current->settings as $setting => $dummy) {
if (!in_array($setting, ['theme_url', 'theme_dir', 'images_url', 'template_dirs'])) {
Theme::$current->settings[$setting] = Utils::htmlspecialcharsRecursive(Theme::$current->settings[$setting]);
Theme::$current->settings[$setting] = Utils::htmlspecialcharsRecursive(Theme::$current->settings[$setting], ENT_QUOTES);
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Actions/Post.php
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,7 @@ protected function showPreview(): void
Utils::$context['choices'] = [];
$choice_id = 0;

$_POST['options'] = empty($_POST['options']) ? [] : Utils::htmlspecialcharsRecursive($_POST['options']);
$_POST['options'] = empty($_POST['options']) ? [] : Utils::htmlspecialcharsRecursive($_POST['options'], ENT_QUOTES);

foreach ($_POST['options'] as $option) {
if (trim($option) == '') {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Actions/Profile/Main.php
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ public function execute(): void
if (Utils::$context['completed_save']) {
// Clean up the POST variables.
$_POST = Utils::htmlTrimRecursive($_POST);
$_POST = Utils::htmlspecialcharsRecursive($_POST);
$_POST = Utils::htmlspecialcharsRecursive($_POST, ENT_QUOTES);
Profile::$member->post_sanitized = true;

if ($this->check_password) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Actions/Register2.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ function (&$value, $key) {
}

// Make sure they are clean, dammit!
$reg_options['theme_vars'] = Utils::htmlspecialcharsRecursive($reg_options['theme_vars']);
$reg_options['theme_vars'] = Utils::htmlspecialcharsRecursive($reg_options['theme_vars'], ENT_QUOTES);

// Check whether we have fields that simply MUST be displayed?
$request = Db::$db->query(
Expand Down
65 changes: 32 additions & 33 deletions Sources/Mentions.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public static function getMentionedMembers(string $body): array
LIMIT {int:count}',
[
'ids' => array_keys($existing_mentions),
'names' => $possible_names,
'names' => Utils::htmlspecialcharsRecursive($possible_names, ENT_QUOTES, double_encode: false),
'count' => count($possible_names),
],
);
Expand All @@ -258,33 +258,6 @@ public static function getMentionedMembers(string $body): array
return $members;
}

/**
* Like getPossibleMentions(), but for `[member=1]name[/member]` format.
*
* @static
* @param string $body The text to look for mentions in.
* @return array An array of arrays containing info about members that are in fact mentioned in the body.
*/
public static function getExistingMentions(string $body): array
{
if (empty(self::$excluded_bbc_regex)) {
self::setExcludedBbcRegex();
}

// Don't include mentions inside quotations, etc.
$body = preg_replace('~\[(' . self::$excluded_bbc_regex . ')[^\]]*\](?' . '>(?' . '>[^\[]|\[(?!/?\1[^\]]*\]))|(?0))*\[/\1\]~', '', $body);

$existing_mentions = [];

preg_match_all('~\[member=([0-9]+)\]([^\[]*)\[/member\]~', $body, $matches, PREG_SET_ORDER);

foreach ($matches as $match_set) {
$existing_mentions[$match_set[1]] = trim($match_set[2]);
}

return $existing_mentions;
}

/**
* Verifies that members really are mentioned in the text.
*
Expand Down Expand Up @@ -429,7 +402,7 @@ protected static function getPossibleMentions(string $body): array
}

// preparse code does a few things which might mess with our parsing
$body = htmlspecialchars_decode(preg_replace('~<br\s*/?' . '>~', "\n", str_replace('&nbsp;', ' ', $body)), ENT_QUOTES);
$body = Utils::htmlspecialcharsDecode(preg_replace('~<br[^>]*>~', "\n", $body), ENT_QUOTES);

if (empty(self::$excluded_bbc_regex)) {
self::setExcludedBbcRegex();
Expand All @@ -440,7 +413,7 @@ protected static function getPossibleMentions(string $body): array

$matches = [];
// Split before every Unicode character.
$string = preg_split('/(?=\X)/u', $body, -1, PREG_SPLIT_NO_EMPTY);
$string = Utils::entityStrSplit($body);
$depth = 0;

foreach ($string as $k => $char) {
Expand Down Expand Up @@ -470,12 +443,11 @@ protected static function getPossibleMentions(string $body): array
$names = [];

foreach ($matches as $match) {
// '[^\p{L}\p{M}\p{N}_]' is the Unicode equivalent of '[^\w]'
$match = preg_split('/([^\p{L}\p{M}\p{N}_])/u', $match, -1, PREG_SPLIT_DELIM_CAPTURE);
$match = Utils::entityStrSplit($match);
$count = count($match);

for ($i = 1; $i <= $count; $i++) {
$names[] = Utils::htmlspecialchars(Utils::htmlTrim(implode('', array_slice($match, 0, $i))));
$names[] = Utils::htmlspecialchars(Utils::htmlTrim(implode('', array_slice($match, 0, $i))), ENT_COMPAT);
}
}

Expand All @@ -484,6 +456,33 @@ protected static function getPossibleMentions(string $body): array
return $names;
}

/**
* Like getPossibleMentions(), but for `[member=1]name[/member]` format.
*
* @static
* @param string $body The text to look for mentions in.
* @return array An array of arrays containing info about members that are in fact mentioned in the body.
*/
protected static function getExistingMentions(string $body): array
{
if (empty(self::$excluded_bbc_regex)) {
self::setExcludedBbcRegex();
}

// Don't include mentions inside quotations, etc.
$body = preg_replace('~\[(' . self::$excluded_bbc_regex . ')[^\]]*\](?' . '>(?' . '>[^\[]|\[(?!/?\1[^\]]*\]))|(?0))*\[/\1\]~', '', $body);

$existing_mentions = [];

preg_match_all('~\[member=([0-9]+)\]([^\[]*)\[/member\]~', $body, $matches, PREG_SET_ORDER);

foreach ($matches as $match_set) {
$existing_mentions[$match_set[1]] = Utils::htmlspecialchars(Utils::htmlTrim(Utils::htmlspecialcharsDecode($match_set[2], ENT_QUOTES)), ENT_COMPAT);
}

return $existing_mentions;
}

/**
* Builds a regular expression matching BBC that can't contain mentions.
*
Expand Down
2 changes: 1 addition & 1 deletion Sources/PackageManager/PackageUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ public static function loadInstalledPackages(): array

$found[] = $row['package_id'];

$row = Utils::htmlspecialcharsRecursive($row);
$row = Utils::htmlspecialcharsRecursive($row, ENT_QUOTES);

$installed[] = [
'id' => $row['id_install'],
Expand Down
2 changes: 1 addition & 1 deletion Sources/Poll.php
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ public static function sanitizeInput(array &$errors): void
$_POST['question'] = Utils::htmlspecialchars($_POST['question']);
$_POST['question'] = Utils::truncate($_POST['question'], 255);
$_POST['question'] = preg_replace('~&amp;#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', $_POST['question']);
$_POST['options'] = Utils::htmlspecialcharsRecursive($_POST['options']);
$_POST['options'] = Utils::htmlspecialcharsRecursive($_POST['options'], ENT_QUOTES);
}

/******************
Expand Down
2 changes: 1 addition & 1 deletion Sources/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -1519,7 +1519,7 @@ public function save(): void
// If $_POST hasn't already been sanitized, do that now.
if (!$this->post_sanitized) {
$_POST = Utils::htmlTrimRecursive($_POST);
$_POST = Utils::htmlspecialcharsRecursive($_POST);
$_POST = Utils::htmlspecialcharsRecursive($_POST, ENT_QUOTES);
$this->post_sanitized = true;
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/QueryString.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public static function cleanRequest(): void
}

// Add entities to GET. This is kinda like the slashes on everything else.
$_GET = Utils::htmlspecialcharsRecursive($_GET);
$_GET = Utils::htmlspecialcharsRecursive($_GET, ENT_QUOTES);

// Let's not depend on the ini settings... why even have COOKIE in there, anyway?
$_REQUEST = $_POST + $_GET;
Expand Down
146 changes: 145 additions & 1 deletion Sources/Subs-Compat.php
Original file line number Diff line number Diff line change
Expand Up @@ -11398,7 +11398,7 @@ function normalize_spaces($string, $vspace = true, $hspace = false, $options = [
*/
function htmlspecialchars__recursive(array|string $var, int $level = 0): array|string
{
return SMF\Utils::htmlspecialcharsRecursive($var);
return SMF\Utils::htmlspecialcharsRecursive($var, ENT_QUOTES);
}

/**
Expand Down Expand Up @@ -12090,3 +12090,147 @@ function array_find_key(array $array, callable $callback): mixed
return null;
}
}

if (!function_exists('grapheme_str_split')) {
function grapheme_str_split(string $string, int $length = 1): array|false
{
if ($length < 1 || $length > 1073741823) {
throw new \ValueError('grapheme_str_split(): Argument #2 ($length) must be greater than 0 and less than or equal to 1073741823');
}

try {
return preg_split('/(\X{' . $length . '})/u', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
} catch (\Throwable $e) {
return false;
}
}
}

if (!function_exists('grapheme_strlen')) {
function grapheme_strlen(string $string): int|false|null
{
if (
@preg_match('//u', $string) === false
&& preg_last_error() === PREG_BAD_UTF8_ERROR
) {
return null;
}

return count(grapheme_str_split($string, 1));
}
}

if (!function_exists('grapheme_substr')) {
function grapheme_substr(string $string, int $offset = 0, ?int $length = null): string|false
{
if (($graphemes = grapheme_str_split($string, 1)) === false) {
return false;
}

return implode('', array_slice($graphemes, $offset, $length));
}
}

if (!function_exists('grapheme_strpos')) {
function grapheme_strpos(string $haystack, string $needle, int $offset = 0): int|false
{
if (!str_contains($haystack, $needle)) {
return false;
}

$haystack = grapheme_str_split($haystack, 1);

if ($haystack === false) {
return false;
}

if (abs($offset) >= count($haystack)) {
throw new \ValueError('grapheme_strpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack)');
}

$skipped = array_splice($haystack, 0, $offset);

$haystack = implode('', $haystack);

$before = grapheme_str_split(substr($haystack, 0, strpos($haystack, $needle)), 1);

return $before === false ? false : count($skipped) + count($before);
}
}

if (!function_exists('grapheme_stripos')) {
function grapheme_stripos(string $haystack, string $needle, int $offset = 0): int|false
{
$haystack = mb_convert_case($haystack, MB_CASE_FOLD_SIMPLE);
$needle = mb_convert_case($needle, MB_CASE_FOLD_SIMPLE);

return grapheme_strpos($haystack, $needle, $offset);
}
}

if (!function_exists('grapheme_strrpos')) {
function grapheme_strrpos(string $haystack, string $needle, int $offset = 0): int|false
{
if (!str_contains($haystack, $needle)) {
return false;
}

$haystack = grapheme_str_split($haystack, 1);
$needle = grapheme_str_split($needle, 1);

if ($haystack === false || $needle === false) {
return false;
}

$haystack_len = count($haystack);
$needle_len = count($needle);

if (abs($offset) >= $haystack_len) {
throw new \ValueError('grapheme_strrpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack)');
}

if ($offset < 0) {
$offset = ($haystack_len + $offset) % $haystack_len;

for ($i = $offset; $i > -1; $i--) {
if (array_slice($haystack, $i, $needle_len) === $needle) {
break;
}
}

return $i < 0 ? false : $i;
}

for ($i = $haystack_len; $i > $offset; $i--) {
if (array_slice($haystack, $i, $needle_len) === $needle) {
break;
}
}

return $i >= $haystack_len - $needle_len ? false : $i;
}
}

if (!function_exists('grapheme_strripos')) {
function grapheme_strripos(string $haystack, string $needle, int $offset = 0): int|false
{
$haystack = mb_convert_case($haystack, MB_CASE_FOLD_SIMPLE);
$needle = mb_convert_case($needle, MB_CASE_FOLD_SIMPLE);

return grapheme_strrpos($haystack, $needle, $offset);
}
}

if (!function_exists('grapheme_strstr')) {
function grapheme_strstr(string $haystack, string $needle, bool $before_needle = false): string|false
{
return $before_needle ? grapheme_substr($haystack, 0, grapheme_strpos($haystack, $needle)) : grapheme_substr($haystack, grapheme_strpos($haystack, $needle));
}
}

if (!function_exists('grapheme_stristr')) {
function grapheme_strstr(string $haystack, string $needle, bool $before_needle = false): string|false
{
return $before_needle ? grapheme_substr($haystack, 0, grapheme_stripos($haystack, $needle)) : grapheme_substr($haystack, grapheme_stripos($haystack, $needle));
}
}
Loading