From ea192ddd6d5b538e0bd0f6f1721890eb3c25de30 Mon Sep 17 00:00:00 2001 From: snowleo Date: Wed, 12 Oct 2011 03:14:07 +0200 Subject: EssentialsUpdate WIP --- .../earth2me/essentials/update/EssentialsHelp.java | 601 +++++++++++++++++++++ .../essentials/update/EssentialsUpdate.java | 66 +++ .../com/earth2me/essentials/update/GetFile.java | 112 ++++ .../com/earth2me/essentials/update/ModuleInfo.java | 35 ++ .../earth2me/essentials/update/PastieUpload.java | 40 ++ .../com/earth2me/essentials/update/PostToUrl.java | 66 +++ .../earth2me/essentials/update/UpdateCheck.java | 203 +++++++ .../com/earth2me/essentials/update/UpdateFile.java | 204 +++++++ .../earth2me/essentials/update/UpdateProcess.java | 128 +++++ .../essentials/update/UpdatesDownloader.java | 19 + .../com/earth2me/essentials/update/Version.java | 173 ++++++ .../earth2me/essentials/update/VersionInfo.java | 48 ++ .../earth2me/essentials/update/states/Modules.java | 7 + 13 files changed, 1702 insertions(+) create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/Version.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java (limited to 'EssentialsUpdate/src/com') diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java new file mode 100644 index 000000000..5b134b8b8 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsHelp.java @@ -0,0 +1,601 @@ +package com.earth2me.essentials.update; + +import f00f.net.irc.martyr.GenericAutoService; +import f00f.net.irc.martyr.IRCConnection; +import f00f.net.irc.martyr.InCommand; +import f00f.net.irc.martyr.State; +import f00f.net.irc.martyr.clientstate.Channel; +import f00f.net.irc.martyr.clientstate.Member; +import f00f.net.irc.martyr.commands.InviteCommand; +import f00f.net.irc.martyr.commands.KickCommand; +import f00f.net.irc.martyr.commands.MessageCommand; +import f00f.net.irc.martyr.commands.NoticeCommand; +import f00f.net.irc.martyr.commands.QuitCommand; +import f00f.net.irc.martyr.commands.TopicCommand; +import f00f.net.irc.martyr.errors.GenericJoinError; +import f00f.net.irc.martyr.services.AutoJoin; +import f00f.net.irc.martyr.services.AutoReconnect; +import f00f.net.irc.martyr.services.AutoRegister; +import f00f.net.irc.martyr.services.AutoResponder; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Enumeration; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.Event.Priority; +import org.bukkit.event.Event.Type; +import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.event.player.PlayerListener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + + +public class EssentialsHelp extends PlayerListener +{ + private transient Player chatUser; + private transient IRCConnection connection; + private transient AutoReconnect autoReconnect; + private transient boolean shouldQuit = false; + private final transient Server server; + private final transient Plugin plugin; + private final static Charset UTF8 = Charset.forName("utf-8"); + + public EssentialsHelp(Plugin plugin) + { + this.plugin = plugin; + this.server = plugin.getServer(); + } + + public void registerEvents() + { + final PluginManager pluginManager = server.getPluginManager(); + pluginManager.registerEvent(Type.PLAYER_QUIT, this, Priority.Low, plugin); + pluginManager.registerEvent(Type.PLAYER_CHAT, this, Priority.Low, plugin); + } + + public void onCommand(CommandSender sender) + { + if (sender instanceof Player && sender.hasPermission("essentials.helpchat")) + { + if (chatUser == null) + { + chatUser = (Player)sender; + connection = null; + sender.sendMessage("You will be connected to the Essentials Help Chat."); + sender.sendMessage("All your chat messages will be forwarded to the channel. You can't chat with other players on your server while in help chat, but you can use commands."); + sender.sendMessage("Please be patient, if noone is available, check back later."); + sender.sendMessage("Type !help to get a list of all commands."); + sender.sendMessage("Type !quit to leave the channel."); + sender.sendMessage("Do you want to join the channel now? (yes/no)"); + } + if (!chatUser.equals(sender)) + { + sender.sendMessage("The player " + chatUser.getDisplayName() + " is already using the essentialshelp."); + } + } + else + { + sender.sendMessage("Please run the command as op from in game."); + } + } + + public void onDisable() + { + if (autoReconnect != null && connection != null) + { + autoReconnect.disable(); + shouldQuit = true; + connection.disconnect(); + } + } + + private void sendChatMessage(final Player player, final String message) + { + final String messageCleaned = message.trim(); + if (messageCleaned.isEmpty()) + { + return; + } + if (connection == null) + { + if (messageCleaned.equalsIgnoreCase("yes")) + { + player.sendMessage("Connecting..."); + connectToIRC(player); + } + if (messageCleaned.equalsIgnoreCase("no") || message.equalsIgnoreCase("!quit")) + { + chatUser = null; + } + } + else + { + final String lowMessage = messageCleaned.toLowerCase(); + if (lowMessage.startsWith("!quit")) + { + chatUser = null; + autoReconnect.disable(); + shouldQuit = true; + connection.sendCommand(new QuitCommand("Connection closed by user.")); + player.sendMessage("Connection closed."); + return; + } + if (!connection.getClientState().getChannels().hasMoreElements()) + { + player.sendMessage("Not connected yet!"); + return; + } + if (lowMessage.startsWith("!list")) + { + final Enumeration members = ((Channel)connection.getClientState().getChannels().nextElement()).getMembers(); + final StringBuilder sb = new StringBuilder(); + while (members.hasMoreElements()) + { + if (sb.length() > 0) + { + sb.append("§f, "); + } + final Member member = (Member)members.nextElement(); + if (member.hasOps() || member.hasVoice()) + { + sb.append("§6"); + } + else + { + sb.append("§7"); + } + sb.append(member.getNick()); + } + player.sendMessage(sb.toString()); + return; + } + if (lowMessage.startsWith("!help")) + { + player.sendMessage("Commands: (Note: Files send to the chat will be public viewable.)"); + player.sendMessage("!errors - Send the last server errors to the chat."); + player.sendMessage("!startup - Send the last startup messages to the chat."); + player.sendMessage("!config - Sends your Essentials config to the chat."); + player.sendMessage("!list - List all players in chat."); + player.sendMessage("!quit - Leave chat."); + return; + } + if (lowMessage.startsWith("!errors")) + { + sendErrors(); + return; + } + if (lowMessage.startsWith("!startup")) + { + sendStartup(); + return; + } + if (lowMessage.startsWith("!config")) + { + sendConfig(); + return; + } + final Channel channel = (Channel)connection.getClientState().getChannels().nextElement(); + connection.sendCommand(new MessageCommand(channel.getName(), messageCleaned)); + chatUser.sendMessage("§6" + connection.getClientState().getNick().getNick() + ": §7" + messageCleaned); + } + } + + private void connectToIRC(final Player player) + { + connection = new IRCConnection(); + // Required services + new AutoResponder(connection); + int versionNumber = 0; + final StringBuilder nameBuilder = new StringBuilder(); + nameBuilder.append(player.getName()); + + final Matcher versionMatch = Pattern.compile("git-Bukkit-([0-9]+).([0-9]+).([0-9]+)-[0-9]+-[0-9a-z]+-b([0-9]+)jnks.*").matcher(server.getVersion()); + if (versionMatch.matches()) + { + nameBuilder.append(" CB"); + nameBuilder.append(versionMatch.group(4)); + } + + final Plugin essentials = server.getPluginManager().getPlugin("Essentials"); + if (essentials != null) + { + nameBuilder.append(" ESS"); + nameBuilder.append(essentials.getDescription().getVersion()); + } + + final Plugin groupManager = server.getPluginManager().getPlugin("GroupManager"); + if (groupManager != null) + { + nameBuilder.append(" GM"); + if (!groupManager.isEnabled()) + { + nameBuilder.append('!'); + } + } + + final Plugin pex = server.getPluginManager().getPlugin("PermissionsEx"); + if (pex != null) + { + nameBuilder.append(" PEX"); + if (!pex.isEnabled()) + { + nameBuilder.append('!'); + } + nameBuilder.append(pex.getDescription().getVersion()); + } + + final Plugin pb = server.getPluginManager().getPlugin("PermissionsBukkit"); + if (pb != null) + { + nameBuilder.append(" PB"); + if (!pb.isEnabled()) + { + nameBuilder.append('!'); + } + nameBuilder.append(pb.getDescription().getVersion()); + } + + final Plugin bp = server.getPluginManager().getPlugin("bPermissions"); + if (bp != null) + { + nameBuilder.append(" BP"); + if (!bp.isEnabled()) + { + nameBuilder.append('!'); + } + nameBuilder.append(bp.getDescription().getVersion()); + } + + final Plugin perm = server.getPluginManager().getPlugin("Permissions"); + if (perm != null) + { + nameBuilder.append(" P"); + if (!perm.isEnabled()) + { + nameBuilder.append('!'); + } + nameBuilder.append(perm.getDescription().getVersion()); + } + + new AutoRegister(connection, "Ess_" + player.getName(), "esshelp", nameBuilder.toString()); + + autoReconnect = new AutoReconnect(connection); + new KickAutoJoin(connection, "#essentials"); + + new IRCListener(connection); + autoReconnect.go("irc.esper.net", 6667); + } + + private void handleIRCmessage(final String nick, final String message) + { + + if (chatUser != null) + { + final StringBuilder sb = new StringBuilder(); + sb.append("§6"); + sb.append(nick); + sb.append(": §7"); + final String coloredmessage = message.replace("\u000300", "§f").replace("\u000301", "§0").replace("\u000302", "§1").replace("\u000303", "§2").replace("\u000304", "§c").replace("\u000305", "§4").replace("\u000306", "§5").replace("\u000307", "§6").replace("\u000308", "§e").replace("\u000309", "§a").replace("\u00030", "§f").replace("\u000310", "§b").replace("\u000311", "§f").replace("\u000312", "§9").replace("\u000313", "§d").replace("\u000314", "§8").replace("\u000315", "§7").replace("\u00031", "§0").replace("\u00032", "§1").replace("\u00033", "§2").replace("\u00034", "§c").replace("\u00035", "§4").replace("\u00036", "§5").replace("\u00037", "§6").replace("\u00038", "§e").replace("\u00039", "§a").replace("\u0003", "§7"); + sb.append(coloredmessage); + chatUser.sendMessage(sb.toString()); + } + } + + private void sendErrors() + { + BufferedReader page = null; + try + { + File bukkitFolder = plugin.getDataFolder().getAbsoluteFile().getParentFile().getParentFile(); + if (bukkitFolder == null || !bukkitFolder.exists()) + { + chatUser.sendMessage("Bukkit folder not found."); + return; + } + File logFile = new File(bukkitFolder, "server.log"); + if (!logFile.exists()) + { + chatUser.sendMessage("Server log not found."); + return; + } + FileInputStream fis = new FileInputStream(logFile); + if (logFile.length() > 1000000) + { + fis.skip(logFile.length()-1000000); + } + page = new BufferedReader(new InputStreamReader(fis)); + final StringBuilder input = new StringBuilder(); + String line; + Pattern pattern = Pattern.compile("^[0-9 :-]+\\[INFO\\].*"); + while ((line = page.readLine()) != null) + { + if (!pattern.matcher(line).matches()) { + input.append(line).append("\n"); + } + } + if (input.length()>10000) { + input.delete(0, input.length()-10000); + } + final PastieUpload pastie = new PastieUpload(); + final String url = pastie.send(input.toString()); + final Channel channel = (Channel)connection.getClientState().getChannels().nextElement(); + String message = "Errors: " + url; + chatUser.sendMessage("§6" + connection.getClientState().getNick().getNick() + ": §7" + message); + connection.sendCommand(new MessageCommand(channel.getName(), message)); + } + catch (IOException ex) + { + Bukkit.getLogger().log(Level.SEVERE, null, ex); + chatUser.sendMessage(ex.getMessage()); + } + finally + { + try + { + if (page != null) + { + page.close(); + } + } + catch (IOException ex) + { + Logger.getLogger(EssentialsHelp.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + private void sendStartup() + { + BufferedReader page = null; + try + { + File bukkitFolder = plugin.getDataFolder().getAbsoluteFile().getParentFile().getParentFile(); + if (bukkitFolder == null || !bukkitFolder.exists()) + { + chatUser.sendMessage("Bukkit folder not found."); + return; + } + File logFile = new File(bukkitFolder, "server.log"); + if (!logFile.exists()) + { + chatUser.sendMessage("Server log not found."); + return; + } + FileInputStream fis = new FileInputStream(logFile); + if (logFile.length() > 1000000) + { + fis.skip(logFile.length()-1000000); + } + page = new BufferedReader(new InputStreamReader(fis)); + final StringBuilder input = new StringBuilder(); + String line; + Pattern patternStart = Pattern.compile("^[0-9 :-]+\\[INFO\\] Starting minecraft server version.*"); + Pattern patternEnd = Pattern.compile("^[0-9 :-]+\\[INFO\\] Done \\([0-9.,]+s\\)! For help, type \"help\".*"); + boolean log = false; + while ((line = page.readLine()) != null) + { + if (patternStart.matcher(line).matches()) { + if (input.length() > 0) { + input.delete(0, input.length()); + } + log = true; + } + if (log) { + input.append(line).append("\n"); + } + if (patternEnd.matcher(line).matches()) { + log = false; + } + } + if (input.length()>10000) { + input.delete(0, input.length()-10000); + } + final PastieUpload pastie = new PastieUpload(); + final String url = pastie.send(input.toString()); + final Channel channel = (Channel)connection.getClientState().getChannels().nextElement(); + String message = "Startup: " + url; + chatUser.sendMessage("§6" + connection.getClientState().getNick().getNick() + ": §7" + message); + connection.sendCommand(new MessageCommand(channel.getName(), message)); + } + catch (IOException ex) + { + Bukkit.getLogger().log(Level.SEVERE, null, ex); + chatUser.sendMessage(ex.getMessage()); + } + finally + { + try + { + if (page != null) + { + page.close(); + } + } + catch (IOException ex) + { + Logger.getLogger(EssentialsHelp.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + private void sendConfig() + { + BufferedReader page = null; + try + { + File configFolder = new File(plugin.getDataFolder().getParentFile(), "Essentials"); + if (!configFolder.exists()) + { + chatUser.sendMessage("Essentials plugin folder not found."); + return; + } + File configFile = new File(configFolder, "config.yml"); + if (!configFile.exists()) + { + chatUser.sendMessage("Essentials config file not found."); + return; + } + page = new BufferedReader(new InputStreamReader(new FileInputStream(configFile), UTF8)); + final StringBuilder input = new StringBuilder(); + String line; + while ((line = page.readLine()) != null) + { + input.append(line).append("\n"); + } + final PastieUpload pastie = new PastieUpload(); + final String url = pastie.send(input.toString()); + final Channel channel = (Channel)connection.getClientState().getChannels().nextElement(); + String message = "Essentials config.yml: " + url; + chatUser.sendMessage("§6" + connection.getClientState().getNick().getNick() + ": §7" + message); + connection.sendCommand(new MessageCommand(channel.getName(), message)); + + } + catch (IOException ex) + { + Bukkit.getLogger().log(Level.SEVERE, null, ex); + chatUser.sendMessage(ex.getMessage()); + } + finally + { + try + { + if (page != null) + { + page.close(); + } + } + catch (IOException ex) + { + Logger.getLogger(EssentialsHelp.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + + @Override + public void onPlayerChat(PlayerChatEvent event) + { + if (event.getPlayer() == chatUser) + { + sendChatMessage(event.getPlayer(), event.getMessage()); + event.setCancelled(true); + return; + } + } + + @Override + public void onPlayerQuit(PlayerQuitEvent event) + { + chatUser = null; + if (autoReconnect != null) + { + autoReconnect.disable(); + } + shouldQuit = true; + if (connection != null) + { + connection.sendCommand(new QuitCommand("Connection closed by user.")); + } + return; + } + + + class KickAutoJoin extends AutoJoin + { + private String channel; + + public KickAutoJoin(IRCConnection connection, String channel) + { + super(connection, channel); + this.channel = channel; + } + + @Override + protected void updateCommand(InCommand command_o) + { + if (command_o instanceof KickCommand) + { + final KickCommand kickCommand = (KickCommand)command_o; + + if (kickCommand.kickedUs(getConnection().getClientState())) + { + if (Channel.areEqual(kickCommand.getChannel(), channel)) + { + chatUser.sendMessage("You have been kicked from the channel: " + kickCommand.getComment()); + chatUser = null; + autoReconnect.disable(); + shouldQuit = true; + connection.sendCommand(new QuitCommand("Connection closed by user.")); + } + } + } + else if (command_o instanceof GenericJoinError) + { + GenericJoinError joinErr = (GenericJoinError)command_o; + + if (Channel.areEqual(joinErr.getChannel(), channel)) + { + scheduleJoin(); + } + } + else if (command_o instanceof InviteCommand) + { + InviteCommand invite = (InviteCommand)command_o; + if (!getConnection().getClientState().isOnChannel(invite.getChannel())) + { + performJoin(); + } + } + } + } + + + class IRCListener extends GenericAutoService + { + public IRCListener(final IRCConnection connection) + { + super(connection); + enable(); + } + + @Override + protected void updateState(final State state) + { + if (state == State.UNCONNECTED && shouldQuit) + { + connection = null; + shouldQuit = false; + } + } + + @Override + protected void updateCommand(final InCommand command) + { + if (command instanceof MessageCommand) + { + final MessageCommand msg = (MessageCommand)command; + EssentialsHelp.this.handleIRCmessage(msg.getSource().getNick(), msg.getMessage()); + } + if (command instanceof TopicCommand) + { + final TopicCommand msg = (TopicCommand)command; + EssentialsHelp.this.handleIRCmessage(msg.getChannel(), msg.getTopic()); + } + if (command instanceof NoticeCommand) + { + final NoticeCommand msg = (NoticeCommand)command; + EssentialsHelp.this.handleIRCmessage(msg.getFrom().getNick(), msg.getNotice()); + } + } + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java new file mode 100644 index 000000000..d4ee6c0fc --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/EssentialsUpdate.java @@ -0,0 +1,66 @@ +package com.earth2me.essentials.update; + +import com.earth2me.essentials.update.UpdateCheck.CheckResult; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.java.JavaPlugin; + + +public class EssentialsUpdate extends JavaPlugin +{ + private transient EssentialsHelp essentialsHelp; + private transient UpdateProcess updateProcess; + + @Override + public void onEnable() + { + if (!getDataFolder().exists() && !getDataFolder().mkdirs() ) { + Bukkit.getLogger().severe("Could not create data folder:"+getDataFolder().getPath()); + } + essentialsHelp = new EssentialsHelp(this); + essentialsHelp.registerEvents(); + + final UpdateCheck updateCheck = new UpdateCheck(this); + updateProcess = new UpdateProcess(this, updateCheck); + updateProcess.registerEvents(); + + Bukkit.getLogger().info("EssentialsUpdate " + getDescription().getVersion() + " loaded."); + + if (updateCheck.isEssentialsInstalled()) + { + updateCheck.checkForUpdates(); + final Version myVersion = new Version(getDescription().getVersion()); + if (updateCheck.getResult() == CheckResult.NEW_ESS && myVersion.equals(updateCheck.getNewVersion())) + { + Bukkit.getLogger().info("Versions of EssentialsUpdate and Essentials do not match. Starting automatic update."); + updateProcess.doAutomaticUpdate(); + } + updateCheck.scheduleUpdateTask(); + } + else + { + Bukkit.getLogger().info("Essentials is ready for installation. Join the game and follow the instructions."); + } + } + + @Override + public void onDisable() + { + essentialsHelp.onDisable(); + } + + @Override + public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) + { + if (command.getName().equalsIgnoreCase("essentialsupdate")) + { + updateProcess.onCommand(sender); + } + if (command.getName().equalsIgnoreCase("essentialshelp")) + { + essentialsHelp.onCommand(sender); + } + return true; + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java b/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java new file mode 100644 index 000000000..888950f34 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java @@ -0,0 +1,112 @@ +package com.earth2me.essentials.update; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.logging.Logger; + + +public class GetFile +{ + private transient URLConnection connection; + private transient MessageDigest digest; + + public GetFile(final String urlString) throws MalformedURLException, IOException + { + final URL url = new URL(urlString); + this.connection = url.openConnection(); + this.connection.setConnectTimeout(1000); + this.connection.setReadTimeout(5000); + this.connection.setUseCaches(false); + this.connection.connect(); + final int respCode = ((HttpURLConnection)this.connection).getResponseCode(); + if (respCode >= 300 && respCode < 400 && this.connection.getHeaderField("Location") != null) + { + connection.getInputStream().close(); + final URL redirect = new URL(this.connection.getHeaderField("Location")); + this.connection = redirect.openConnection(); + this.connection.setConnectTimeout(1000); + this.connection.setReadTimeout(5000); + this.connection.setUseCaches(false); + this.connection.connect(); + } + } + + public void saveTo(final File file) throws IOException + { + try + { + saveTo(file, null); + } + catch (NoSuchAlgorithmException ex) + { + // Ignore because the code is never called + } + } + + public void saveTo(final File file, final String key) throws IOException, NoSuchAlgorithmException + { + if (key != null) + { + digest = MessageDigest.getInstance("SHA256"); + } + final byte[] buffer = new byte[1024 * 8]; + boolean brokenFile = false; + final BufferedInputStream input = new BufferedInputStream(connection.getInputStream()); + try + { + final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file)); + try + { + int length; + do + { + length = input.read(buffer); + if (length >= 0) + { + if (key != null) + { + digest.update(buffer, 0, length); + } + output.write(buffer, 0, length); + } + } + while (length >= 0); + if (key != null) + { + final byte[] checksum = digest.digest(); + final String checksumString = new BigInteger(checksum).toString(36); + if (!checksumString.equals(key)) + { + brokenFile = true; + } + } + } + finally + { + output.close(); + } + if (brokenFile && !file.delete()) + { + Logger.getLogger("Minecraft").severe("Could not delete file " + file.getPath()); + } + } + finally + { + input.close(); + } + if (brokenFile) + { + throw new IOException("Checksum check failed."); + } + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java b/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java new file mode 100644 index 000000000..722fca3e1 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/ModuleInfo.java @@ -0,0 +1,35 @@ +package com.earth2me.essentials.update; + +import java.net.MalformedURLException; +import java.net.URL; +import org.bukkit.configuration.Configuration; + + +public class ModuleInfo +{ + private final transient String url; + private final transient String version; + private final transient String hash; + + public ModuleInfo(final Configuration updateConfig, final String path) + { + url = updateConfig.getString(path + ".url", null); + version = updateConfig.getString(path + ".version", null); + hash = updateConfig.getString(path + ".hash", null); + } + + public URL getUrl() throws MalformedURLException + { + return new URL(url); + } + + public String getVersion() + { + return version; + } + + public String getHash() + { + return hash; + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java b/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java new file mode 100644 index 000000000..6cad44e4d --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/PastieUpload.java @@ -0,0 +1,40 @@ +package com.earth2me.essentials.update; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class PastieUpload +{ + private final transient PostToUrl connection; + + public PastieUpload() throws MalformedURLException + { + connection = new PostToUrl(new URL("http://pastie.org/pastes")); + } + + public String send(final String data) throws IOException + { + final Map map = new HashMap(); + map.put("paste[parser_id]", "19"); + map.put("paste[authorization]", "burger"); + map.put("paste[body]", data); + map.put("paste[restricted]", "1"); + final String html = connection.send(map); + final Matcher matcher = Pattern.compile("(?s).*\\?key=([a-z0-9]+).*").matcher(html); + if (matcher.matches()) + { + final String key = matcher.group(1); + return "http://pastie.org/private/" + key; + } + else + { + throw new IOException("Failed to upload to pastie.org"); + } + } +} \ No newline at end of file diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java b/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java new file mode 100644 index 000000000..c8978961b --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/PostToUrl.java @@ -0,0 +1,66 @@ +package com.earth2me.essentials.update; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.Charset; +import java.util.Map; +import java.util.Random; + + +public class PostToUrl +{ + private final transient URL url; + private final transient String boundary; + private final transient Random random = new Random(); + private final static String CRLF = "\r\n"; + private final static Charset UTF8 = Charset.forName("utf-8"); + + public PostToUrl(final URL url) + { + this.url = url; + final byte[] bytes = new byte[32]; + random.nextBytes(bytes); + this.boundary = "----------" + new BigInteger(bytes).toString(Character.MAX_RADIX) + "_$"; + } + + public String send(final Map data) throws IOException + { + final URLConnection connection = url.openConnection(); + connection.setRequestProperty("content-type", "multipart/form-data; boundary=" + boundary); + final StringBuilder dataBuilder = new StringBuilder(); + for (Map.Entry entry : data.entrySet()) + { + if (entry.getValue() instanceof String) + { + dataBuilder.append("--").append(boundary).append(CRLF); + dataBuilder.append("Content-Disposition: form-data; name=\"").append(entry.getKey()).append('"').append(CRLF); + dataBuilder.append(CRLF); + dataBuilder.append(entry.getValue()).append(CRLF); + } + // TODO: Add support for file upload + } + dataBuilder.append("--").append(boundary).append("--").append(CRLF); + dataBuilder.append(CRLF); + connection.setDoOutput(true); + final byte[] message = dataBuilder.toString().getBytes(UTF8); + connection.setRequestProperty("content-length", Integer.toString(message.length)); + connection.connect(); + final OutputStream stream = connection.getOutputStream(); + stream.write(message); + stream.close(); + final BufferedReader page = new BufferedReader(new InputStreamReader(connection.getInputStream(), UTF8)); + final StringBuilder input = new StringBuilder(); + String line; + while ((line = page.readLine()) != null) + { + input.append(line).append("\n"); + } + page.close(); + return input.toString(); + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java new file mode 100644 index 000000000..dcda252a0 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateCheck.java @@ -0,0 +1,203 @@ +package com.earth2me.essentials.update; + +import java.io.File; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + + +public class UpdateCheck +{ + private transient CheckResult result = CheckResult.UNKNOWN; + private transient Version currentVersion; + private transient Version newVersion = null; + private transient int bukkitResult = 0; + private transient UpdateFile updateFile; + private final static int CHECK_INTERVAL = 20 * 60 * 60 * 6; + private final transient Plugin plugin; + private transient boolean essentialsInstalled; + + public UpdateCheck(Plugin plugin) + { + this.plugin = plugin; + updateFile = new UpdateFile(plugin); + checkForEssentials(); + } + + private void checkForEssentials() + { + PluginManager pm = plugin.getServer().getPluginManager(); + Plugin essentials = pm.getPlugin("Essentials"); + if (essentials == null) + { + essentialsInstalled = false; + if (new File(plugin.getDataFolder().getParentFile(), "Essentials.jar").exists()) + { + Bukkit.getLogger().severe("Essentials.jar found, but not recognized by Bukkit. Broken download?"); + } + } + else + { + essentialsInstalled = true; + currentVersion = new Version(essentials.getDescription().getVersion()); + } + } + + public void scheduleUpdateTask() + { + plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() + { + @Override + public void run() + { + updateFile = new UpdateFile(plugin); + checkForUpdates(); + } + }, CHECK_INTERVAL, CHECK_INTERVAL); + } + + public boolean isEssentialsInstalled() + { + return essentialsInstalled; + } + + public CheckResult getResult() + { + return result; + } + + int getNewBukkitVersion() + { + return bukkitResult; + } + + VersionInfo getNewVersionInfo() + { + return updateFile.getVersions().get(newVersion); + } + + public enum CheckResult + { + NEW_ESS, NEW_ESS_BUKKIT, NEW_BUKKIT, OK, UNKNOWN + } + + public void checkForUpdates() + { + if (currentVersion == null) + { + return; + } + final Map versions = updateFile.getVersions(); + final int bukkitVersion = getBukkitVersion(); + Version higher = null; + Version found = null; + Version lower = null; + int bukkitHigher = 0; + int bukkitLower = 0; + for (Entry entry : versions.entrySet()) + { + final int minBukkit = entry.getValue().getMinBukkit(); + final int maxBukkit = entry.getValue().getMaxBukkit(); + if (minBukkit == 0 || maxBukkit == 0) + { + continue; + } + if (bukkitVersion <= maxBukkit) + { + if (bukkitVersion < minBukkit) + { + if (higher == null || higher.compareTo(entry.getKey()) < 0) + { + + higher = entry.getKey(); + bukkitHigher = minBukkit; + } + } + else + { + if (found == null || found.compareTo(entry.getKey()) < 0) + { + found = entry.getKey(); + } + } + } + else + { + if (lower == null || lower.compareTo(entry.getKey()) < 0) + { + lower = entry.getKey(); + bukkitLower = minBukkit; + } + } + } + if (found != null) + { + if (found.compareTo(currentVersion) > 0) + { + result = CheckResult.NEW_ESS; + newVersion = found; + } + else + { + result = CheckResult.OK; + } + } + else if (higher != null) + { + if (higher.compareTo(currentVersion) > 0) + { + newVersion = higher; + result = CheckResult.NEW_ESS_BUKKIT; + bukkitResult = bukkitHigher; + } + else if (higher.compareTo(currentVersion) < 0) + { + result = CheckResult.UNKNOWN; + } + else + { + result = CheckResult.NEW_BUKKIT; + bukkitResult = bukkitHigher; + } + } + else if (lower != null) + { + if (lower.compareTo(currentVersion) > 0) + { + result = CheckResult.NEW_ESS_BUKKIT; + newVersion = lower; + bukkitResult = bukkitLower; + } + else if (lower.compareTo(currentVersion) < 0) + { + result = CheckResult.UNKNOWN; + } + else + { + result = CheckResult.NEW_BUKKIT; + bukkitResult = bukkitLower; + } + } + + } + + private int getBukkitVersion() + { + final Matcher versionMatch = Pattern.compile("git-Bukkit-([0-9]+).([0-9]+).([0-9]+)-[0-9]+-[0-9a-z]+-b([0-9]+)jnks.*").matcher(plugin.getServer().getVersion()); + if (versionMatch.matches()) + { + return Integer.parseInt(versionMatch.group(4)); + } + throw new NumberFormatException("Bukkit Version changed!"); + } + + public Version getNewVersion() + { + return newVersion; + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java new file mode 100644 index 000000000..8f34bffc4 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateFile.java @@ -0,0 +1,204 @@ +package com.earth2me.essentials.update; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.bukkit.plugin.Plugin; +import org.bukkit.configuration.file.YamlConfiguration; + + +public class UpdateFile +{ + private final static Logger LOGGER = Logger.getLogger("Minecraft"); + private final static String UPDATE_URL = "http://goo.gl/67jev"; + private final static BigInteger PUBLIC_KEY = new BigInteger("5ha6a2d4qdy17ttkg8evh74sl5a87djojwenu12k1lvy8ui6003e6l06rntczpoh99mhc3txj8mqlxw111oyy9yl7s7qpyluyzix3j1odxrxx4u52gxvyu6qiteapczkzvi7rxgeqsozz7b19rdx73a7quo9ybwpz1cr82r7x5k0pg2a73pjjsv2j1awr13azo7klrcxp9y5xxwf5qv1s3tw4zqftli18u0ek5qkbzfbgk1v5n2f11pkwwk6p0mibrn26wnjbv11vyiqgu95o7busmt6vf5q7grpcenl637w83mbin56s3asj1131b2mscj9xep3cbj7la9tgsxl5bj87vzy8sk2d34kzwqdqgh9nry43nqqus12l1stmiv184r8r3jcy8w43e8h1u1mzklldb5eytkuhayqik8l3ns04hwt8sgacvw534be8sx26qrn5s1", 36); + private final transient File file; + private transient YamlConfiguration updateConfig; + private final transient Plugin plugin; + private final transient TreeMap versions = new TreeMap(); + + public UpdateFile(final Plugin plugin) + { + this.plugin = plugin; + final long lastUpdate = Long.parseLong(plugin.getConfig().getString("lastupdate", "0")); + file = new File(plugin.getDataFolder(), "update.yml"); + if (lastUpdate < System.currentTimeMillis() - 1000 * 60 * 60 * 6 || !file.exists()) + { + if (file.exists() && !file.delete()) + { + LOGGER.log(Level.SEVERE, "Could not delete file update.yml!"); + return; + } + if (!downloadFile() || !checkFile()) + { + LOGGER.log(Level.SEVERE, "Could not download and verify file update.yml!"); + return; + } + } + try + { + readVersions(); + } + catch (Exception ex) + { + LOGGER.log(Level.SEVERE, "Could not load update.yml!"); + return; + } + } + + private boolean downloadFile() + { + GetFile getFile; + try + { + getFile = new GetFile(UPDATE_URL); + getFile.saveTo(file); + plugin.getConfig().set("lastupdate", System.currentTimeMillis()); + plugin.getConfig().save(new File(plugin.getDataFolder(),"config.yml")); + return true; + } + catch (IOException ex) + { + LOGGER.log(Level.SEVERE, "Error while downloading update.yml", ex); + return false; + } + } + + private boolean checkFile() + { + BufferedInputStream bis = null; + try + { + bis = new BufferedInputStream(new FileInputStream(file)); + if (bis.read() != '#') + { + throw new IOException("File has to start with #"); + } + final StringBuilder length = new StringBuilder(); + final StringBuilder signature = new StringBuilder(); + boolean isSignature = false; + do + { + final int cur = bis.read(); + if (cur == -1) + { + break; + } + if (cur == ':') + { + isSignature = true; + } + else if (cur == '\n') + { + break; + } + else if ((cur >= '0' && cur <= '9') + || (cur >= 'a' && cur <= 'z')) + { + if (isSignature) + { + signature.append((char)cur); + } + else + { + length.append((char)cur); + } + } + else + { + throw new IOException("Illegal character in signature!"); + } + } + while (true); + if (length.length() == 0 || signature.length() == 0) + { + throw new IOException("Broken signature!"); + } + final int sigLength = new BigInteger(length.toString(), 36).intValue(); + if (sigLength < 0 || sigLength > 2048) + { + throw new IOException("Invalid signature length!"); + } + final byte[] sigBytes = new BigInteger(signature.toString(), 36).toByteArray(); + if (sigLength < sigBytes.length) + { + throw new IOException("Length is less then available bytes."); + } + byte[] realBytes; + if (sigLength == sigBytes.length) + { + realBytes = sigBytes; + } + else + { + realBytes = new byte[sigLength]; + System.arraycopy(sigBytes, 0, realBytes, sigLength - sigBytes.length, sigBytes.length); + } + final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(PUBLIC_KEY.toByteArray()); + final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + final PublicKey pubKey = keyFactory.generatePublic(pubKeySpec); + final Signature rsa = Signature.getInstance("SHA256withRSA"); + rsa.initVerify(pubKey); + final byte[] buffer = new byte[2048]; + int readLength; + do + { + readLength = bis.read(buffer); + if (readLength >= 0) + { + rsa.update(buffer, 0, readLength); + } + } + while (readLength >= 0); + return rsa.verify(realBytes); + } + catch (Exception ex) + { + LOGGER.log(Level.SEVERE, ex.getMessage(), ex); + } + finally + { + try + { + if (bis != null) + { + bis.close(); + } + } + catch (IOException ex) + { + LOGGER.log(Level.SEVERE, ex.getMessage(), ex); + } + } + return false; + } + + private void readVersions() throws Exception + { + updateConfig = new YamlConfiguration(); + updateConfig.load(file); + versions.clear(); + for (String versionString : updateConfig.getKeys(false)) + { + final Version version = new Version(versionString); + final VersionInfo info = new VersionInfo(updateConfig, versionString); + versions.put(version, info); + } + } + + public Map getVersions() + { + return Collections.unmodifiableMap(versions.descendingMap()); + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java new file mode 100644 index 000000000..95898bcb6 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java @@ -0,0 +1,128 @@ +package com.earth2me.essentials.update; + +import java.util.List; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.Event.Priority; +import org.bukkit.event.Event.Type; +import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerListener; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + + +public class UpdateProcess extends PlayerListener +{ + private transient Player currentPlayer; + private final transient Plugin plugin; + private final transient UpdateCheck updateCheck; + + public UpdateProcess(final Plugin plugin, final UpdateCheck updateCheck) + { + this.plugin = plugin; + this.updateCheck = updateCheck; + } + + public void registerEvents() + { + final PluginManager pluginManager = plugin.getServer().getPluginManager(); + pluginManager.registerEvent(Type.PLAYER_QUIT, this, Priority.Low, plugin); + pluginManager.registerEvent(Type.PLAYER_CHAT, this, Priority.Lowest, plugin); + } + + @Override + public void onPlayerChat(final PlayerChatEvent event) + { + if (event.getPlayer() == currentPlayer) + { + reactOnMessage(event.getMessage()); + event.setCancelled(true); + return; + } + } + + @Override + public void onPlayerJoin(final PlayerJoinEvent event) + { + final Player player = event.getPlayer(); + if (player.hasPermission("essentials.update") && !updateCheck.isEssentialsInstalled()) + { + player.sendMessage("Hello " + player.getDisplayName()); + player.sendMessage("Please type /essentialsupdate into the chat to start the installation of Essentials."); + } + if (player.hasPermission("essentials.update")) + { + final UpdateCheck.CheckResult result = updateCheck.getResult(); + switch (result) + { + case NEW_ESS: + player.sendMessage("The new version " + updateCheck.getNewVersion().toString() + " for Essentials is available. Please type /essentialsupdate to update."); + break; + case NEW_BUKKIT: + player.sendMessage("Your bukkit version is not the recommended build for Essentials, please update to version " + updateCheck.getNewBukkitVersion() + "."); + break; + case NEW_ESS_BUKKIT: + player.sendMessage("There is a new version " + updateCheck.getNewVersion().toString() + " of Essentials for Bukkit " + updateCheck.getNewBukkitVersion()); + break; + default: + } + } + } + + void doAutomaticUpdate() + { + final UpdatesDownloader downloader = new UpdatesDownloader(); + final VersionInfo info = updateCheck.getNewVersionInfo(); + final List changelog = info.getChangelog(); + Bukkit.getLogger().info("Essentials changelog " + updateCheck.getNewVersion().toString()); + for (String line : changelog) + { + Bukkit.getLogger().info(" - "+line); + } + downloader.start(plugin.getServer().getUpdateFolderFile(), info); + } + + void doManualUpdate() + { + + } + + void onCommand(CommandSender sender) + { + if (sender instanceof Player && sender.hasPermission("essentials.install")) + { + if (currentPlayer == null) + { + currentPlayer = (Player)sender; + if (updateCheck.isEssentialsInstalled()) + { + doManualUpdate(); + } + else + { + sender.sendMessage("Thank you for choosing Essentials."); + sender.sendMessage("The following installation wizard will guide you through the installation of Essentials."); + sender.sendMessage("Your answers will be saved for a later update."); + sender.sendMessage("Please answer the messages with yes or no, if not otherwise stated."); + sender.sendMessage("Write bye/exit/quit if you want to exit the wizard at anytime."); + + } + } + if (!currentPlayer.equals(sender)) + { + sender.sendMessage("The player " + currentPlayer.getDisplayName() + " is already using the wizard."); + } + } + else + { + sender.sendMessage("Please run the command as op from in game."); + } + } + + private void reactOnMessage(String message) + { + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java new file mode 100644 index 000000000..28ffdfe3c --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java @@ -0,0 +1,19 @@ +package com.earth2me.essentials.update; + +import java.io.File; + + +public class UpdatesDownloader +{ + + UpdatesDownloader() + { + + } + + void start(File updateFolderFile, VersionInfo newVersion) + { + + } + +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java b/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java new file mode 100644 index 000000000..8e6cbc97f --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/Version.java @@ -0,0 +1,173 @@ +package com.earth2me.essentials.update; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class Version implements Comparable +{ + public enum Type + { + STABLE, PREVIEW, DEVELOPER + } + + public int getMajor() + { + return major; + } + + public int getMinor() + { + return minor; + } + + public int getBuild() + { + return build; + } + + public Type getType() + { + return type; + } + private final transient int major; + private final transient int minor; + private final transient int build; + private final transient Type type; + + public Version(final String versionString) + { + final Matcher matcher = Pattern.compile("(Pre|Dev)?([0-9]+)[_\\.]([0-9]+)[_\\.]([0-9]+).*").matcher(versionString); + if (!matcher.matches() || matcher.groupCount() < 4) + { + type = Type.DEVELOPER; + major = 99; + minor = build = 0; + return; + } + if (versionString.startsWith("Pre")) + { + type = Type.PREVIEW; + } + else if (versionString.startsWith("Dev")) + { + type = Type.DEVELOPER; + } + else + { + type = Type.STABLE; + } + major = Integer.parseInt(matcher.group(2)); + minor = Integer.parseInt(matcher.group(3)); + build = Integer.parseInt(matcher.group(4)); + } + + @Override + public int compareTo(final Version other) + { + int ret = 0; + if (other.getType() == Type.DEVELOPER && getType() != Type.DEVELOPER) + { + ret = -1; + } + else if (getType() == Type.DEVELOPER && other.getType() != Type.DEVELOPER) + { + ret = 1; + } + else if (other.getMajor() > getMajor()) + { + ret = -1; + } + else if (getMajor() > other.getMajor()) + { + ret = 1; + } + else if (other.getMinor() > getMinor()) + { + ret = -1; + } + else if (getMinor() > other.getMinor()) + { + ret = 1; + } + else if (other.getBuild() > getBuild()) + { + ret = -1; + } + else if (getBuild() > other.getBuild()) + { + ret = 1; + } + else if (other.getType() == Type.STABLE && getType() == Type.PREVIEW) + { + ret = -1; + } + else if (getType() == Type.STABLE && other.getType() == Type.PREVIEW) + { + ret = 1; + } + return ret; + } + + @Override + public boolean equals(final Object obj) + { + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Version other = (Version)obj; + if (this.major != other.major) + { + return false; + } + if (this.minor != other.minor) + { + return false; + } + if (this.build != other.build) + { + return false; + } + if (this.type != other.type) + { + return false; + } + return true; + } + + @Override + public int hashCode() + { + int hash = 5; + hash = 71 * hash + this.major; + hash = 71 * hash + this.minor; + hash = 71 * hash + this.build; + hash = 71 * hash + (this.type != null ? this.type.hashCode() : 0); + return hash; + } + + @Override + public String toString() + { + final StringBuilder builder = new StringBuilder(); + if (type == Type.DEVELOPER) + { + builder.append("Dev"); + } + if (type == Type.PREVIEW) + { + builder.append("Pre"); + } + builder.append(major); + builder.append('.'); + builder.append(minor); + builder.append('.'); + builder.append(build); + return builder.toString(); + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java b/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java new file mode 100644 index 000000000..9cd1e5edb --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java @@ -0,0 +1,48 @@ +package com.earth2me.essentials.update; + +import java.util.ArrayList; +import org.bukkit.configuration.Configuration; +import java.util.Collections; +import java.util.List; + + +public class VersionInfo +{ + private final transient List changelog; + private final transient int minBukkit; + private final transient int maxBukkit; + private final transient List modules; + + public VersionInfo(final Configuration updateConfig, final String path) + { + changelog = updateConfig.getList(path + ".changelog", Collections.emptyList()); + minBukkit = updateConfig.getInt(path + ".min-bukkit", 0); + maxBukkit = updateConfig.getInt(path + ".max-bukkit", 0); + modules = new ArrayList(); + final String modulesPath = path + ".modules"; + for (String module : updateConfig.getKeys(false)) + { + modules.add(new ModuleInfo(updateConfig, modulesPath + module)); + } + } + + public List getChangelog() + { + return Collections.unmodifiableList(changelog); + } + + public int getMinBukkit() + { + return minBukkit; + } + + public int getMaxBukkit() + { + return maxBukkit; + } + + public List getModules() + { + return Collections.unmodifiableList(modules); + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java b/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java new file mode 100644 index 000000000..56d2445c9 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java @@ -0,0 +1,7 @@ +package com.earth2me.essentials.update.states; + + +public class Modules +{ + +} -- cgit v1.2.3