diff --git a/src/main/java/me/glaremasters/guilds/updater/UpdateChecker.java b/src/main/java/me/glaremasters/guilds/updater/UpdateChecker.java index 4ddc21f58..aabebc22e 100644 --- a/src/main/java/me/glaremasters/guilds/updater/UpdateChecker.java +++ b/src/main/java/me/glaremasters/guilds/updater/UpdateChecker.java @@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import me.glaremasters.guilds.Guilds; @@ -14,7 +15,9 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -22,7 +25,7 @@ * A utility class to assist in checking for updates for plugins uploaded to * SpigotMC. Before any members of this * class are accessed, {@link #init(JavaPlugin, int)} must be invoked by the plugin, - * preferrably in its {@link JavaPlugin#onEnable()} method, though that is not a + * preferably in its {@link JavaPlugin#onEnable()} method, though that is not a * requirement. *

* This class performs asynchronous queries to SpiGet, @@ -34,25 +37,11 @@ */ public final class UpdateChecker { - public static final VersionScheme VERSION_SCHEME_DECIMAL = (first, second) -> { - String[] firstSplit = splitVersionInfo(first), secondSplit = splitVersionInfo(second); - if (firstSplit == null || secondSplit == null) return null; - - for (int i = 0; i < Math.min(firstSplit.length, secondSplit.length); i++) { - int currentValue = toInt(firstSplit[i], 0), newestValue = toInt(secondSplit[i], 0); - - if (newestValue > currentValue) { - return second; - } else if (newestValue < currentValue) { - return first; - } - } - - return (secondSplit.length > firstSplit.length) ? second : first; - }; + public static final VersionScheme VERSION_SCHEME_DECIMAL = UpdateChecker::compareDecimalVersions; private static final String USER_AGENT = "CHOCO-update-checker"; private static final String UPDATE_URL = "https://api.spigotmc.org/simple/0.1/index.php?action=getResource&id=%d"; + private static final String CURRENT_VERSION_KEY = "current_version"; private static final Pattern DECIMAL_SCHEME_PATTERN = Pattern.compile("\\d+(?:\\.\\d+)*"); private static UpdateChecker instance; @@ -77,37 +66,33 @@ private UpdateChecker(JavaPlugin plugin, int pluginID, VersionScheme versionSche */ public CompletableFuture requestUpdateCheck() { return CompletableFuture.supplyAsync(() -> { - int responseCode = -1; + HttpURLConnection connection = null; + try { URL url = new URL(String.format(UPDATE_URL, pluginID)); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection = (HttpURLConnection) url.openConnection(); connection.addRequestProperty("User-Agent", USER_AGENT); - InputStreamReader reader = new InputStreamReader(connection.getInputStream()); - responseCode = connection.getResponseCode(); - - JsonElement element = new JsonParser().parse(reader); - - reader.close(); - - JsonObject versionObject = element.getAsJsonObject(); - String current = plugin.getDescription().getVersion(), newest = versionObject.get("current_version").getAsString(); - String latest = versionScheme.compareVersions(current, newest); + int responseCode = connection.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + return new UpdateResult(responseCode == HttpURLConnection.HTTP_UNAUTHORIZED ? + UpdateReason.UNAUTHORIZED_QUERY : UpdateReason.UNKNOWN_ERROR); + } - if (latest == null) { - return new UpdateResult(UpdateReason.UNSUPPORTED_VERSION_SCHEME); - } else if (latest.equals(current)) { - return new UpdateResult(current.equals(newest) ? UpdateReason.UP_TO_DATE : UpdateReason.UNRELEASED_VERSION); - } else if (latest.equals(newest)) { - return new UpdateResult(UpdateReason.NEW_UPDATE, latest); + try (InputStreamReader reader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)) { + String current = plugin.getDescription().getVersion(); + String newest = readNewestVersion(reader); + return createUpdateResult(current, newest); } } catch (IOException e) { return new UpdateResult(UpdateReason.COULD_NOT_CONNECT); - } catch (JsonSyntaxException e) { + } catch (JsonParseException | IllegalStateException | UnsupportedOperationException e) { return new UpdateResult(UpdateReason.INVALID_JSON); + } finally { + if (connection != null) { + connection.disconnect(); + } } - - return new UpdateResult(responseCode == 401 ? UpdateReason.UNAUTHORIZED_QUERY : UpdateReason.UNKNOWN_ERROR); }); } @@ -121,7 +106,61 @@ public UpdateResult getLastResult() { return lastResult; } + private UpdateResult createUpdateResult(String current, String newest) { + String latest = versionScheme.compareVersions(current, newest); + + if (latest == null) { + return new UpdateResult(UpdateReason.UNSUPPORTED_VERSION_SCHEME); + } + + if (latest.equals(current)) { + return new UpdateResult(current.equals(newest) ? UpdateReason.UP_TO_DATE : UpdateReason.UNRELEASED_VERSION); + } + + if (latest.equals(newest)) { + return new UpdateResult(UpdateReason.NEW_UPDATE, latest); + } + + return new UpdateResult(UpdateReason.UNKNOWN_ERROR); + } + + private static String readNewestVersion(InputStreamReader reader) { + JsonElement element = JsonParser.parseReader(reader); + if (element == null || !element.isJsonObject()) { + throw new JsonSyntaxException("Update response was not a JSON object"); + } + + JsonObject versionObject = element.getAsJsonObject(); + JsonElement currentVersionElement = versionObject.get(CURRENT_VERSION_KEY); + if (currentVersionElement == null || !currentVersionElement.isJsonPrimitive()) { + throw new JsonSyntaxException("Update response did not contain a valid " + CURRENT_VERSION_KEY + " value"); + } + + return currentVersionElement.getAsString(); + } + + private static String compareDecimalVersions(String first, String second) { + String[] firstSplit = splitVersionInfo(first); + String[] secondSplit = splitVersionInfo(second); + if (firstSplit == null || secondSplit == null) return null; + + for (int i = 0; i < Math.min(firstSplit.length, secondSplit.length); i++) { + int currentValue = toInt(firstSplit[i], 0); + int newestValue = toInt(secondSplit[i], 0); + + if (newestValue > currentValue) { + return second; + } else if (newestValue < currentValue) { + return first; + } + } + + return (secondSplit.length > firstSplit.length) ? second : first; + } + private static String[] splitVersionInfo(String version) { + if (version == null) return null; + Matcher matcher = DECIMAL_SCHEME_PATTERN.matcher(version); if (!matcher.find()) return null; @@ -327,20 +366,25 @@ public static int toInt(final String str, final int defaultValue) { public static void runCheck(Guilds guilds, SettingsManager settingsManager) { if (settingsManager.getProperty(PluginSettings.UPDATE_CHECK)) { UpdateChecker.init(guilds, 66176).requestUpdateCheck().whenComplete((result, exception) -> { + if (exception != null || result == null) { + guilds.getLogger().log(Level.WARNING, "Could not check for a new version of Guilds.", exception); + return; + } + if (result.requiresUpdate()) { guilds.getLogger().info(String.format("An update is available! Guilds %s may be downloaded on SpigotMC", result.getNewestVersion())); return; } - String reason = result.getReason().toString(); - switch (reason) { - case "UP_TO_DATE": + + switch (result.getReason()) { + case UP_TO_DATE: guilds.getLogger().info(String.format("Your version of Guilds (%s) is up to date!", result.getNewestVersion())); break; - case "UNRELEASED_VERSION": + case UNRELEASED_VERSION: guilds.getLogger().info(String.format("Your version of Guilds (%s) is more recent than the one publicly available. Are you on a development build?", result.getNewestVersion())); break; default: - guilds.getLogger().warning("Could not check for a new version of Guilds. Reason: " + reason); + guilds.getLogger().warning("Could not check for a new version of Guilds. Reason: " + result.getReason()); break; } });