package nl.sbdeveloper.themeparkplus.util; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * License class for SBDevelopment * * v1.6 - Changed on 06-08-2020 * * @author Stijn [SBDeveloper] * @since 23-12-2019 */ public class License implements Listener { /* This file is part of ThemeParkRidecountAddon. Copyright (c) 2018-2020 SBDevelopment - All Rights Reserved Unauthorized copying of this file, via any medium is strictly prohibited Proprietary and confidential Written by Stijn Bannink , January 2020 */ private JavaPlugin plugin; // The plugin instance private String license; // The license code private String prefix; // The correct prefix for this plugin @Nullable private String invalidReason; // The reason the license is invalid, if null it's not invalid! @Nullable private static Boolean valid; // If true, it's valid, if false, it's not valid, if null it's not checked! /** * Construct a new license * @param plugin The Main class [Javaplugin] * @param prefix The prefix, like TPP or AF * @param license The license from the config */ public License(JavaPlugin plugin, String prefix, String license) { this.prefix = prefix; this.plugin = plugin; this.license = license; Bukkit.getPluginManager().registerEvents(this, plugin); validateLicense(); } @EventHandler public void onJoin(PlayerJoinEvent e) { if (this.invalidReason == null) return; Player p = e.getPlayer(); if (p.isOp() || p.hasPermission("sbd.licensemessages")) { Bukkit.getScheduler().runTaskLater(this.plugin, () -> p.sendMessage(ChatColor.GOLD + "[" + ChatColor.RED + this.plugin.getName() + ChatColor.GOLD + "] " + ChatColor.RED + "The license is incorrect! Reason: " + ChatColor.GOLD + this.invalidReason), 3 * 20L /* 3 sec */); } } /** * Check a license * */ private void validateLicense() { //STEP 1: Check prefix if (!this.license.split("-")[0].contains(this.prefix)) { disable("You used the wrong license for this product."); return; } //STEP 2: Send license request String url = "https://sbdplugins.nl/wp-json/lmfwc/v2/licenses/" + this.license; @Nullable JsonObject response; try { response = sendGETRequestJSON(url); } catch (IOException e) { disable("Couldn't send the request."); return; } if (response == null) { disable("Couldn't send the request."); return; } JsonObject dataObject = response.get("data").getAsJsonObject(); //STEP 3: Check status switch(dataObject.get("status").getAsString()) { case "2": //Delivered -> Try to activate (double check timesActivated) break; case "3": //Activated! break; default: disable("Your license has a wrong status."); return; } //STEP 4: Check times activated, and if not activated, activate. if (dataObject.get("timesActivated").isJsonNull() || dataObject.get("timesActivated").getAsString().equalsIgnoreCase("0")) { activate(); return; } //STEP 5: Check expire date if (dataObject.get("expiresAt").isJsonNull()) { disable("Your license has no expire date."); return; } SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date; try { date = format.parse(dataObject.get("expiresAt").getAsString()); } catch (ParseException e) { e.printStackTrace(); disable("Your license has a wrong expire date."); return; } if (date == null) { disable("Your license has a wrong expire date."); return; } if (!(date.after(new Date()))) { disable("Your license has expired."); return; } //STEP 6: Check IP and port. if (!dataObject.get("ipcheck").getAsBoolean()) { disable("Your license has been used with another IP. Update it in our Discord."); return; } if (dataObject.get("port").isJsonNull()) { disable("Your license has no port."); return; } String por = dataObject.get("port").getAsString(); if (!checkPortValue(Bukkit.getServer().getPort(), por)) { disable("Your license has been used with another Port. Update it in our Discord."); return; } valid = true; } /** * Activate the license * */ private void activate() { //STEP 1: Send license activate request String url = "https://sbdplugins.nl/wp-json/lmfwc/v2/licenses/activate/" + this.license + "?port=" + Bukkit.getServer().getPort(); @Nullable JsonObject response; try { response = sendGETRequestJSON(url); } catch (IOException e) { disable("Couldn't send the activate request."); return; } if (response == null) { disable("Couldn't send the activate request."); return; } JsonObject dataObject = response.get("data").getAsJsonObject(); //STEP 2: Check status switch(dataObject.get("status").getAsString()) { case "2": //Delivered -> STILL NOT ACTIVATED?! -> Double check break; case "3": //Activated! break; default: disable("Your license has a wrong status."); return; } //STEP 3: Check times activated, and if still not activated, disable. if (dataObject.get("timesActivated").isJsonNull() || dataObject.get("timesActivated").getAsString().equalsIgnoreCase("0")) { disable("Couldn't activate the license."); } } /** * Disable the plugin (private) * * @param reason The disabling reason */ private void disable(String reason) { this.invalidReason = reason; Bukkit.getScheduler().runTask(this.plugin, () -> { valid = false; Bukkit.getLogger().severe("[" + this.plugin.getName() + "] Stopping plugin because licensing system check failed."); Bukkit.getLogger().severe("[" + this.plugin.getName() + "] Reason: " + reason); Bukkit.getLogger().severe("[" + this.plugin.getName() + "] Contact the developer if you believe something is wrong on their side."); Bukkit.getPluginManager().disablePlugin(this.plugin); }); } /** * Send an GET request with JSONObject response * * @param uri The URL * * @return The JSONObject * @throws IOException URL errors */ @Nullable private JsonObject sendGETRequestJSON(String uri) throws IOException { URL url = new URL(uri); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); con.setRequestProperty("User-Agent", "Mozilla/5.0"); String authStringEnc = "Y2tfMGEzNWEzMWE2NzExNmM3NDg2MGEwYTJhNjUxNGVjZjM4NTBmM2JmMDpjc185NmYxZGNlYjI4MWRkZDExOTBjMzQ3ZjJjYzMwMGNjZTIzYWNhODI1"; con.setRequestProperty("Authorization", "Basic " + authStringEnc); int code = con.getResponseCode(); if (code == 404) { disable("404_error"); return null; } BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); JsonParser parser = new JsonParser(); return parser.parse(response.toString()).getAsJsonObject(); } private boolean checkPortValue(int input, @NotNull String dataValue) { //STEP 1: Check wildcard if (dataValue.equals("*")) return true; //STEP 2: Check if equals try { int dataVal = Integer.parseInt(dataValue); return input == dataVal; } catch (NumberFormatException ignored) {} //STEP 3: Check if range if (dataValue.contains("-")) { String[] dataSplit = dataValue.split("-"); //STEP 3.1: Check if min or max is wildcard if (dataSplit[0].equals("*") && !dataSplit[1].equals("*")) { int max = Integer.parseInt(dataSplit[1]); return input <= max; } else if (dataSplit[1].equals("*") && !dataSplit[0].equals("*")) { int min = Integer.parseInt(dataSplit[0]); return min <= input; } else { try { int min = Integer.parseInt(dataSplit[0]); int max = Integer.parseInt(dataSplit[1]); return (min <= input) && (input <= max); } catch (NumberFormatException ex) { return false; } } } //Else, invalid value return false; } /** * Check if the license is valid * * @return true -> VALID, false -> INVALID, null -> UNCHECKED */ @Nullable public static Boolean isValid() { return valid; } }