From f68a3203acb86bfe95298c44e3c381b9aea03935 Mon Sep 17 00:00:00 2001 From: Daniel B Date: Wed, 17 Mar 2021 22:46:05 +0100 Subject: [PATCH] Allow specifying of valid player names (#410) This allows plugins which allow differently named players to work e.g. Floodgate. (Fixes #383) Co-authored-by: Phoenix616 --- .../ChestShop/Configuration/Properties.java | 3 ++ .../PreTransaction/InvalidNameIgnorer.java | 4 +-- .../ChestShop/Signs/ChestShopSign.java | 30 +++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java b/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java index 6e1431a..1fc8129 100644 --- a/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java +++ b/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java @@ -197,6 +197,9 @@ public class Properties { @ConfigurationComment("This makes sure that the UUIDs of player shop accounts match the server's online-mode setting. Disabling this might lead to issues with offline players and is therefore unsupported!") public static boolean ENSURE_CORRECT_PLAYERID = true; + @ConfigurationComment("This regexp validates the name of the player. If the name doesn't match, the player will neither be able to create a valid shop sign, nor buy/sell from a shop. Note to Bedrock players: If you have Floodgate on your server, you should set this regexp to ^\\\\*?\\\\w+$ and ENSURE_CORRECT_PLAYERID to false") + public static String VALID_PLAYERNAME_REGEXP = "^\\\\w+$"; + @PrecededBySpace @ConfigurationComment("Should we block shops that sell things for more than they buy? (This prevents newbies from creating shops that would be exploited)") public static boolean BLOCK_SHOPS_WITH_SELL_PRICE_HIGHER_THAN_BUY_PRICE = true; diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/PreTransaction/InvalidNameIgnorer.java b/src/main/java/com/Acrobot/ChestShop/Listeners/PreTransaction/InvalidNameIgnorer.java index c351415..435e321 100644 --- a/src/main/java/com/Acrobot/ChestShop/Listeners/PreTransaction/InvalidNameIgnorer.java +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/PreTransaction/InvalidNameIgnorer.java @@ -1,5 +1,6 @@ package com.Acrobot.ChestShop.Listeners.PreTransaction; +import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Events.PreTransactionEvent; import com.Acrobot.ChestShop.Signs.ChestShopSign; import org.bukkit.event.EventHandler; @@ -10,14 +11,13 @@ import java.util.regex.Pattern; public class InvalidNameIgnorer implements Listener { - private final static Pattern USERNAME_PATTERN = Pattern.compile("^\\w+$"); - @EventHandler(priority = EventPriority.LOWEST) public static void onPreTransaction(PreTransactionEvent event) { if (event.isCancelled()) { return; } + Pattern USERNAME_PATTERN = Pattern.compile(Properties.VALID_PLAYERNAME_REGEXP); String name = event.getClient().getName(); if (ChestShopSign.isAdminShop(name) || !USERNAME_PATTERN.matcher(name).matches()) { event.setCancelled(PreTransactionEvent.TransactionOutcome.INVALID_CLIENT_NAME); diff --git a/src/main/java/com/Acrobot/ChestShop/Signs/ChestShopSign.java b/src/main/java/com/Acrobot/ChestShop/Signs/ChestShopSign.java index 7264588..cc30f4f 100644 --- a/src/main/java/com/Acrobot/ChestShop/Signs/ChestShopSign.java +++ b/src/main/java/com/Acrobot/ChestShop/Signs/ChestShopSign.java @@ -34,7 +34,6 @@ public class ChestShopSign { public static final byte ITEM_LINE = 3; public static final Pattern[][] SHOP_SIGN_PATTERN = { - { Pattern.compile("^?[\\w \\-.:]*$") }, { Pattern.compile("^[1-9][0-9]{0,5}$"), Pattern.compile("^Q [1-9][0-9]{0,4} : C [0-9]{0,5}$") }, { Pattern.compile("(?i)^((\\d*([.e]\\d+)?)|free)$"), @@ -150,10 +149,33 @@ public class ChestShopSign { } public static boolean isValidPreparedSign(String[] lines) { - for (int i = 0; i < 4; i++) { + // The first line is the name of the shop owner + String playername = lines[0]; + + // If the shop owner is not blank (auto-filled) or the admin shop string, we need to validate it + if ((!isAdminShop(playername)) && (playername.length() > 0)) { + + // Prepare regexp patterns + Pattern playernamePattern = Pattern.compile(Properties.VALID_PLAYERNAME_REGEXP); // regexp from config file + Pattern playernameWithIdPattern = Pattern.compile(":[A-Za-z0-9]+$"); // regexp to match ':' and a base62 encoded string + + // Check if the playername has an ID. This can happen on duplicate or too long names + if (playernameWithIdPattern.matcher(playername).matches()) { + // Playername matches the id pattern, so validate everything before the last ':' + playername = playername.substring(0, playername.lastIndexOf(":") - 1); + } + + // If the playername doesn't match, this is not a valid sign, so return + if (!playernamePattern.matcher(playername).matches()) { + return false; + } + } + + // The first line is valid. Now validate the last 3 lines against the predefined regexp patterns. + for (int i = 0; i < 3; i++) { boolean matches = false; for (Pattern pattern : SHOP_SIGN_PATTERN[i]) { - if (pattern.matcher(lines[i]).matches()) { + if (pattern.matcher(lines[i+1]).matches()) { matches = true; break; } @@ -162,6 +184,8 @@ public class ChestShopSign { return false; } } + + // All lines are looking good. If the price line contains only one ':', then this is a valid prepared sign. return lines[PRICE_LINE].indexOf(':') == lines[PRICE_LINE].lastIndexOf(':'); } }