diff --git a/pom.xml b/pom.xml index db21ff8e..334a47c7 100644 --- a/pom.xml +++ b/pom.xml @@ -189,6 +189,7 @@ com.dumptruckman.minecraft:buscript org.mcstats.bukkit:metrics com.dumptruckman.minecraft:Logging + org.codehaus.jettison:jettison @@ -216,6 +217,10 @@ com.dumptruckman.minecraft.util.DebugLog com.onarandombox.MultiverseCore.utils.DebugFileLogger + + org.codehaus.jettison.json + org.codehaus.jettison.json.multiverse + @@ -334,6 +339,13 @@ + + + org.codehaus.jettison + jettison + 1.3.7 + + junit diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/VersionCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/VersionCommand.java index a1fbdb5c..37a6bf97 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/VersionCommand.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commands/VersionCommand.java @@ -20,8 +20,12 @@ import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.permissions.PermissionDefault; +import org.bukkit.scheduler.BukkitRunnable; +import java.io.*; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Dumps version info to the console. @@ -32,7 +36,7 @@ public class VersionCommand extends MultiverseCommand { public VersionCommand(MultiverseCore plugin) { super(plugin); this.setName("Multiverse Version"); - this.setCommandUsage("/mv version " + ChatColor.GOLD + "-[pb]"); + this.setCommandUsage("/mv version " + ChatColor.GOLD + "-[pbg]"); this.setArgRange(0, 1); this.addKey("mv version"); this.addKey("mvv"); @@ -41,6 +45,98 @@ public class VersionCommand extends MultiverseCommand { "Dumps version info to the console, optionally to pastie.org with -p or pastebin.com with a -b.", PermissionDefault.TRUE); } + private String getLegacyString() { + StringBuilder legacyFile = new StringBuilder(); + legacyFile.append("[Multiverse-Core] Multiverse-Core Version: ").append(this.plugin.getDescription().getVersion()).append('\n'); + legacyFile.append("[Multiverse-Core] Bukkit Version: ").append(this.plugin.getServer().getVersion()).append('\n'); + legacyFile.append("[Multiverse-Core] Loaded Worlds: ").append(this.plugin.getMVWorldManager().getMVWorlds()).append('\n'); + legacyFile.append("[Multiverse-Core] Multiverse Plugins Loaded: ").append(this.plugin.getPluginCount()).append('\n'); + legacyFile.append("[Multiverse-Core] Economy being used: ").append(plugin.getEconomist().getEconomyName()).append('\n'); + legacyFile.append("[Multiverse-Core] Permissions Plugin: ").append(this.plugin.getMVPerms().getType()).append('\n'); + legacyFile.append("[Multiverse-Core] Dumping Config Values: (version ") + .append(this.plugin.getMVConfig().getVersion()).append(")").append('\n'); + legacyFile.append("[Multiverse-Core] messagecooldown: ").append(plugin.getMessaging().getCooldown()).append('\n'); + legacyFile.append("[Multiverse-Core] teleportcooldown: ").append(plugin.getMVConfig().getTeleportCooldown()).append('\n'); + legacyFile.append("[Multiverse-Core] worldnameprefix: ").append(plugin.getMVConfig().getPrefixChat()).append('\n'); + legacyFile.append("[Multiverse-Core] worldnameprefixFormat: ").append(plugin.getMVConfig().getPrefixChatFormat()).append('\n'); + legacyFile.append("[Multiverse-Core] enforceaccess: ").append(plugin.getMVConfig().getEnforceAccess()).append('\n'); + legacyFile.append("[Multiverse-Core] displaypermerrors: ").append(plugin.getMVConfig().getDisplayPermErrors()).append('\n'); + legacyFile.append("[Multiverse-Core] teleportintercept: ").append(plugin.getMVConfig().getTeleportIntercept()).append('\n'); + legacyFile.append("[Multiverse-Core] firstspawnoverride: ").append(plugin.getMVConfig().getFirstSpawnOverride()).append('\n'); + legacyFile.append("[Multiverse-Core] firstspawnworld: ").append(plugin.getMVConfig().getFirstSpawnWorld()).append('\n'); + legacyFile.append("[Multiverse-Core] debug: ").append(plugin.getMVConfig().getGlobalDebug()).append('\n'); + legacyFile.append("[Multiverse-Core] Special Code: FRN002").append('\n'); + return legacyFile.toString(); + } + + private String getMarkdownString() { + StringBuilder markdownString = new StringBuilder(); + markdownString.append("# Multiverse-Core\n"); + markdownString.append("## Overview\n"); + markdownString.append("| Name | Value |\n"); + markdownString.append("| --- | --- |\n"); + markdownString.append("| Multiverse-Core Version | `").append(this.plugin.getDescription().getVersion()).append("` |\n"); + markdownString.append("| Bukkit Version | `").append(this.plugin.getServer().getVersion()).append("` |\n"); + //markdownString.append("| Loaded Worlds | `").append(this.plugin.getMVWorldManager().getMVWorlds()).append("` |\n"); + markdownString.append("| Multiverse Plugins Loaded | `").append(this.plugin.getPluginCount()).append("` |\n"); + markdownString.append("| Economy being used | `").append(plugin.getEconomist().getEconomyName()).append("` |\n"); + markdownString.append("| Permissions Plugin | `").append(this.plugin.getMVPerms().getType()).append("` |\n"); + markdownString.append("## Parsed Config\n"); + markdownString.append("These are what Multiverse thought the in-memory values of the config were.\n\n"); + markdownString.append("| Config Key | Value |\n"); + markdownString.append("| --- | --- |\n"); + markdownString.append("| version | `").append(this.plugin.getMVConfig().getVersion()).append("` |\n"); + markdownString.append("| messagecooldown | `").append(plugin.getMessaging().getCooldown()).append("` |\n"); + markdownString.append("| teleportcooldown | `").append(plugin.getMVConfig().getTeleportCooldown()).append("` |\n"); + markdownString.append("| worldnameprefix | `").append(plugin.getMVConfig().getPrefixChat()).append("` |\n"); + markdownString.append("| worldnameprefixFormat | `").append(plugin.getMVConfig().getPrefixChatFormat()).append("` |\n"); + markdownString.append("| enforceaccess | `").append(plugin.getMVConfig().getEnforceAccess()).append("` |\n"); + markdownString.append("| displaypermerrors | `").append(plugin.getMVConfig().getDisplayPermErrors()).append("` |\n"); + markdownString.append("| teleportintercept | `").append(plugin.getMVConfig().getTeleportIntercept()).append("` |\n"); + markdownString.append("| firstspawnoverride | `").append(plugin.getMVConfig().getFirstSpawnOverride()).append("` |\n"); + markdownString.append("| firstspawnworld | `").append(plugin.getMVConfig().getFirstSpawnWorld()).append("` |\n"); + markdownString.append("| debug | `").append(plugin.getMVConfig().getGlobalDebug()).append("` |\n"); + return markdownString.toString(); + } + + private String readFile(final String filename) { + String result; + try { + FileReader reader = new FileReader(filename); + BufferedReader bufferedReader = new BufferedReader(reader); + String line; + result = ""; + while ((line = bufferedReader.readLine()) != null) { + result += line + '\n'; + } + } catch (FileNotFoundException e) { + Logging.severe("Unable to find %s. Here's the traceback: %s", filename, e.getMessage()); + e.printStackTrace(); + result = String.format("ERROR: Could not load: %s", filename); + } catch (IOException e) { + Logging.severe("Something bad happend when reading %s. Here's the traceback: %s", filename, e.getMessage()); + e.printStackTrace(); + result = String.format("ERROR: Could not load: %s", filename); + } + return result; + } + + private Map getVersionFiles() { + Map files = new HashMap(); + + // Add the legacy file, but as markdown so it's readable + files.put("version.md", this.getMarkdownString()); + + // Add the config.yml + File configFile = new File(this.plugin.getDataFolder(), "config.yml"); + files.put(configFile.getName(), this.readFile(configFile.getAbsolutePath())); + + // Add the config.yml + File worldConfig = new File(this.plugin.getDataFolder(), "worlds.yml"); + files.put(worldConfig.getName(), this.readFile(worldConfig.getAbsolutePath())); + return files; + } + @Override public void runCommand(final CommandSender sender, final List args) { // Check if the command was sent from a Player. @@ -48,28 +144,8 @@ public class VersionCommand extends MultiverseCommand { sender.sendMessage("Version info dumped to console. Please check your server logs."); } - StringBuilder buffer = new StringBuilder(); - buffer.append("[Multiverse-Core] Multiverse-Core Version: ").append(this.plugin.getDescription().getVersion()).append('\n'); - buffer.append("[Multiverse-Core] Bukkit Version: ").append(this.plugin.getServer().getVersion()).append('\n'); - buffer.append("[Multiverse-Core] Loaded Worlds: ").append(this.plugin.getMVWorldManager().getMVWorlds()).append('\n'); - buffer.append("[Multiverse-Core] Multiverse Plugins Loaded: ").append(this.plugin.getPluginCount()).append('\n'); - buffer.append("[Multiverse-Core] Economy being used: ").append(plugin.getEconomist().getEconomyName()).append('\n'); - buffer.append("[Multiverse-Core] Permissions Plugin: ").append(this.plugin.getMVPerms().getType()).append('\n'); - buffer.append("[Multiverse-Core] Dumping Config Values: (version ") - .append(this.plugin.getMVConfig().getVersion()).append(")").append('\n'); - buffer.append("[Multiverse-Core] messagecooldown: ").append(plugin.getMessaging().getCooldown()).append('\n'); - buffer.append("[Multiverse-Core] teleportcooldown: ").append(plugin.getMVConfig().getTeleportCooldown()).append('\n'); - buffer.append("[Multiverse-Core] worldnameprefix: ").append(plugin.getMVConfig().getPrefixChat()).append('\n'); - buffer.append("[Multiverse-Core] worldnameprefixFormat: ").append(plugin.getMVConfig().getPrefixChatFormat()).append('\n'); - buffer.append("[Multiverse-Core] enforceaccess: ").append(plugin.getMVConfig().getEnforceAccess()).append('\n'); - buffer.append("[Multiverse-Core] displaypermerrors: ").append(plugin.getMVConfig().getDisplayPermErrors()).append('\n'); - buffer.append("[Multiverse-Core] teleportintercept: ").append(plugin.getMVConfig().getTeleportIntercept()).append('\n'); - buffer.append("[Multiverse-Core] firstspawnoverride: ").append(plugin.getMVConfig().getFirstSpawnOverride()).append('\n'); - buffer.append("[Multiverse-Core] firstspawnworld: ").append(plugin.getMVConfig().getFirstSpawnWorld()).append('\n'); - buffer.append("[Multiverse-Core] debug: ").append(plugin.getMVConfig().getGlobalDebug()).append('\n'); - buffer.append("[Multiverse-Core] Special Code: FRN002").append('\n'); - - MVVersionEvent versionEvent = new MVVersionEvent(buffer.toString()); + MVVersionEvent versionEvent = new MVVersionEvent(this.getLegacyString(), this.getVersionFiles()); + final Map files = this.getVersionFiles(); this.plugin.getServer().getPluginManager().callEvent(versionEvent); // log to console @@ -85,9 +161,14 @@ public class VersionCommand extends MultiverseCommand { if (args.size() == 1) { String pasteUrl; if (args.get(0).equalsIgnoreCase("-p")) { - pasteUrl = postToService(PasteServiceType.PASTIE, true, data); // private post to pastie + // private post to pastie + pasteUrl = postToService(PasteServiceType.PASTIE, true, data, files); } else if (args.get(0).equalsIgnoreCase("-b")) { - pasteUrl = postToService(PasteServiceType.PASTEBIN, true, data); // private post to pastie + // private post to pastebin + pasteUrl = postToService(PasteServiceType.PASTEBIN, true, data, files); + } else if (args.get(0).equalsIgnoreCase("-g")) { + // private post to github + pasteUrl = postToService(PasteServiceType.GITHUB, true, data, files); } else { return; } @@ -102,14 +183,23 @@ public class VersionCommand extends MultiverseCommand { /** * Send the current contents of this.pasteBinBuffer to a web service. * - * @param type Service type to send to - * @param isPrivate Should the paste be marked as private. + * @param type Service type to send paste data to. + * @param isPrivate Should the paste be marked as private. + * @param pasteData Legacy string only data to post to a service. + * @param pasteFiles Map of filenames/contents of debug info. * @return URL of visible paste */ - private static String postToService(PasteServiceType type, boolean isPrivate, String pasteData) { + private static String postToService(PasteServiceType type, boolean isPrivate, String pasteData, + Map pasteFiles) { PasteService ps = PasteServiceFactory.getService(type, isPrivate); try { - return SHORTENER.shorten(ps.postData(ps.encodeData(pasteData), ps.getPostURL())); + String result; + if (ps.supportsMultiFile()) { + result = ps.postData(ps.encodeData(pasteFiles), ps.getPostURL()); + } else { + result = ps.postData(ps.encodeData(pasteData), ps.getPostURL()); + } + return SHORTENER.shorten(result); } catch (PasteFailedException e) { System.out.print(e); return "Error posting to service"; diff --git a/src/main/java/com/onarandombox/MultiverseCore/event/MVVersionEvent.java b/src/main/java/com/onarandombox/MultiverseCore/event/MVVersionEvent.java index d1bbeeb7..43c1ddfc 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/event/MVVersionEvent.java +++ b/src/main/java/com/onarandombox/MultiverseCore/event/MVVersionEvent.java @@ -3,15 +3,19 @@ package com.onarandombox.MultiverseCore.event; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import java.util.Map; + /** * Called when somebody requests version information about Multiverse. */ public class MVVersionEvent extends Event { private final StringBuilder versionInfoBuilder; + private final Map detailedVersionInfo; - public MVVersionEvent(String versionInfo) { - this.versionInfoBuilder = new StringBuilder(versionInfo); + public MVVersionEvent(String legacyVersionInfo, Map files) { + this.versionInfoBuilder = new StringBuilder(legacyVersionInfo); + this.detailedVersionInfo = files; } private static final HandlerList HANDLERS = new HandlerList(); @@ -40,6 +44,21 @@ public class MVVersionEvent extends Event { return this.versionInfoBuilder.toString(); } + /** + * Gets the key/value pair of the detailed version info. + * + * This information is used for advanced paste services that would prefer + * to get the information as several files. Examples include config.yml or + * portals.yml. + * + * The keys are filenames, the values are the contents of the files. + * + * @return The key value mapping of files and the contents of those files. + */ + public Map getDetailedVersionInfo() { + return this.detailedVersionInfo; + } + /** * Appends more version-info to the version-info currently saved in this event. * @param moreVersionInfo The version-info to add. Should end with '\n'. diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/GithubPasteService.java b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/GithubPasteService.java new file mode 100644 index 00000000..86f9888e --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/GithubPasteService.java @@ -0,0 +1,105 @@ +package com.onarandombox.MultiverseCore.utils.webpaste; + +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashMap; +import java.util.Map; + +public class GithubPasteService implements PasteService { + + private final boolean isPrivate; + + public GithubPasteService(boolean isPrivate) { + this.isPrivate = isPrivate; + } + + @Override + public String encodeData(String data) { + Map mapData = new HashMap(); + mapData.put("multiverse.txt", data); + return this.encodeData(mapData); + } + + @Override + public String encodeData(Map files) { + JSONObject root = new JSONObject(); + String result = ""; + try { + root.put("description", "Multiverse-Core Debug Info"); + root.put("public", !this.isPrivate); + JSONObject fileList = new JSONObject(); + for (Map.Entry entry : files.entrySet()) + { + JSONObject fileObject = new JSONObject(); + fileObject.put("content", entry.getValue()); + fileList.put(entry.getKey(), fileObject); + } + root.put("files", fileList); + result = root.toString(); + } catch (JSONException e) { + e.printStackTrace(); + } + return result; + } + + @Override + public URL getPostURL() { + try { + return new URL("https://api.github.com/gists"); + //return new URL("http://jsonplaceholder.typicode.com/posts"); + } catch (MalformedURLException e) { + return null; // should never hit here + } + } + + @Override + public String postData(String encodedData, URL url) throws PasteFailedException { + OutputStreamWriter wr = null; + BufferedReader rd = null; + try { + URLConnection conn = url.openConnection(); + conn.setDoOutput(true); + wr = new OutputStreamWriter(conn.getOutputStream()); + wr.write(encodedData); + wr.flush(); + + rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + String pastieUrl = ""; + //Pattern pastiePattern = this.getURLMatchingPattern(); + StringBuilder responseString = new StringBuilder(); + + while ((line = rd.readLine()) != null) { + responseString.append(line); + } + JSONObject response = new JSONObject(responseString.toString()); + return response.get("html_url").toString(); + } catch (Exception e) { + throw new PasteFailedException(e); + } finally { + if (wr != null) { + try { + wr.close(); + } catch (IOException ignore) { } + } + if (rd != null) { + try { + rd.close(); + } catch (IOException ignore) { } + } + } + } + + @Override + public boolean supportsMultiFile() { + return true; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteService.java b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteService.java index 2bc9324e..71ba3a8e 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteService.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteService.java @@ -1,6 +1,7 @@ package com.onarandombox.MultiverseCore.utils.webpaste; import java.net.URL; +import java.util.Map; /** * An interface to a web-based text-pasting service. Classes implementing this @@ -24,6 +25,14 @@ public interface PasteService { */ String encodeData(String data); + /** + * Encode the given Map data into a format suitable for transmission in an HTTP request. + * + * @param data The raw data to encode. + * @return A URL-encoded string. + */ + String encodeData(Map data); + /** * Get the URL to which this paste service sends new pastes. * @@ -42,4 +51,13 @@ public interface PasteService { */ String postData(String encodedData, URL url) throws PasteFailedException; + /** + * Does this service support uploading multiple files. + * + * Newer services like gist support multi-file which allows us to upload configs + * in addition to the standard logs. + * + * @return True if this service supports multiple file upload. + */ + boolean supportsMultiFile(); } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceFactory.java b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceFactory.java index e5a63f84..1319ac20 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceFactory.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceFactory.java @@ -18,6 +18,8 @@ public class PasteServiceFactory { return new PastebinPasteService(isPrivate); case PASTIE: return new PastiePasteService(isPrivate); + case GITHUB: + return new GithubPasteService(isPrivate); default: return null; } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceType.java b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceType.java index 51d3089a..5954dd88 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceType.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PasteServiceType.java @@ -14,5 +14,9 @@ public enum PasteServiceType { /** * @see PastiePasteService */ - PASTIE + PASTIE, + /** + * @see GithubPasteService + */ + GITHUB } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastebinPasteService.java b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastebinPasteService.java index 8dce6b99..3f06f612 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastebinPasteService.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastebinPasteService.java @@ -9,6 +9,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; +import java.util.Map; /** * Pastes to {@code pastebin.com}. @@ -50,6 +51,11 @@ public class PastebinPasteService implements PasteService { } } + @Override + public String encodeData(Map data) { + return null; + } + /** * {@inheritDoc} */ @@ -86,4 +92,9 @@ public class PastebinPasteService implements PasteService { } } } + + @Override + public boolean supportsMultiFile() { + return false; + } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastiePasteService.java b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastiePasteService.java index 3cb0f169..16c062a2 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastiePasteService.java +++ b/src/main/java/com/onarandombox/MultiverseCore/utils/webpaste/PastiePasteService.java @@ -9,6 +9,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -51,6 +52,11 @@ public class PastiePasteService implements PasteService { } } + @Override + public String encodeData(Map data) { + return null; + } + /** * {@inheritDoc} */ @@ -93,6 +99,11 @@ public class PastiePasteService implements PasteService { } } + @Override + public boolean supportsMultiFile() { + return false; + } + private Pattern getURLMatchingPattern() { if (this.isPrivate) { return Pattern.compile(".*http://pastie.org/.*key=([0-9a-z]+).*");