diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..9438ccd --- /dev/null +++ b/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..6cac1a3 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + AntiBook + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/README.md b/README.md new file mode 100644 index 0000000..c319c56 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# HeroVerseMC +HeroVerseMC's new plugin. + + +# Copyright +Copyright (C) 2015 - Samuel McCoder and the HeroVerseMC's project team - All Rights Reserved
+If you have any issues or questions about this repository, please leave a post in this repositories issues section, which can be found here.
+Ensure to read the license section below before using any of this project.
+ + +# License ++Licensed under the Apache License, Version 2.0 (Apache-2.0); you may not use this file except in compliance with the License. You may obtain a copy of the License at +
http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bin/me/mccoder/com/AbstractConfigHandler.class b/bin/me/mccoder/com/AbstractConfigHandler.class new file mode 100644 index 0000000..cbaa1b2 Binary files /dev/null and b/bin/me/mccoder/com/AbstractConfigHandler.class differ diff --git a/bin/me/mccoder/com/AntiBook.class b/bin/me/mccoder/com/AntiBook.class new file mode 100644 index 0000000..728efed Binary files /dev/null and b/bin/me/mccoder/com/AntiBook.class differ diff --git a/bin/me/mccoder/com/BookFilter.class b/bin/me/mccoder/com/BookFilter.class new file mode 100644 index 0000000..9505dda Binary files /dev/null and b/bin/me/mccoder/com/BookFilter.class differ diff --git a/bin/me/mccoder/com/BookListener$1.class b/bin/me/mccoder/com/BookListener$1.class new file mode 100644 index 0000000..7205a96 Binary files /dev/null and b/bin/me/mccoder/com/BookListener$1.class differ diff --git a/bin/me/mccoder/com/BookListener.class b/bin/me/mccoder/com/BookListener.class new file mode 100644 index 0000000..d902f31 Binary files /dev/null and b/bin/me/mccoder/com/BookListener.class differ diff --git a/bin/me/mccoder/com/ConfigHandler.class b/bin/me/mccoder/com/ConfigHandler.class new file mode 100644 index 0000000..93096c4 Binary files /dev/null and b/bin/me/mccoder/com/ConfigHandler.class differ diff --git a/bin/me/mccoder/com/FilterCommand.class b/bin/me/mccoder/com/FilterCommand.class new file mode 100644 index 0000000..7d50707 Binary files /dev/null and b/bin/me/mccoder/com/FilterCommand.class differ diff --git a/bin/me/mccoder/com/IBookFilter.class b/bin/me/mccoder/com/IBookFilter.class new file mode 100644 index 0000000..b7a26f1 Binary files /dev/null and b/bin/me/mccoder/com/IBookFilter.class differ diff --git a/bin/me/mccoder/com/v1_8_R1/BookFilter.class b/bin/me/mccoder/com/v1_8_R1/BookFilter.class new file mode 100644 index 0000000..f60774e Binary files /dev/null and b/bin/me/mccoder/com/v1_8_R1/BookFilter.class differ diff --git a/bin/me/mccoder/com/v1_8_R2/BookFilter.class b/bin/me/mccoder/com/v1_8_R2/BookFilter.class new file mode 100644 index 0000000..78370cd Binary files /dev/null and b/bin/me/mccoder/com/v1_8_R2/BookFilter.class differ diff --git a/bin/me/mccoder/com/v1_8_R3/BookFilter.class b/bin/me/mccoder/com/v1_8_R3/BookFilter.class new file mode 100644 index 0000000..0151d5d Binary files /dev/null and b/bin/me/mccoder/com/v1_8_R3/BookFilter.class differ diff --git a/plugin.yml b/plugin.yml new file mode 100644 index 0000000..b04de57 --- /dev/null +++ b/plugin.yml @@ -0,0 +1,10 @@ +name: AntiBook +version: 1.0 +main: me.mccoder.com.AntiBook +author: McCoder +description: Fixes an exploit with JSON books +commands: + filter: + aliases: [bookfilter, filterbook] + usage: / + permission: antibook.filter \ No newline at end of file diff --git a/src/me/mccoder/com/AbstractConfigHandler.java b/src/me/mccoder/com/AbstractConfigHandler.java new file mode 100644 index 0000000..df7a1e6 --- /dev/null +++ b/src/me/mccoder/com/AbstractConfigHandler.java @@ -0,0 +1,34 @@ +package me.mccoder.com; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.FileConfigurationOptions; +import org.bukkit.plugin.Plugin; + +public abstract class AbstractConfigHandler +{ + protected final Plugin plugin; + protected FileConfiguration config; + + public AbstractConfigHandler(Plugin plugin) + { + this.plugin = plugin; + } + + public void loadConfig() + { + this.config = this.plugin.getConfig(); + this.config.options().copyDefaults(true); + addDefaults(); + saveConfig(); + loadData(); + } + + public void saveConfig() + { + this.plugin.saveConfig(); + } + + protected abstract void loadData(); + + protected abstract void addDefaults(); +} diff --git a/src/me/mccoder/com/AntiBook.java b/src/me/mccoder/com/AntiBook.java new file mode 100644 index 0000000..e94780c --- /dev/null +++ b/src/me/mccoder/com/AntiBook.java @@ -0,0 +1,90 @@ +package me.mccoder.com; + +import java.util.logging.Level; +import java.util.logging.Logger; +import org.bukkit.Bukkit; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitScheduler; + +public class AntiBook + extends JavaPlugin +{ + ConfigHandler config; + IBookFilter filter; + + public void onDisable() + { + Bukkit.getScheduler().cancelTasks(this); + + PluginDescriptionFile desc = getDescription(); + getLogger().log(Level.INFO, "{0} vers. {1} by McCoder deactivated", new String[] { desc.getName(), desc.getVersion() }); + } + + public void onEnable() + { + boolean enable = true; + + loadConfig(); + + String version = Bukkit.getServer().getClass().getCanonicalName(); + version = version.substring(0, version.lastIndexOf('.')); + version = version.substring(version.lastIndexOf('.') + 1); + switch (version) + { + case "v1_8_R1": + this.filter = new me.mccoder.com.v1_8_R1.BookFilter(); + getLogger().log(Level.INFO, "Detected supported server version {0}", version); + break; + case "v1_8_R2": + this.filter = new me.mccoder.com.v1_8_R2.BookFilter(); + getLogger().log(Level.INFO, "Detected supported server version {0}", version); + break; + case "v1_8_R3": + this.filter = new me.mccoder.com.v1_8_R3.BookFilter(); + getLogger().log(Level.INFO, "Detected supported server version {0}", version); + break; + default: + getLogger().log(Level.SEVERE, "Unsupported server version {0}. Disabling plugin.", version); + getLogger().log(Level.SEVERE, "Please check for updates at {0}", getDescription().getWebsite()); + enable = false; + Bukkit.getPluginManager().disablePlugin(this); + } + if (enable) + { + this.filter.setLogger(getLogger()); + this.filter.setHoverMsg(this.config.getHoverMsg()); + this.filter.setFilterActions(this.config.getFilterActions()); + + registerCommands(); + + registerEvents(); + + PluginDescriptionFile desc = getDescription(); + getLogger().log(Level.INFO, "{0} vers. {1} by McCoder activated", new String[] { desc.getName(), desc.getVersion() }); + } + } + + private void loadConfig() + { + this.config = new ConfigHandler(this); + this.config.loadConfig(); + } + + public IBookFilter getFilter() + { + return this.filter; + } + + private void registerCommands() + { + getCommand("filter").setExecutor(new FilterCommand(this)); + } + + private void registerEvents() + { + Bukkit.getPluginManager().registerEvents(new BookListener(this), this); + } +} diff --git a/src/me/mccoder/com/BookFilter.java b/src/me/mccoder/com/BookFilter.java new file mode 100644 index 0000000..c2d5e93 --- /dev/null +++ b/src/me/mccoder/com/BookFilter.java @@ -0,0 +1,18 @@ +package me.mccoder.com; + +import java.util.List; +import java.util.logging.Logger; +import org.bukkit.inventory.ItemStack; + +public abstract interface BookFilter +{ + public abstract void setLogger(Logger paramLogger); + + public abstract void setHoverMsg(String paramString); + + public abstract void setFilterActions(List paramList); + + public abstract ItemStack filterBook(ItemStack paramItemStack); + + public abstract Object filterBook(Object paramObject); +} diff --git a/src/me/mccoder/com/BookListener.java b/src/me/mccoder/com/BookListener.java new file mode 100644 index 0000000..c15b5ca --- /dev/null +++ b/src/me/mccoder/com/BookListener.java @@ -0,0 +1,79 @@ +package me.mccoder.com; + +import java.util.logging.Level; +import java.util.logging.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerEditBookEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitScheduler; + +public class BookListener + implements Listener +{ + private final AntiBook plugin; + + public BookListener(AntiBook plugin) + { + this.plugin = plugin; + } + + @EventHandler(priority=EventPriority.LOW) + public void onBookEdit(final PlayerEditBookEvent event) + { + if (this.plugin.config.isOnCreation()) { + Bukkit.getScheduler().runTaskLater(this.plugin, new Runnable() + { + public void run() + { + Inventory inv = event.getPlayer().getInventory(); + for (int i = 0; i < inv.getSize(); i++) + { + ItemStack item = inv.getItem(i); + if ((item != null) && (item.getType() == Material.WRITTEN_BOOK)) + { + if (BookListener.this.plugin.config.isDebug()) { + BookListener.this.plugin.getLogger().log(Level.INFO, "Filtering edited ItemStack: {0}", item); + } + ItemStack filtered = BookListener.this.plugin.getFilter().filterBook(item); + if (filtered != null) + { + BookListener.this.plugin.getLogger().log(Level.WARNING, "Player {0} {1} created a book with illegal JSON content!", new Object[] { event.getPlayer().getName(), event.getPlayer().getUniqueId() }); + + inv.setItem(i, filtered); + } + } + } + } + }, 4L); + } + } + + @EventHandler(priority=EventPriority.LOW) + public void onBookRead(PlayerInteractEvent event) + { + if ((this.plugin.config.isOnRead()) && ((event.getAction() == Action.RIGHT_CLICK_AIR) || (event.getAction() == Action.RIGHT_CLICK_BLOCK)) && (event.getItem() != null) && (event.getItem().getType() == Material.WRITTEN_BOOK) && (!event.getPlayer().hasPermission("antibook.overridefilter"))) + { + if (this.plugin.config.isDebug()) { + this.plugin.getLogger().log(Level.INFO, "Filtering read ItemStack: {0}", event.getPlayer().getItemInHand()); + } + ItemStack filtered = this.plugin.getFilter().filterBook(event.getPlayer().getItemInHand()); + if (filtered != null) + { + this.plugin.getLogger().log(Level.WARNING, "Player {0} {1} just tried to read a book with illegal JSON content!", new Object[] { event.getPlayer().getName(), event.getPlayer().getUniqueId() }); + + event.getPlayer().setItemInHand(filtered); + for (String line : this.plugin.config.getChatWarning()) { + event.getPlayer().sendMessage(line); + } + } + } + } +} diff --git a/src/me/mccoder/com/ConfigHandler.java b/src/me/mccoder/com/ConfigHandler.java new file mode 100644 index 0000000..0dd1d95 --- /dev/null +++ b/src/me/mccoder/com/ConfigHandler.java @@ -0,0 +1,90 @@ +package me.mccoder.com; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.Plugin; + +public class ConfigHandler + extends AbstractConfigHandler +{ + private static final String PATH_FILTER = "click_action_filter"; + private static final String PATH_HOVER = "hover_message"; + private static final String PATH_WARNING = "chat_warning"; + private static final String PATH_ON_CREATION = "activate.on_creation"; + private static final String PATH_ON_READ = "activate.on_read"; + private static final String PATH_DEBUG = "log_debug"; + private static final List DEF_FILTER = Arrays.asList(new String[] { "RUN_COMMAND", "OPEN_FILE", "OPEN_URL", "SUGGEST_COMMAND", "TWITCH_USER_INFO" }); + private static final String DEF_HOVER = "Illegal content was filtered from this book"; + private static final List DEF_WARNING = Arrays.asList(new String[] { " ==== WARNING ==== WARNING ==== WARNING ====", "", " DO NOT CLICK ON ANYTHING IN THIS BOOK!", "", " It contains illegal code and is potential harmful", "", " ==== WARNING ==== WARNING ==== WARNING ====" }); + private List filterActions; + private String hoverMsg; + private List chatWarning; + private boolean onCreation; + private boolean onRead; + private boolean debug; + + public ConfigHandler(Plugin plugin) + { + super(plugin); + } + + protected void loadData() + { + this.filterActions = this.config.getStringList("click_action_filter"); + + this.hoverMsg = ChatColor.translateAlternateColorCodes('&', this.config.getString("hover_message")); + + this.chatWarning = new ArrayList(this.config.getStringList("chat_warning")); + for (int i = 0; i < getChatWarning().size(); i++) { + getChatWarning().set(i, ChatColor.RED + ChatColor.translateAlternateColorCodes('&', (String)getChatWarning().get(i))); + } + this.onCreation = this.config.getBoolean("activate.on_creation"); + + this.onRead = this.config.getBoolean("activate.on_read"); + + this.debug = this.config.getBoolean("log_debug"); + } + + protected void addDefaults() + { + this.config.addDefault("click_action_filter", DEF_FILTER); + this.config.addDefault("hover_message", "Illegal content was filtered from this book"); + this.config.addDefault("chat_warning", DEF_WARNING); + this.config.addDefault("activate.on_creation", Boolean.valueOf(true)); + this.config.addDefault("activate.on_read", Boolean.valueOf(true)); + this.config.addDefault("log_debug", Boolean.valueOf(false)); + } + + public List getFilterActions() + { + return this.filterActions; + } + + public String getHoverMsg() + { + return this.hoverMsg; + } + + public List getChatWarning() + { + return this.chatWarning; + } + + public boolean isOnCreation() + { + return this.onCreation; + } + + public boolean isOnRead() + { + return this.onRead; + } + + public boolean isDebug() + { + return this.debug; + } +} diff --git a/src/me/mccoder/com/FilterCommand.java b/src/me/mccoder/com/FilterCommand.java new file mode 100644 index 0000000..03375f7 --- /dev/null +++ b/src/me/mccoder/com/FilterCommand.java @@ -0,0 +1,56 @@ +package me.mccoder.com; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +public class FilterCommand + implements CommandExecutor +{ + private final AntiBook plugin; + + public FilterCommand(AntiBook plugin) + { + this.plugin = plugin; + } + + public boolean onCommand(CommandSender sender, Command command, String s, String[] strings) + { + if ((command.getName().equalsIgnoreCase("filter")) && (sender.hasPermission("antibook.filter"))) + { + if ((sender instanceof Player)) + { + Player player = (Player)sender; + ItemStack book = player.getItemInHand(); + if ((book != null) && (book.getType() == Material.WRITTEN_BOOK)) + { + ItemStack newBook = this.plugin.getFilter().filterBook(book); + if (newBook != null) + { + player.getInventory().addItem(new ItemStack[] { newBook }); + sender.sendMessage(ChatColor.AQUA + "A filtered version of this book has been set to your inventory"); + } + else + { + sender.sendMessage(ChatColor.AQUA + "This book does not contain any illegal JSON code"); + } + } + else + { + sender.sendMessage(ChatColor.RED + "You must hold a written book in your hand to filter"); + } + } + else + { + sender.sendMessage(ChatColor.RED + "This command can only be used by a player!"); + } + return true; + } + return false; + } +} diff --git a/src/me/mccoder/com/IBookFilter.java b/src/me/mccoder/com/IBookFilter.java new file mode 100644 index 0000000..8e9a31b --- /dev/null +++ b/src/me/mccoder/com/IBookFilter.java @@ -0,0 +1,18 @@ +package me.mccoder.com; + +import java.util.List; +import java.util.logging.Logger; +import org.bukkit.inventory.ItemStack; + +public abstract interface IBookFilter +{ + public abstract void setLogger(Logger paramLogger); + + public abstract void setHoverMsg(String paramString); + + public abstract void setFilterActions(List paramList); + + public abstract ItemStack filterBook(ItemStack paramItemStack); + + public abstract Object filterBook(Object paramObject); +} diff --git a/src/me/mccoder/com/v1_8_R1/BookFilter.java b/src/me/mccoder/com/v1_8_R1/BookFilter.java new file mode 100644 index 0000000..923c995 --- /dev/null +++ b/src/me/mccoder/com/v1_8_R1/BookFilter.java @@ -0,0 +1,161 @@ +package me.mccoder.com.v1_8_R1; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import me.mccoder.com.IBookFilter; +import net.minecraft.server.v1_8_R1.ChatClickable; +import net.minecraft.server.v1_8_R1.ChatComponentText; +import net.minecraft.server.v1_8_R1.ChatHoverable; +import net.minecraft.server.v1_8_R1.ChatModifier; +import net.minecraft.server.v1_8_R1.ChatSerializer; +import net.minecraft.server.v1_8_R1.EnumClickAction; +import net.minecraft.server.v1_8_R1.EnumHoverAction; +import net.minecraft.server.v1_8_R1.IChatBaseComponent; +import net.minecraft.server.v1_8_R1.ItemWrittenBook; +import net.minecraft.server.v1_8_R1.NBTTagCompound; +import net.minecraft.server.v1_8_R1.NBTTagList; +import net.minecraft.server.v1_8_R1.NBTTagString; +import org.bukkit.craftbukkit.v1_8_R1.inventory.CraftItemStack; + +public class BookFilter + implements IBookFilter +{ + private static final String PAGES_KEY = "pages"; + private static final int PAGES_KEY_VALUE = 9; + private static final int PAGES_LIST_VALUE = 8; + private Logger logger = Logger.getLogger(BookFilter.class.getName()); + private Set filterActions = EnumSet.of(EnumClickAction.RUN_COMMAND, EnumClickAction.OPEN_FILE, EnumClickAction.OPEN_URL, EnumClickAction.SUGGEST_COMMAND); + private String hoverMsg = "Illegal content was filtered from this book"; + + public org.bukkit.inventory.ItemStack filterBook(org.bukkit.inventory.ItemStack filterItem) + { + Object result = filterBook(CraftItemStack.asNMSCopy(filterItem)); + if (result != null) { + return CraftItemStack.asBukkitCopy((net.minecraft.server.v1_8_R1.ItemStack)result); + } + return null; + } + + public Object filterBook(Object filterItem) + { + if (filterItem == null) { + return null; + } + if (!(filterItem instanceof net.minecraft.server.v1_8_R1.ItemStack)) { + throw new IllegalArgumentException("Expected object of type net.minecraft.server.v1_8_R1.ItemStack. Received " + filterItem.getClass()); + } + net.minecraft.server.v1_8_R1.ItemStack mcStack = (net.minecraft.server.v1_8_R1.ItemStack)filterItem; + try + { + if (!(mcStack.getItem() instanceof ItemWrittenBook)) { + return null; + } + mcStack = mcStack.cloneItemStack(); + + NBTTagCompound tag = mcStack.hasTag() ? mcStack.getTag() : null; + NBTTagList pages = (tag != null) && (tag.hasKeyOfType("pages", 9)) ? tag.getList("pages", 8) : null; + + boolean filtered = false; + for (int i = 0; (pages != null) && (i < pages.size()); i++) + { + String jsonPage = pages.getString(i); + IChatBaseComponent component; + try + { + component = jsonPage != null ? ChatSerializer.a(jsonPage) : null; + } + catch (Exception e) + { + component = null; + this.logger.log(Level.WARNING, "Unable to parse page {0} of book type {1} to IChatBaseComponent.This means it is probably not a JSON book and can be ignored.", new Object[] { Integer.valueOf(i), mcStack.getItem().getClass() }); + + this.logger.log(Level.WARNING, "Page: {0}", jsonPage); + this.logger.log(Level.WARNING, "Original Exception:", e); + } + List subComponents = new ArrayList(); + addComponents(component, subComponents); + + boolean changedPage = false; + for (IChatBaseComponent subComponent : subComponents) + { + ChatModifier modifier = subComponent.getChatModifier(); + ChatClickable clickable = modifier.h(); + if ((clickable != null) && (this.filterActions.contains(clickable.a()))) + { + this.logger.log(Level.WARNING, "Filtered illegal content from item!"); + this.logger.log(Level.WARNING, subComponent.toString()); + + modifier.setChatHoverable(new ChatHoverable(EnumHoverAction.SHOW_TEXT, new ChatComponentText(this.hoverMsg))); + + modifier.setChatClickable(null); + subComponent.setChatModifier(modifier); + + changedPage = true; + filtered = true; + } + } + if (changedPage) + { + jsonPage = ChatSerializer.a(component); + pages.a(i, new NBTTagString(jsonPage)); + } + } + if (filtered) + { + tag.set("pages", pages); + mcStack.setTag(tag); + return mcStack; + } + return null; + } + catch (Throwable e) + { + this.logger.log(Level.SEVERE, "Failed to filter book due to an unexpected exception", e); + } + return null; + } + + private static void addComponents(IChatBaseComponent component, List list) + { + if (component != null) + { + list.add(component); + + List children = component.a(); + if (children != null) { + for (IChatBaseComponent child : children) { + addComponents(child, list); + } + } + } + } + + public void setLogger(Logger logger) + { + this.logger = logger; + } + + public void setHoverMsg(String hoverMsg) + { + this.hoverMsg = hoverMsg; + } + + public void setFilterActions(List actions) + { + this.filterActions = EnumSet.noneOf(EnumClickAction.class); + for (String actionString : actions) { + try + { + this.filterActions.add(EnumClickAction.valueOf(actionString.toUpperCase())); + } + catch (IllegalArgumentException e) + { + this.logger.log(Level.WARNING, "Invalid ActionEnum: {0}", actionString.toUpperCase()); + } + } + } +} diff --git a/src/me/mccoder/com/v1_8_R2/BookFilter.java b/src/me/mccoder/com/v1_8_R2/BookFilter.java new file mode 100644 index 0000000..4f16089 --- /dev/null +++ b/src/me/mccoder/com/v1_8_R2/BookFilter.java @@ -0,0 +1,161 @@ +package me.mccoder.com.v1_8_R2; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import me.mccoder.com.IBookFilter; +import net.minecraft.server.v1_8_R2.ChatClickable; +import net.minecraft.server.v1_8_R2.ChatClickable.EnumClickAction; +import net.minecraft.server.v1_8_R2.ChatComponentText; +import net.minecraft.server.v1_8_R2.ChatHoverable; +import net.minecraft.server.v1_8_R2.ChatHoverable.EnumHoverAction; +import net.minecraft.server.v1_8_R2.ChatModifier; +import net.minecraft.server.v1_8_R2.IChatBaseComponent; +import net.minecraft.server.v1_8_R2.IChatBaseComponent.ChatSerializer; +import net.minecraft.server.v1_8_R2.ItemWrittenBook; +import net.minecraft.server.v1_8_R2.NBTTagCompound; +import net.minecraft.server.v1_8_R2.NBTTagList; +import net.minecraft.server.v1_8_R2.NBTTagString; +import org.bukkit.craftbukkit.v1_8_R2.inventory.CraftItemStack; + +public class BookFilter + implements IBookFilter +{ + private static final String PAGES_KEY = "pages"; + private static final int PAGES_KEY_VALUE = 9; + private static final int PAGES_LIST_VALUE = 8; + private Logger logger = Logger.getLogger(BookFilter.class.getName()); + private Set filterActions = EnumSet.of(ChatClickable.EnumClickAction.RUN_COMMAND, ChatClickable.EnumClickAction.OPEN_FILE, ChatClickable.EnumClickAction.OPEN_URL, ChatClickable.EnumClickAction.SUGGEST_COMMAND); + private String hoverMsg = "Illegal content was filtered from this book"; + + public org.bukkit.inventory.ItemStack filterBook(org.bukkit.inventory.ItemStack filterItem) + { + Object result = filterBook(CraftItemStack.asNMSCopy(filterItem)); + if (result != null) { + return CraftItemStack.asBukkitCopy((net.minecraft.server.v1_8_R2.ItemStack)result); + } + return null; + } + + public Object filterBook(Object filterItem) + { + if (filterItem == null) { + return null; + } + if (!(filterItem instanceof net.minecraft.server.v1_8_R2.ItemStack)) { + throw new IllegalArgumentException("Expected object of type net.minecraft.server.v1_8_R2.ItemStack. Received " + filterItem.getClass()); + } + net.minecraft.server.v1_8_R2.ItemStack mcStack = (net.minecraft.server.v1_8_R2.ItemStack)filterItem; + try + { + if (!(mcStack.getItem() instanceof ItemWrittenBook)) { + return null; + } + mcStack = mcStack.cloneItemStack(); + + NBTTagCompound tag = mcStack.hasTag() ? mcStack.getTag() : null; + NBTTagList pages = (tag != null) && (tag.hasKeyOfType("pages", 9)) ? tag.getList("pages", 8) : null; + + boolean filtered = false; + for (int i = 0; (pages != null) && (i < pages.size()); i++) + { + String jsonPage = pages.getString(i); + IChatBaseComponent component; + try + { + component = jsonPage != null ? IChatBaseComponent.ChatSerializer.a(jsonPage) : null; + } + catch (Exception e) + { + component = null; + this.logger.log(Level.WARNING, "Unable to parse page {0} of book type {1} to IChatBaseComponent.This means it is probably not a JSON book and can be ignored.", new Object[] { Integer.valueOf(i), mcStack.getItem().getClass() }); + + this.logger.log(Level.WARNING, "Page: {0}", jsonPage); + this.logger.log(Level.WARNING, "Original Exception:", e); + } + List subComponents = new ArrayList(); + addComponents(component, subComponents); + + boolean changedPage = false; + for (IChatBaseComponent subComponent : subComponents) + { + ChatModifier modifier = subComponent.getChatModifier(); + ChatClickable clickable = modifier.h(); + if ((clickable != null) && (this.filterActions.contains(clickable.a()))) + { + this.logger.log(Level.WARNING, "Filtered illegal content from item!"); + this.logger.log(Level.WARNING, subComponent.toString()); + + modifier.setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_TEXT, new ChatComponentText(this.hoverMsg))); + + modifier.setChatClickable(null); + subComponent.setChatModifier(modifier); + + changedPage = true; + filtered = true; + } + } + if (changedPage) + { + jsonPage = IChatBaseComponent.ChatSerializer.a(component); + pages.a(i, new NBTTagString(jsonPage)); + } + } + if (filtered) + { + tag.set("pages", pages); + mcStack.setTag(tag); + return mcStack; + } + return null; + } + catch (Throwable e) + { + this.logger.log(Level.SEVERE, "Failed to filter book due to an unexpected exception", e); + } + return null; + } + + private static void addComponents(IChatBaseComponent component, List list) + { + if (component != null) + { + list.add(component); + + List children = component.a(); + if (children != null) { + for (IChatBaseComponent child : children) { + addComponents(child, list); + } + } + } + } + + public void setLogger(Logger logger) + { + this.logger = logger; + } + + public void setHoverMsg(String hoverMsg) + { + this.hoverMsg = hoverMsg; + } + + public void setFilterActions(List actions) + { + this.filterActions = EnumSet.noneOf(ChatClickable.EnumClickAction.class); + for (String actionString : actions) { + try + { + this.filterActions.add(ChatClickable.EnumClickAction.valueOf(actionString.toUpperCase())); + } + catch (IllegalArgumentException e) + { + this.logger.log(Level.WARNING, "Invalid ActionEnum: {0}", actionString.toUpperCase()); + } + } + } +} diff --git a/src/me/mccoder/com/v1_8_R3/BookFilter.java b/src/me/mccoder/com/v1_8_R3/BookFilter.java new file mode 100644 index 0000000..df208e6 --- /dev/null +++ b/src/me/mccoder/com/v1_8_R3/BookFilter.java @@ -0,0 +1,161 @@ +package me.mccoder.com.v1_8_R3; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import me.mccoder.com.IBookFilter; +import net.minecraft.server.v1_8_R3.ChatClickable; +import net.minecraft.server.v1_8_R3.ChatClickable.EnumClickAction; +import net.minecraft.server.v1_8_R3.ChatComponentText; +import net.minecraft.server.v1_8_R3.ChatHoverable; +import net.minecraft.server.v1_8_R3.ChatHoverable.EnumHoverAction; +import net.minecraft.server.v1_8_R3.ChatModifier; +import net.minecraft.server.v1_8_R3.IChatBaseComponent; +import net.minecraft.server.v1_8_R3.IChatBaseComponent.ChatSerializer; +import net.minecraft.server.v1_8_R3.ItemWrittenBook; +import net.minecraft.server.v1_8_R3.NBTTagCompound; +import net.minecraft.server.v1_8_R3.NBTTagList; +import net.minecraft.server.v1_8_R3.NBTTagString; +import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack; + +public class BookFilter + implements IBookFilter +{ + private static final String PAGES_KEY = "pages"; + private static final int PAGES_KEY_VALUE = 9; + private static final int PAGES_LIST_VALUE = 8; + private Logger logger = Logger.getLogger(BookFilter.class.getName()); + private Set filterActions = EnumSet.of(ChatClickable.EnumClickAction.RUN_COMMAND, ChatClickable.EnumClickAction.OPEN_FILE, ChatClickable.EnumClickAction.OPEN_URL, ChatClickable.EnumClickAction.SUGGEST_COMMAND); + private String hoverMsg = "Illegal content was filtered from this book"; + + public org.bukkit.inventory.ItemStack filterBook(org.bukkit.inventory.ItemStack filterItem) + { + Object result = filterBook(CraftItemStack.asNMSCopy(filterItem)); + if (result != null) { + return CraftItemStack.asBukkitCopy((net.minecraft.server.v1_8_R3.ItemStack)result); + } + return null; + } + + public Object filterBook(Object filterItem) + { + if (filterItem == null) { + return null; + } + if (!(filterItem instanceof net.minecraft.server.v1_8_R3.ItemStack)) { + throw new IllegalArgumentException("Expected object of type net.minecraft.server.v1_8_R3.ItemStack. Received " + filterItem.getClass()); + } + net.minecraft.server.v1_8_R3.ItemStack mcStack = (net.minecraft.server.v1_8_R3.ItemStack)filterItem; + try + { + if (!(mcStack.getItem() instanceof ItemWrittenBook)) { + return null; + } + mcStack = mcStack.cloneItemStack(); + + NBTTagCompound tag = mcStack.hasTag() ? mcStack.getTag() : null; + NBTTagList pages = (tag != null) && (tag.hasKeyOfType("pages", 9)) ? tag.getList("pages", 8) : null; + + boolean filtered = false; + for (int i = 0; (pages != null) && (i < pages.size()); i++) + { + String jsonPage = pages.getString(i); + IChatBaseComponent component; + try + { + component = jsonPage != null ? IChatBaseComponent.ChatSerializer.a(jsonPage) : null; + } + catch (Exception e) + { + component = null; + this.logger.log(Level.WARNING, "Unable to parse page {0} of book type {1} to IChatBaseComponent.This means it is probably not a JSON book and can be ignored.", new Object[] { Integer.valueOf(i), mcStack.getItem().getClass() }); + + this.logger.log(Level.WARNING, "Page: {0}", jsonPage); + this.logger.log(Level.WARNING, "Original Exception:", e); + } + List subComponents = new ArrayList(); + addComponents(component, subComponents); + + boolean changedPage = false; + for (IChatBaseComponent subComponent : subComponents) + { + ChatModifier modifier = subComponent.getChatModifier(); + ChatClickable clickable = modifier.h(); + if ((clickable != null) && (this.filterActions.contains(clickable.a()))) + { + this.logger.log(Level.WARNING, "Filtered illegal content from item!"); + this.logger.log(Level.WARNING, subComponent.toString()); + + modifier.setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_TEXT, new ChatComponentText(this.hoverMsg))); + + modifier.setChatClickable(null); + subComponent.setChatModifier(modifier); + + changedPage = true; + filtered = true; + } + } + if (changedPage) + { + jsonPage = IChatBaseComponent.ChatSerializer.a(component); + pages.a(i, new NBTTagString(jsonPage)); + } + } + if (filtered) + { + tag.set("pages", pages); + mcStack.setTag(tag); + return mcStack; + } + return null; + } + catch (Throwable e) + { + this.logger.log(Level.SEVERE, "Failed to filter book due to an unexpected exception", e); + } + return null; + } + + private static void addComponents(IChatBaseComponent component, List list) + { + if (component != null) + { + list.add(component); + + List children = component.a(); + if (children != null) { + for (IChatBaseComponent child : children) { + addComponents(child, list); + } + } + } + } + + public void setLogger(Logger logger) + { + this.logger = logger; + } + + public void setHoverMsg(String hoverMsg) + { + this.hoverMsg = hoverMsg; + } + + public void setFilterActions(List actions) + { + this.filterActions = EnumSet.noneOf(ChatClickable.EnumClickAction.class); + for (String actionString : actions) { + try + { + this.filterActions.add(ChatClickable.EnumClickAction.valueOf(actionString.toUpperCase())); + } + catch (IllegalArgumentException e) + { + this.logger.log(Level.WARNING, "Invalid ActionEnum: {0}", actionString.toUpperCase()); + } + } + } +}