diff --git a/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java b/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java index c3fd5de52..6bb21afbf 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/CommandManager.java @@ -41,7 +41,6 @@ import me.lucko.luckperms.common.commands.migration.MigrationMainCommand; import me.lucko.luckperms.common.commands.misc.ApplyEditsCommand; import me.lucko.luckperms.common.commands.misc.BulkUpdateCommand; import me.lucko.luckperms.common.commands.misc.CheckCommand; -import me.lucko.luckperms.common.commands.misc.DebugCommand; import me.lucko.luckperms.common.commands.misc.EditorCommand; import me.lucko.luckperms.common.commands.misc.ExportCommand; import me.lucko.luckperms.common.commands.misc.ImportCommand; @@ -111,7 +110,6 @@ public class CommandManager { .add(new SyncCommand(locale)) .add(new InfoCommand(locale)) .add(new EditorCommand(locale)) - .add(new DebugCommand(locale)) .add(new VerboseCommand(locale)) .add(new TreeCommand(locale)) .add(new SearchCommand(locale)) diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java b/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java index cec653a1c..27078df47 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/generic/other/HolderEditor.java @@ -39,6 +39,8 @@ import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; +import me.lucko.luckperms.common.util.gson.GsonProvider; +import me.lucko.luckperms.common.web.AbstractHttpClient; import me.lucko.luckperms.common.web.WebEditor; import net.kyori.text.Component; @@ -48,8 +50,14 @@ import net.kyori.text.event.HoverEvent; import net.kyori.text.format.TextColor; import net.luckperms.api.context.ImmutableContextSet; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import java.util.zip.GZIPOutputStream; public class HolderEditor extends SubCommand { public HolderEditor(LocaleManager locale, boolean user) { @@ -69,8 +77,17 @@ public class HolderEditor extends SubCommand { JsonObject payload = WebEditor.formPayload(Collections.singletonList(holder), Collections.emptyList(), sender, label, plugin); // upload the payload data to gist - String pasteId = plugin.getBytebin().postJson(payload, true).id(); - if (pasteId == null) { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + try (Writer writer = new OutputStreamWriter(new GZIPOutputStream(bytesOut), StandardCharsets.UTF_8)) { + GsonProvider.prettyPrinting().toJson(payload, writer); + } catch (IOException e) { + e.printStackTrace(); + } + + String pasteId; + try { + pasteId = plugin.getBytebin().postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key(); + } catch (IOException e) { Message.EDITOR_UPLOAD_FAILURE.send(sender); return CommandResult.STATE_ERROR; } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/DebugCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/DebugCommand.java deleted file mode 100644 index ceed83513..000000000 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/DebugCommand.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package me.lucko.luckperms.common.commands.misc; - -import me.lucko.luckperms.common.cacheddata.type.MetaCache; -import me.lucko.luckperms.common.cacheddata.type.PermissionCache; -import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import me.lucko.luckperms.common.command.CommandResult; -import me.lucko.luckperms.common.command.abstraction.SingleCommand; -import me.lucko.luckperms.common.command.access.CommandPermission; -import me.lucko.luckperms.common.context.ContextSetJsonSerializer; -import me.lucko.luckperms.common.context.ProxiedContextCalculator; -import me.lucko.luckperms.common.locale.LocaleManager; -import me.lucko.luckperms.common.locale.command.CommandSpec; -import me.lucko.luckperms.common.locale.message.Message; -import me.lucko.luckperms.common.model.User; -import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.sender.Sender; -import me.lucko.luckperms.common.util.Predicates; -import me.lucko.luckperms.common.util.gson.GsonProvider; -import me.lucko.luckperms.common.util.gson.JArray; -import me.lucko.luckperms.common.util.gson.JObject; -import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; -import me.lucko.luckperms.common.web.Hastebin; - -import net.kyori.text.Component; -import net.kyori.text.TextComponent; -import net.kyori.text.event.ClickEvent; -import net.kyori.text.event.HoverEvent; -import net.kyori.text.format.TextColor; -import net.luckperms.api.context.ContextCalculator; -import net.luckperms.api.context.StaticContextCalculator; -import net.luckperms.api.metastacking.MetaStackDefinition; -import net.luckperms.api.metastacking.MetaStackElement; -import net.luckperms.api.query.Flag; -import net.luckperms.api.query.QueryOptions; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; - -public class DebugCommand extends SingleCommand { - public DebugCommand(LocaleManager locale) { - super(CommandSpec.DEBUG.localize(locale), "Debug", CommandPermission.DEBUG, Predicates.alwaysFalse()); - } - - @Override - public CommandResult execute(LuckPermsPlugin plugin, Sender sender, List args, String label) { - Message.DEBUG_START.send(sender); - - StringBuilder sb = new StringBuilder(); - sb.append("LuckPerms Debug Output\n\n\n"); - - BiConsumer builder = (name, content) -> { - sb.append("-- ").append(name).append(" --\n"); - sb.append(GsonProvider.prettyPrinting().toJson(content.toJson())); - sb.append("\n\n"); - }; - - builder.accept("platform.json", getPlatformData(plugin)); - builder.accept("storage.json", getStorageData(plugin)); - builder.accept("context.json", getContextData(plugin)); - builder.accept("players.json", getPlayersData(plugin)); - - String pasteUrl = Hastebin.INSTANCE.postPlain(sb.toString()).url(); - - Message.DEBUG_URL.send(sender); - - Component message = TextComponent.builder(pasteUrl).color(TextColor.AQUA) - .clickEvent(ClickEvent.openUrl(pasteUrl)) - .hoverEvent(HoverEvent.showText(TextComponent.of("Click to open the debugging data.").color(TextColor.GRAY))) - .build(); - - sender.sendMessage(message); - return CommandResult.SUCCESS; - } - - private static JObject getPlatformData(LuckPermsPlugin plugin) { - return new JObject() - .add("type", plugin.getBootstrap().getType().name()) - .add("version", new JObject() - .add("api", plugin.getApiProvider().getPluginMetadata().getApiVersion()) - .add("plugin", plugin.getBootstrap().getVersion()) - ) - .add("server", new JObject() - .add("brand", plugin.getBootstrap().getServerBrand()) - .add("version", plugin.getBootstrap().getServerVersion()) - ); - } - - private static JObject getStorageData(LuckPermsPlugin plugin) { - return new JObject() - .add("storage", new JObject() - .add("name", plugin.getStorage().getName()) - .add("type", plugin.getStorage().getImplementation().getClass().getName()) - .add("meta", () -> { - JObject metaObject = new JObject(); - Map meta = plugin.getStorage().getMeta(); - for (Map.Entry entry : meta.entrySet()) { - metaObject.add(entry.getKey(), entry.getValue()); - } - return metaObject; - })) - .add("messaging", () -> { - JObject messaging = new JObject(); - plugin.getMessagingService().ifPresent(ms -> { - messaging.add("name", ms.getName()); - messaging.add("implementation", new JObject() - .add("messenger", ms.getMessenger().getClass().getName()) - .add("provider", ms.getMessengerProvider().getClass().getName()) - ); - }); - return messaging; - }); - } - - private static JObject getContextData(LuckPermsPlugin plugin) { - return new JObject() - .add("staticContext", ContextSetJsonSerializer.serializeContextSet(plugin.getContextManager().getStaticContext())) - .add("calculators", () -> { - JArray calculators = new JArray(); - for (ContextCalculator calculator : plugin.getContextManager().getCalculators()) { - String name = calculator.getClass().getName(); - if (calculator instanceof ProxiedContextCalculator) { - name = ((ProxiedContextCalculator) calculator).getDelegate().getClass().getName(); - } - calculators.add(name); - } - return calculators; - }) - .add("staticCalculators", () -> { - JArray staticCalculators = new JArray(); - for (StaticContextCalculator calculator : plugin.getContextManager().getStaticCalculators()) { - staticCalculators.add(calculator.getClass().getName()); - } - return staticCalculators; - }); - } - - private static JObject getPlayersData(LuckPermsPlugin plugin) { - JObject ret = new JObject(); - - Set onlinePlayers = plugin.getBootstrap().getOnlinePlayers().collect(Collectors.toSet()); - ret.add("count", onlinePlayers.size()); - - JArray playerArray = new JArray(); - for (UUID uuid : onlinePlayers) { - User user = plugin.getUserManager().getIfLoaded(uuid); - if (user == null) { - playerArray.add(new JObject() - .add("uniqueId", uuid.toString()) - .add("loaded", false) - ); - continue; - } - - playerArray.add(new JObject() - .add("uniqueId", uuid.toString()) - .add("loaded", true) - .add("username", user.getName().orElse("null")) - .add("primaryGroup", new JObject() - .add("type", user.getPrimaryGroup().getClass().getName()) - .add("value", user.getPrimaryGroup().getValue()) - .add("storedValue", user.getPrimaryGroup().getStoredValue().orElse("null")) - ) - .add("activeContext", () -> { - JObject obj = new JObject(); - QueryOptions queryOptions = plugin.getQueryOptionsForUser(user).orElse(null); - if (queryOptions != null) { - obj.add("data", new JObject() - .add("permissions", serializePermissionsData(user.getCachedData().getPermissionData(queryOptions))) - .add("meta", serializeMetaData(user.getCachedData().getMetaData(queryOptions))) - ) - .add("contextSet", ContextSetJsonSerializer.serializeContextSet(queryOptions.context())) - .add("queryOptions", serializeQueryOptions(queryOptions)) - .add("metaSettings", serializeMetaContextsSettings(queryOptions)); - } - return obj; - }) - ); - } - ret.add("players", playerArray); - return ret; - } - - private static JArray serializeQueryOptions(QueryOptions queryOptions) { - JArray array = new JArray(); - for (Flag setting : queryOptions.flags()) { - array.add(setting.name()); - } - return array; - } - - private static JObject serializeMetaContextsSettings(QueryOptions queryOptions) { - return new JObject() - .consume(obj -> { - Optional prefixStack = queryOptions.option(MetaStackDefinition.PREFIX_STACK_KEY); - prefixStack.ifPresent(metaStackDefinition -> obj.add("prefixStack", serializeMetaStackData(metaStackDefinition))); - }) - .consume(obj -> { - Optional suffixStack = queryOptions.option(MetaStackDefinition.SUFFIX_STACK_KEY); - suffixStack.ifPresent(metaStackDefinition -> obj.add("suffixStack", serializeMetaStackData(metaStackDefinition))); - }); - } - - private static JObject serializePermissionsData(PermissionCache permissionData) { - return new JObject() - .add("processors", () -> { - JArray processors = new JArray(); - for (PermissionProcessor processor : permissionData.getCalculator().getProcessors()) { - processors.add(processor.getClass().getName()); - } - return processors; - }); - } - - private static JObject serializeMetaData(MetaCache metaData) { - return new JObject() - .add("prefix", metaData.getPrefix(MetaCheckEvent.Origin.INTERNAL)) - .add("suffix", metaData.getSuffix(MetaCheckEvent.Origin.INTERNAL)) - .add("prefixes", () -> { - JArray prefixes = new JArray(); - for (Map.Entry entry : metaData.getPrefixes().entrySet()) { - prefixes.add(new JObject() - .add("weight", entry.getKey()) - .add("value", entry.getValue()) - ); - } - return prefixes; - }) - .add("suffixes", () -> { - JArray suffixes = new JArray(); - for (Map.Entry entry : metaData.getSuffixes().entrySet()) { - suffixes.add(new JObject() - .add("weight", entry.getKey()) - .add("value", entry.getValue()) - ); - } - return suffixes; - }) - .add("meta", () -> { - JObject metaMultimap = new JObject(); - for (Map.Entry> entry : metaData.getMeta().entrySet()) { - JArray values = new JArray(); - for (String v : entry.getValue()) { - values.add(v); - } - metaMultimap.add(entry.getKey(), values); - } - return metaMultimap; - }); - } - - private static JObject serializeMetaStackData(MetaStackDefinition definition) { - return new JObject() - .add("type", definition.getClass().getName()) - .add("startSpacer", definition.getStartSpacer()) - .add("middleSpacer", definition.getMiddleSpacer()) - .add("endSpacer", definition.getEndSpacer()) - .add("elements", () -> { - JArray elements = new JArray(); - for (MetaStackElement element : definition.getElements()) { - elements.add(new JObject() - .add("type", element.getClass().getName()) - .add("info", element.toString()) - ); - } - return elements; - }); - } - -} diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java index c0fa60011..23c9611e0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/misc/EditorCommand.java @@ -41,6 +41,8 @@ import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.Predicates; +import me.lucko.luckperms.common.util.gson.GsonProvider; +import me.lucko.luckperms.common.web.AbstractHttpClient; import me.lucko.luckperms.common.web.WebEditor; import net.kyori.text.Component; @@ -49,8 +51,14 @@ import net.kyori.text.event.ClickEvent; import net.kyori.text.event.HoverEvent; import net.kyori.text.format.TextColor; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.zip.GZIPOutputStream; public class EditorCommand extends SingleCommand { public EditorCommand(LocaleManager locale) { @@ -110,8 +118,17 @@ public class EditorCommand extends SingleCommand { JsonObject payload = WebEditor.formPayload(holders, tracks, sender, label, plugin); // upload the payload data to gist - String pasteId = plugin.getBytebin().postJson(payload, true).id(); - if (pasteId == null) { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + try (Writer writer = new OutputStreamWriter(new GZIPOutputStream(bytesOut), StandardCharsets.UTF_8)) { + GsonProvider.prettyPrinting().toJson(payload, writer); + } catch (IOException e) { + e.printStackTrace(); + } + + String pasteId; + try { + pasteId = plugin.getBytebin().postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key(); + } catch (IOException e) { Message.EDITOR_UPLOAD_FAILURE.send(sender); return CommandResult.STATE_ERROR; } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index b55bb89fb..b8762c264 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -53,10 +53,12 @@ import me.lucko.luckperms.common.storage.implementation.file.FileWatcher; import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; -import me.lucko.luckperms.common.web.Bytebin; +import me.lucko.luckperms.common.web.BytebinClient; import net.luckperms.api.LuckPerms; +import okhttp3.OkHttpClient; + import java.io.IOException; import java.util.EnumSet; import java.util.Optional; @@ -74,7 +76,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { private LogDispatcher logDispatcher; private LuckPermsConfiguration configuration; private LocaleManager localeManager; - private Bytebin bytebin; + private BytebinClient bytebin; private FileWatcher fileWatcher = null; private Storage storage; private InternalMessagingService messagingService = null; @@ -115,7 +117,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { this.localeManager.tryLoad(this, getBootstrap().getConfigDirectory().resolve("lang.yml")); // setup a bytebin instance - this.bytebin = new Bytebin(getConfiguration().get(ConfigKeys.BYTEBIN_URL)); + this.bytebin = new BytebinClient(new OkHttpClient(), getConfiguration().get(ConfigKeys.BYTEBIN_URL), "luckperms"); // now the configuration is loaded, we can create a storage factory and load initial dependencies StorageFactory storageFactory = new StorageFactory(this); @@ -302,7 +304,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { } @Override - public Bytebin getBytebin() { + public BytebinClient getBytebin() { return this.bytebin; } diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java index 3fffa7eab..1ac7ab84b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java @@ -53,7 +53,7 @@ import me.lucko.luckperms.common.storage.implementation.file.FileWatcher; import me.lucko.luckperms.common.tasks.SyncTask; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; -import me.lucko.luckperms.common.web.Bytebin; +import me.lucko.luckperms.common.web.BytebinClient; import net.luckperms.api.query.QueryOptions; @@ -237,7 +237,7 @@ public interface LuckPermsPlugin { * * @return the bytebin instance */ - Bytebin getBytebin(); + BytebinClient getBytebin(); /** * Gets a calculated context instance for the user using the rules of the platform. diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java index 1a95c2ea4..c7053bc26 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java @@ -31,15 +31,23 @@ import com.google.gson.JsonObject; import me.lucko.luckperms.common.cacheddata.type.PermissionCache; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.sender.Sender; +import me.lucko.luckperms.common.util.gson.GsonProvider; import me.lucko.luckperms.common.util.gson.JObject; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; -import me.lucko.luckperms.common.web.Bytebin; +import me.lucko.luckperms.common.web.AbstractHttpClient; +import me.lucko.luckperms.common.web.BytebinClient; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.zip.GZIPOutputStream; /** * A readable view of a branch of {@link TreeNode}s. @@ -122,7 +130,7 @@ public class TreeView { * @param checker the permission data instance to check against, or null * @return the id, or null */ - public String uploadPasteData(Bytebin bytebin, Sender sender, User user, PermissionCache checker) { + public String uploadPasteData(BytebinClient bytebin, Sender sender, User user, PermissionCache checker) { // only paste if there is actually data here if (!hasData()) { throw new IllegalStateException(); @@ -169,7 +177,19 @@ public class TreeView { ) .toJson(); - return bytebin.postJson(payload, true).id(); + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + try (Writer writer = new OutputStreamWriter(new GZIPOutputStream(bytesOut), StandardCharsets.UTF_8)) { + GsonProvider.prettyPrinting().toJson(payload, writer); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java index 243c4a1e4..5e4dee736 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java @@ -35,24 +35,32 @@ import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.util.DurationFormatter; import me.lucko.luckperms.common.util.StackTracePrinter; import me.lucko.luckperms.common.util.TextUtils; +import me.lucko.luckperms.common.util.gson.GsonProvider; import me.lucko.luckperms.common.util.gson.JArray; import me.lucko.luckperms.common.util.gson.JObject; import me.lucko.luckperms.common.verbose.event.MetaCheckEvent; import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; import me.lucko.luckperms.common.verbose.event.VerboseEvent; -import me.lucko.luckperms.common.web.Bytebin; +import me.lucko.luckperms.common.web.AbstractHttpClient; +import me.lucko.luckperms.common.web.BytebinClient; import net.kyori.text.TextComponent; import net.kyori.text.event.HoverEvent; import net.luckperms.api.node.Tristate; import net.luckperms.api.query.QueryMode; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.zip.GZIPOutputStream; /** * Accepts and processes {@link VerboseEvent}, passed from the {@link VerboseHandler}. @@ -244,7 +252,7 @@ public class VerboseListener { * @param bytebin the bytebin instance to upload with * @return the url */ - public String uploadPasteData(Bytebin bytebin) { + public String uploadPasteData(BytebinClient bytebin) { // retrieve variables long now = System.currentTimeMillis(); String startDate = DATE_FORMAT.format(new Date(this.startTime)); @@ -287,7 +295,19 @@ public class VerboseListener { .add("data", data) .toJson(); - return bytebin.postJson(payload, true).id(); + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + try (Writer writer = new OutputStreamWriter(new GZIPOutputStream(bytesOut), StandardCharsets.UTF_8)) { + GsonProvider.prettyPrinting().toJson(payload, writer); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } } private static String getTristateColor(Tristate tristate) { diff --git a/common/src/main/java/me/lucko/luckperms/common/web/Bytebin.java b/common/src/main/java/me/lucko/luckperms/common/web/AbstractHttpClient.java similarity index 60% rename from common/src/main/java/me/lucko/luckperms/common/web/Bytebin.java rename to common/src/main/java/me/lucko/luckperms/common/web/AbstractHttpClient.java index 96648afb2..11224eef9 100644 --- a/common/src/main/java/me/lucko/luckperms/common/web/Bytebin.java +++ b/common/src/main/java/me/lucko/luckperms/common/web/AbstractHttpClient.java @@ -25,40 +25,29 @@ package me.lucko.luckperms.common.web; -import com.google.gson.JsonObject; - -import me.lucko.luckperms.common.util.gson.GsonProvider; - +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; import okhttp3.Response; -import okhttp3.ResponseBody; -import java.io.BufferedReader; +import java.io.IOException; -public class Bytebin extends AbstractPastebin { - private final String url; - private final String postUrl; +public class AbstractHttpClient { - public Bytebin(String url) { - if (!url.endsWith("/")) { - url += "/"; + public static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8"); + + /** The http client */ + protected final OkHttpClient okHttp; + + public AbstractHttpClient(OkHttpClient okHttp) { + this.okHttp = okHttp; + } + + protected Response makeHttpRequest(Request request) throws IOException { + Response response = this.okHttp.newCall(request).execute(); + if (!response.isSuccessful()) { + throw new RuntimeException("Request was unsuccessful: " + response.code() + " - " + response.message()); } - this.url = url; - this.postUrl = url + "post"; - } - - @Override - protected String getPostUrl() { - return this.postUrl; - } - - @Override - protected String parseIdFromResult(Response response, ResponseBody responseBody, BufferedReader responseBodyReader) { - JsonObject object = GsonProvider.prettyPrinting().fromJson(responseBodyReader, JsonObject.class); - return object.get("key").getAsString(); - } - - @Override - public String getPasteUrl(String id) { - return this.url + id; + return response; } } diff --git a/common/src/main/java/me/lucko/luckperms/common/web/AbstractPastebin.java b/common/src/main/java/me/lucko/luckperms/common/web/AbstractPastebin.java deleted file mode 100644 index 642bfe03f..000000000 --- a/common/src/main/java/me/lucko/luckperms/common/web/AbstractPastebin.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package me.lucko.luckperms.common.web; - -import com.google.gson.JsonElement; - -import me.lucko.luckperms.common.util.gson.GsonProvider; - -import okhttp3.MediaType; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.util.zip.GZIPOutputStream; - -/** - * Represents a pastebin service - */ -public abstract class AbstractPastebin { - - private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8"); - private static final MediaType PLAIN_TYPE = MediaType.parse("text/plain; charset=utf-8"); - - /** - * Gets the URL that post requests should be made to. - * - * @return the post URL - */ - protected abstract String getPostUrl(); - - /** - * Gets the id of the resultant post from the response of an upload request - * - * @param response the response - * @param responseBody the response body - * @param responseBodyReader the response body content - * @return - */ - protected abstract String parseIdFromResult(Response response, ResponseBody responseBody, BufferedReader responseBodyReader); - - /** - * Gets the raw url of a paste's data from an id - * - * @param id the id - * @return a url - */ - public abstract String getPasteUrl(String id); - - /** - * Posts the given json to the pastebin - * - * @param content the json element to post - * @param compress whether to compress and post the data using gzip - * @return a paste - */ - public Paste postJson(JsonElement content, boolean compress) { - ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - - OutputStream outputStream; - if (compress) { - try { - outputStream = new GZIPOutputStream(byteOut); - } catch (IOException e) { - throw new RuntimeException(e); - } - } else { - outputStream = byteOut; - } - - try (Writer writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) { - GsonProvider.prettyPrinting().toJson(content, writer); - } catch (IOException e) { - throw new RuntimeException(e); - } - - return post(RequestBody.create(JSON_TYPE, byteOut.toByteArray()), compress); - } - - /** - * Posts "plain" content to the pastebin - * - * @param content the content - * @return a paste - */ - public Paste postPlain(String content) { - return post(RequestBody.create(PLAIN_TYPE, content), false); - } - - private Paste post(RequestBody body, boolean compressed) { - Request.Builder requestBuilder = new Request.Builder() - .url(getPostUrl()) - .post(body); - - if (compressed) { - requestBuilder.header("Content-Encoding", "gzip"); - } - - Request request = requestBuilder.build(); - try (Response response = HttpClient.makeCall(request)) { - try (ResponseBody responseBody = response.body()) { - if (responseBody == null) { - throw new RuntimeException("No response"); - } - - try (InputStream inputStream = responseBody.byteStream()) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - String id = parseIdFromResult(response, responseBody, reader); - String url = getPasteUrl(id); - return new Paste(url, id); - } - } - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Encapsulates the properties of a specific "paste" entry - */ - public static final class Paste { - private final String url; - private final String id; - - Paste(String url, String id) { - this.url = url; - this.id = id; - } - - /** - * Gets the url of the paste - * - * @return the url - */ - public String url() { - return this.url; - } - - /** - * Gets the unique id of the paste - * - * @return the id - */ - public String id() { - return this.id; - } - } - -} diff --git a/common/src/main/java/me/lucko/luckperms/common/web/BytebinClient.java b/common/src/main/java/me/lucko/luckperms/common/web/BytebinClient.java new file mode 100644 index 000000000..e95327048 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/web/BytebinClient.java @@ -0,0 +1,159 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.web; + +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +import java.io.IOException; + +public class BytebinClient extends AbstractHttpClient { + + /** The bytebin URL */ + private final String url; + /** The client user agent */ + private final String userAgent; + + /** + * Creates a new bytebin instance + * + * @param url the bytebin url + * @param userAgent the client user agent string + */ + public BytebinClient(OkHttpClient okHttpClient, String url, String userAgent) { + super(okHttpClient); + if (url.endsWith("/")) { + this.url = url; + } else { + this.url = url + "/"; + } + this.userAgent = userAgent; + } + + public String getUrl() { + return this.url; + } + + public String getUserAgent() { + return this.userAgent; + } + + @Override + public Response makeHttpRequest(Request request) throws IOException { + return super.makeHttpRequest(request); + } + + /** + * POSTs GZIP compressed content to bytebin. + * + * @param buf the compressed content + * @param contentType the type of the content + * @param allowModification if the paste should be modifiable + * @return the key of the resultant content + * @throws IOException if an error occurs + */ + public Content postContent(byte[] buf, MediaType contentType, boolean allowModification) throws IOException { + RequestBody body = RequestBody.create(contentType, buf); + + Request.Builder requestBuilder = new Request.Builder() + .url(this.url + "post") + .header("User-Agent", this.userAgent) + .header("Content-Encoding", "gzip"); + + if (allowModification) { + requestBuilder.header("Allow-Modification", "true"); + } + + Request request = requestBuilder.post(body).build(); + try (Response response = makeHttpRequest(request)) { + String key = response.header("Location"); + if (key == null) { + throw new IllegalStateException("Key not returned"); + } + + if (allowModification) { + String modificationKey = response.header("Modification-Key"); + if (modificationKey == null) { + throw new IllegalStateException("Modification key not returned"); + } + return new Content(key, modificationKey); + } else { + return new Content(key); + } + } + } + + /** + * PUTs modified GZIP compressed content to bytebin in place of existing content. + * + * @param existingContent the existing content + * @param buf the compressed content to put + * @param contentType the type of the content + * @throws IOException if an error occurs + */ + public void modifyContent(Content existingContent, byte[] buf, MediaType contentType) throws IOException { + if (!existingContent.modifiable) { + throw new IllegalArgumentException("Existing content is not modifiable"); + } + + RequestBody body = RequestBody.create(contentType, buf); + + Request.Builder requestBuilder = new Request.Builder() + .url(this.url + existingContent.key()) + .header("User-Agent", this.userAgent) + .header("Content-Encoding", "gzip") + .header("Modification-Key", existingContent.modificationKey); + + Request request = requestBuilder.put(body).build(); + makeHttpRequest(request).close(); + } + + public static final class Content { + private final String key; + private final boolean modifiable; + private final String modificationKey; + + Content(String key) { + this.key = key; + this.modifiable = false; + this.modificationKey = null; + } + + Content(String key, String modificationKey) { + this.key = key; + this.modifiable = true; + this.modificationKey = modificationKey; + } + + public String key() { + return this.key; + } + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/web/Hastebin.java b/common/src/main/java/me/lucko/luckperms/common/web/Hastebin.java deleted file mode 100644 index 26d864d15..000000000 --- a/common/src/main/java/me/lucko/luckperms/common/web/Hastebin.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package me.lucko.luckperms.common.web; - -import com.google.gson.JsonObject; - -import me.lucko.luckperms.common.util.gson.GsonProvider; - -import okhttp3.Response; -import okhttp3.ResponseBody; - -import java.io.BufferedReader; - -public class Hastebin extends AbstractPastebin { - public static final Hastebin INSTANCE = new Hastebin(); - - private static final String URL = "https://hastebin.com/"; - private static final String RAW_URL = URL + "raw/"; - private static final String POST_URL = URL + "documents"; - - private Hastebin() { - - } - - @Override - protected String getPostUrl() { - return POST_URL; - } - - @Override - protected String parseIdFromResult(Response response, ResponseBody responseBody, BufferedReader responseBodyReader) { - JsonObject object = GsonProvider.prettyPrinting().fromJson(responseBodyReader, JsonObject.class); - return object.get("key").getAsString(); - } - - @Override - public String getPasteUrl(String id) { - return RAW_URL + id; - } -} diff --git a/common/src/main/java/me/lucko/luckperms/common/web/HttpClient.java b/common/src/main/java/me/lucko/luckperms/common/web/HttpClient.java deleted file mode 100644 index 1eaf929c3..000000000 --- a/common/src/main/java/me/lucko/luckperms/common/web/HttpClient.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of LuckPerms, licensed under the MIT License. - * - * Copyright (c) lucko (Luck) - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package me.lucko.luckperms.common.web; - -import okhttp3.Interceptor; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; - -import java.io.IOException; - -/** - * Utilities for the OkHttp client - */ -public final class HttpClient { - private HttpClient() {} - - private static final OkHttpClient CLIENT = new OkHttpClient.Builder() - .addInterceptor(new LuckPermsUserAgentInterceptor()) - .build(); - - public static Response makeCall(Request request) throws IOException { - Response response = CLIENT.newCall(request).execute(); - if (!response.isSuccessful()) { - throw exceptionForUnsuccessfulResponse(response); - } - return response; - } - - private static RuntimeException exceptionForUnsuccessfulResponse(Response response) { - String msg = ""; - try (ResponseBody responseBody = response.body()) { - if (responseBody != null) { - msg = responseBody.string(); - } - } catch (IOException e) { - // ignore - } - return new RuntimeException("Got response: " + response.code() + " - " + response.message() + " - " + msg); - } - - private static final class LuckPermsUserAgentInterceptor implements Interceptor { - @Override - public Response intercept(Chain chain) throws IOException { - Request orig = chain.request(); - Request modified = orig.newBuilder() - .header("User-Agent", "luckperms") - .build(); - - return chain.proceed(modified); - } - } - -} diff --git a/common/src/main/java/me/lucko/luckperms/common/web/WebEditor.java b/common/src/main/java/me/lucko/luckperms/common/web/WebEditor.java index 333f1fde4..646699ffb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/web/WebEditor.java +++ b/common/src/main/java/me/lucko/luckperms/common/web/WebEditor.java @@ -116,12 +116,13 @@ public final class WebEditor { ).toJson(); } - public static JsonObject readDataFromBytebin(Bytebin bytebin, String id) { + public static JsonObject readDataFromBytebin(BytebinClient bytebin, String id) { Request request = new Request.Builder() - .url(bytebin.getPasteUrl(id)) + .header("User-Agent", bytebin.getUserAgent()) + .url(bytebin.getUrl() + id) .build(); - try (Response response = HttpClient.makeCall(request)) { + try (Response response = bytebin.makeHttpRequest(request)) { try (ResponseBody responseBody = response.body()) { if (responseBody == null) { throw new RuntimeException("No response");