/* * This file is part of MapReflectionAPI. * Copyright (c) 2022 inventivetalent / SBDevelopment - All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package tech.sbdevelopment.mapreflectionapi.utils; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import javax.net.ssl.HttpsURLConnection; import java.io.*; import java.lang.reflect.Method; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.util.function.BiConsumer; /** * Update checker class * * @author Stijn [SBDeveloper] * @version 2.3 [27-09-2022] - Added Polymart support ; fixed Spigot support * @since 05-03-2020 */ public class UpdateManager { private static final String SPIGOT_API = "https://api.spigotmc.org/legacy/update.php?resource=%d"; private static final String SPIGOT_DOWNLOAD = "https://api.spiget.org/v2/resources/%s/download"; private static final String POLYMART_API = "https://api.polymart.org/v1/getResourceInfoSimple/?resource_id=%d&key=version"; private static final String POLYMART_DOWNLOAD = "https://api.polymart.org/v1/requestUpdateURL/?inject_version=%d&resource_id=%d&user_id=%d&nonce=%d&download_agent=%d&download_time=%d&download_token=%s"; private final Plugin plugin; private final Version currentVersion; private final CheckType type; //Spigot & Polymart private final int resourceID; //Polymart only private int injector_version; private int user_id; private int nonce; private int download_agent; private int download_time; private String download_token; private BiConsumer versionResponse; private BiConsumer downloadResponse; /** * Construct a new UpdateManager * * @param plugin The plugin instance */ public UpdateManager(Plugin plugin) { this.plugin = plugin; this.currentVersion = new Version(plugin.getDescription().getVersion()); this.type = CheckType.POLYMART_PAID; this.resourceID = Integer.parseInt("%%__RESOURCE__%%"); this.injector_version = Integer.parseInt("%%__INJECT_VER__%%"); this.user_id = Integer.parseInt("%%__USER__%%"); this.nonce = Integer.parseInt("%%__NONCE__%%"); this.download_agent = Integer.parseInt("%%__AGENT__%%"); this.download_time = Integer.parseInt("%%__TIMESTAMP__%%"); this.download_token = "%%__VERIFY_TOKEN__%%"; } public UpdateManager(Plugin plugin, int resourceID) { this.plugin = plugin; this.currentVersion = new Version(plugin.getDescription().getVersion()); this.type = CheckType.SPIGOT; this.resourceID = resourceID; } /** * Handle the response given by check(); * * @param versionResponse The response * @return The updatemanager */ public UpdateManager handleResponse(BiConsumer versionResponse) { this.versionResponse = versionResponse; return this; } public UpdateManager handleDownloadResponse(BiConsumer downloadResponse) { this.downloadResponse = downloadResponse; return this; } /** * Check for a new version */ public void check() { Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { try { HttpsURLConnection con; if (type == CheckType.POLYMART_PAID) { con = (HttpsURLConnection) new URL(String.format(POLYMART_API, this.resourceID)).openConnection(); } else { con = (HttpsURLConnection) new URL(String.format(SPIGOT_API, this.resourceID)).openConnection(); } con.setRequestMethod("GET"); con.setRequestProperty("User-Agent", "SBDChecker/2.1"); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); Version onlineVersion = new Version(response.toString()); VersionResponse verRes = this.currentVersion.check(onlineVersion); Bukkit.getScheduler().runTask(this.plugin, () -> this.versionResponse.accept(verRes, onlineVersion)); } catch (IOException | NullPointerException e) { e.printStackTrace(); Bukkit.getScheduler().runTask(this.plugin, () -> this.versionResponse.accept(VersionResponse.UNAVAILABLE, null)); } }); } public void runUpdate() { File pluginFile = getPluginFile(); // /plugins/XXX.jar if (pluginFile == null) { this.downloadResponse.accept(DownloadResponse.ERROR, null); return; } File updateFolder = Bukkit.getUpdateFolderFile(); if (!updateFolder.exists()) { if (!updateFolder.mkdirs()) { this.downloadResponse.accept(DownloadResponse.ERROR, null); return; } } final File updateFile = new File(updateFolder, pluginFile.getName()); Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { ReadableByteChannel channel; try { //https://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java HttpsURLConnection connection; if (type == CheckType.POLYMART_PAID) { connection = (HttpsURLConnection) new URL(String.format(POLYMART_DOWNLOAD, this.injector_version, this.resourceID, this.user_id, this.nonce, this.download_agent, this.download_time, this.download_token)).openConnection(); } else { connection = (HttpsURLConnection) new URL(String.format(SPIGOT_DOWNLOAD, this.resourceID)).openConnection(); } connection.setRequestProperty("User-Agent", "Mozilla/5.0"); InputStream stream = connection.getInputStream(); if (connection.getResponseCode() != 200) { BufferedReader in = new BufferedReader(new InputStreamReader(stream)); String inputLine; StringBuilder responsestr = new StringBuilder(); while ((inputLine = in.readLine()) != null) { responsestr.append(inputLine); } in.close(); throw new RuntimeException("Download returned status #" + connection.getResponseCode(), new Throwable(responsestr.toString())); } channel = Channels.newChannel(stream); } catch (IOException e) { Bukkit.getScheduler().runTask(this.plugin, () -> this.downloadResponse.accept(DownloadResponse.ERROR, null)); e.printStackTrace(); return; } FileChannel fileChannel = null; try { FileOutputStream fosForDownloadedFile = new FileOutputStream(updateFile); fileChannel = fosForDownloadedFile.getChannel(); fileChannel.transferFrom(channel, 0, Long.MAX_VALUE); } catch (IOException e) { Bukkit.getScheduler().runTask(this.plugin, () -> this.downloadResponse.accept(DownloadResponse.ERROR, null)); e.printStackTrace(); return; } finally { if (channel != null) { try { channel.close(); } catch (IOException ioe) { System.out.println("Error while closing response body channel"); } } if (fileChannel != null) { try { fileChannel.close(); } catch (IOException ioe) { System.out.println("Error while closing file channel for downloaded file"); } } } Bukkit.getScheduler().runTask(this.plugin, () -> this.downloadResponse.accept(DownloadResponse.DONE, updateFile.getPath())); }); } private File getPluginFile() { if (!(this.plugin instanceof JavaPlugin)) { return null; } try { Method method = JavaPlugin.class.getDeclaredMethod("getFile"); method.setAccessible(true); return (File) method.invoke(this.plugin); } catch (ReflectiveOperationException e) { throw new RuntimeException("Could not get plugin file", e); } } public enum CheckType { SPIGOT, POLYMART_PAID } public enum VersionResponse { LATEST, //Latest version FOUND_NEW, //Newer available THIS_NEWER, //Local version is newer? UNAVAILABLE //Error } public enum DownloadResponse { DONE, ERROR, UNAVAILABLE } public static class Version { private final String version; public final String get() { return this.version; } private Version(String version) { if (version == null) throw new IllegalArgumentException("Version can not be null"); if (!version.matches("[0-9]+(\\.[0-9]+)*")) throw new IllegalArgumentException("Invalid version format"); this.version = version; } private VersionResponse check(Version that) { String[] thisParts = this.get().split("\\."); String[] thatParts = that.get().split("\\."); int length = Math.max(thisParts.length, thatParts.length); for (int i = 0; i < length; i++) { int thisPart = i < thisParts.length ? Integer.parseInt(thisParts[i]) : 0; int thatPart = i < thatParts.length ? Integer.parseInt(thatParts[i]) : 0; if (thisPart < thatPart) return VersionResponse.FOUND_NEW; if (thisPart > thatPart) return VersionResponse.THIS_NEWER; } return VersionResponse.LATEST; } } }