Skip to content

Commit 1a70eab

Browse files
Merge pull request #9256 from Sesquipedalian/3.0/checkReservedName
Respects `reserveCase` setting in SpoofDetector::checkReservedName()
2 parents d62a034 + 9bece8c commit 1a70eab

3 files changed

Lines changed: 81 additions & 17 deletions

File tree

Sources/Actions/Admin/Registration.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ public function reservedNames(): void
609609

610610
$_POST['reserved'] = Utils::normalize($_POST['reserved']);
611611

612+
$reserve_case = Config::$modSettings['reserveCase'];
613+
612614
// Set all the options....
613615
Config::updateModSettings([
614616
'reserveWord' => (int) !empty($_POST['matchword']),
@@ -619,6 +621,28 @@ public function reservedNames(): void
619621
]);
620622

621623
Utils::$context['saved_successful'] = true;
624+
625+
// If the case setting changed, must rebuild the spoofdetector_name
626+
// values in the members table.
627+
if ($reserve_case != Config::$modSettings['reserveCase']) {
628+
Db::$db->insert(
629+
'insert',
630+
'{db_prefix}background_tasks',
631+
[
632+
'task_class' => 'string',
633+
'task_data' => 'string',
634+
'claimed_time' => 'int',
635+
],
636+
[
637+
[
638+
'SMF\\Tasks\\UpdateSpoofDetectorNames',
639+
json_encode(['last_member_id' => 0]),
640+
0,
641+
],
642+
],
643+
['id_task'],
644+
);
645+
}
622646
}
623647

624648
// Get the reserved word options and words.

Sources/Tasks/UpdateSpoofDetectorNames.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,13 @@ public function execute(): bool
107107
while ($row = Db::$db->fetch_assoc($request)) {
108108
$this->_details['last_member_id'] = $row['id_member'];
109109

110-
$skeleton = Utils::htmlspecialchars(SpoofDetector::getSkeletonString(html_entity_decode($row['real_name'], ENT_QUOTES)));
110+
$name = Utils::entityDecode($row['real_name'], nbsp_to_space: true);
111+
112+
if (empty(Config::$modSettings['reserveCase'])) {
113+
$name = Utils::casefold($name);
114+
}
115+
116+
$skeleton = SpoofDetector::getSkeletonString($name);
111117

112118
// Don't bother updating if there's been no change.
113119
if ($row['spoofdetector_name'] === $skeleton) {

Sources/Unicode/SpoofDetector.php

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,16 @@ public static function enhanceWordCensor(string $text): bool
228228
*/
229229
public static function checkReservedName(string $name, bool $fatal = false): bool
230230
{
231-
$skeleton = self::getSkeletonString(html_entity_decode($name, ENT_QUOTES));
231+
// For ease of comparison, decode any entities.
232+
$name = Utils::entityDecode($name, nbsp_to_space: true);
233+
234+
// Do we want caseless comparison?
235+
if (empty(Config::$modSettings['reserveCase'])) {
236+
$name = Utils::casefold($name);
237+
}
238+
239+
// Get the "skeleton" for this name.
240+
$skeleton = self::getSkeletonString($name);
232241

233242
// This will hold all the names that are similar to $name.
234243
$homograph_names = [];
@@ -241,19 +250,19 @@ public static function checkReservedName(string $name, bool $fatal = false): boo
241250
continue;
242251
}
243252

244-
// The admin might've used entities too, level the playing field.
245-
$reserved_check = html_entity_decode($reserved, ENT_QUOTES);
253+
// The admin might've used entities too, so level the playing field.
254+
$reserved_check = Utils::entityDecode($reserved, nbsp_to_space: true);
246255

247256
// Case sensitive name?
248257
if (empty(Config::$modSettings['reserveCase'])) {
249-
$reserved_check = Utils::strtolower($reserved_check);
258+
$reserved_check = Utils::casefold($reserved_check);
250259
}
251260

252261
$reserved_skeleton = self::getSkeletonString($reserved_check);
253262

254263
// Skeletons match.
255264
if ($skeleton == $reserved_skeleton) {
256-
$homograph_names[] = $reserved_check;
265+
$homograph_names[$reserved] = $reserved_check;
257266
}
258267
// Skeleton of the name includes skeleton of a reserved name.
259268
elseif (
@@ -324,6 +333,14 @@ public static function checkReservedName(string $name, bool $fatal = false): boo
324333
*/
325334
public static function checkSimilarMemberName(string $name, int $id_member = 0, bool $fatal = false): bool
326335
{
336+
$name = Utils::entityDecode($name, nbsp_to_space: true);
337+
338+
if (empty(Config::$modSettings['reserveCase'])) {
339+
$name = Utils::casefold($name);
340+
}
341+
342+
$skeleton = self::getSkeletonString($name);
343+
327344
// This will hold all the names that are similar to $name.
328345
$homograph_names = [];
329346

@@ -335,12 +352,18 @@ public static function checkSimilarMemberName(string $name, int $id_member = 0,
335352
AND id_member != {int:current_member}') . '',
336353
[
337354
'current_member' => $id_member,
338-
'skeleton' => Utils::htmlspecialchars(self::getSkeletonString(html_entity_decode($name, ENT_QUOTES))),
355+
'skeleton' => $skeleton,
339356
],
340357
);
341358

342359
while ($row = Db::$db->fetch_assoc($request)) {
343-
$homograph_names[] = html_entity_decode($row['real_name'], ENT_QUOTES);
360+
$real_name = Utils::entityDecode($row['real_name'], nbsp_to_space: true);
361+
362+
if (empty(Config::$modSettings['reserveCase'])) {
363+
$real_name = Utils::casefold($real_name);
364+
}
365+
366+
$homograph_names[$row['real_name']] = $real_name;
344367
}
345368

346369
Db::$db->free_result($request);
@@ -363,21 +386,32 @@ public static function checkSimilarMemberName(string $name, int $id_member = 0,
363386
*/
364387
public static function checkSimilarGroupName(string $name, bool $fatal = false): bool
365388
{
366-
$skeleton = self::getSkeletonString(html_entity_decode($name, ENT_QUOTES));
389+
$name = Utils::entityDecode($name, nbsp_to_space: true);
390+
391+
if (empty(Config::$modSettings['reserveCase'])) {
392+
$name = Utils::casefold($name);
393+
}
394+
395+
$skeleton = self::getSkeletonString($name);
367396

368397
// This will hold all the names that are similar to $name.
369398
$homograph_names = [];
370399

371400
// Get all the membergroup names.
372401
$request = Db::$db->query(
373-
'SELECT group_name AS name
402+
'SELECT group_name
374403
FROM {db_prefix}membergroups',
375-
[],
376404
);
377405

378406
while ($row = Db::$db->fetch_assoc($request)) {
379-
if ($skeleton === self::getSkeletonString(html_entity_decode($row['name'], ENT_QUOTES))) {
380-
$homograph_names[] = $row['name'];
407+
$group_name = Utils::entityDecode($row['group_name'], nbsp_to_space: true);
408+
409+
if (empty(Config::$modSettings['reserveCase'])) {
410+
$group_name = Utils::casefold($group_name);
411+
}
412+
413+
if ($skeleton === self::getSkeletonString($group_name)) {
414+
$homograph_names[$row['group_name']] = $group_name;
381415
}
382416
}
383417
Db::$db->free_result($request);
@@ -407,7 +441,7 @@ protected static function checkHomographNames(string $name, array $homograph_nam
407441
{
408442
$name_script_set = self::resolveScriptSet($name);
409443

410-
foreach ($homograph_names as $homograph_name) {
444+
foreach ($homograph_names as $orig => $homograph_name) {
411445
$homograph_name_script_set = self::resolveScriptSet($homograph_name);
412446

413447
// If they are mixed script confusables, reject.
@@ -417,7 +451,7 @@ protected static function checkHomographNames(string $name, array $homograph_nam
417451
&& array_intersect($name_script_set, $homograph_name_script_set) === []
418452
) {
419453
if ($fatal) {
420-
ErrorHandler::fatalLang('username_reserved', 'password', [$homograph_name]);
454+
ErrorHandler::fatalLang('username_reserved', 'password', [$orig]);
421455
}
422456

423457
return true;
@@ -436,7 +470,7 @@ protected static function checkHomographNames(string $name, array $homograph_nam
436470
// This takes care of "ljeto" vs. "ljeto".
437471
if ($name_kc === $homograph_name_kc) {
438472
if ($fatal) {
439-
ErrorHandler::fatalLang('username_reserved', 'password', [$homograph_name]);
473+
ErrorHandler::fatalLang('username_reserved', 'password', [$orig]);
440474
}
441475

442476
return true;
@@ -449,7 +483,7 @@ protected static function checkHomographNames(string $name, array $homograph_nam
449483
// This takes care of "Bogden" vs. "Boɡden".
450484
if (!preg_match('~^[' . $regexes['Allowed'] . ']*$~u', $name_kc) || !preg_match('~^[' . $regexes['Allowed'] . ']*$~u', $homograph_name_kc)) {
451485
if ($fatal) {
452-
ErrorHandler::fatalLang('username_reserved', 'password', [$homograph_name]);
486+
ErrorHandler::fatalLang('username_reserved', 'password', [$orig]);
453487
}
454488

455489
return true;

0 commit comments

Comments
 (0)