mirror of
https://github.com/funkemunky/AntiVPN.git
synced 2026-05-31 17:31:55 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 827851d4cc |
-1288
File diff suppressed because it is too large
Load Diff
@@ -18,10 +18,8 @@ jobs:
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
gradle-version: '9.4.1'
|
||||
cache-read-only: true
|
||||
cache-cleanup: on-success
|
||||
- name: Build
|
||||
run: gradle build -x test --no-daemon
|
||||
run: gradle build --no-daemon
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Get Version Number from Gradle
|
||||
@@ -33,13 +31,10 @@ jobs:
|
||||
id: changelog
|
||||
run: |
|
||||
CHANGELOG_CONTENT=$(awk 'BEGIN {print_section=0;} /^## \[/ {if (print_section == 0) {print_section=1;} else {exit;}} print_section {print;}' CHANGELOG.md)
|
||||
echo "Extracted latest release notes from CHANGELOG.md:"
|
||||
echo "$CHANGELOG_CONTENT"
|
||||
{
|
||||
echo 'content<<EOF'
|
||||
echo "$CHANGELOG_CONTENT"
|
||||
echo 'EOF'
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
CHANGELOG_ESCAPED=$(echo "$CHANGELOG_CONTENT" | sed ':a;N;$!ba;s/\n/%0A/g')
|
||||
echo "Extracted latest release notes from CHANGELOG.md:"
|
||||
echo -e "$CHANGELOG_CONTENT"
|
||||
echo "content=$CHANGELOG_ESCAPED" >> "$GITHUB_OUTPUT"
|
||||
- name: Create Release
|
||||
uses: actions/create-release@v1
|
||||
id: create_release
|
||||
@@ -59,3 +54,18 @@ jobs:
|
||||
asset_path: ./build/libs/AntiVPN-${{ env.VERSION }}-universal.jar
|
||||
asset_name: AntiVPN-v${{ env.VERSION }}.jar
|
||||
asset_content_type: application/java-archive
|
||||
- name: Upload to Hangar
|
||||
uses: benwoo1110/hangar-upload-action@v1
|
||||
with:
|
||||
api_token: ${{ secrets.HANGAR_API_TOKEN }}
|
||||
slug: AntiVPN
|
||||
version: ${{ env.VERSION }}
|
||||
channel: release
|
||||
files: |
|
||||
[
|
||||
{
|
||||
"path": "./build/libs/AntiVPN-${{ env.VERSION }}-universal.jar",
|
||||
"platforms": ["PAPER", "VELOCITY", "WATERFALL"]
|
||||
}
|
||||
]
|
||||
description: ${{ steps.changelog.outputs.content }}
|
||||
|
||||
@@ -23,11 +23,8 @@ jobs:
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
gradle-version: '9.4.1'
|
||||
cache-overwrite-existing: 'true'
|
||||
cache-read-only: ${{ github.event_name == 'pull_request' }}
|
||||
cache-cleanup: on-success
|
||||
- name: Build
|
||||
run: gradle build -x test --no-daemon
|
||||
run: gradle build --no-daemon
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload AntiVPN
|
||||
@@ -35,3 +32,8 @@ jobs:
|
||||
with:
|
||||
name: AntiVPN-Universal
|
||||
path: build/libs/AntiVPN-*-universal.jar
|
||||
- name: Upload Sponge plugin
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AntiVPN-Sponge
|
||||
path: Sponge/SpongeLoader/build/libs/*.jar
|
||||
|
||||
@@ -2,6 +2,26 @@ on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
JAVA_TOOL_OPTIONS: -Djavax.net.ssl.trustStoreType=JKS -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts -Djavax.net.ssl.trustStorePassword=changeit
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'zulu'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
gradle-version: '9.4.1'
|
||||
- name: Build
|
||||
run: gradle build -x test --no-daemon
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
@@ -18,17 +38,7 @@ jobs:
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
gradle-version: '9.4.1'
|
||||
cache-overwrite-existing: 'true'
|
||||
cache-read-only: ${{ github.event_name == 'pull_request' }}
|
||||
cache-cleanup: on-success
|
||||
- name: Build
|
||||
run: gradle build -x test --no-daemon
|
||||
- name: Build and Test
|
||||
run: gradle build --no-daemon
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload AntiVPN
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AntiVPN-Universal
|
||||
path: build/libs/AntiVPN-*-universal.jar
|
||||
- name: Test
|
||||
run: gradle test --no-daemon --parallel
|
||||
|
||||
@@ -295,5 +295,3 @@ $RECYCLE.BIN/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,maven,java,intellij,eclipse,netbeans
|
||||
/.gradle/
|
||||
.grade/**
|
||||
/run/
|
||||
|
||||
@@ -10,12 +10,6 @@ dependencies {
|
||||
implementation project(':Common:loader-utils')
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
filesMatching('plugin.yml') {
|
||||
expand(project: project, projectVersion: project.version)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveClassifier.set('')
|
||||
|
||||
@@ -30,8 +24,4 @@ tasks.named('shadowJar') {
|
||||
dependsOn(':Bukkit:Plugin:shadowJar')
|
||||
}
|
||||
|
||||
tasks.named('compileJava') {
|
||||
dependsOn(':Bukkit:Plugin:shadowJar')
|
||||
}
|
||||
|
||||
tasks.build.dependsOn shadowJar
|
||||
|
||||
+22
-21
@@ -22,30 +22,31 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class BukkitLoaderPlugin extends JavaPlugin {
|
||||
|
||||
private static final String JAR_NAME = "antivpn-bukkit.jarinjar";
|
||||
private static final String SOURCE_NAME = "antivpn-source.jarinjar";
|
||||
private static final String BOOTSTRAP_CLASS = "dev.brighten.antivpn.bukkit.BukkitPlugin";
|
||||
private static final String JAR_NAME = "antivpn-bukkit.jarinjar";
|
||||
private static final String SOURCE_NAME = "antivpn-source.jarinjar";
|
||||
private static final String BOOTSTRAP_CLASS = "dev.brighten.antivpn.bukkit.BukkitPlugin";
|
||||
|
||||
private final LoaderBootstrap plugin;
|
||||
private final LoaderBootstrap plugin;
|
||||
|
||||
public BukkitLoaderPlugin() {
|
||||
JarInJarClassLoader loader =
|
||||
new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
||||
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, JavaPlugin.class, this);
|
||||
}
|
||||
public BukkitLoaderPlugin() {
|
||||
JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
||||
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, JavaPlugin.class, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.plugin.onLoad(getDataFolder());
|
||||
}
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.plugin.onLoad(getDataFolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.plugin.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.plugin.onDisable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.plugin.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.plugin.onDisable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ main: dev.brighten.antivpn.bukkit.loader.BukkitLoaderPlugin
|
||||
version: ${project.version}
|
||||
author: funkemunky
|
||||
api-version: 1.13
|
||||
folia-supported: true
|
||||
folia-supported: true
|
||||
@@ -12,12 +12,12 @@ dependencies {
|
||||
testImplementation 'org.mockito:mockito-core:5.11.0'
|
||||
testImplementation 'org.mockito:mockito-subclass:5.11.0'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
|
||||
testImplementation testFixtures(project(':Common:Source'))
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveClassifier.set('')
|
||||
relocate 'org.bstats', 'dev.brighten.antivpn.bukkit.org.bstats'
|
||||
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
+21
-20
@@ -19,37 +19,38 @@ package dev.brighten.antivpn.bukkit;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitCommandExecutor implements CommandExecutor {
|
||||
|
||||
private final CommandSender sender;
|
||||
private final CommandSender sender;
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message, Object... objects) {
|
||||
sender.sendMessage(
|
||||
ChatColor.translateAlternateColorCodes('&', String.format(message, objects)));
|
||||
}
|
||||
@Override
|
||||
public void sendMessage(String message, Object... objects) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
String.format(message, objects)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer() {
|
||||
if (!isPlayer()) return Optional.empty();
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer() {
|
||||
if(!isPlayer()) return Optional.empty();
|
||||
|
||||
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((Player) sender).getUniqueId());
|
||||
}
|
||||
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((Player)sender).getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof Player;
|
||||
}
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof Player;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,9 @@ package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.CheckResult;
|
||||
import dev.brighten.antivpn.api.OfflinePlayer;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import java.util.logging.Level;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -32,92 +30,86 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BukkitListener extends VPNExecutor implements Listener {
|
||||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
Bukkit.getPluginManager().registerEvents(this, BukkitPlugin.pluginInstance.getPlugin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
Bukkit.getLogger().log(level, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
Bukkit.getServer()
|
||||
.dispatchCommand(
|
||||
Bukkit.getConsoleSender(), ChatColor.translateAlternateColorCodes('&', command));
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
HandlerList.unregisterAll(this);
|
||||
Bukkit.getPluginManager().disablePlugin(BukkitPlugin.pluginInstance.getPlugin());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onLogin(final PlayerLoginEvent event) {
|
||||
APIPlayer player =
|
||||
AntiVPN.getInstance()
|
||||
.getPlayerExecutor()
|
||||
.getPlayer(event.getPlayer().getUniqueId())
|
||||
.orElse(
|
||||
new OfflinePlayer(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getName(),
|
||||
event.getAddress()));
|
||||
|
||||
CheckResult result = player.checkPlayer();
|
||||
|
||||
if (!result.resultType().isShouldBlock()) return;
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
return;
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
Bukkit.getPluginManager()
|
||||
.registerEvents(this, BukkitPlugin.pluginInstance.getPlugin());
|
||||
}
|
||||
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_BANNED);
|
||||
event.setKickMessage(
|
||||
switch (result.resultType()) {
|
||||
case DENIED_COUNTRY ->
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getCountryVanillaKickReason(),
|
||||
player,
|
||||
result.response());
|
||||
case DENIED_PROXY ->
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickMessage(), player, result.response());
|
||||
default -> "You were kicked by KauriVPN for an unknown reason!";
|
||||
@Override
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
Bukkit.getLogger().log(level, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
|
||||
ChatColor.translateAlternateColorCodes('&', command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
HandlerList.unregisterAll(this);
|
||||
Bukkit.getPluginManager().disablePlugin(BukkitPlugin.pluginInstance.getPlugin());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onLogin(final PlayerLoginEvent event) {
|
||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.orElse(new OfflinePlayer(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getName(),
|
||||
event.getAddress()
|
||||
));
|
||||
|
||||
player.checkPlayer(result -> {
|
||||
if(!result.resultType().isShouldBlock()) return;
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_BANNED);
|
||||
event.setKickMessage(switch (result.resultType()) {
|
||||
case DENIED_COUNTRY -> StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getCountryVanillaKickReason(),
|
||||
player,
|
||||
result.response()
|
||||
);
|
||||
case DENIED_PROXY ->
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickMessage(),
|
||||
player,
|
||||
result.response()
|
||||
);
|
||||
default -> "You were kicked by KauriVPN for an unknown reason!";
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onJoin(final PlayerJoinEvent event) {
|
||||
AntiVPN.getInstance()
|
||||
.getPlayerExecutor()
|
||||
.getPlayer(event.getPlayer().getUniqueId())
|
||||
.ifPresent(APIPlayer::checkAlertsState);
|
||||
}
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onJoin(final PlayerJoinEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.ifPresent(APIPlayer::checkAlertsState);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,39 +17,38 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class BukkitPlayer extends APIPlayer {
|
||||
|
||||
private final Player player;
|
||||
private final Player player;
|
||||
public BukkitPlayer(Player player) {
|
||||
super(player.getUniqueId(), player.getName(), player.getAddress().getAddress());
|
||||
|
||||
public BukkitPlayer(Player player) {
|
||||
super(
|
||||
player.getUniqueId(),
|
||||
player.getName(),
|
||||
player.getAddress() != null ? player.getAddress().getAddress() : null);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
this.player = player;
|
||||
}
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
player.sendMessage(ChatColor.translateAlternateColorCodes('&', message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
player.sendMessage(ChatColor.translateAlternateColorCodes('&', message));
|
||||
}
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
if(!Bukkit.isPrimaryThread()) {
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||
} else player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,48 +18,49 @@ package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BukkitPlayerExecutor implements PlayerExecutor {
|
||||
|
||||
private final Map<UUID, BukkitPlayer> cachedPlayers = new HashMap<>();
|
||||
private final Map<UUID, BukkitPlayer> cachedPlayers = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
final Player player = Bukkit.getPlayer(name);
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
final Player player = Bukkit.getPlayer(name);
|
||||
|
||||
if (player == null) {
|
||||
return Optional.empty();
|
||||
if(player == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
|
||||
}
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(UUID uuid) {
|
||||
final Player player = Bukkit.getPlayer(uuid);
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(UUID uuid) {
|
||||
final Player player = Bukkit.getPlayer(uuid);
|
||||
if(player == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (player == null) {
|
||||
return Optional.empty();
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
|
||||
}
|
||||
@Override
|
||||
public void unloadPlayer(UUID uuid) {
|
||||
cachedPlayers.remove(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadPlayer(UUID uuid) {
|
||||
cachedPlayers.remove(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), k -> new BukkitPlayer(pl)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), k -> new BukkitPlayer(pl)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,11 +24,6 @@ import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
@@ -39,124 +34,129 @@ import org.bukkit.plugin.SimplePluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BukkitPlugin implements LoaderBootstrap {
|
||||
|
||||
public static BukkitPlugin pluginInstance;
|
||||
private SimpleCommandMap commandMap;
|
||||
@Getter private File dataFolder;
|
||||
private final List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
|
||||
@Getter private final JavaPlugin plugin;
|
||||
public static BukkitPlugin pluginInstance;
|
||||
private SimpleCommandMap commandMap;
|
||||
@Getter
|
||||
private File dataFolder;
|
||||
private final List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
|
||||
@Getter
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public BukkitPlugin(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
public BukkitPlugin(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Getter private PlayerCommandRunner playerCommandRunner;
|
||||
@Getter
|
||||
private PlayerCommandRunner playerCommandRunner;
|
||||
|
||||
@Override
|
||||
public void onLoad(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
@Override
|
||||
public void onLoad(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
|
||||
Bukkit.getLogger().info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BukkitListener(), new BukkitPlayerExecutor(), getDataFolder());
|
||||
Bukkit.getLogger().info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BukkitListener(), new BukkitPlayerExecutor(), getDataFolder());
|
||||
|
||||
playerCommandRunner = new PlayerCommandRunner();
|
||||
playerCommandRunner.start();
|
||||
playerCommandRunner = new PlayerCommandRunner();
|
||||
playerCommandRunner.start();
|
||||
|
||||
// Loading our bStats metrics to be pushed to https://bstats.org
|
||||
if (AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
Bukkit.getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(plugin, 12615);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0;
|
||||
// Loading our bStats metrics to be pushed to https://bstats.org
|
||||
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
Bukkit.getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(plugin, 12615);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0;
|
||||
}
|
||||
}.runTaskTimerAsynchronously(plugin, 12000, 12000);
|
||||
}
|
||||
}.runTaskTimerAsynchronously(plugin, 12000, 12000);
|
||||
|
||||
Bukkit.getLogger().info("Setting up and registering commands...");
|
||||
// We need access to the commandMap to register our commands without using the "proper" method
|
||||
if (Bukkit.getServer().getPluginManager() instanceof SimplePluginManager manager) {
|
||||
try {
|
||||
Field field = SimplePluginManager.class.getDeclaredField("commandMap");
|
||||
field.setAccessible(true);
|
||||
commandMap = (SimpleCommandMap) field.get(manager);
|
||||
} catch (IllegalArgumentException | SecurityException | NoSuchFieldException | IllegalAccessException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Registering commands
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
// Wraps our general command API to Bukkit specific calls
|
||||
BukkitCommand newCommand = new BukkitCommand(command);
|
||||
|
||||
// Adding to our own list for later referencing
|
||||
registeredCommands.add(newCommand);
|
||||
|
||||
// This tells Bukkit to register our command for use.
|
||||
commandMap.register(plugin.getName(), newCommand);
|
||||
}
|
||||
|
||||
//TODO Finish system before implementing on startup
|
||||
/*Bukkit.getLogger().info("Getting strings...");
|
||||
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
|
||||
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), BukkitPlugin.pluginInstance)
|
||||
.get());
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();*/
|
||||
|
||||
plugin.reloadConfig();
|
||||
}
|
||||
|
||||
Bukkit.getLogger().info("Setting up and registering commands...");
|
||||
// We need access to the commandMap to register our commands without using the "proper" method
|
||||
if (Bukkit.getServer().getPluginManager() instanceof SimplePluginManager manager) {
|
||||
try {
|
||||
Field field = SimplePluginManager.class.getDeclaredField("commandMap");
|
||||
field.setAccessible(true);
|
||||
commandMap = (SimpleCommandMap) field.get(manager);
|
||||
} catch (IllegalArgumentException
|
||||
| SecurityException
|
||||
| NoSuchFieldException
|
||||
| IllegalAccessException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onDisable() {
|
||||
Bukkit.getLogger().info("Stopping plugin services...");
|
||||
AntiVPN.getInstance().stop();
|
||||
playerCommandRunner.stop();
|
||||
|
||||
Bukkit.getLogger().info("Unregistering commands...");
|
||||
try {
|
||||
Field field = SimpleCommandMap.class.getDeclaredField("knownCommands");
|
||||
field.setAccessible(true);
|
||||
|
||||
if(field.get(commandMap) instanceof Map<?, ?> knownCommands) {
|
||||
Map<String, org.bukkit.command.Command> casted = (Map<String, org.bukkit.command.Command>) knownCommands;
|
||||
casted.values().removeAll(registeredCommands);
|
||||
registeredCommands.clear();
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
|
||||
Bukkit.getLogger().info("Unregistering listeners...");
|
||||
HandlerList.unregisterAll(plugin);
|
||||
|
||||
Bukkit.getLogger().info("Cancelling any running tasks...");
|
||||
Bukkit.getScheduler().cancelTasks(plugin);
|
||||
}
|
||||
|
||||
// Registering commands
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
// Wraps our general command API to Bukkit specific calls
|
||||
BukkitCommand newCommand = new BukkitCommand(command);
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
|
||||
// Adding to our own list for later referencing
|
||||
registeredCommands.add(newCommand);
|
||||
|
||||
// This tells Bukkit to register our command for use.
|
||||
commandMap.register(plugin.getName(), newCommand);
|
||||
if(database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Finish system before implementing on startup
|
||||
/*Bukkit.getLogger().info("Getting strings...");
|
||||
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
|
||||
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), BukkitPlugin.pluginInstance)
|
||||
.get());
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();*/
|
||||
|
||||
plugin.reloadConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onDisable() {
|
||||
Bukkit.getLogger().info("Stopping plugin services...");
|
||||
AntiVPN.getInstance().stop();
|
||||
playerCommandRunner.stop();
|
||||
|
||||
Bukkit.getLogger().info("Unregistering commands...");
|
||||
try {
|
||||
Field field = SimpleCommandMap.class.getDeclaredField("knownCommands");
|
||||
field.setAccessible(true);
|
||||
|
||||
if (field.get(commandMap) instanceof Map<?, ?> knownCommands) {
|
||||
Map<String, org.bukkit.command.Command> casted =
|
||||
(Map<String, org.bukkit.command.Command>) knownCommands;
|
||||
casted.values().removeAll(registeredCommands);
|
||||
registeredCommands.clear();
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
|
||||
Bukkit.getLogger().info("Unregistering listeners...");
|
||||
HandlerList.unregisterAll(plugin);
|
||||
|
||||
Bukkit.getLogger().info("Cancelling any running tasks...");
|
||||
Bukkit.getScheduler().cancelTasks(plugin);
|
||||
}
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
|
||||
if (database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if (database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if (database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,64 +17,61 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class PlayerCommandRunner {
|
||||
private final ScheduledExecutorService executorService;
|
||||
private final Queue<PlayerAction> playerActions = new ArrayBlockingQueue<>(10000);
|
||||
private final ScheduledExecutorService executorService;
|
||||
private final Queue<PlayerAction> playerActions = new ArrayBlockingQueue<>(10000);
|
||||
|
||||
public PlayerCommandRunner() {
|
||||
executorService =
|
||||
Executors.newSingleThreadScheduledExecutor(
|
||||
MiscUtils.createThreadFactory("AntiVPN:PlayerCommandRunner"));
|
||||
}
|
||||
public PlayerCommandRunner() {
|
||||
executorService = Executors.newSingleThreadScheduledExecutor(
|
||||
MiscUtils.createThreadFactory("AntiVPN:PlayerCommandRunner")
|
||||
);
|
||||
}
|
||||
|
||||
void start() {
|
||||
executorService.scheduleAtFixedRate(
|
||||
() -> {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
while (!playerActions.isEmpty()) {
|
||||
PlayerAction action = playerActions.peek();
|
||||
void start() {
|
||||
executorService.scheduleAtFixedRate(() -> {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
while(!playerActions.isEmpty()) {
|
||||
PlayerAction action = playerActions.peek();
|
||||
|
||||
if (action == null) continue;
|
||||
if(action == null) continue;
|
||||
|
||||
if (currentTime - action.start > 2000L || Bukkit.getPlayer(action.getUuid()) != null) {
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
action.getAction().run();
|
||||
if(currentTime - action.start > 2000L || Bukkit.getPlayer(action.getUuid()) != null) {
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
action.getAction().run();
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||
|
||||
playerActions.poll();
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||
|
||||
playerActions.poll();
|
||||
}
|
||||
}
|
||||
},
|
||||
1000,
|
||||
100,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}, 1000, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
executorService.shutdown();
|
||||
playerActions.clear();
|
||||
}
|
||||
void stop() {
|
||||
executorService.shutdown();
|
||||
playerActions.clear();
|
||||
}
|
||||
|
||||
void addAction(UUID uuid, Runnable action) {
|
||||
playerActions.add(new PlayerAction(uuid, System.currentTimeMillis(), action));
|
||||
}
|
||||
void addAction(UUID uuid, Runnable action) {
|
||||
playerActions.add(new PlayerAction(uuid, System.currentTimeMillis(), action));
|
||||
}
|
||||
|
||||
@Data
|
||||
static class PlayerAction {
|
||||
private final UUID uuid;
|
||||
private final long start;
|
||||
private final Runnable action;
|
||||
}
|
||||
@Data
|
||||
static class PlayerAction {
|
||||
private final UUID uuid;
|
||||
private final long start;
|
||||
private final Runnable action;
|
||||
}
|
||||
}
|
||||
|
||||
+56
-71
@@ -19,90 +19,75 @@ package dev.brighten.antivpn.bukkit.command;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.bukkit.BukkitCommandExecutor;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import lombok.val;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class BukkitCommand extends org.bukkit.command.Command {
|
||||
|
||||
private final Command command;
|
||||
private final Command command;
|
||||
public BukkitCommand(Command command) {
|
||||
super(command.name(), command.description(), command.usage(), Arrays.asList(command.aliases()));
|
||||
|
||||
public BukkitCommand(Command command) {
|
||||
super(command.name(), command.description(), command.usage(), Arrays.asList(command.aliases()));
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
this.command = command;
|
||||
}
|
||||
@Override
|
||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args)
|
||||
throws IllegalArgumentException {
|
||||
val children = command.children();
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args)
|
||||
throws IllegalArgumentException {
|
||||
val children = command.children();
|
||||
|
||||
if (children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if (child.name().equalsIgnoreCase(args[0])
|
||||
|| Arrays.stream(child.aliases())
|
||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||
return child.tabComplete(
|
||||
new BukkitCommandExecutor(sender),
|
||||
alias,
|
||||
IntStream.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1])
|
||||
.toArray(String[]::new));
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||
return child.tabComplete(new BukkitCommandExecutor(sender), alias, IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return command.tabComplete(new BukkitCommandExecutor(sender), alias, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String s, String[] args) {
|
||||
if (!sender.hasPermission("antivpn.command.*") && !sender.hasPermission(command.permission())) {
|
||||
sender.sendMessage(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
|
||||
return true;
|
||||
return command.tabComplete(new BukkitCommandExecutor(sender), alias, args);
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if (children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if (child.name().equalsIgnoreCase(args[0])
|
||||
|| Arrays.stream(child.aliases()).anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||
if (!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
sender.sendMessage(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&',
|
||||
AntiVPN.getInstance()
|
||||
.getMessageHandler()
|
||||
.getString("no-permission")
|
||||
.getMessage()));
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String s, String[] args) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(command.permission())) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&',
|
||||
child.execute(
|
||||
new BukkitCommandExecutor(sender),
|
||||
IntStream.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1])
|
||||
.toArray(String[]::new))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
child.execute(new BukkitCommandExecutor(sender), IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
command.execute(new BukkitCommandExecutor(sender), args)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&', command.execute(new BukkitCommandExecutor(sender), args)));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,203 +1,111 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import be.seeseemelk.mockbukkit.MockBukkit;
|
||||
import be.seeseemelk.mockbukkit.ServerMock;
|
||||
import be.seeseemelk.mockbukkit.entity.PlayerMock;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.StandardTest;
|
||||
import dev.brighten.antivpn.api.*;
|
||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
import dev.brighten.antivpn.api.VPNConfig;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.message.MessageHandler;
|
||||
import dev.brighten.antivpn.message.VpnString;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class BukkitListenerTest extends StandardTest {
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
private ServerMock server;
|
||||
private BukkitListener listener;
|
||||
private VPNExecutor vpnExecutor;
|
||||
public class BukkitListenerTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
server = MockBukkit.mock(new RecordingServerMock());
|
||||
JavaPlugin plugin =
|
||||
MockBukkit.loadWith(
|
||||
TestPlugin.class,
|
||||
new PluginDescriptionFile("AntiVPNTest", "1.0.0", TestPlugin.class.getName()));
|
||||
BukkitPlugin.pluginInstance = new BukkitPlugin(plugin);
|
||||
private ServerMock server;
|
||||
private BukkitListener listener;
|
||||
private VPNExecutor vpnExecutor;
|
||||
|
||||
AntiVPN antiVPN = mock(AntiVPN.class);
|
||||
VPNConfig config = mock(VPNConfig.class);
|
||||
PlayerExecutor playerExecutor = mock(PlayerExecutor.class);
|
||||
vpnExecutor = mock(VPNExecutor.class);
|
||||
MessageHandler messageHandler = mock(MessageHandler.class);
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
server = MockBukkit.mock();
|
||||
|
||||
when(antiVPN.getVpnConfig()).thenReturn(config);
|
||||
when(antiVPN.getPlayerExecutor()).thenReturn(playerExecutor);
|
||||
when(antiVPN.getExecutor()).thenReturn(vpnExecutor);
|
||||
when(antiVPN.getMessageHandler()).thenReturn(messageHandler);
|
||||
|
||||
when(playerExecutor.getPlayer(any(UUID.class))).thenReturn(Optional.empty());
|
||||
when(config.getPrefixWhitelists()).thenReturn(java.util.Collections.emptyList());
|
||||
when(config.getCountryList()).thenReturn(java.util.Collections.emptyList());
|
||||
when(config.isKickPlayers()).thenReturn(true);
|
||||
when(config.getKickMessage()).thenReturn("Blocked!");
|
||||
|
||||
VpnString mockVpnString = mock(VpnString.class);
|
||||
when(mockVpnString.getFormattedMessage(any())).thenReturn("Blocked!");
|
||||
when(messageHandler.getString(anyString())).thenReturn(mockVpnString);
|
||||
|
||||
when(vpnExecutor.checkIp(anyString()))
|
||||
.thenReturn(
|
||||
CompletableFuture.completedFuture(
|
||||
VPNResponse.builder()
|
||||
.success(true)
|
||||
.proxy(false)
|
||||
.ip("127.0.0.1")
|
||||
.method("N/A")
|
||||
.countryName("N/A")
|
||||
.city("N/A")
|
||||
.build()));
|
||||
|
||||
// Use reflection to set the private static INSTANCE field
|
||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
instanceField.set(null, antiVPN);
|
||||
|
||||
listener = new BukkitListener();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() throws Exception {
|
||||
// Reset the singleton
|
||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
instanceField.set(null, null);
|
||||
BukkitPlugin.pluginInstance = null;
|
||||
|
||||
MockBukkit.unmock();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginEventAllowed() throws Exception {
|
||||
PlayerMock player = server.addPlayer("TestPlayer");
|
||||
InetAddress address = InetAddress.getByName("127.0.0.1");
|
||||
|
||||
mockCache(
|
||||
"127.0.0.1",
|
||||
new CheckResult(
|
||||
VPNResponse.builder()
|
||||
.success(true)
|
||||
.proxy(false)
|
||||
.ip("127.0.0.1")
|
||||
.method("N/A")
|
||||
.countryName("N/A")
|
||||
.countryCode("N/A")
|
||||
.city("N/A")
|
||||
.build(),
|
||||
ResultType.ALLOWED,
|
||||
true));
|
||||
|
||||
PlayerLoginEvent event = new PlayerLoginEvent(player, "localhost", address);
|
||||
|
||||
listener.onLogin(event);
|
||||
|
||||
assertEquals(PlayerLoginEvent.Result.ALLOWED, event.getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginPipelineProxyPlayerIsKickedWithoutErrors() throws Exception {
|
||||
PlayerMock player = server.addPlayer("PipelineProxyPlayer");
|
||||
InetAddress address = InetAddress.getByName("1.1.1.1");
|
||||
|
||||
mockCache();
|
||||
PlayerLoginEvent event = new PlayerLoginEvent(player, "localhost", address);
|
||||
|
||||
assertDoesNotThrow(() -> listener.onLogin(event));
|
||||
|
||||
assertEquals(PlayerLoginEvent.Result.KICK_BANNED, event.getResult());
|
||||
assertEquals(
|
||||
"Blocked!",
|
||||
net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection()
|
||||
.serialize(event.kickMessage()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunCommandDispatchesOnPrimaryThreadWhenCalledAsynchronously() {
|
||||
RecordingServerMock recordingServer = (RecordingServerMock) server;
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
try {
|
||||
CompletableFuture<Void> asyncCall =
|
||||
CompletableFuture.runAsync(() -> listener.runCommand("antivpn-test &aok"), executor);
|
||||
|
||||
assertDoesNotThrow(() -> asyncCall.get(5, TimeUnit.SECONDS));
|
||||
assertFalse(
|
||||
recordingServer.commandDispatched(),
|
||||
"Command should be scheduled, not dispatched asynchronously");
|
||||
|
||||
server.getScheduler().performOneTick();
|
||||
|
||||
assertTrue(
|
||||
recordingServer.commandDispatched(),
|
||||
"Scheduled command should be dispatched on the next server tick");
|
||||
assertTrue(
|
||||
recordingServer.dispatchedOnPrimaryThread(),
|
||||
"Command dispatch must happen on Bukkit's primary thread");
|
||||
assertEquals("antivpn-test §aok", recordingServer.dispatchedCommand());
|
||||
} finally {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestPlugin extends JavaPlugin {}
|
||||
|
||||
private static class RecordingServerMock extends ServerMock {
|
||||
private final AtomicBoolean commandDispatched = new AtomicBoolean();
|
||||
private final AtomicBoolean dispatchedOnPrimaryThread = new AtomicBoolean();
|
||||
private final AtomicReference<String> dispatchedCommand = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public boolean dispatchCommand(@NotNull CommandSender sender, @NotNull String commandLine) {
|
||||
commandDispatched.set(true);
|
||||
dispatchedOnPrimaryThread.set(isPrimaryThread());
|
||||
dispatchedCommand.set(commandLine);
|
||||
return super.dispatchCommand(sender, commandLine);
|
||||
AntiVPN antiVPN = mock(AntiVPN.class);
|
||||
VPNConfig config = mock(VPNConfig.class);
|
||||
PlayerExecutor playerExecutor = mock(PlayerExecutor.class);
|
||||
vpnExecutor = mock(VPNExecutor.class);
|
||||
MessageHandler messageHandler = mock(MessageHandler.class);
|
||||
|
||||
when(antiVPN.getVpnConfig()).thenReturn(config);
|
||||
when(antiVPN.getPlayerExecutor()).thenReturn(playerExecutor);
|
||||
when(antiVPN.getExecutor()).thenReturn(vpnExecutor);
|
||||
when(antiVPN.getMessageHandler()).thenReturn(messageHandler);
|
||||
|
||||
when(playerExecutor.getPlayer(any(UUID.class))).thenReturn(Optional.empty());
|
||||
when(config.getPrefixWhitelists()).thenReturn(java.util.Collections.emptyList());
|
||||
when(config.getCountryList()).thenReturn(java.util.Collections.emptyList());
|
||||
when(config.isKickPlayers()).thenReturn(true);
|
||||
when(config.getKickMessage()).thenReturn("Blocked!");
|
||||
|
||||
VpnString mockVpnString = mock(VpnString.class);
|
||||
when(mockVpnString.getFormattedMessage(any())).thenReturn("Blocked!");
|
||||
when(messageHandler.getString(anyString())).thenReturn(mockVpnString);
|
||||
|
||||
when(vpnExecutor.checkIp(anyString())).thenReturn(CompletableFuture.completedFuture(
|
||||
VPNResponse.builder().success(true).proxy(false).ip("127.0.0.1")
|
||||
.method("N/A").countryName("N/A").city("N/A").build()
|
||||
));
|
||||
|
||||
// Use reflection to set the private static INSTANCE field
|
||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
instanceField.set(null, antiVPN);
|
||||
|
||||
listener = new BukkitListener();
|
||||
}
|
||||
|
||||
private boolean commandDispatched() {
|
||||
return commandDispatched.get();
|
||||
@AfterEach
|
||||
public void tearDown() throws Exception {
|
||||
// Reset the singleton
|
||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
instanceField.set(null, null);
|
||||
|
||||
MockBukkit.unmock();
|
||||
}
|
||||
|
||||
private boolean dispatchedOnPrimaryThread() {
|
||||
return dispatchedOnPrimaryThread.get();
|
||||
@Test
|
||||
public void testLoginEventAllowed() throws Exception {
|
||||
PlayerMock player = server.addPlayer("TestPlayer");
|
||||
InetAddress address = InetAddress.getByName("127.0.0.1");
|
||||
|
||||
PlayerLoginEvent event = new PlayerLoginEvent(player, "localhost", address);
|
||||
|
||||
listener.onLogin(event);
|
||||
|
||||
assertEquals(PlayerLoginEvent.Result.ALLOWED, event.getResult());
|
||||
}
|
||||
|
||||
private String dispatchedCommand() {
|
||||
return dispatchedCommand.get();
|
||||
@Test
|
||||
public void testLoginEventBlocked() throws Exception {
|
||||
PlayerMock player = server.addPlayer("ProxyPlayer");
|
||||
InetAddress address = InetAddress.getByName("1.1.1.1");
|
||||
|
||||
// Mock proxy response
|
||||
when(vpnExecutor.checkIp("1.1.1.1")).thenReturn(CompletableFuture.completedFuture(
|
||||
VPNResponse.builder().success(true).proxy(true).ip("1.1.1.1")
|
||||
.method("N/A").countryName("N/A").countryCode("N/A").city("N/A").build()
|
||||
));
|
||||
|
||||
PlayerLoginEvent event = new PlayerLoginEvent(player, "localhost", address);
|
||||
|
||||
listener.onLogin(event);
|
||||
|
||||
assertEquals(PlayerLoginEvent.Result.KICK_BANNED, event.getResult());
|
||||
assertEquals("Blocked!", net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(event.kickMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import be.seeseemelk.mockbukkit.MockBukkit;
|
||||
import be.seeseemelk.mockbukkit.ServerMock;
|
||||
import be.seeseemelk.mockbukkit.entity.PlayerMock;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class BukkitPlayerTest {
|
||||
|
||||
private ServerMock server;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
server = MockBukkit.mock();
|
||||
|
||||
BukkitPlugin pluginBootstrap = mock(BukkitPlugin.class);
|
||||
JavaPlugin plugin = MockBukkit.createMockPlugin();
|
||||
when(pluginBootstrap.getPlugin()).thenReturn(plugin);
|
||||
BukkitPlugin.pluginInstance = pluginBootstrap;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
BukkitPlugin.pluginInstance = null;
|
||||
MockBukkit.unmock();
|
||||
}
|
||||
|
||||
@Test
|
||||
void kickPlayerCalledFromAsyncContext_isScheduledAndExecutedOnMainThread() {
|
||||
PlayerMock player = server.addPlayer("AsyncKickPlayer");
|
||||
BukkitPlayer bukkitPlayer = new BukkitPlayer(player);
|
||||
|
||||
assertTrue(player.isOnline());
|
||||
|
||||
CompletableFuture<Void> asyncKick =
|
||||
CompletableFuture.runAsync(() -> bukkitPlayer.kickPlayer("&cBlocked!"));
|
||||
assertDoesNotThrow(() -> asyncKick.get(1, TimeUnit.SECONDS));
|
||||
|
||||
assertTrue(player.isOnline(), "Kick should be deferred to the server scheduler");
|
||||
|
||||
server.getScheduler().performTicks(1);
|
||||
|
||||
assertFalse(player.isOnline(), "Player should be kicked when scheduled task runs");
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,6 @@ dependencies {
|
||||
implementation project(':Common:loader-utils')
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
filesMatching('bungee.yml') {
|
||||
expand(project: project, projectVersion: project.version)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveClassifier.set('')
|
||||
|
||||
@@ -29,8 +23,4 @@ tasks.named('shadowJar') {
|
||||
dependsOn(':Bungee:BungeePlugin:shadowJar')
|
||||
}
|
||||
|
||||
tasks.named('compileJava') {
|
||||
dependsOn(':Bungee:BungeePlugin:shadowJar')
|
||||
}
|
||||
|
||||
tasks.build.dependsOn shadowJar
|
||||
|
||||
+22
-21
@@ -22,30 +22,31 @@ import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
public class BungeeLoaderPlugin extends Plugin {
|
||||
|
||||
private static final String JAR_NAME = "antivpn-bungee.jarinjar";
|
||||
private static final String SOURCE_NAME = "antivpn-source.jarinjar";
|
||||
private static final String BOOTSTRAP_CLASS = "dev.brighten.antivpn.bungee.BungeePlugin";
|
||||
private static final String JAR_NAME = "antivpn-bungee.jarinjar";
|
||||
private static final String SOURCE_NAME = "antivpn-source.jarinjar";
|
||||
private static final String BOOTSTRAP_CLASS = "dev.brighten.antivpn.bungee.BungeePlugin";
|
||||
|
||||
private final LoaderBootstrap plugin;
|
||||
private final LoaderBootstrap plugin;
|
||||
|
||||
public BungeeLoaderPlugin() {
|
||||
JarInJarClassLoader loader =
|
||||
new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
||||
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, Plugin.class, this);
|
||||
}
|
||||
public BungeeLoaderPlugin() {
|
||||
JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
||||
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, Plugin.class, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.plugin.onLoad(getDataFolder());
|
||||
}
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.plugin.onLoad(getDataFolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.plugin.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.plugin.onDisable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.plugin.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.plugin.onDisable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ name: KauriVPN
|
||||
main: dev.brighten.antivpn.bungee.BungeeLoaderPlugin
|
||||
description: A simple and fast antivpn plugin.
|
||||
version: ${project.version}
|
||||
author: funkemunky
|
||||
author: funkemunky
|
||||
@@ -13,7 +13,6 @@ dependencies {
|
||||
testImplementation 'org.mockito:mockito-subclass:5.11.0'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
|
||||
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
||||
testImplementation testFixtures(project(':Common:Source'))
|
||||
}
|
||||
tasks.compileJava.dependsOn(':Common:Source:jar')
|
||||
|
||||
@@ -25,6 +24,7 @@ test {
|
||||
shadowJar {
|
||||
archiveClassifier.set('')
|
||||
relocate 'org.bstats', 'dev.brighten.antivpn.bungee.org.bstats'
|
||||
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
|
||||
}
|
||||
|
||||
tasks.build.dependsOn shadowJar
|
||||
|
||||
+76
-108
@@ -20,9 +20,6 @@ import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.*;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
@@ -32,123 +29,94 @@ import net.md_5.bungee.api.scheduler.ScheduledTask;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BungeeListener extends VPNExecutor implements Listener {
|
||||
|
||||
private ScheduledTask cacheResetTask;
|
||||
private ScheduledTask cacheResetTask;
|
||||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
BungeePlugin.pluginInstance
|
||||
.getProxy()
|
||||
.getPluginManager()
|
||||
.registerListener(BungeePlugin.pluginInstance.getPlugin(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, log, objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
BungeePlugin.pluginInstance
|
||||
.getProxy()
|
||||
.getPluginManager()
|
||||
.dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
BungeePlugin.pluginInstance
|
||||
.getProxy()
|
||||
.getPluginManager()
|
||||
.unregisterListeners(BungeePlugin.pluginInstance.getPlugin());
|
||||
if (cacheResetTask != null) {
|
||||
cacheResetTask.cancel();
|
||||
cacheResetTask = null;
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager()
|
||||
.registerListener(BungeePlugin.pluginInstance.getPlugin(), this);
|
||||
}
|
||||
BungeePlugin.pluginInstance
|
||||
.getProxy()
|
||||
.getPluginManager()
|
||||
.unregisterCommands(BungeePlugin.pluginInstance.getPlugin());
|
||||
BungeePlugin.pluginInstance.onDisable();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onListener(final PreLoginEvent event) {
|
||||
APIPlayer player =
|
||||
AntiVPN.getInstance()
|
||||
.getPlayerExecutor()
|
||||
.getPlayer(event.getConnection().getUniqueId())
|
||||
.orElseGet(
|
||||
() -> {
|
||||
UUID uuid = MiscUtils.lookupUUID(event.getConnection().getName());
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
Level.INFO,
|
||||
"Getting offline player for %s with name %s",
|
||||
event.getConnection().getUniqueId(),
|
||||
uuid);
|
||||
@Override
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
return new OfflinePlayer(
|
||||
uuid,
|
||||
event.getConnection().getName(),
|
||||
((InetSocketAddress) event.getConnection().getSocketAddress()).getAddress());
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, log, objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager()
|
||||
.dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance.getPlugin());
|
||||
if (cacheResetTask != null) {
|
||||
cacheResetTask.cancel();
|
||||
cacheResetTask = null;
|
||||
}
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance.getPlugin());
|
||||
BungeePlugin.pluginInstance.onDisable();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onListener(final PreLoginEvent event) {
|
||||
|
||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getConnection().getUniqueId())
|
||||
.orElseGet(() -> {
|
||||
UUID uuid = MiscUtils.lookupUUID(event.getConnection().getName());
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "Getting offline player for %s with name %s",
|
||||
event.getConnection().getUniqueId(), uuid);
|
||||
|
||||
return new OfflinePlayer(uuid, event.getConnection().getName(),
|
||||
((InetSocketAddress) event.getConnection().getSocketAddress()).getAddress());
|
||||
});
|
||||
|
||||
CheckResult result = player.checkPlayer();
|
||||
player.checkPlayer(result -> {
|
||||
if (!result.resultType().isShouldBlock()) return;
|
||||
|
||||
if (!result.resultType().isShouldBlock()) return;
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
return;
|
||||
event.setCancelled(true);
|
||||
event.setReason(TextComponent.fromLegacy(StringUtil.varReplace(switch (result.resultType()) {
|
||||
case DENIED_PROXY -> StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickMessage(), player, result.response());
|
||||
case DENIED_COUNTRY -> StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getCountryVanillaKickReason(), player, result.response());
|
||||
default -> "You were kicked by KauriVPN for an unknown reason!";
|
||||
}, player, result.response())));
|
||||
});
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
event.setReason(
|
||||
TextComponent.fromLegacy(
|
||||
StringUtil.varReplace(
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY ->
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickMessage(),
|
||||
player,
|
||||
result.response());
|
||||
case DENIED_COUNTRY ->
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getCountryVanillaKickReason(),
|
||||
player,
|
||||
result.response());
|
||||
default -> "You were kicked by KauriVPN for an unknown reason!";
|
||||
},
|
||||
player,
|
||||
result.response())));
|
||||
}
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onJoin(LoginEvent event) {
|
||||
if(event.isCancelled()) return;
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onJoin(LoginEvent event) {
|
||||
if (event.isCancelled()) return;
|
||||
// Handling player alerts on join
|
||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getConnection().getUniqueId())
|
||||
.ifPresent(APIPlayer::checkAlertsState);
|
||||
}
|
||||
|
||||
// Handling player alerts on join
|
||||
AntiVPN.getInstance()
|
||||
.getPlayerExecutor()
|
||||
.getPlayer(event.getConnection().getUniqueId())
|
||||
.ifPresent(APIPlayer::checkAlertsState);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onLeave(PlayerDisconnectEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
@EventHandler
|
||||
public void onLeave(PlayerDisconnectEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,28 +23,28 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public class BungeePlayer extends APIPlayer {
|
||||
|
||||
private final ProxiedPlayer player;
|
||||
private final ProxiedPlayer player;
|
||||
public BungeePlayer(ProxiedPlayer player) {
|
||||
super(player.getUniqueId(), player.getName(), player.getAddress().getAddress());
|
||||
|
||||
public BungeePlayer(ProxiedPlayer player) {
|
||||
super(player.getUniqueId(), player.getName(), player.getAddress().getAddress());
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
player.sendMessage(
|
||||
TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', message)));
|
||||
}
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
player.sendMessage(TextComponent.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&', message)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
player.disconnect(
|
||||
TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', reason)));
|
||||
}
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
player.disconnect(TextComponent.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&', reason)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
}
|
||||
|
||||
+25
-25
@@ -18,42 +18,42 @@ package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public class BungeePlayerExecutor implements PlayerExecutor {
|
||||
|
||||
private final Map<UUID, BungeePlayer> cachedPlayers = new HashMap<>();
|
||||
private final Map<UUID, BungeePlayer> cachedPlayers = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
ProxiedPlayer player = BungeePlugin.pluginInstance.getProxy().getPlayer(name);
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
ProxiedPlayer player = BungeePlugin.pluginInstance.getProxy().getPlayer(name);
|
||||
|
||||
if (player == null) return Optional.empty();
|
||||
if(player == null) return Optional.empty();
|
||||
|
||||
return Optional.of(
|
||||
cachedPlayers.computeIfAbsent(player.getUniqueId(), key -> new BungeePlayer(player)));
|
||||
}
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), key -> new BungeePlayer(player)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(UUID uuid) {
|
||||
ProxiedPlayer player = BungeePlugin.pluginInstance.getProxy().getPlayer(uuid);
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(UUID uuid) {
|
||||
ProxiedPlayer player = BungeePlugin.pluginInstance.getProxy().getPlayer(uuid);
|
||||
|
||||
if (player == null) return Optional.empty();
|
||||
if(player == null) return Optional.empty();
|
||||
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(uuid, key -> new BungeePlayer(player)));
|
||||
}
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(uuid, key -> new BungeePlayer(player)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadPlayer(UUID uuid) {
|
||||
this.cachedPlayers.remove(uuid);
|
||||
}
|
||||
@Override
|
||||
public void unloadPlayer(UUID uuid) {
|
||||
this.cachedPlayers.remove(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
return BungeePlugin.pluginInstance.getProxy().getPlayers().stream()
|
||||
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new BungeePlayer(pl)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
return BungeePlugin.pluginInstance.getProxy().getPlayers().stream()
|
||||
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new BungeePlayer(pl)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,82 +24,79 @@ import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BungeePlugin implements LoaderBootstrap {
|
||||
|
||||
public static BungeePlugin pluginInstance;
|
||||
public static BungeePlugin pluginInstance;
|
||||
|
||||
@Getter
|
||||
private File dataFolder;
|
||||
|
||||
@Getter private File dataFolder;
|
||||
@Getter
|
||||
private final Plugin plugin;
|
||||
|
||||
@Getter private final Plugin plugin;
|
||||
|
||||
public BungeePlugin(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
|
||||
// Setting up config
|
||||
ProxyServer.getInstance().getLogger().info("Loading config...");
|
||||
|
||||
// Loading plugin
|
||||
ProxyServer.getInstance().getLogger().info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
|
||||
|
||||
if (AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
ProxyServer.getInstance().getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(getPlugin(), 12616);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
ProxyServer.getInstance()
|
||||
.getScheduler()
|
||||
.schedule(
|
||||
getPlugin(),
|
||||
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
|
||||
10,
|
||||
10,
|
||||
TimeUnit.MINUTES);
|
||||
public BungeePlugin(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
ProxyServer.getInstance()
|
||||
.getPluginManager()
|
||||
.registerCommand(getPlugin(), new BungeeCommand(command));
|
||||
@Override
|
||||
public void onLoad(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
AntiVPN.getInstance().stop();
|
||||
}
|
||||
@Override
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
if (database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if (database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if (database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
//Setting up config
|
||||
ProxyServer.getInstance().getLogger().info("Loading config...");
|
||||
|
||||
|
||||
//Loading plugin
|
||||
ProxyServer.getInstance().getLogger().info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
ProxyServer.getInstance().getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(getPlugin(), 12616);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
ProxyServer.getInstance().getScheduler().schedule(getPlugin(),
|
||||
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
|
||||
10, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
ProxyServer.getInstance().getPluginManager().registerCommand(getPlugin(), new BungeeCommand(command));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ProxyServer getProxy() {
|
||||
return ProxyServer.getInstance();
|
||||
}
|
||||
@Override
|
||||
public void onDisable() {
|
||||
AntiVPN.getInstance().stop();
|
||||
}
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
if(database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
}
|
||||
}
|
||||
|
||||
public ProxyServer getProxy() {
|
||||
return ProxyServer.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
+56
-73
@@ -17,8 +17,6 @@
|
||||
package dev.brighten.antivpn.bungee.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.IntStream;
|
||||
import lombok.val;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
@@ -26,87 +24,72 @@ import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class BungeeCommand extends Command implements TabExecutor {
|
||||
|
||||
private final dev.brighten.antivpn.command.Command command;
|
||||
private final dev.brighten.antivpn.command.Command command;
|
||||
public BungeeCommand(dev.brighten.antivpn.command.Command command) {
|
||||
super(command.name(), command.permission(), command.aliases());
|
||||
|
||||
public BungeeCommand(dev.brighten.antivpn.command.Command command) {
|
||||
super(command.name(), command.permission(), command.aliases());
|
||||
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (!sender.hasPermission("antivpn.command.*") && !sender.hasPermission(command.permission())) {
|
||||
sender.sendMessage(
|
||||
TextComponent.fromLegacyText(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&',
|
||||
AntiVPN.getInstance()
|
||||
.getMessageHandler()
|
||||
.getString("no-permission")
|
||||
.getMessage())));
|
||||
return;
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if (children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if (child.name().equalsIgnoreCase(args[0])
|
||||
|| Arrays.stream(child.aliases()).anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||
if (!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
sender.sendMessage(
|
||||
TextComponent.fromLegacyText(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&',
|
||||
AntiVPN.getInstance()
|
||||
.getMessageHandler()
|
||||
.getString("no-permission")
|
||||
.getMessage())));
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(command.permission())) {
|
||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(
|
||||
TextComponent.fromLegacyText(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&',
|
||||
child.execute(
|
||||
new BungeeCommandExecutor(sender),
|
||||
IntStream.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1])
|
||||
.toArray(String[]::new)))));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(TextComponent
|
||||
.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
child.execute(new BungeeCommandExecutor(sender), IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new)))));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sender.sendMessage(TextComponent
|
||||
.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
command.execute(new BungeeCommandExecutor(sender), args))));
|
||||
}
|
||||
|
||||
sender.sendMessage(
|
||||
TextComponent.fromLegacyText(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&', command.execute(new BungeeCommandExecutor(sender), args))));
|
||||
}
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
val children = command.children();
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
val children = command.children();
|
||||
|
||||
if (children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if (child.name().equalsIgnoreCase(args[0])
|
||||
|| Arrays.stream(child.aliases())
|
||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||
return child.tabComplete(
|
||||
new BungeeCommandExecutor(sender),
|
||||
"alias",
|
||||
IntStream.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1])
|
||||
.toArray(String[]::new));
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||
return child.tabComplete(new BungeeCommandExecutor(sender), "alias", IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return command.tabComplete(new BungeeCommandExecutor(sender), "alias", args);
|
||||
}
|
||||
return command.tabComplete(new BungeeCommandExecutor(sender), "alias", args);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-23
@@ -19,41 +19,39 @@ package dev.brighten.antivpn.bungee.command;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeCommandExecutor implements CommandExecutor {
|
||||
|
||||
private final CommandSender sender;
|
||||
private final CommandSender sender;
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message, Object... objects) {
|
||||
sender.sendMessage(
|
||||
TextComponent.fromLegacyText(
|
||||
ChatColor.translateAlternateColorCodes('&', String.format(message, objects))));
|
||||
}
|
||||
@Override
|
||||
public void sendMessage(String message, Object... objects) {
|
||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&', String.format(message, objects))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer() {
|
||||
if (!isPlayer()) return Optional.empty();
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer() {
|
||||
if(!isPlayer()) return Optional.empty();
|
||||
|
||||
return AntiVPN.getInstance()
|
||||
.getPlayerExecutor()
|
||||
.getPlayer(((ProxiedPlayer) sender).getUniqueId());
|
||||
}
|
||||
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((ProxiedPlayer) sender).getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof ProxiedPlayer;
|
||||
}
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof ProxiedPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
+76
-80
@@ -1,114 +1,110 @@
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.StandardTest;
|
||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
import dev.brighten.antivpn.api.VPNConfig;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.message.MessageHandler;
|
||||
import dev.brighten.antivpn.message.VpnString;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class BungeeListenerTest extends StandardTest {
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
private BungeeListener listener;
|
||||
private VPNExecutor vpnExecutor;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
AntiVPN antiVPN = mock(AntiVPN.class);
|
||||
VPNConfig config = mock(VPNConfig.class);
|
||||
PlayerExecutor playerExecutor = mock(PlayerExecutor.class);
|
||||
vpnExecutor = mock(VPNExecutor.class);
|
||||
MessageHandler messageHandler = mock(MessageHandler.class);
|
||||
public class BungeeListenerTest {
|
||||
|
||||
when(antiVPN.getVpnConfig()).thenReturn(config);
|
||||
when(antiVPN.getPlayerExecutor()).thenReturn(playerExecutor);
|
||||
when(antiVPN.getExecutor()).thenReturn(vpnExecutor);
|
||||
when(antiVPN.getMessageHandler()).thenReturn(messageHandler);
|
||||
private BungeeListener listener;
|
||||
private VPNExecutor vpnExecutor;
|
||||
|
||||
when(playerExecutor.getPlayer(any(UUID.class))).thenReturn(Optional.empty());
|
||||
when(config.getPrefixWhitelists()).thenReturn(java.util.Collections.emptyList());
|
||||
when(config.getCountryList()).thenReturn(java.util.Collections.emptyList());
|
||||
when(config.isKickPlayers()).thenReturn(true);
|
||||
when(config.getKickMessage()).thenReturn("Blocked!");
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
AntiVPN antiVPN = mock(AntiVPN.class);
|
||||
VPNConfig config = mock(VPNConfig.class);
|
||||
PlayerExecutor playerExecutor = mock(PlayerExecutor.class);
|
||||
vpnExecutor = mock(VPNExecutor.class);
|
||||
MessageHandler messageHandler = mock(MessageHandler.class);
|
||||
|
||||
VpnString mockVpnString = mock(VpnString.class);
|
||||
when(mockVpnString.getFormattedMessage(any())).thenReturn("Blocked!");
|
||||
when(messageHandler.getString(anyString())).thenReturn(mockVpnString);
|
||||
when(antiVPN.getVpnConfig()).thenReturn(config);
|
||||
when(antiVPN.getPlayerExecutor()).thenReturn(playerExecutor);
|
||||
when(antiVPN.getExecutor()).thenReturn(vpnExecutor);
|
||||
when(antiVPN.getMessageHandler()).thenReturn(messageHandler);
|
||||
|
||||
when(vpnExecutor.checkIp(anyString()))
|
||||
.thenReturn(
|
||||
CompletableFuture.completedFuture(
|
||||
VPNResponse.builder()
|
||||
.success(true)
|
||||
.proxy(false)
|
||||
.ip("127.0.0.1")
|
||||
.method("N/A")
|
||||
.countryName("N/A")
|
||||
.city("N/A")
|
||||
.build()));
|
||||
when(playerExecutor.getPlayer(any(UUID.class))).thenReturn(Optional.empty());
|
||||
when(config.getPrefixWhitelists()).thenReturn(java.util.Collections.emptyList());
|
||||
when(config.getCountryList()).thenReturn(java.util.Collections.emptyList());
|
||||
when(config.isKickPlayers()).thenReturn(true);
|
||||
when(config.getKickMessage()).thenReturn("Blocked!");
|
||||
|
||||
// Use reflection to set the private static INSTANCE field
|
||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
instanceField.set(null, antiVPN);
|
||||
VpnString mockVpnString = mock(VpnString.class);
|
||||
when(mockVpnString.getFormattedMessage(any())).thenReturn("Blocked!");
|
||||
when(messageHandler.getString(anyString())).thenReturn(mockVpnString);
|
||||
|
||||
listener = new BungeeListener();
|
||||
}
|
||||
when(vpnExecutor.checkIp(anyString())).thenReturn(CompletableFuture.completedFuture(
|
||||
VPNResponse.builder().success(true).proxy(false).ip("127.0.0.1")
|
||||
.method("N/A").countryName("N/A").city("N/A").build()
|
||||
));
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() throws Exception {
|
||||
// Reset the singleton
|
||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
instanceField.set(null, null);
|
||||
}
|
||||
// Use reflection to set the private static INSTANCE field
|
||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
instanceField.set(null, antiVPN);
|
||||
|
||||
@Test
|
||||
public void testPreLoginEventAllowed() {
|
||||
PreLoginEvent event = mock(PreLoginEvent.class);
|
||||
PendingConnection connection = mock(PendingConnection.class);
|
||||
listener = new BungeeListener();
|
||||
}
|
||||
|
||||
when(event.getConnection()).thenReturn(connection);
|
||||
when(connection.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
when(connection.getName()).thenReturn("TestPlayer");
|
||||
when(connection.getSocketAddress()).thenReturn(new InetSocketAddress("127.0.0.1", 12345));
|
||||
@AfterEach
|
||||
public void tearDown() throws Exception {
|
||||
// Reset the singleton
|
||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||
instanceField.setAccessible(true);
|
||||
instanceField.set(null, null);
|
||||
}
|
||||
|
||||
listener.onListener(event);
|
||||
@Test
|
||||
public void testPreLoginEventAllowed() {
|
||||
PreLoginEvent event = mock(PreLoginEvent.class);
|
||||
PendingConnection connection = mock(PendingConnection.class);
|
||||
|
||||
verify(event, never()).setCancelled(true);
|
||||
}
|
||||
when(event.getConnection()).thenReturn(connection);
|
||||
when(connection.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
when(connection.getName()).thenReturn("TestPlayer");
|
||||
when(connection.getSocketAddress()).thenReturn(new InetSocketAddress("127.0.0.1", 12345));
|
||||
|
||||
@Test
|
||||
public void testPreLoginEventBlocked() throws NoSuchFieldException, IllegalAccessException {
|
||||
PreLoginEvent event = mock(PreLoginEvent.class);
|
||||
PendingConnection connection = mock(PendingConnection.class);
|
||||
listener.onListener(event);
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
when(event.getConnection()).thenReturn(connection);
|
||||
when(connection.getUniqueId()).thenReturn(uuid);
|
||||
when(connection.getName()).thenReturn("ProxyPlayer");
|
||||
when(connection.getSocketAddress()).thenReturn(new InetSocketAddress("1.1.1.1", 12345));
|
||||
verify(event, never()).setCancelled(true);
|
||||
}
|
||||
|
||||
// Mock proxy response
|
||||
mockCache();
|
||||
@Test
|
||||
public void testPreLoginEventBlocked() {
|
||||
PreLoginEvent event = mock(PreLoginEvent.class);
|
||||
PendingConnection connection = mock(PendingConnection.class);
|
||||
|
||||
listener.onListener(event);
|
||||
UUID uuid = UUID.randomUUID();
|
||||
when(event.getConnection()).thenReturn(connection);
|
||||
when(connection.getUniqueId()).thenReturn(uuid);
|
||||
when(connection.getName()).thenReturn("ProxyPlayer");
|
||||
when(connection.getSocketAddress()).thenReturn(new InetSocketAddress("1.1.1.1", 12345));
|
||||
|
||||
verify(event).setCancelled(true);
|
||||
verify(event).setReason(any());
|
||||
}
|
||||
// Mock proxy response
|
||||
when(vpnExecutor.checkIp("1.1.1.1")).thenReturn(CompletableFuture.completedFuture(
|
||||
VPNResponse.builder().success(true).proxy(true).ip("1.1.1.1")
|
||||
.method("N/A").countryName("N/A").countryCode("N/A").city("N/A").build()
|
||||
));
|
||||
|
||||
listener.onListener(event);
|
||||
|
||||
verify(event).setCancelled(true);
|
||||
verify(event).setReason(any());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,28 +4,6 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.10.1.2] 2026-05-03
|
||||
|
||||
### Fixed
|
||||
- Kick spam on non-cached VPN detection events.
|
||||
|
||||
### Changed
|
||||
- Project cleanup and performance improvements.
|
||||
|
||||
## [1.10.1.1] 2026-04-29
|
||||
|
||||
### Fixed
|
||||
- Startup error on velocity instances is now corrected.
|
||||
|
||||
### Changed
|
||||
- Bumped dependency versions for H2 database.
|
||||
|
||||
## [1.10.1] - 2026-04-28
|
||||
|
||||
### Fixed
|
||||
- Async kick error that caused issues during player removal when running commands.
|
||||
- Startup error caused by a packaging issue is now corrected.
|
||||
|
||||
## [1.10.0] - 2026-04-07
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
id 'com.gradleup.shadow'
|
||||
id 'java-test-fixtures'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -8,12 +7,12 @@ dependencies {
|
||||
implementation 'org.ow2.asm:asm-commons:9.8'
|
||||
implementation 'org.yaml:snakeyaml:2.2'
|
||||
implementation 'org.jetbrains:annotations:26.0.2'
|
||||
|
||||
|
||||
compileOnly 'com.mysql:mysql-connector-j:9.3.0'
|
||||
compileOnly 'com.h2database:h2:2.4.240'
|
||||
implementation'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
||||
compileOnly 'com.h2database:h2:2.2.220'
|
||||
compileOnly 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
||||
compileOnly 'org.mongodb:mongo-java-driver:3.12.14'
|
||||
|
||||
|
||||
testImplementation 'org.mockito:mockito-core:5.11.0'
|
||||
testImplementation "org.junit.jupiter:junit-jupiter:5.8.1"
|
||||
testImplementation "org.testcontainers:testcontainers:2.0.4"
|
||||
@@ -22,14 +21,25 @@ dependencies {
|
||||
testImplementation 'org.testcontainers:mongodb:1.20.4'
|
||||
testRuntimeOnly 'org.slf4j:slf4j-simple:2.0.16'
|
||||
testImplementation 'com.mysql:mysql-connector-j:9.3.0'
|
||||
testImplementation 'com.h2database:h2:2.4.240'
|
||||
testImplementation 'com.h2database:h2:2.2.220'
|
||||
testImplementation 'org.mongodb:mongo-java-driver:3.12.14'
|
||||
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
||||
testFixturesImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveClassifier.set('')
|
||||
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
|
||||
relocate 'com.github.benmanes.caffeine', 'dev.brighten.antivpn.shaded.com.github.benmanes.caffeine'
|
||||
relocate 'org.h2', 'dev.brighten.antivpn.shaded.org.h2'
|
||||
relocate 'org.bson', 'dev.brighten.antivpn.shaded.org.bson'
|
||||
relocate 'com.mongodb', 'dev.brighten.antivpn.shaded.com.mongodb'
|
||||
relocate 'com.mysql.cj', 'dev.brighten.antivpn.shaded.com.mysql.cj'
|
||||
relocate 'com.mysql.jdbc', 'dev.brighten.antivpn.shaded.com.mysql.jdbc'
|
||||
|
||||
dependencies {
|
||||
exclude 'dev/brighten/antivpn/depends/Relocate*'
|
||||
exclude 'dev/brighten/antivpn/depends/MavenLibraries*'
|
||||
}
|
||||
}
|
||||
|
||||
tasks.build.dependsOn shadowJar
|
||||
|
||||
@@ -27,12 +27,17 @@ import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.depends.LibraryLoader;
|
||||
import dev.brighten.antivpn.depends.MavenLibrary;
|
||||
import dev.brighten.antivpn.depends.Relocate;
|
||||
import dev.brighten.antivpn.message.MessageHandler;
|
||||
import dev.brighten.antivpn.utils.ConfigDefault;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import dev.brighten.antivpn.utils.config.Configuration;
|
||||
import dev.brighten.antivpn.utils.config.ConfigurationProvider;
|
||||
import dev.brighten.antivpn.utils.config.YamlConfiguration;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -40,253 +45,235 @@ import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@MavenLibrary(groupId = "com.h2database", artifactId = "h2", version = "2.4.240")
|
||||
@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14")
|
||||
@MavenLibrary(groupId = "com.mysql", artifactId = "mysql-connector-j", version = "9.3.0")
|
||||
@MavenLibrary(groupId = "com.h2database", artifactId ="h2", version = "2.2.220", relocations = {
|
||||
@Relocate(from ="org" + ".\\h2", to ="dev.brighten.antivpn.shaded.org.h2")})
|
||||
@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14", relocations = {
|
||||
@Relocate(from = "com." + "\\mongodb", to = "dev.brighten.antivpn.shaded.com.mongodb"),
|
||||
@Relocate(from = "org" + "\\.bson", to = "dev.brighten.antivpn.shaded.org.bson")
|
||||
})
|
||||
@MavenLibrary(
|
||||
groupId = "com.mysql",
|
||||
artifactId = "mysql-connector-j",
|
||||
version = "9.3.0",
|
||||
relocations = {
|
||||
@Relocate(from = "com.my\\" + "sql.cj", to = "dev.brighten.antivpn.shaded.com.mysql.cj"),
|
||||
@Relocate(from = "com.my\\" + "sql.jdbc", to = "dev.brighten.antivpn.shaded.com.mysql.jdbc"),
|
||||
}
|
||||
)
|
||||
@MavenLibrary(groupId = "com.\\github\\.ben-manes\\.caffeine", artifactId = "caffeine", version = "3.1.8",
|
||||
relocations = {
|
||||
@Relocate(from = "com\\.github\\.benmanes\\.caffeine", to = "dev.brighten.antivpn.shaded.com.github.benmanes.caffeine"),
|
||||
})
|
||||
public class AntiVPN {
|
||||
|
||||
private static AntiVPN INSTANCE;
|
||||
private VPNConfig vpnConfig;
|
||||
private VPNExecutor executor;
|
||||
private PlayerExecutor playerExecutor;
|
||||
private VPNDatabase database;
|
||||
private MessageHandler messageHandler;
|
||||
private Configuration config;
|
||||
private List<Command> commands = new ArrayList<>();
|
||||
public int detections, checked;
|
||||
private File pluginFolder;
|
||||
private static AntiVPN INSTANCE;
|
||||
private VPNConfig vpnConfig;
|
||||
private VPNExecutor executor;
|
||||
private PlayerExecutor playerExecutor;
|
||||
private VPNDatabase database;
|
||||
private MessageHandler messageHandler;
|
||||
private Configuration config;
|
||||
private List<Command> commands = new ArrayList<>();
|
||||
public int detections, checked;
|
||||
private File pluginFolder;
|
||||
|
||||
public static void start(VPNExecutor executor, PlayerExecutor playerExecutor, File pluginFolder) {
|
||||
// Initializing
|
||||
public static void start(VPNExecutor executor, PlayerExecutor playerExecutor, File pluginFolder) {
|
||||
//Initializing
|
||||
|
||||
INSTANCE = new AntiVPN();
|
||||
INSTANCE = new AntiVPN();
|
||||
|
||||
INSTANCE.pluginFolder = pluginFolder;
|
||||
INSTANCE.executor = executor;
|
||||
INSTANCE.playerExecutor = playerExecutor;
|
||||
INSTANCE.pluginFolder = pluginFolder;
|
||||
INSTANCE.executor = executor;
|
||||
INSTANCE.playerExecutor = playerExecutor;
|
||||
|
||||
LibraryLoader.loadAll(INSTANCE);
|
||||
LibraryLoader.loadAll(INSTANCE);
|
||||
|
||||
try {
|
||||
File configFile = new File(pluginFolder, "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
if (configFile.getParentFile().mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created plugin folder!");
|
||||
try {
|
||||
File configFile = new File(pluginFolder, "config.yml");
|
||||
if(!configFile.exists()){
|
||||
if(configFile.getParentFile().mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created plugin folder!");
|
||||
}
|
||||
MiscUtils.copy(INSTANCE.getResource( "config.yml"), configFile);
|
||||
}
|
||||
INSTANCE.config = ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||
.load(configFile);
|
||||
} catch (IOException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not load config.yml, plugin disabling...", e);
|
||||
executor.disablePlugin();
|
||||
return;
|
||||
}
|
||||
MiscUtils.copy(INSTANCE.getResource("config.yml"), configFile);
|
||||
}
|
||||
INSTANCE.config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
|
||||
} catch (IOException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not load config.yml, plugin disabling...", e);
|
||||
executor.disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
INSTANCE.vpnConfig = new VPNConfig();
|
||||
INSTANCE.vpnConfig = new VPNConfig();
|
||||
|
||||
INSTANCE.executor.registerListeners();
|
||||
INSTANCE.vpnConfig.update();
|
||||
INSTANCE.executor.registerListeners();
|
||||
INSTANCE.vpnConfig.update();
|
||||
|
||||
INSTANCE.messageHandler = new MessageHandler();
|
||||
INSTANCE.messageHandler = new MessageHandler();
|
||||
|
||||
try {
|
||||
switch (INSTANCE.vpnConfig.getDatabaseType().toLowerCase()) {
|
||||
case "h2":
|
||||
case "local":
|
||||
case "flatfile":
|
||||
{
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
||||
INSTANCE.database = new H2VPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mysql":
|
||||
case "sql":
|
||||
{
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||
INSTANCE.database = new MySqlVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mongo":
|
||||
case "mongodb":
|
||||
case "mongod":
|
||||
{
|
||||
INSTANCE.database = new MongoVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
"Could not find database type \""
|
||||
+ INSTANCE.vpnConfig.getDatabaseType()
|
||||
+ "\". "
|
||||
+ "Options: [MySQL]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not initialize database, plugin disabling...", e);
|
||||
executor.disablePlugin();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
switch(INSTANCE.vpnConfig.getDatabaseType().toLowerCase()) {
|
||||
case "h2":
|
||||
case "local":
|
||||
case "flatfile": {
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
||||
INSTANCE.database = new H2VPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mysql":
|
||||
case "sql": {
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||
INSTANCE.database = new MySqlVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mongo":
|
||||
case "mongodb":
|
||||
case "mongod": {
|
||||
INSTANCE.database = new MongoVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " +
|
||||
"Options: [MySQL]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not initialize database, plugin disabling...", e);
|
||||
executor.disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
// Registering commands
|
||||
INSTANCE.registerCommands();
|
||||
//Registering commands
|
||||
INSTANCE.registerCommands();
|
||||
|
||||
// Turning on alerts of players who are already online.
|
||||
playerExecutor
|
||||
.getOnlinePlayers()
|
||||
.forEach(
|
||||
player -> {
|
||||
// We want to make sure they even have permission to see alerts before we make a bunch
|
||||
// of unnecessary database queries.
|
||||
if (player.hasPermission("antivpn.command.alerts")) {
|
||||
// Running database check for enabled alerts.
|
||||
//Turning on alerts of players who are already online.
|
||||
playerExecutor.getOnlinePlayers().forEach(player -> {
|
||||
//We want to make sure they even have permission to see alerts before we make a bunch
|
||||
//of unnecessary database queries.
|
||||
if(player.hasPermission("antivpn.command.alerts")) {
|
||||
//Running database check for enabled alerts.
|
||||
INSTANCE.database.alertsState(player.getUuid(), player::setAlertsEnabled);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getMessageHandler()
|
||||
.initStrings(
|
||||
vpnString ->
|
||||
new ConfigDefault<>(
|
||||
vpnString.getDefaultMessage(),
|
||||
"messages." + vpnString.getKey(),
|
||||
AntiVPN.getInstance())
|
||||
.get());
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();
|
||||
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
|
||||
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), AntiVPN.getInstance())
|
||||
.get());
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();
|
||||
|
||||
// Starting kick checks
|
||||
AntiVPN.getInstance().getExecutor().startKickChecks();
|
||||
}
|
||||
// Starting kick checks
|
||||
AntiVPN.getInstance().getExecutor().startKickChecks();
|
||||
}
|
||||
|
||||
public InputStream getResource(String filename) {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("Filename cannot be null");
|
||||
} else {
|
||||
try {
|
||||
URL url = executor.getClass().getClassLoader().getResource(filename);
|
||||
if (url == null) {
|
||||
return null;
|
||||
public InputStream getResource(String filename) {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("Filename cannot be null");
|
||||
} else {
|
||||
URLConnection connection = url.openConnection();
|
||||
connection.setUseCaches(false);
|
||||
return connection.getInputStream();
|
||||
}
|
||||
} catch (IOException var4) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (database instanceof H2VPN) {
|
||||
database.shutdown();
|
||||
|
||||
// Try to deregister driver
|
||||
try {
|
||||
java.sql.Driver driver = java.sql.DriverManager.getDriver("jdbc:h2:");
|
||||
if (driver != null) {
|
||||
java.sql.DriverManager.deregisterDriver(driver);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Log but don't throw
|
||||
executor.log("Failed to deregister H2 driver: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (executor != null && executor.getThreadExecutor() != null) {
|
||||
executor.getThreadExecutor().shutdown();
|
||||
}
|
||||
if (database != null) database.shutdown();
|
||||
|
||||
INSTANCE = null;
|
||||
}
|
||||
|
||||
public void reloadDatabase() {
|
||||
database.shutdown();
|
||||
|
||||
switch (AntiVPN.getInstance().getVpnConfig().getDatabaseType().toLowerCase()) {
|
||||
case "h2":
|
||||
case "local":
|
||||
case "flatfile":
|
||||
{
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
||||
INSTANCE.database = new H2VPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mysql":
|
||||
case "sql":
|
||||
{
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||
INSTANCE.database = new MySqlVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mongo":
|
||||
case "mongodb":
|
||||
case "mongod":
|
||||
{
|
||||
INSTANCE.database = new MongoVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
"Could not find database type \""
|
||||
+ INSTANCE.vpnConfig.getDatabaseType()
|
||||
+ "\". "
|
||||
+ "Options: [MySQL]");
|
||||
break;
|
||||
try {
|
||||
URL url = executor.getClass().getClassLoader().getResource(filename);
|
||||
if (url == null) {
|
||||
return null;
|
||||
} else {
|
||||
URLConnection connection = url.openConnection();
|
||||
connection.setUseCaches(false);
|
||||
return connection.getInputStream();
|
||||
}
|
||||
} catch (IOException var4) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AntiVPN getInstance() {
|
||||
assert INSTANCE != null : "AntiVPN has not been initialized!";
|
||||
public void stop() {
|
||||
if (database instanceof H2VPN) {
|
||||
database.shutdown();
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
// Try to deregister driver
|
||||
try {
|
||||
java.sql.Driver driver = java.sql.DriverManager.getDriver("jdbc:h2:");
|
||||
if (driver != null) {
|
||||
java.sql.DriverManager.deregisterDriver(driver);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Log but don't throw
|
||||
executor.log("Failed to deregister H2 driver: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (executor != null && executor.getThreadExecutor() != null) {
|
||||
executor.getThreadExecutor().shutdown();
|
||||
}
|
||||
if(database != null) database.shutdown();
|
||||
|
||||
public void saveConfig() {
|
||||
try {
|
||||
ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||
.save(getConfig(), new File(pluginFolder.getPath() + File.separator + "config.yml"));
|
||||
} catch (IOException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
INSTANCE = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void reloadConfig() {
|
||||
try {
|
||||
public void reloadDatabase() {
|
||||
database.shutdown();
|
||||
|
||||
config =
|
||||
ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||
.load(new File(pluginFolder.getPath() + File.separator + "config.yml"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
switch(AntiVPN.getInstance().getVpnConfig().getDatabaseType().toLowerCase()) {
|
||||
case "h2":
|
||||
case "local":
|
||||
case "flatfile": {
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
||||
INSTANCE.database = new H2VPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mysql":
|
||||
case "sql":{
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||
INSTANCE.database = new MySqlVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mongo":
|
||||
case "mongodb":
|
||||
case "mongod": {
|
||||
INSTANCE.database = new MongoVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " +
|
||||
"Options: [MySQL]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
commands.add(new AntiVPNCommand());
|
||||
}
|
||||
public static AntiVPN getInstance() {
|
||||
assert INSTANCE != null: "AntiVPN has not been initialized!";
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public void saveConfig() {
|
||||
try {
|
||||
ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||
.save(getConfig(), new File(pluginFolder.getPath() + File.separator + "config.yml"));
|
||||
} catch (IOException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void reloadConfig() {
|
||||
try {
|
||||
|
||||
config = ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||
.load(new File(pluginFolder.getPath() + File.separator + "config.yml"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
commands.add(new AntiVPNCommand());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,152 +20,126 @@ import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.message.VpnString;
|
||||
import java.net.InetAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@Getter
|
||||
public abstract class APIPlayer {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
private final InetAddress ip;
|
||||
@Setter private boolean alertsEnabled;
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
private final InetAddress ip;
|
||||
@Setter
|
||||
private boolean alertsEnabled;
|
||||
|
||||
public static final Cache<String, CheckResult> checkResultCache =
|
||||
Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).maximumSize(2000).build();
|
||||
private static final Cache<String, CheckResult> checkResultCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.maximumSize(2000)
|
||||
.build();
|
||||
|
||||
public APIPlayer(UUID uuid, String name, InetAddress ip) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public abstract void sendMessage(String message);
|
||||
|
||||
public abstract void kickPlayer(String reason);
|
||||
|
||||
public abstract boolean hasPermission(String permission);
|
||||
|
||||
public void updateAlertsState() {
|
||||
// Updating into database so its synced across servers and saved on logout.
|
||||
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
|
||||
|
||||
sendMessage(
|
||||
AntiVPN.getInstance()
|
||||
.getMessageHandler()
|
||||
.getString("command-alerts-toggled")
|
||||
.getFormattedMessage(new VpnString.Var<>("state", alertsEnabled)));
|
||||
}
|
||||
|
||||
public void checkAlertsState() {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.execute(
|
||||
() ->
|
||||
AntiVPN.getInstance()
|
||||
.getDatabase()
|
||||
.alertsState(
|
||||
uuid,
|
||||
state -> {
|
||||
if (state) {
|
||||
alertsEnabled = true;
|
||||
updateAlertsState();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/***
|
||||
* The result is only returned if it is cached. Otherwise, there is an asynchronous call to the API
|
||||
* and will handle kicking the player if necessary.
|
||||
* @return CheckResult - The cached result of the check if it exists.
|
||||
*/
|
||||
public CheckResult checkPlayer() {
|
||||
if (hasPermission("antivpn.bypass") // Has bypass permission
|
||||
// Is exempt
|
||||
|| (uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
|
||||
// Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress() + "/32")
|
||||
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
|
||||
.anyMatch(name::startsWith)) {
|
||||
return new CheckResult(null, ResultType.WHITELISTED, false);
|
||||
public APIPlayer(UUID uuid, String name, InetAddress ip) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress());
|
||||
public abstract void sendMessage(String message);
|
||||
|
||||
if (cachedResult != null) {
|
||||
if (cachedResult.response().getIp().equals(ip.getHostAddress())) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
Level.FINE,
|
||||
"Cached result for " + ip.getHostAddress() + " is " + cachedResult.resultType());
|
||||
if (cachedResult.resultType().isShouldBlock()) {
|
||||
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(cachedResult, this);
|
||||
public abstract void kickPlayer(String reason);
|
||||
|
||||
public abstract boolean hasPermission(String permission);
|
||||
|
||||
public void updateAlertsState() {
|
||||
//Updating into database so its synced across servers and saved on logout.
|
||||
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
|
||||
|
||||
sendMessage(AntiVPN.getInstance().getMessageHandler()
|
||||
.getString("command-alerts-toggled")
|
||||
.getFormattedMessage(new VpnString.Var<>("state", alertsEnabled)));
|
||||
}
|
||||
|
||||
public void checkAlertsState() {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() ->
|
||||
AntiVPN.getInstance().getDatabase().alertsState(uuid, state -> {
|
||||
if(state) {
|
||||
alertsEnabled = true;
|
||||
updateAlertsState();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public void checkPlayer(Consumer<CheckResult> onResult) {
|
||||
if (hasPermission("antivpn.bypass") //Has bypass permission
|
||||
//Is exempt
|
||||
|| (uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
|
||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress() + "/32")
|
||||
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
|
||||
.anyMatch(name::startsWith)) {
|
||||
onResult.accept(new CheckResult(null, ResultType.WHITELISTED, false));
|
||||
return;
|
||||
}
|
||||
return cachedResult;
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.checkIp(ip.getHostAddress())
|
||||
.thenAccept(
|
||||
result -> {
|
||||
if (!result.isSuccess()) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
Level.WARNING,
|
||||
"The API query was not a success! "
|
||||
+ "You may need to upgrade your license on "
|
||||
+ "https://funkemunky.cc/shop");
|
||||
CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress());
|
||||
|
||||
if(cachedResult != null) {
|
||||
if(cachedResult.response().getIp().equals(ip.getHostAddress())) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Cached result for " + ip.getHostAddress() + " is " + cachedResult.resultType());
|
||||
if(cachedResult.resultType().isShouldBlock()) {
|
||||
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(cachedResult, this);
|
||||
}
|
||||
onResult.accept(cachedResult);
|
||||
return;
|
||||
}
|
||||
// If the countryList() size is zero, no need to check.
|
||||
// Running country check first
|
||||
CheckResult checkResult;
|
||||
if (!AntiVPN.getInstance().getVpnConfig().getCountryList().isEmpty()
|
||||
&& !((uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
|
||||
// Or has a name that starts with a certain prefix. This is for Bedrock
|
||||
// exempting.
|
||||
|| AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.isWhitelisted(ip.getHostAddress() + "/32"))
|
||||
// This bit of code will decide whether or not to kick the player
|
||||
// If it contains the code and it is set to whitelist, it will not kick
|
||||
// as they are equal and vise versa. However, if the contains does not match
|
||||
// the state, it will kick.
|
||||
&& AntiVPN.getInstance()
|
||||
.getVpnConfig()
|
||||
.getCountryList()
|
||||
.contains(result.getCountryCode())
|
||||
!= AntiVPN.getInstance().getVpnConfig().getWhitelistCountries()) {
|
||||
// Using our built in kicking system if no commands are configured
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_COUNTRY, false);
|
||||
} else if (result.isProxy()) {
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_PROXY, false);
|
||||
} else {
|
||||
checkResult = new CheckResult(result, ResultType.ALLOWED, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
Level.FINE,
|
||||
"Result for " + ip.getHostAddress() + " is " + checkResult.resultType());
|
||||
AntiVPN.getInstance().getExecutor().checkIp(ip.getHostAddress())
|
||||
.thenAccept(result -> {
|
||||
if(!result.isSuccess()) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "The API query was not a success! " +
|
||||
"You may need to upgrade your license on " +
|
||||
"https://funkemunky.cc/shop");
|
||||
onResult.accept(new CheckResult(null, ResultType.API_FAILURE, false));
|
||||
return;
|
||||
}
|
||||
// If the countryList() size is zero, no need to check.
|
||||
// Running country check first
|
||||
CheckResult checkResult;
|
||||
if (!AntiVPN.getInstance().getVpnConfig().getCountryList().isEmpty()
|
||||
&& !((uuid != null && AntiVPN.getInstance().getExecutor()
|
||||
.isWhitelisted(uuid))
|
||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress() + "/32"))
|
||||
// This bit of code will decide whether or not to kick the player
|
||||
// If it contains the code and it is set to whitelist, it will not kick
|
||||
// as they are equal and vise versa. However, if the contains does not match
|
||||
// the state, it will kick.
|
||||
&& AntiVPN.getInstance().getVpnConfig().getCountryList()
|
||||
.contains(result.getCountryCode())
|
||||
!= AntiVPN.getInstance().getVpnConfig().getWhitelistCountries()) {
|
||||
//Using our built in kicking system if no commands are configured
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_COUNTRY, false);
|
||||
} else if (result.isProxy()) {
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_PROXY, false);
|
||||
} else {
|
||||
checkResult = new CheckResult(result, ResultType.ALLOWED, false);
|
||||
}
|
||||
|
||||
checkResultCache.put(
|
||||
ip.getHostAddress(),
|
||||
new CheckResult(checkResult.response(), checkResult.resultType(), true));
|
||||
if (checkResult.resultType().isShouldBlock()) {
|
||||
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(checkResult, this);
|
||||
}
|
||||
AntiVPN.getInstance().checked++;
|
||||
});
|
||||
return new CheckResult(null, ResultType.UNKNOWN, false);
|
||||
}
|
||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Result for " + ip.getHostAddress() + " is " + checkResult.resultType());
|
||||
|
||||
checkResultCache.put(ip.getHostAddress(), new CheckResult(checkResult.response(), checkResult.resultType(), true));
|
||||
if(checkResult.resultType().isShouldBlock()) {
|
||||
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(checkResult, this);
|
||||
}
|
||||
onResult.accept(checkResult);
|
||||
AntiVPN.getInstance().checked++;
|
||||
});
|
||||
onResult.accept(new CheckResult(null, ResultType.UNKNOWN, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,5 @@ package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
public record CheckResult(VPNResponse response, ResultType resultType, boolean isFromCache) {}
|
||||
public record CheckResult(VPNResponse response, ResultType resultType, boolean isFromCache) {
|
||||
}
|
||||
|
||||
@@ -21,18 +21,22 @@ import java.util.UUID;
|
||||
|
||||
public class OfflinePlayer extends APIPlayer {
|
||||
|
||||
public OfflinePlayer(UUID uuid, String name, InetAddress ip) {
|
||||
super(uuid, name, ip);
|
||||
}
|
||||
public OfflinePlayer(UUID uuid, String name, InetAddress ip) {
|
||||
super(uuid, name, ip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {}
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String reason) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ import java.util.UUID;
|
||||
|
||||
public interface PlayerExecutor {
|
||||
|
||||
Optional<APIPlayer> getPlayer(String name);
|
||||
Optional<APIPlayer> getPlayer(String name);
|
||||
|
||||
Optional<APIPlayer> getPlayer(UUID uuid);
|
||||
Optional<APIPlayer> getPlayer(UUID uuid);
|
||||
|
||||
void unloadPlayer(UUID uuid);
|
||||
void unloadPlayer(UUID uuid);
|
||||
|
||||
List<APIPlayer> getOnlinePlayers();
|
||||
List<APIPlayer> getOnlinePlayers();
|
||||
}
|
||||
|
||||
@@ -19,16 +19,17 @@ package dev.brighten.antivpn.api;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum ResultType {
|
||||
ALLOWED(false),
|
||||
WHITELISTED(false),
|
||||
DENIED_COUNTRY(true),
|
||||
DENIED_PROXY(true),
|
||||
API_FAILURE(false),
|
||||
UNKNOWN(false);
|
||||
ALLOWED(false),
|
||||
WHITELISTED(false),
|
||||
DENIED_COUNTRY(true),
|
||||
DENIED_PROXY(true),
|
||||
API_FAILURE(false),
|
||||
UNKNOWN(false);
|
||||
|
||||
@Getter private final boolean shouldBlock;
|
||||
@Getter
|
||||
private final boolean shouldBlock;
|
||||
|
||||
ResultType(boolean shouldBlock) {
|
||||
this.shouldBlock = shouldBlock;
|
||||
}
|
||||
ResultType(boolean shouldBlock) {
|
||||
this.shouldBlock = shouldBlock;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,203 +18,208 @@ package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.ConfigDefault;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
public class VPNConfig {
|
||||
private final ConfigDefault<String>
|
||||
licenseDefault = new ConfigDefault<>("", "license", AntiVPN.getInstance()),
|
||||
kickStringDefault =
|
||||
new ConfigDefault<>(
|
||||
"Proxies are not allowed on our server", "kickMessage", AntiVPN.getInstance()),
|
||||
defaultDatabaseType = new ConfigDefault<>("H2", "database.type", AntiVPN.getInstance()),
|
||||
defaultDatabaseName =
|
||||
new ConfigDefault<>("kaurivpn", "database.database", AntiVPN.getInstance()),
|
||||
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", AntiVPN.getInstance()),
|
||||
defaultUsername = new ConfigDefault<>("root", "database.username", AntiVPN.getInstance()),
|
||||
defaultPassword = new ConfigDefault<>("password", "database.password", AntiVPN.getInstance()),
|
||||
defaultCountryKickReason =
|
||||
new ConfigDefault<>(
|
||||
"&cSorry, but our server does not allow connections from\n&f%country%",
|
||||
"countries.vanillaKickReason", AntiVPN.getInstance()),
|
||||
defaultIp = new ConfigDefault<>("localhost", "database.ip", AntiVPN.getInstance()),
|
||||
defaultAlertMsg =
|
||||
new ConfigDefault<>(
|
||||
"&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy"
|
||||
+ " &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)",
|
||||
"alerts.message", AntiVPN.getInstance());
|
||||
private final ConfigDefault<Boolean>
|
||||
cacheResultsDefault = new ConfigDefault<>(true, "cachedResults", AntiVPN.getInstance()),
|
||||
defaultUseCredentials =
|
||||
new ConfigDefault<>(true, "database.useCredentials", AntiVPN.getInstance()),
|
||||
defaultDatabaseEnabled =
|
||||
new ConfigDefault<>(false, "database.enabled", AntiVPN.getInstance()),
|
||||
defaultCommandsEnable = new ConfigDefault<>(false, "commands.enabled", AntiVPN.getInstance()),
|
||||
defaultKickPlayers = new ConfigDefault<>(true, "kickPlayers", AntiVPN.getInstance()),
|
||||
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled", AntiVPN.getInstance()),
|
||||
defaultWhitelistCountries =
|
||||
new ConfigDefault<>(true, "countries.whitelist", AntiVPN.getInstance()),
|
||||
defaultMetrics = new ConfigDefault<>(true, "bstats", AntiVPN.getInstance());
|
||||
private final ConfigDefault<Integer> defaultPort =
|
||||
new ConfigDefault<>(-1, "database.port", AntiVPN.getInstance());
|
||||
private final ConfigDefault<List<String>>
|
||||
prefixWhitelistsDefault =
|
||||
new ConfigDefault<>(new ArrayList<>(), "prefixWhitelists", AntiVPN.getInstance()),
|
||||
defaultCommands =
|
||||
new ConfigDefault<>(
|
||||
Collections.singletonList("kick %player% VPNs are not allowed on our server!"),
|
||||
"commands.execute",
|
||||
AntiVPN.getInstance()),
|
||||
defCountryKickCommands =
|
||||
new ConfigDefault<>(Collections.emptyList(), "countries.commands", AntiVPN.getInstance()),
|
||||
defCountrylist =
|
||||
new ConfigDefault<>(new ArrayList<>(), "countries.list", AntiVPN.getInstance());
|
||||
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
|
||||
"license", AntiVPN.getInstance()), kickStringDefault =
|
||||
new ConfigDefault<>("Proxies are not allowed on our server",
|
||||
"kickMessage", AntiVPN.getInstance()),
|
||||
defaultDatabaseType = new ConfigDefault<>("H2",
|
||||
"database.type", AntiVPN.getInstance()),
|
||||
defaultDatabaseName = new ConfigDefault<>("kaurivpn",
|
||||
"database.database", AntiVPN.getInstance()),
|
||||
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", AntiVPN.getInstance()),
|
||||
defaultUsername = new ConfigDefault<>("root",
|
||||
"database.username", AntiVPN.getInstance()),
|
||||
defaultPassword = new ConfigDefault<>("password",
|
||||
"database.password", AntiVPN.getInstance()),
|
||||
defaultCountryKickReason = new ConfigDefault<>(
|
||||
"&cSorry, but our server does not allow connections from\n&f%country%",
|
||||
"countries.vanillaKickReason", AntiVPN.getInstance()),
|
||||
defaultIp = new ConfigDefault<>("localhost", "database.ip", AntiVPN.getInstance()),
|
||||
defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" +
|
||||
" &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message",
|
||||
AntiVPN.getInstance());
|
||||
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
|
||||
"cachedResults", AntiVPN.getInstance()),
|
||||
defaultUseCredentials = new ConfigDefault<>(true,
|
||||
"database.useCredentials", AntiVPN.getInstance()),
|
||||
defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled",
|
||||
AntiVPN.getInstance()), defaultCommandsEnable = new ConfigDefault<>(false,
|
||||
"commands.enabled", AntiVPN.getInstance()), defaultKickPlayers
|
||||
= new ConfigDefault<>(true, "kickPlayers", AntiVPN.getInstance()),
|
||||
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled",
|
||||
AntiVPN.getInstance()),
|
||||
defaultWhitelistCountries = new ConfigDefault<>(true, "countries.whitelist",
|
||||
AntiVPN.getInstance()),
|
||||
defaultMetrics = new ConfigDefault<>(true, "bstats", AntiVPN.getInstance());
|
||||
private final ConfigDefault<Integer>
|
||||
defaultPort = new ConfigDefault<>(-1, "database.port", AntiVPN.getInstance());
|
||||
private final ConfigDefault<List<String>> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(),
|
||||
"prefixWhitelists", AntiVPN.getInstance()), defaultCommands = new ConfigDefault<>(
|
||||
Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute",
|
||||
AntiVPN.getInstance()),
|
||||
defCountryKickCommands = new ConfigDefault<>(Collections.emptyList(),
|
||||
"countries.commands", AntiVPN.getInstance()),
|
||||
defCountrylist = new ConfigDefault<>(new ArrayList<>(), "countries.list",
|
||||
AntiVPN.getInstance());
|
||||
|
||||
@Getter private String license;
|
||||
@Getter private String kickMessage;
|
||||
@Getter private String databaseType;
|
||||
@Getter private String databaseName;
|
||||
private String mongoURL;
|
||||
@Getter private String username;
|
||||
@Getter private String password;
|
||||
@Getter private String ip;
|
||||
@Getter private String alertMsg;
|
||||
@Getter private String countryVanillaKickReason;
|
||||
@Getter private List<String> prefixWhitelists;
|
||||
private List<String> commands;
|
||||
@Getter private List<String> countryList;
|
||||
private List<String> countryKickCommands;
|
||||
private int port;
|
||||
private boolean cacheResults;
|
||||
@Getter private boolean databaseEnabled;
|
||||
private boolean useCredentials;
|
||||
@Getter private boolean commandsEnabled;
|
||||
@Getter private boolean kickPlayers;
|
||||
private boolean alertToStaff;
|
||||
private boolean metrics;
|
||||
private boolean whitelistCountries;
|
||||
@Getter
|
||||
private String license;
|
||||
@Getter
|
||||
private String kickMessage;
|
||||
@Getter
|
||||
private String databaseType;
|
||||
@Getter
|
||||
private String databaseName;
|
||||
private String mongoURL;
|
||||
@Getter
|
||||
private String username;
|
||||
@Getter
|
||||
private String password;
|
||||
@Getter
|
||||
private String ip;
|
||||
@Getter
|
||||
private String alertMsg;
|
||||
@Getter
|
||||
private String countryVanillaKickReason;
|
||||
@Getter
|
||||
private List<String> prefixWhitelists;
|
||||
private List<String> commands;
|
||||
@Getter
|
||||
private List<String> countryList;
|
||||
private List<String> countryKickCommands;
|
||||
private int port;
|
||||
private boolean cacheResults;
|
||||
@Getter
|
||||
private boolean databaseEnabled;
|
||||
private boolean useCredentials;
|
||||
@Getter
|
||||
private boolean commandsEnabled;
|
||||
@Getter
|
||||
private boolean kickPlayers;
|
||||
private boolean alertToStaff;
|
||||
private boolean metrics;
|
||||
private boolean whitelistCountries;
|
||||
|
||||
/**
|
||||
* If true, results will be cached to reduce queries to <a href="https://funkemunky.cc">...</a>
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean cachedResults() {
|
||||
return cacheResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, staff will be alerted on proxy detection.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isAlertToSTaff() {
|
||||
return alertToStaff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commands to run on proxy detection.
|
||||
*
|
||||
* @return List
|
||||
*/
|
||||
public List<String> commands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the database we want to connect to requires credentials.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean useDatabaseCreds() {
|
||||
return useCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for Mongo only. URL used for connecting to database. Overrides other fields
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String mongoDatabaseURL() {
|
||||
return mongoURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist
|
||||
* them.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean getWhitelistCountries() {
|
||||
return whitelistCountries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns our configured commands to run on player country detection.
|
||||
*
|
||||
* @return List
|
||||
*/
|
||||
public List<String> countryKickCommands() {
|
||||
return countryKickCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port
|
||||
* based on {@link VPNConfig#getDatabaseType()} lowerCase().
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getPort() {
|
||||
if (port == -1) {
|
||||
switch (getDatabaseType().toLowerCase()) {
|
||||
case "mongodb":
|
||||
case "mongo":
|
||||
case "mongod":
|
||||
return 27017;
|
||||
case "sql":
|
||||
case "mysql":
|
||||
return 3306;
|
||||
}
|
||||
/**
|
||||
* If true, results will be cached to reduce queries to <a href="https://funkemunky.cc">...</a>
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean cachedResults() {
|
||||
return cacheResults;
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
/**
|
||||
* If true, staff will be alerted on proxy detection.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isAlertToSTaff() {
|
||||
return alertToStaff;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, <a href="https://bstats.org">...</a> metrics will be collected to improve KauriVPN.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean metrics() {
|
||||
return metrics;
|
||||
}
|
||||
/**
|
||||
* Commands to run on proxy detection.
|
||||
* @return List
|
||||
*/
|
||||
public List<String> commands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
/** Grabs all information from the config.yml */
|
||||
public void update() {
|
||||
license = licenseDefault.get();
|
||||
kickMessage = kickStringDefault.get();
|
||||
cacheResults = cacheResultsDefault.get();
|
||||
prefixWhitelists = prefixWhitelistsDefault.get();
|
||||
databaseEnabled = defaultDatabaseEnabled.get();
|
||||
useCredentials = defaultUseCredentials.get();
|
||||
databaseType = defaultDatabaseType.get();
|
||||
databaseName = defaultDatabaseName.get();
|
||||
mongoURL = defaultMongoURL.get();
|
||||
username = defaultUsername.get();
|
||||
password = defaultPassword.get();
|
||||
ip = defaultIp.get();
|
||||
port = defaultPort.get();
|
||||
commandsEnabled = defaultCommandsEnable.get();
|
||||
commands = defaultCommands.get();
|
||||
kickPlayers = defaultKickPlayers.get();
|
||||
alertToStaff = defaultAlertToStaff.get();
|
||||
alertMsg = defaultAlertMsg.get();
|
||||
metrics = defaultMetrics.get();
|
||||
countryList = defCountrylist.get();
|
||||
whitelistCountries = defaultWhitelistCountries.get();
|
||||
countryKickCommands = defCountryKickCommands.get();
|
||||
countryVanillaKickReason = defaultCountryKickReason.get();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Whether or not the database we want to connect to requires credentials.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean useDatabaseCreds() {
|
||||
return useCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for Mongo only. URL used for connecting to database. Overrides other fields
|
||||
* @return String
|
||||
*/
|
||||
public String mongoDatabaseURL() {
|
||||
return mongoURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist them.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean getWhitelistCountries() {
|
||||
return whitelistCountries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns our configured commands to run on player country detection.
|
||||
* @return List
|
||||
*/
|
||||
public List<String> countryKickCommands() {
|
||||
return countryKickCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port
|
||||
* based on {@link VPNConfig#getDatabaseType()} lowerCase().
|
||||
* @return int
|
||||
*/
|
||||
public int getPort() {
|
||||
if(port == -1) {
|
||||
switch (getDatabaseType().toLowerCase()) {
|
||||
case "mongodb":
|
||||
case "mongo":
|
||||
case "mongod":
|
||||
return 27017;
|
||||
case "sql":
|
||||
case "mysql":
|
||||
return 3306;
|
||||
}
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If true, <a href="https://bstats.org">...</a> metrics will be collected to improve KauriVPN.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean metrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs all information from the config.yml
|
||||
*/
|
||||
public void update() {
|
||||
license = licenseDefault.get();
|
||||
kickMessage = kickStringDefault.get();
|
||||
cacheResults = cacheResultsDefault.get();
|
||||
prefixWhitelists = prefixWhitelistsDefault.get();
|
||||
databaseEnabled = defaultDatabaseEnabled.get();
|
||||
useCredentials = defaultUseCredentials.get();
|
||||
databaseType = defaultDatabaseType.get();
|
||||
databaseName = defaultDatabaseName.get();
|
||||
mongoURL = defaultMongoURL.get();
|
||||
username = defaultUsername.get();
|
||||
password = defaultPassword.get();
|
||||
ip = defaultIp.get();
|
||||
port = defaultPort.get();
|
||||
commandsEnabled = defaultCommandsEnable.get();
|
||||
commands = defaultCommands.get();
|
||||
kickPlayers = defaultKickPlayers.get();
|
||||
alertToStaff = defaultAlertToStaff.get();
|
||||
alertMsg = defaultAlertMsg.get();
|
||||
metrics = defaultMetrics.get();
|
||||
countryList = defCountrylist.get();
|
||||
whitelistCountries = defaultWhitelistCountries.get();
|
||||
countryKickCommands = defCountryKickCommands.get();
|
||||
countryVanillaKickReason = defaultCountryKickReason.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,193 +25,166 @@ import dev.brighten.antivpn.utils.Tuple;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.Level;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public abstract class VPNExecutor {
|
||||
private final ScheduledExecutorService threadExecutor = Executors.newScheduledThreadPool(2);
|
||||
private final Set<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Set<CIDRUtils> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Queue<Tuple<CheckResult, UUID>> toKick = new LinkedBlockingQueue<>();
|
||||
private final Queue<APIPlayer> playersToRecheck = new LinkedBlockingQueue<>();
|
||||
private ScheduledFuture<?> kickTask = null;
|
||||
private final ScheduledExecutorService threadExecutor = Executors.newScheduledThreadPool(2);
|
||||
private final Set<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Set<CIDRUtils> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Queue<Tuple<CheckResult, UUID>> toKick = new LinkedBlockingQueue<>();
|
||||
private final Queue<APIPlayer> playersToRecheck = new LinkedBlockingQueue<>();
|
||||
private ScheduledFuture<?> kickTask = null;
|
||||
|
||||
public abstract void registerListeners();
|
||||
|
||||
public abstract void log(Level level, String log, Object... objects);
|
||||
public abstract void registerListeners();
|
||||
|
||||
public abstract void log(String log, Object... objects);
|
||||
public abstract void log(Level level, String log, Object... objects);
|
||||
|
||||
public abstract void logException(String message, Throwable ex);
|
||||
public abstract void log(String log, Object... objects);
|
||||
|
||||
public abstract void runCommand(String command);
|
||||
public abstract void logException(String message, Throwable ex);
|
||||
|
||||
public void logException(Throwable ex) {
|
||||
logException("An exception occurred: " + ex.getMessage(), ex);
|
||||
}
|
||||
public abstract void runCommand(String command);
|
||||
|
||||
public void startKickChecks() {
|
||||
kickTask =
|
||||
threadExecutor.scheduleAtFixedRate(
|
||||
() -> {
|
||||
synchronized (toKick) {
|
||||
if (toKick.isEmpty()) return;
|
||||
public void logException(Throwable ex) {
|
||||
logException("An exception occurred: " + ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public void startKickChecks() {
|
||||
kickTask = threadExecutor.scheduleAtFixedRate(() -> {
|
||||
synchronized (toKick) {
|
||||
if(toKick.isEmpty()) return;
|
||||
|
||||
Tuple<CheckResult, UUID> toCheck;
|
||||
|
||||
while ((toCheck = toKick.poll()) != null) {
|
||||
Optional<APIPlayer> player =
|
||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second());
|
||||
while((toCheck = toKick.poll()) != null) {
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second());
|
||||
|
||||
if (player.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if(player.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handleKickingOfPlayer(toCheck.first(), player.get());
|
||||
handleKickingOfPlayer(toCheck.first(), player.get());
|
||||
}
|
||||
}
|
||||
},
|
||||
8,
|
||||
2,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void handleKickingOfPlayer(CheckResult result, APIPlayer player) {
|
||||
|
||||
// Ensuring kick task is always running
|
||||
if (kickTask == null || kickTask.isDone() || kickTask.isCancelled()) {
|
||||
startKickChecks();
|
||||
}
|
||||
}, 8, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
if (AntiVPN.getInstance().getVpnConfig().isAlertToSTaff())
|
||||
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.filter(APIPlayer::isAlertsEnabled)
|
||||
.forEach(
|
||||
pl ->
|
||||
pl.sendMessage(
|
||||
StringUtil.translateAlternateColorCodes(
|
||||
'&',
|
||||
StringUtil.varReplace(
|
||||
dev.brighten.antivpn.AntiVPN.getInstance()
|
||||
.getVpnConfig()
|
||||
.getAlertMsg(),
|
||||
player,
|
||||
result.response()))));
|
||||
public void handleKickingOfPlayer(CheckResult result, APIPlayer player) {
|
||||
|
||||
if (AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY ->
|
||||
player.kickPlayer(
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickMessage(),
|
||||
player,
|
||||
result.response()));
|
||||
case DENIED_COUNTRY ->
|
||||
player.kickPlayer(
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getCountryVanillaKickReason(),
|
||||
player,
|
||||
result.response()));
|
||||
}
|
||||
} else {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isCommandsEnabled()) return;
|
||||
}
|
||||
//Ensuring kick task is always running
|
||||
if(kickTask == null || kickTask.isDone() || kickTask.isCancelled()) {
|
||||
startKickChecks();
|
||||
}
|
||||
|
||||
Runnable runCommands =
|
||||
() -> {
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
||||
runCommand(StringUtil.varReplace(command, player, result.response()));
|
||||
}
|
||||
if (AntiVPN.getInstance().getVpnConfig().isAlertToSTaff()) AntiVPN.getInstance().getPlayerExecutor()
|
||||
.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(APIPlayer::isAlertsEnabled)
|
||||
.forEach(pl ->
|
||||
pl.sendMessage(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig()
|
||||
.getAlertMsg(), player, result.response()))));
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickMessage(), player, result.response()));
|
||||
case DENIED_COUNTRY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getCountryVanillaKickReason(), player, result.response()));
|
||||
}
|
||||
case DENIED_COUNTRY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
|
||||
runCommand(StringUtil.varReplace(command, player, result.response()));
|
||||
}
|
||||
} else {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isCommandsEnabled()) return;
|
||||
}
|
||||
|
||||
Runnable runCommands = () -> {
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
||||
runCommand(StringUtil.varReplace(command, player, result.response()));
|
||||
}
|
||||
}
|
||||
case DENIED_COUNTRY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
|
||||
runCommand(StringUtil.varReplace(command, player, result.response()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Fixes the commands running too fast and causing messaging errors by any downstream plugins
|
||||
// like LiteBans
|
||||
var scheduleResult = threadExecutor.schedule(runCommands, 200, TimeUnit.MILLISECONDS);
|
||||
// Fixes the commands running too fast and causing messaging errors by any downstream plugins like LiteBans
|
||||
var scheduleResult = threadExecutor.schedule(runCommands, 1, TimeUnit.SECONDS);
|
||||
|
||||
if (scheduleResult.isCancelled()) {
|
||||
runCommands.run();
|
||||
if(scheduleResult.isCancelled()) {
|
||||
runCommands.run();
|
||||
}
|
||||
|
||||
//Ensuring players are actually kicked as they are supposed to be.
|
||||
toKick.add(new Tuple<>(result, player.getUuid()));
|
||||
}
|
||||
|
||||
var toAdd = new Tuple<>(result, player.getUuid());
|
||||
// Ensuring players are actually kicked as they are supposed to be.
|
||||
threadExecutor.schedule(
|
||||
() -> {
|
||||
toKick.add(toAdd);
|
||||
},
|
||||
500,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if (AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(uuid);
|
||||
}
|
||||
return whitelisted.contains(uuid);
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
if (AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(cidr);
|
||||
}
|
||||
try {
|
||||
return whitelistedIps.contains(new CIDRUtils(cidr));
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses =
|
||||
Caffeine.newBuilder().expireAfterWrite(20, TimeUnit.MINUTES).maximumSize(4000).build();
|
||||
|
||||
public CompletableFuture<VPNResponse> checkIp(String ip) {
|
||||
VPNResponse cached = cachedResponses.getIfPresent(ip);
|
||||
|
||||
if (cached != null) {
|
||||
return CompletableFuture.completedFuture(cached);
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(uuid);
|
||||
}
|
||||
return whitelisted.contains(uuid);
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
Optional<VPNResponse> cachedRes =
|
||||
AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(cidr);
|
||||
}
|
||||
try {
|
||||
return whitelistedIps.contains(new CIDRUtils(cidr));
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedRes.isPresent()) {
|
||||
return cachedRes.get();
|
||||
} else {
|
||||
try {
|
||||
VPNResponse response =
|
||||
FunkemunkyAPI.getVPNResponse(
|
||||
ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
if (response.isSuccess()) {
|
||||
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
||||
} else {
|
||||
log("Query to VPN API failed! Reason: " + response.getFailureReason());
|
||||
}
|
||||
public CompletableFuture<VPNResponse> checkIp(String ip) {
|
||||
VPNResponse cached = cachedResponses.getIfPresent(ip);
|
||||
|
||||
return response;
|
||||
} catch (JSONException | IOException e) {
|
||||
log("Query to VPN API failed! Reason: " + e.getMessage());
|
||||
return VPNResponse.FAILED_RESPONSE;
|
||||
if(cached != null) {
|
||||
return CompletableFuture.completedFuture(cached);
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
||||
|
||||
if(cachedRes.isPresent()) {
|
||||
return cachedRes.get();
|
||||
}
|
||||
}
|
||||
},
|
||||
threadExecutor);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
VPNResponse response = FunkemunkyAPI
|
||||
.getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
||||
|
||||
public abstract void disablePlugin();
|
||||
if (response.isSuccess()) {
|
||||
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
||||
} else {
|
||||
log("Query to VPN API failed! Reason: " + response.getFailureReason());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (JSONException | IOException e) {
|
||||
log("Query to VPN API failed! Reason: " + e.getMessage());
|
||||
return VPNResponse.FAILED_RESPONSE;
|
||||
}
|
||||
}
|
||||
}, threadExecutor);
|
||||
}
|
||||
|
||||
public abstract void disablePlugin();
|
||||
}
|
||||
|
||||
@@ -20,21 +20,21 @@ import java.util.List;
|
||||
|
||||
public abstract class Command {
|
||||
|
||||
public abstract String permission();
|
||||
public abstract String permission();
|
||||
|
||||
public abstract String name();
|
||||
public abstract String name();
|
||||
|
||||
public abstract String[] aliases();
|
||||
public abstract String[] aliases();
|
||||
|
||||
public abstract String description();
|
||||
public abstract String description();
|
||||
|
||||
public abstract String usage();
|
||||
public abstract String usage();
|
||||
|
||||
public abstract String parent();
|
||||
public abstract String parent();
|
||||
|
||||
public abstract Command[] children();
|
||||
public abstract Command[] children();
|
||||
|
||||
public abstract String execute(CommandExecutor executor, String[] args);
|
||||
|
||||
public abstract String execute(CommandExecutor executor, String[] args);
|
||||
|
||||
public abstract List<String> tabComplete(CommandExecutor executor, String alias, String[] args);
|
||||
public abstract List<String> tabComplete(CommandExecutor executor, String alias, String[] args);
|
||||
}
|
||||
|
||||
@@ -17,15 +17,14 @@
|
||||
package dev.brighten.antivpn.command;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CommandExecutor {
|
||||
|
||||
void sendMessage(String message, Object... objects);
|
||||
void sendMessage(String message, Object... objects);
|
||||
boolean hasPermission(String permission);
|
||||
Optional<APIPlayer> getPlayer();
|
||||
boolean isPlayer();
|
||||
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
Optional<APIPlayer> getPlayer();
|
||||
|
||||
boolean isPlayer();
|
||||
}
|
||||
|
||||
@@ -21,68 +21,64 @@ import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.message.VpnString;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AlertsCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.alerts";
|
||||
}
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.alerts";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "alerts";
|
||||
}
|
||||
@Override
|
||||
public String name() {
|
||||
return "alerts";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"valerts", "vpnalerts"};
|
||||
}
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"valerts", "vpnalerts"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "toggle VPN use alerts";
|
||||
}
|
||||
@Override
|
||||
public String description() {
|
||||
return "toggle VPN use alerts";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
Optional<APIPlayer> pgetter = executor.getPlayer();
|
||||
if (!pgetter.isPresent())
|
||||
return AntiVPN.getInstance()
|
||||
.getMessageHandler()
|
||||
.getString("command-misc-playerRequired")
|
||||
.getMessage();
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
Optional<APIPlayer> pgetter = executor.getPlayer();
|
||||
if(!pgetter.isPresent()) return AntiVPN.getInstance().getMessageHandler()
|
||||
.getString("command-misc-playerRequired").getMessage();
|
||||
|
||||
APIPlayer player = pgetter.get();
|
||||
APIPlayer player = pgetter.get();
|
||||
|
||||
player.setAlertsEnabled(!player.isAlertsEnabled());
|
||||
player.updateAlertsState();
|
||||
player.setAlertsEnabled(!player.isAlertsEnabled());
|
||||
player.updateAlertsState();
|
||||
|
||||
return AntiVPN.getInstance()
|
||||
.getMessageHandler()
|
||||
.getString("command-alerts-toggled")
|
||||
.getFormattedMessage(new VpnString.Var<>("state", player.isAlertsEnabled()));
|
||||
}
|
||||
return AntiVPN.getInstance().getMessageHandler().getString("command-alerts-toggled")
|
||||
.getFormattedMessage(new VpnString.Var<>("state", player.isAlertsEnabled()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
+286
-322
@@ -22,349 +22,313 @@ import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AllowlistCommand extends Command {
|
||||
|
||||
private static final String[] secondArgs = new String[] {"add", "remove", "show", "search"};
|
||||
private static final String[] secondArgs = new String[] {"add", "remove", "show", "search"};
|
||||
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.allowlist";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "allowlist";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"whitelist"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Add/remove players to/from exemption list.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "<add <player/uuid/ip> | remove <player/uuid/ip> | show [page] | search <query> [page]>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
if (args.length == 0
|
||||
|| Arrays.stream(secondArgs).noneMatch(arg -> arg.equalsIgnoreCase(args[0]))) {
|
||||
return "&cUsage: /antivpn allowlist " + usage();
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.allowlist";
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("show")) {
|
||||
// args[1] = optional page number (defaults to 1)
|
||||
int page = 1;
|
||||
if (args.length > 1) {
|
||||
@Override
|
||||
public String name() {
|
||||
return "allowlist";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"whitelist"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Add/remove players to/from exemption list.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "<add <player/uuid/ip> | remove <player/uuid/ip> | show [page] | search <query> [page]>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
if(args.length == 0 || Arrays.stream(secondArgs).noneMatch(arg -> arg.equalsIgnoreCase(args[0]))) {
|
||||
return "&cUsage: /antivpn allowlist " + usage();
|
||||
}
|
||||
|
||||
if(args[0].equalsIgnoreCase("show")) {
|
||||
// args[1] = optional page number (defaults to 1)
|
||||
int page = 1;
|
||||
if (args.length > 1) {
|
||||
try {
|
||||
page = Integer.parseInt(args[1]);
|
||||
if (page < 1) page = 1;
|
||||
} catch (NumberFormatException e) {
|
||||
page = 1;
|
||||
}
|
||||
}
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
List<UUID> uuids = databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
||||
List<CIDRUtils> ips = databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
||||
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (UUID uuid : uuids) {
|
||||
entries.add("&7- &fUUID: &e" + uuid);
|
||||
}
|
||||
for (CIDRUtils cidr : ips) {
|
||||
entries.add("&7- &fIP: &e" + cidr.getCidr());
|
||||
}
|
||||
|
||||
return buildPage(entries, page, null, "show");
|
||||
}
|
||||
|
||||
if(args[0].equalsIgnoreCase("search")) {
|
||||
// args[1..n-1] = query terms; args[n] = optional page number if last arg is an integer
|
||||
if (args.length < 2) {
|
||||
return "&cUsage: /antivpn allowlist search <query> [page]";
|
||||
}
|
||||
|
||||
// Detect optional trailing page number
|
||||
int page = 1;
|
||||
int queryEnd = args.length;
|
||||
try {
|
||||
int candidate = Integer.parseInt(args[args.length - 1]);
|
||||
if (candidate >= 1 && args.length > 2) {
|
||||
page = candidate;
|
||||
queryEnd = args.length - 1;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
|
||||
String search = String.join(" ", Arrays.copyOfRange(args, 1, queryEnd)).toLowerCase();
|
||||
// Strip color code characters to prevent formatting injection in output
|
||||
String safeSearch = search.replace("&", "");
|
||||
|
||||
if (safeSearch.isEmpty()) {
|
||||
return "&cUsage: /antivpn allowlist search <query> [page]";
|
||||
}
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
List<UUID> uuids = databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
||||
List<CIDRUtils> ips = databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
||||
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (UUID uuid : uuids) {
|
||||
String entry = uuid.toString();
|
||||
if (entry.toLowerCase().contains(search)) {
|
||||
entries.add("&7- &fUUID: &e" + entry);
|
||||
}
|
||||
}
|
||||
for (CIDRUtils cidr : ips) {
|
||||
String entry = cidr.getCidr();
|
||||
if (entry.toLowerCase().contains(search)) {
|
||||
entries.add("&7- &fIP: &e" + entry);
|
||||
}
|
||||
}
|
||||
|
||||
return buildPage(entries, page, safeSearch, "search " + safeSearch);
|
||||
}
|
||||
|
||||
if(args.length == 1)
|
||||
return "&cYou have to provide a player to allow or deny exemption.";
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
if(!databaseEnabled) executor.sendMessage("&cThe database is currently not setup, " +
|
||||
"so any changes here will disappear after a restart.");
|
||||
|
||||
CIDRUtils cidrUtils;
|
||||
|
||||
try {
|
||||
page = Integer.parseInt(args[1]);
|
||||
if (page < 1) page = 1;
|
||||
} catch (NumberFormatException e) {
|
||||
return "&cUsage: /antivpn allowlist show [page]";
|
||||
cidrUtils = new CIDRUtils(args[1]);
|
||||
} catch(IllegalArgumentException | UnknownHostException e) {
|
||||
cidrUtils = null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
List<UUID> uuids =
|
||||
databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
||||
List<CIDRUtils> ips =
|
||||
databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
||||
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (UUID uuid : uuids) {
|
||||
entries.add("&7- &fUUID: &e" + uuid);
|
||||
}
|
||||
for (CIDRUtils cidr : ips) {
|
||||
entries.add("&7- &fIP: &e" + cidr.getCidr());
|
||||
}
|
||||
|
||||
return buildPage(entries, page, null, "show");
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("search")) {
|
||||
// args[1..n-1] = query terms; args[n] = optional page number if last arg is an integer
|
||||
if (args.length < 2) {
|
||||
return "&cUsage: /antivpn allowlist search <query> [page]";
|
||||
}
|
||||
|
||||
// Detect optional trailing page number
|
||||
int page = 1;
|
||||
int queryEnd = args.length;
|
||||
try {
|
||||
int candidate = Integer.parseInt(args[args.length - 1]);
|
||||
if (candidate >= 1 && args.length > 2) {
|
||||
page = candidate;
|
||||
queryEnd = args.length - 1;
|
||||
if(cidrUtils != null) {
|
||||
if(!databaseEnabled) {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(cidrUtils);
|
||||
yield String.format("&aAdded &6%s &ato exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
||||
yield String.format("&cRemoved &%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} else return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(cidrUtils);
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(cidrUtils);
|
||||
yield String.format("&aAdded &6%s &ato exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(cidrUtils);
|
||||
yield String.format("&cRemoved &6%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
|
||||
String search = String.join(" ", Arrays.copyOfRange(args, 1, queryEnd)).toLowerCase();
|
||||
// Strip color code characters to prevent formatting injection in output
|
||||
String safeSearch = search.replace("&", "");
|
||||
|
||||
if (safeSearch.isEmpty()) {
|
||||
return "&cUsage: /antivpn allowlist search <query> [page]";
|
||||
}
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
List<UUID> uuids =
|
||||
databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
||||
List<CIDRUtils> ips =
|
||||
databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
||||
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (UUID uuid : uuids) {
|
||||
String entry = uuid.toString();
|
||||
if (entry.toLowerCase().contains(search)) {
|
||||
entries.add("&7- &fUUID: &e" + entry);
|
||||
}
|
||||
}
|
||||
for (CIDRUtils cidr : ips) {
|
||||
String entry = cidr.getCidr();
|
||||
if (entry.toLowerCase().contains(search)) {
|
||||
entries.add("&7- &fIP: &e" + entry);
|
||||
}
|
||||
}
|
||||
|
||||
return buildPage(entries, page, safeSearch, "search " + safeSearch);
|
||||
}
|
||||
|
||||
if (args.length == 1) return "&cYou have to provide a player to allow or deny exemption.";
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
if (!databaseEnabled)
|
||||
executor.sendMessage(
|
||||
"&cThe database is currently not setup, "
|
||||
+ "so any changes here will disappear after a restart.");
|
||||
|
||||
CIDRUtils cidrUtils;
|
||||
|
||||
try {
|
||||
cidrUtils = new CIDRUtils(args[1]);
|
||||
} catch (IllegalArgumentException | UnknownHostException e) {
|
||||
cidrUtils = null;
|
||||
}
|
||||
|
||||
if (cidrUtils != null) {
|
||||
if (!databaseEnabled) {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(cidrUtils);
|
||||
yield String.format("&aAdded &6%s &ato exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
||||
yield String.format(
|
||||
"&cRemoved &%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} else
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(cidrUtils);
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(cidrUtils);
|
||||
yield String.format("&aAdded &6%s &ato exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(cidrUtils);
|
||||
yield String.format(
|
||||
"&cRemoved &6%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
}
|
||||
if (MiscUtils.isIpv4(args[1])) {
|
||||
if (!databaseEnabled) {
|
||||
try {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getWhitelistedIps()
|
||||
.add(new CIDRUtils(args[1] + "/32"));
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&aAdded &6%s &ato the exemption allowlist.", args[1] + "/32");
|
||||
if(MiscUtils.isIpv4(args[1])) {
|
||||
if(!databaseEnabled) {
|
||||
try {
|
||||
return switch(args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(new CIDRUtils(args[1] + "/32"));
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&aAdded &6%s &ato the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(new CIDRUtils(args[1] + "/32"));
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&cRemoved &6%s &cfrom the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Invalid IP format for allowlist command", e);
|
||||
return "&cInvalid IP format for allowlist command";
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&aAdded &6%s &a to the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&cRemoved &6%s &c from the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Invalid IP format for allowlist command", e);
|
||||
return "&cInvalid IP format for allowlist command";
|
||||
}
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getWhitelistedIps()
|
||||
.remove(new CIDRUtils(args[1] + "/32"));
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format(
|
||||
"&cRemoved &6%s &cfrom the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Invalid IP format for allowlist command", e);
|
||||
return "&cInvalid IP format for allowlist command";
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&aAdded &6%s &a to the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format(
|
||||
"&cRemoved &6%s &c from the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Invalid IP format for allowlist command", e);
|
||||
return "&cInvalid IP format for allowlist command";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UUID uuid;
|
||||
try {
|
||||
uuid = UUID.fromString(args[1]);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[1]);
|
||||
if (player.isPresent()) {
|
||||
uuid = player.get().getUuid();
|
||||
} else {
|
||||
uuid = MiscUtils.lookupUUID(args[1]);
|
||||
if (uuid == null) {
|
||||
return "&cCould not find a UUID for \""
|
||||
+ args[1]
|
||||
+ "\". They might not have provided a valid username.";
|
||||
}
|
||||
}
|
||||
}
|
||||
UUID uuid;
|
||||
try {
|
||||
uuid = UUID.fromString(args[1]);
|
||||
} catch(IllegalArgumentException e) {
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[1]);
|
||||
|
||||
if (!databaseEnabled) {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
yield String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
yield String.format(
|
||||
"&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} else {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add" -> {
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(uuid);
|
||||
yield String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(uuid);
|
||||
yield String.format(
|
||||
"&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
}
|
||||
if(player.isEmpty()) {
|
||||
return "&cThe player \"" + args[1] + "\" is not online, so please provide a UUID.";
|
||||
}
|
||||
|
||||
uuid = player.get().getUuid();
|
||||
}
|
||||
|
||||
if(!databaseEnabled) {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
yield String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
yield String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} else {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add" -> {
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(uuid);
|
||||
yield String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(uuid);
|
||||
yield String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return switch (args.length) {
|
||||
case 1 ->
|
||||
Arrays.stream(secondArgs)
|
||||
.filter(narg -> narg.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
case 2 -> {
|
||||
if (args[0].equalsIgnoreCase("show") || args[0].equalsIgnoreCase("search")) {
|
||||
yield Collections.emptyList();
|
||||
}
|
||||
yield AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.map(APIPlayer::getName)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[1].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
default -> Collections.emptyList();
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return switch (args.length) {
|
||||
case 1 -> Arrays.stream(secondArgs)
|
||||
.filter(narg -> narg.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
case 2 -> {
|
||||
if (args[0].equalsIgnoreCase("show") || args[0].equalsIgnoreCase("search")) {
|
||||
yield Collections.emptyList();
|
||||
}
|
||||
yield AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.map(APIPlayer::getName)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[1].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
default -> Collections.emptyList();
|
||||
};
|
||||
}
|
||||
|
||||
private String buildPage(
|
||||
List<String> entries, int page, String safeSearch, String subcommandPrefix) {
|
||||
int pageSize = 10;
|
||||
int totalPages = Math.max(1, (entries.size() + pageSize - 1) / pageSize);
|
||||
if (page > totalPages) page = totalPages;
|
||||
private String buildPage(List<String> entries, int page, String safeSearch, String subcommandPrefix) {
|
||||
int pageSize = 10;
|
||||
int totalPages = Math.max(1, (entries.size() + pageSize - 1) / pageSize);
|
||||
if (page > totalPages) page = totalPages;
|
||||
|
||||
List<String> messages = new ArrayList<>();
|
||||
messages.add("&8&m-----------------------------------------------------");
|
||||
messages.add(
|
||||
"&6&lAllowlist Entries &8(&7Page &f"
|
||||
+ page
|
||||
+ "&7/&f"
|
||||
+ totalPages
|
||||
+ "&8)"
|
||||
+ (safeSearch != null ? " &7(search: &f" + safeSearch + "&7)" : ""));
|
||||
messages.add("");
|
||||
|
||||
if (entries.isEmpty()) {
|
||||
messages.add(
|
||||
safeSearch != null
|
||||
? "&cNo allowlist entries matching &f\"" + safeSearch + "&c\" were found."
|
||||
: "&cThe allowlist is empty.");
|
||||
} else {
|
||||
int start = (page - 1) * pageSize;
|
||||
int end = Math.min(start + pageSize, entries.size());
|
||||
for (int i = start; i < end; i++) {
|
||||
messages.add(entries.get(i));
|
||||
}
|
||||
if (totalPages > 1) {
|
||||
List<String> messages = new ArrayList<>();
|
||||
messages.add("&8&m-----------------------------------------------------");
|
||||
messages.add("&6&lAllowlist Entries &8(&7Page &f" + page + "&7/&f" + totalPages + "&8)"
|
||||
+ (safeSearch != null ? " &7(search: &f" + safeSearch + "&7)" : ""));
|
||||
messages.add("");
|
||||
if (page > 1) {
|
||||
messages.add(
|
||||
"&7Previous page: &f/antivpn allowlist " + subcommandPrefix + " " + (page - 1));
|
||||
|
||||
if (entries.isEmpty()) {
|
||||
messages.add(safeSearch != null
|
||||
? "&cNo allowlist entries matching &f\"" + safeSearch + "&c\" were found."
|
||||
: "&cThe allowlist is empty.");
|
||||
} else {
|
||||
int start = (page - 1) * pageSize;
|
||||
int end = Math.min(start + pageSize, entries.size());
|
||||
for (int i = start; i < end; i++) {
|
||||
messages.add(entries.get(i));
|
||||
}
|
||||
if (totalPages > 1) {
|
||||
messages.add("");
|
||||
if (page > 1) {
|
||||
messages.add("&7Previous page: &f/antivpn allowlist " + subcommandPrefix + " " + (page - 1));
|
||||
}
|
||||
if (page < totalPages) {
|
||||
messages.add("&7Next page: &f/antivpn allowlist " + subcommandPrefix + " " + (page + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (page < totalPages) {
|
||||
messages.add("&7Next page: &f/antivpn allowlist " + subcommandPrefix + " " + (page + 1));
|
||||
}
|
||||
}
|
||||
messages.add("&8&m-----------------------------------------------------");
|
||||
return String.join("\n", messages);
|
||||
}
|
||||
messages.add("&8&m-----------------------------------------------------");
|
||||
return String.join("\n", messages);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -27,95 +28,73 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AntiVPNCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"kaurivpn", "kvpn", "vpn", "avpn"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "The main help command";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[] {
|
||||
new LookupCommand(),
|
||||
new AllowlistCommand(),
|
||||
new AlertsCommand(),
|
||||
new ClearCacheCommand(),
|
||||
new PlanCommand(),
|
||||
new ReloadCommand()
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor uuid, String[] args) {
|
||||
List<String> messages = new ArrayList<>();
|
||||
|
||||
messages.add(StringUtil.line("&8"));
|
||||
messages.add("&6&lAntiVPN Help Page");
|
||||
messages.add("");
|
||||
for (Command cmd : AntiVPN.getInstance().getCommands()) {
|
||||
messages.add(
|
||||
String.format(
|
||||
"&8/&f%s &8- &7&o%s",
|
||||
"&7"
|
||||
+ cmd.parent()
|
||||
+ (cmd.parent().length() > 0 ? " " : "")
|
||||
+ "&f"
|
||||
+ cmd.name()
|
||||
+ " &7"
|
||||
+ cmd.usage(),
|
||||
cmd.description()));
|
||||
}
|
||||
for (Command child : children()) {
|
||||
messages.add(
|
||||
String.format(
|
||||
"&8/&f%s &8- &7&o%s",
|
||||
"&7"
|
||||
+ child.parent()
|
||||
+ (child.parent().length() > 0 ? " " : "")
|
||||
+ "&f"
|
||||
+ child.name()
|
||||
+ " &7"
|
||||
+ child.usage(),
|
||||
child.description()));
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command";
|
||||
}
|
||||
|
||||
messages.add(StringUtil.line("&8"));
|
||||
@Override
|
||||
public String name() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
return String.join("\n", messages);
|
||||
}
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"kaurivpn", "kvpn", "vpn", "avpn"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
if (args.length == 1)
|
||||
return Arrays.stream(children())
|
||||
.map(Command::name)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
@Override
|
||||
public String description() {
|
||||
return "The main help command";
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[] {new LookupCommand(), new AllowlistCommand(), new AlertsCommand(),
|
||||
new ClearCacheCommand(), new PlanCommand(), new ReloadCommand()};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor uuid, String[] args) {
|
||||
List<String> messages = new ArrayList<>();
|
||||
|
||||
messages.add(StringUtil.line("&8"));
|
||||
messages.add("&6&lAntiVPN Help Page");
|
||||
messages.add("");
|
||||
for (Command cmd : AntiVPN.getInstance().getCommands()) {
|
||||
messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + cmd.parent()
|
||||
+ (cmd.parent().length() > 0 ? " " : "") + "&f" + cmd.name() + " &7"
|
||||
+ cmd.usage(), cmd.description()));
|
||||
}
|
||||
for (Command child : children()) {
|
||||
messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + child.parent()
|
||||
+ (child.parent().length() > 0 ? " " : "") + "&f" + child.name() + " &7"
|
||||
+ child.usage(), child.description()));
|
||||
}
|
||||
|
||||
messages.add(StringUtil.line("&8"));
|
||||
|
||||
return String.join("\n", messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
if(args.length == 1)
|
||||
return Arrays.stream(children())
|
||||
.map(Command::name)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
+39
-40
@@ -17,58 +17,57 @@
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ClearCacheCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.clearcache";
|
||||
}
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.clearcache";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "clearcache";
|
||||
}
|
||||
@Override
|
||||
public String name() {
|
||||
return "clearcache";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"clear", "cc"};
|
||||
}
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"clear", "cc"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Clear the API response cache if you're having problems.";
|
||||
}
|
||||
@Override
|
||||
public String description() {
|
||||
return "Clear the API response cache if you're having problems.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.execute(() -> AntiVPN.getInstance().getDatabase().clearResponses());
|
||||
return "&aCleared all cached API response information!";
|
||||
}
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> AntiVPN.getInstance().getDatabase().clearResponses());
|
||||
return "&aCleared all cached API response information!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,97 +21,94 @@ import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LookupCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.lookup";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "lookup";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"check"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Lookup a player's ip info";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "<player>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
if (args.length == 0) {
|
||||
return "&cPlease supply a player to check.";
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.lookup";
|
||||
}
|
||||
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[0]);
|
||||
|
||||
if (player.isEmpty()) {
|
||||
return String.format("&cNo player found with the name \"%s\"", args[0]);
|
||||
@Override
|
||||
public String name() {
|
||||
return "lookup";
|
||||
}
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.checkIp(player.get().getIp().getHostAddress())
|
||||
.thenAccept(
|
||||
result -> {
|
||||
if (!result.isSuccess()) {
|
||||
executor.sendMessage(
|
||||
"&cThere was an error trying to find the " + "information of this player.");
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"check"};
|
||||
}
|
||||
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
executor.sendMessage(
|
||||
"&6&l" + player.get().getName() + "&7&l's Connection Information");
|
||||
executor.sendMessage("");
|
||||
executor.sendMessage(
|
||||
"&e%s&8: &f%s", "Proxy", result.isProxy() ? "&a" + result.getMethod() : "&cNo");
|
||||
executor.sendMessage("&e%s&8: &f%s", "ISP", result.getIsp());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName());
|
||||
executor.sendMessage("&e%s&8: &f%s", "City", result.getCity());
|
||||
executor.sendMessage(
|
||||
"&e%s&8: &f%s",
|
||||
"Coordinates", result.getLatitude() + "&7/&f" + result.getLongitude());
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
});
|
||||
@Override
|
||||
public String description() {
|
||||
return "Lookup a player's ip info";
|
||||
}
|
||||
|
||||
return "&7Looking up the IP information for player " + player.get().getName() + "...";
|
||||
}
|
||||
@Override
|
||||
public String usage() {
|
||||
return "<player>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
if (args.length == 1)
|
||||
return AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.map(APIPlayer::getName)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
if(args.length == 0) {
|
||||
return "&cPlease supply a player to check.";
|
||||
}
|
||||
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[0]);
|
||||
|
||||
if(player.isEmpty()) {
|
||||
return String.format("&cNo player found with the name \"%s\"", args[0]);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.checkIp(player.get().getIp().getHostAddress())
|
||||
.thenAccept(result -> {
|
||||
if(!result.isSuccess()) {
|
||||
executor.sendMessage("&cThere was an error trying to find the " +
|
||||
"information of this player.");
|
||||
return;
|
||||
}
|
||||
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information");
|
||||
executor.sendMessage("");
|
||||
executor.sendMessage("&e%s&8: &f%s", "Proxy", result.isProxy()
|
||||
? "&a" + result.getMethod() : "&cNo");
|
||||
executor.sendMessage("&e%s&8: &f%s", "ISP", result.getIsp());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName());
|
||||
executor.sendMessage("&e%s&8: &f%s", "City", result.getCity());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Coordinates", result.getLatitude()
|
||||
+ "&7/&f" + result.getLongitude());
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
});
|
||||
|
||||
|
||||
return "&7Looking up the IP information for player " + player.get().getName() + "...";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
|
||||
if(args.length == 1) return AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.map(APIPlayer::getName)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,110 +17,101 @@
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
||||
import dev.brighten.antivpn.web.objects.QueryResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PlanCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.plan";
|
||||
}
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.plan";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "plan";
|
||||
}
|
||||
@Override
|
||||
public String name() {
|
||||
return "plan";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"queries", "query"};
|
||||
}
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"queries", "query"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Info related to KauriVPN Plan";
|
||||
}
|
||||
@Override
|
||||
public String description() {
|
||||
return "Info related to KauriVPN Plan";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.execute(
|
||||
() -> {
|
||||
QueryResponse result;
|
||||
try {
|
||||
if (AntiVPN.getInstance().getVpnConfig().getLicense().isEmpty()) {
|
||||
result = FunkemunkyAPI.getQueryResponse();
|
||||
} else {
|
||||
result =
|
||||
FunkemunkyAPI.getQueryResponse(
|
||||
AntiVPN.getInstance().getVpnConfig().getLicense());
|
||||
|
||||
if (!result.isValidPlan()) {
|
||||
executor.sendMessage(
|
||||
"&cThe license &f%s &cis not a valid license, "
|
||||
+ "checking your Free plan information...",
|
||||
AntiVPN.getInstance().getVpnConfig().getLicense());
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
||||
QueryResponse result;
|
||||
try {
|
||||
if(AntiVPN.getInstance().getVpnConfig().getLicense().isEmpty()) {
|
||||
result = FunkemunkyAPI.getQueryResponse();
|
||||
}
|
||||
} else {
|
||||
result = FunkemunkyAPI.getQueryResponse(AntiVPN.getInstance().getVpnConfig().getLicense());
|
||||
|
||||
if(!result.isValidPlan()) {
|
||||
executor.sendMessage("&cThe license &f%s &cis not a valid license, " +
|
||||
"checking your Free plan information...",
|
||||
AntiVPN.getInstance().getVpnConfig().getLicense());
|
||||
|
||||
result = FunkemunkyAPI.getQueryResponse();
|
||||
}
|
||||
}
|
||||
|
||||
String plan = result.getPlanType();
|
||||
if (plan.equals("IP")) plan += " (Free)";
|
||||
if(plan.equals("IP")) plan+= " (Free)";
|
||||
|
||||
String queryMax =
|
||||
result.getQueriesMax() == Long.MAX_VALUE
|
||||
? "Unlimited"
|
||||
: String.valueOf(result.getQueriesMax());
|
||||
String queryMax = result.getQueriesMax() == Long.MAX_VALUE
|
||||
? "Unlimited" : String.valueOf(result.getQueriesMax());
|
||||
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
executor.sendMessage("&6&lKauriVPN Plan Information");
|
||||
executor.sendMessage("");
|
||||
executor.sendMessage("&e%s&8: &f%s", "Plan", plan);
|
||||
executor.sendMessage(
|
||||
"&e%s&8: &f%s&7/&f%s", "Queries Used", result.getQueries(), queryMax);
|
||||
executor.sendMessage("&e%s&8: &f%s&7/&f%s", "Queries Used",
|
||||
result.getQueries(), queryMax);
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
} catch (JSONException e) {
|
||||
} catch(JSONException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
executor.sendMessage(
|
||||
"&cThere was a JSONException thrown while looking up your query "
|
||||
+ "information. Check console for more details.");
|
||||
} catch (IOException e) {
|
||||
executor.sendMessage("&cThere was a JSONException thrown while looking up your query " +
|
||||
"information. Check console for more details.");
|
||||
} catch (IOException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
executor.sendMessage(
|
||||
"&cThere was a IOException thrown while looking up your query "
|
||||
+ "information. Check console for more details.");
|
||||
}
|
||||
});
|
||||
return "&7Looking up your query information...";
|
||||
}
|
||||
executor.sendMessage("&cThere was a IOException thrown while looking up your query " +
|
||||
"information. Check console for more details.");
|
||||
}
|
||||
});
|
||||
return "&7Looking up your query information...";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,65 +19,63 @@ package dev.brighten.antivpn.command.impl;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ReloadCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.reload";
|
||||
}
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "reload";
|
||||
}
|
||||
@Override
|
||||
public String name() {
|
||||
return "reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[0];
|
||||
}
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Reload the plugin";
|
||||
}
|
||||
@Override
|
||||
public String description() {
|
||||
return "Reload the plugin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
// Loading changes from the config.yml
|
||||
AntiVPN.getInstance().reloadConfig();
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
// Loading changes from the config.yml
|
||||
AntiVPN.getInstance().reloadConfig();
|
||||
|
||||
// Updating the cache of these values in VPNConfig
|
||||
AntiVPN.getInstance().getVpnConfig().update();
|
||||
// Updating the cache of these values in VPNConfig
|
||||
AntiVPN.getInstance().getVpnConfig().update();
|
||||
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();
|
||||
|
||||
AntiVPN.getInstance().reloadDatabase();
|
||||
AntiVPN.getInstance().reloadDatabase();
|
||||
|
||||
return AntiVPN.getInstance()
|
||||
.getMessageHandler()
|
||||
.getString("command-reload-complete")
|
||||
.getMessage();
|
||||
}
|
||||
return AntiVPN.getInstance().getMessageHandler().getString("command-reload-complete").getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.brighten.antivpn.database;
|
||||
|
||||
public class DatabaseException extends RuntimeException {
|
||||
public DatabaseException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
public DatabaseException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,43 +18,44 @@ package dev.brighten.antivpn.database;
|
||||
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface VPNDatabase {
|
||||
Optional<VPNResponse> getStoredResponse(String ip);
|
||||
Optional<VPNResponse> getStoredResponse(String ip);
|
||||
|
||||
void cacheResponse(VPNResponse toCache);
|
||||
void cacheResponse(VPNResponse toCache);
|
||||
|
||||
void deleteResponse(String ip);
|
||||
void deleteResponse(String ip);
|
||||
|
||||
boolean isWhitelisted(UUID uuid);
|
||||
boolean isWhitelisted(UUID uuid);
|
||||
|
||||
boolean isWhitelisted(String cidr);
|
||||
boolean isWhitelisted(String cidr);
|
||||
|
||||
boolean isWhitelisted(CIDRUtils cidr);
|
||||
boolean isWhitelisted(CIDRUtils cidr);
|
||||
|
||||
void addWhitelist(UUID uuid);
|
||||
void addWhitelist(UUID uuid);
|
||||
|
||||
void removeWhitelist(UUID uuid);
|
||||
void removeWhitelist(UUID uuid);
|
||||
|
||||
void addWhitelist(CIDRUtils cidr);
|
||||
void addWhitelist(CIDRUtils cidr);
|
||||
|
||||
void removeWhitelist(CIDRUtils cidr);
|
||||
void removeWhitelist(CIDRUtils cidr);
|
||||
|
||||
List<UUID> getAllWhitelisted();
|
||||
List<UUID> getAllWhitelisted();
|
||||
|
||||
List<CIDRUtils> getAllWhitelistedIps();
|
||||
List<CIDRUtils> getAllWhitelistedIps();
|
||||
|
||||
void alertsState(UUID uuid, Consumer<Boolean> result);
|
||||
void alertsState(UUID uuid, Consumer<Boolean> result);
|
||||
|
||||
void updateAlertsState(UUID uuid, boolean state);
|
||||
void updateAlertsState(UUID uuid, boolean state);
|
||||
|
||||
void clearResponses();
|
||||
void clearResponses();
|
||||
|
||||
void init();
|
||||
void init();
|
||||
|
||||
void shutdown();
|
||||
}
|
||||
void shutdown();
|
||||
}
|
||||
@@ -24,6 +24,8 @@ import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigInteger;
|
||||
import java.net.UnknownHostException;
|
||||
@@ -33,421 +35,346 @@ import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
public class H2VPN implements VPNDatabase {
|
||||
|
||||
public H2VPN() {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.scheduleAtFixedRate(
|
||||
() -> {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
// Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
public H2VPN() {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
// Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
},
|
||||
2,
|
||||
30,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
//Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return Optional.empty();
|
||||
|
||||
try (ExecutableStatement statement =
|
||||
Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip)) {
|
||||
try (ResultSet rs = statement.executeQuery()) {
|
||||
if (rs != null && rs.next()) {
|
||||
return Optional.of(
|
||||
new VPNResponse(
|
||||
rs.getString("asn"),
|
||||
rs.getString("ip"),
|
||||
rs.getString("countryName"),
|
||||
rs.getString("countryCode"),
|
||||
rs.getString("city"),
|
||||
rs.getString("timeZone"),
|
||||
rs.getString("method"),
|
||||
rs.getString("isp"),
|
||||
"N/A",
|
||||
rs.getBoolean("proxy"),
|
||||
rs.getBoolean("cached"),
|
||||
true,
|
||||
rs.getDouble("latitude"),
|
||||
rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(),
|
||||
-1));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("There was a problem getting a response for " + ip, e);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/*
|
||||
* Query.
|
||||
* prepare("create table if not exists `responses` (`ip` varchar(45) not null, "
|
||||
* +
|
||||
* "`countryName` varchar(64), `countryCode` varchar(10), `city` varchar(64), `timeZone` varchar(64), "
|
||||
* +
|
||||
* "`method` varchar(32), `isp` varchar(32), `proxy` boolean, `cached` boolean "
|
||||
* + "`latitude` double, `longitude` double)");
|
||||
*/
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
try (var statement =
|
||||
Query.prepare(
|
||||
"insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
||||
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.append(toCache.getIp())
|
||||
.append(toCache.getAsn())
|
||||
.append(toCache.getCountryName())
|
||||
.append(toCache.getCountryCode())
|
||||
.append(toCache.getCity())
|
||||
.append(toCache.getTimeZone())
|
||||
.append(toCache.getMethod())
|
||||
.append(toCache.getIsp())
|
||||
.append(toCache.isProxy())
|
||||
.append(toCache.isCached())
|
||||
.append(new Timestamp(System.currentTimeMillis()))
|
||||
.append(toCache.getLatitude())
|
||||
.append(toCache.getLongitude())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not cache response for IP: " + toCache.getIp(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteResponse(String ip) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
try (var statement = Query.prepare("delete from `responses` where `ip` = ?").append(ip)) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not delete response from IP: " + ip, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return false;
|
||||
|
||||
try (var statement =
|
||||
Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString())) {
|
||||
try (var set = statement.executeQuery()) {
|
||||
return set != null && set.next() && set.getString("uuid") != null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not check whitelist for uuid '" + uuid + "' due to SQL error.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
return isWhitelisted(new CIDRUtils(cidr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return false;
|
||||
|
||||
BigInteger start = cidr.getStartIpInt();
|
||||
BigInteger end = cidr.getEndIpInt();
|
||||
|
||||
try (var statement =
|
||||
Query.prepare("SELECT * FROM `whitelisted-ranges` WHERE ip_start <= ? AND ip_end >= ?")
|
||||
.append(start)
|
||||
.append(end)) {
|
||||
|
||||
try (var result = statement.executeQuery()) {
|
||||
return result.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not check whitelist for cidr '" + cidr + "' due to SQL error.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
try (var statement =
|
||||
Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not add uuid '" + uuid + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
try (var statement =
|
||||
Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not remove uuid '" + uuid + "' from whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
try (var statement =
|
||||
Query.prepare(
|
||||
"insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
||||
.append(cidr.getCidr())
|
||||
.append(cidr.getStartIpInt())
|
||||
.append(cidr.getEndIpInt())) {
|
||||
statement.execute();
|
||||
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
try (var statement =
|
||||
Query.prepare("delete from `whitelisted-ranges` where `cidr_string` = ?")
|
||||
.append(cidr.getCidr())) {
|
||||
statement.execute();
|
||||
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not remove cidr '" + cidr + "' from whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAllWhitelisted() {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return uuids;
|
||||
|
||||
try (var statement = Query.prepare("select uuid from `whitelisted`")) {
|
||||
statement.execute(set -> uuids.add(UUID.fromString(set.getString("uuid"))));
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not get all whitelisted players due to SQL error.", e);
|
||||
//Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
}, 2, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
return uuids;
|
||||
}
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed())
|
||||
return Optional.empty();
|
||||
|
||||
@Override
|
||||
public List<CIDRUtils> getAllWhitelistedIps() {
|
||||
List<CIDRUtils> ips = new ArrayList<>();
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return ips;
|
||||
try (var statement =
|
||||
Query.prepare("select `cidr_string`, `ip_start`, `ip_end` from `whitelisted-ranges`")) {
|
||||
statement.execute(
|
||||
set -> {
|
||||
try {
|
||||
String cidrString = set.getString("cidr_string");
|
||||
|
||||
ips.add(new CIDRUtils(cidrString));
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException(
|
||||
"Could not format ip " + set.getString("cidr_string") + " into a CIDR!", e);
|
||||
}
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not get all whitelisted ips due to SQL error.", e);
|
||||
}
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
if (MySQL.isClosed()) return;
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.execute(
|
||||
() -> {
|
||||
try (var statement =
|
||||
Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString())) {
|
||||
try (var set = statement.executeQuery()) {
|
||||
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
||||
try(ExecutableStatement statement = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip)) {
|
||||
try(ResultSet rs = statement.executeQuery()) {
|
||||
if (rs != null && rs.next()) {
|
||||
return Optional.of(new VPNResponse(rs.getString("asn"), rs.getString("ip"),
|
||||
rs.getString("countryName"), rs.getString("countryCode"),
|
||||
rs.getString("city"), rs.getString("timeZone"),
|
||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(), -1));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("There was a problem getting alerts state for " + uuid, e);
|
||||
result.accept(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean enabled) {
|
||||
if (MySQL.isClosed()) return;
|
||||
|
||||
if (enabled) {
|
||||
// We want to make sure there isn't already a uuid inserted to prevent double insertions
|
||||
alertsState(
|
||||
uuid,
|
||||
alreadyEnabled -> { // No need to make another thread execute, already async
|
||||
if (!alreadyEnabled) {
|
||||
try (var statement =
|
||||
Query.prepare("insert into `alerts` (`uuid`) values (?)")
|
||||
.append(uuid.toString())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("There was a problem updating alerts state for " + uuid, e);
|
||||
}
|
||||
} // No need to insert again of already enabled
|
||||
});
|
||||
// Removing any uuid from the alerts table will disable alerts globally.
|
||||
} else {
|
||||
try (var statement =
|
||||
Query.prepare("delete from `alerts` where `uuid` = ?").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("There was a problem updating alerts state for " + uuid, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearResponses() {
|
||||
if (MySQL.isClosed()) return;
|
||||
|
||||
try (var statement = Query.prepare("delete from `responses`")) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("There was a problem clearing responses.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||
AntiVPN.getInstance().getExecutor().log("Initializing H2...");
|
||||
MySQL.initH2();
|
||||
try {
|
||||
for (Version<H2VPN> version : Version.h2Versions) {
|
||||
if (version.needsUpdate(this)) {
|
||||
version.update(this);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting a response for "
|
||||
+ ip, e);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not complete version setup due to SQL error", e);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating tables...");
|
||||
/*
|
||||
* Query.
|
||||
* prepare("create table if not exists `responses` (`ip` varchar(45) not null, "
|
||||
* +
|
||||
* "`countryName` varchar(64), `countryCode` varchar(10), `city` varchar(64), `timeZone` varchar(64), "
|
||||
* +
|
||||
* "`method` varchar(32), `isp` varchar(32), `proxy` boolean, `cached` boolean "
|
||||
* + "`latitude` double, `longitude` double)");
|
||||
*/
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
// Running check for old table types to update
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||
|
||||
MySQL.shutdown();
|
||||
}
|
||||
|
||||
public void backupDatabase() {
|
||||
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases");
|
||||
|
||||
if (!dataFolder.exists() || MySQL.isClosed()) {
|
||||
return;
|
||||
try(var statement = Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
||||
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName())
|
||||
.append(toCache.getCountryCode()).append(toCache.getCity()).append(toCache.getTimeZone())
|
||||
.append(toCache.getMethod()).append(toCache.getIsp()).append(toCache.isProxy())
|
||||
.append(toCache.isCached()).append(new Timestamp(System.currentTimeMillis()))
|
||||
.append(toCache.getLatitude()).append(toCache.getLongitude())) {
|
||||
statement.execute();
|
||||
} catch(SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not cache response for IP: " + toCache.getIp(), e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
var connection = Query.getConn();
|
||||
if (connection == null
|
||||
|| connection.getMetaData() == null
|
||||
|| !connection.getMetaData().getDatabaseProductName().equalsIgnoreCase("H2")) {
|
||||
return;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not verify database type before H2 backup.", e);
|
||||
return;
|
||||
@Override
|
||||
public void deleteResponse(String ip) {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
try(var statement = Query.prepare("delete from `responses` where `ip` = ?").append(ip)) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not delete response from IP: " + ip, e);
|
||||
}
|
||||
}
|
||||
|
||||
File backupDir = new File(dataFolder, "backups");
|
||||
if (!backupDir.exists() && !backupDir.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not create backup directory");
|
||||
return;
|
||||
@Override
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return false;
|
||||
|
||||
try(var statement = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString())) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
return set != null && set.next() && set.getString("uuid") != null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not check whitelist for uuid '" + uuid + "' due to SQL error.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
File backupFile =
|
||||
new File(backupDir, "database.h2_backup_" + System.currentTimeMillis() + ".zip");
|
||||
String backupPath = backupFile.getAbsolutePath().replace("\\", "/").replace("'", "''");
|
||||
|
||||
try (var statement = Query.prepare("BACKUP TO '" + backupPath + "'")) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().log("Created H2 backup at " + backupFile.getName());
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not create H2 backup before migration.", e);
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
return isWhitelisted(new CIDRUtils(cidr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return false;
|
||||
|
||||
BigInteger start = cidr.getStartIpInt();
|
||||
BigInteger end = cidr.getEndIpInt();
|
||||
|
||||
try(var statement = Query.prepare("SELECT * FROM `whitelisted-ranges` WHERE ip_start <= ? AND ip_end >= ?")
|
||||
.append(start).append(end)) {
|
||||
|
||||
try(var result = statement.executeQuery()) {
|
||||
return result.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not check whitelist for cidr '" + cidr + "' due to SQL error.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
try(var statement = Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not add uuid '" + uuid + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
try(var statement = Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not remove uuid '" + uuid + "' from whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
try(var statement = Query.prepare("insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
||||
.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) {
|
||||
statement.execute();
|
||||
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
try(var statement = Query.prepare("delete from `whitelisted-ranges` where `cidr_string` = ?").append(cidr.getCidr())) {
|
||||
statement.execute();
|
||||
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not remove cidr '" + cidr + "' from whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAllWhitelisted() {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return uuids;
|
||||
|
||||
try(var statement = Query.prepare("select uuid from `whitelisted`")) {
|
||||
statement.execute(set -> uuids.add(UUID.fromString(set.getString("uuid"))));
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted players due to SQL error.", e);
|
||||
}
|
||||
|
||||
return uuids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CIDRUtils> getAllWhitelistedIps() {
|
||||
List<CIDRUtils> ips = new ArrayList<>();
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return ips;
|
||||
try(var statement = Query.prepare("select `cidr_string`, `ip_start`, `ip_end` from `whitelisted-ranges`")) {
|
||||
statement.execute(set -> {
|
||||
try {
|
||||
String cidrString = set.getString("cidr_string");
|
||||
|
||||
ips.add(new CIDRUtils(cidrString));
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.logException("Could not format ip "
|
||||
+ set.getString("cidr_string") + " into a CIDR!", e);
|
||||
}
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted ips due to SQL error.", e);
|
||||
}
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
||||
|
||||
try(var statement = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString())) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting alerts state for " + uuid, e);
|
||||
result.accept(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean enabled) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
if(enabled) {
|
||||
//We want to make sure there isn't already a uuid inserted to prevent double insertions
|
||||
alertsState(uuid, alreadyEnabled -> { //No need to make another thread execute, already async
|
||||
if(!alreadyEnabled) {
|
||||
try(var statement = Query.prepare("insert into `alerts` (`uuid`) values (?)")
|
||||
.append(uuid.toString())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.logException("There was a problem updating alerts state for " + uuid, e);
|
||||
}
|
||||
} //No need to insert again of already enabled
|
||||
});
|
||||
//Removing any uuid from the alerts table will disable alerts globally.
|
||||
} else {
|
||||
try(var statement = Query.prepare("delete from `alerts` where `uuid` = ?").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem updating alerts state for "
|
||||
+ uuid, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearResponses() {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
try(var statement = Query.prepare("delete from `responses`")) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem clearing responses.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
AntiVPN.getInstance().getExecutor().log("Initializing H2...");
|
||||
MySQL.initH2();
|
||||
try {
|
||||
for (Version<H2VPN> version : Version.h2Versions) {
|
||||
if(version.needsUpdate(this)) {
|
||||
version.update(this);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not complete version setup due to SQL error", e);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating tables...");
|
||||
|
||||
//Running check for old table types to update
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
|
||||
MySQL.shutdown();
|
||||
}
|
||||
|
||||
public void backupDatabase() {
|
||||
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases");
|
||||
|
||||
if(!dataFolder.exists() || MySQL.isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var connection = Query.getConn();
|
||||
if (connection == null || connection.getMetaData() == null
|
||||
|| !connection.getMetaData().getDatabaseProductName().equalsIgnoreCase("H2")) {
|
||||
return;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not verify database type before H2 backup.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
File backupDir = new File(dataFolder, "backups");
|
||||
if (!backupDir.exists() && !backupDir.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not create backup directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File backupFile = new File(backupDir, "database.h2_backup_" + System.currentTimeMillis() + ".zip");
|
||||
String backupPath = backupFile.getAbsolutePath()
|
||||
.replace("\\", "/")
|
||||
.replace("'", "''");
|
||||
|
||||
try (var statement = Query.prepare("BACKUP TO '" + backupPath + "'")) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().log("Created H2 backup at " + backupFile.getName());
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not create H2 backup before migration.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+91
-95
@@ -23,6 +23,7 @@ import dev.brighten.antivpn.database.sql.utils.ExecutableStatement;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@@ -31,104 +32,99 @@ import java.util.List;
|
||||
|
||||
public class First implements Version<VPNDatabase> {
|
||||
|
||||
private final List<AutoCloseable> toClose = new ArrayList<>();
|
||||
private final List<AutoCloseable> toClose = new ArrayList<>();
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
try {
|
||||
closeOnEnd(Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(Query
|
||||
.prepare("create table if not exists `responses` (`ip` varchar(45) not null, `asn` varchar(12),"
|
||||
+ "`countryName` text, `countryCode` varchar(10), `city` text, `timeZone` varchar(64), "
|
||||
+ "`method` varchar(32), `isp` text, `proxy` boolean, `cached` boolean, `inserted` timestamp,"
|
||||
+ "`latitude` double, `longitude` double)")).execute();
|
||||
closeOnEnd(Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(Query.prepare("create table if not exists `database_version` (`version` int)")).execute();
|
||||
closeOnEnd(Query.prepare("insert into `database_version` (`version`) values (?)")
|
||||
.append(versionNumber())).execute();
|
||||
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
try {
|
||||
closeOnEnd(
|
||||
Query.prepare(
|
||||
"create table if not exists `whitelisted` (`uuid` varchar(36) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(
|
||||
Query.prepare(
|
||||
"create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(
|
||||
Query.prepare(
|
||||
"create table if not exists `responses` (`ip` varchar(45) not null, `asn` varchar(12),"
|
||||
+ "`countryName` text, `countryCode` varchar(10), `city` text, `timeZone` varchar(64), "
|
||||
+ "`method` varchar(32), `isp` text, `proxy` boolean, `cached` boolean, `inserted` timestamp,"
|
||||
+ "`latitude` double, `longitude` double)"))
|
||||
.execute();
|
||||
closeOnEnd(Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(Query.prepare("create table if not exists `database_version` (`version` int)"))
|
||||
.execute();
|
||||
closeOnEnd(
|
||||
Query.prepare("insert into `database_version` (`version`) values (?)")
|
||||
.append(versionNumber()))
|
||||
.execute();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating indexes...");
|
||||
createIndexIfAbsent("whitelisted", "whitelisted_uuid_1", "`uuid`");
|
||||
createIndexIfAbsent("responses", "responses_ip_1", "`ip`");
|
||||
createIndexIfAbsent("responses", "responses_proxy_1", "`proxy`");
|
||||
createIndexIfAbsent("responses", "responses_inserted_1", "`inserted`");
|
||||
createIndexIfAbsent("whitelisted-ips", "whitelisted_ips_ip_1", "`ip`");
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Failed to update database", e);
|
||||
} finally {
|
||||
MiscUtils.close(toClose.toArray(AutoCloseable[]::new));
|
||||
toClose.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutableStatement closeOnEnd(ExecutableStatement statement) {
|
||||
toClose.add(statement);
|
||||
return statement;
|
||||
}
|
||||
|
||||
protected void createIndexIfAbsent(String tableName, String indexName, String columnList)
|
||||
throws SQLException {
|
||||
if (hasIndex(tableName, indexName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeOnEnd(
|
||||
Query.prepare(
|
||||
String.format("create index `%s` on `%s` (%s)", indexName, tableName, columnList)))
|
||||
.execute();
|
||||
}
|
||||
|
||||
protected void dropIndexIfPresent(String tableName, String indexName) throws SQLException {
|
||||
if (!hasIndex(tableName, indexName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeOnEnd(Query.prepare(String.format("drop index `%s` on `%s`", indexName, tableName)))
|
||||
.execute();
|
||||
}
|
||||
|
||||
protected boolean hasIndex(String tableName, String indexName) throws SQLException {
|
||||
DatabaseMetaData metaData = Query.getConn().getMetaData();
|
||||
|
||||
try (ResultSet indexes = metaData.getIndexInfo(null, null, tableName, false, false)) {
|
||||
while (indexes.next()) {
|
||||
String existingIndexName = indexes.getString("INDEX_NAME");
|
||||
|
||||
if (existingIndexName != null && existingIndexName.equalsIgnoreCase(indexName)) {
|
||||
return true;
|
||||
AntiVPN.getInstance().getExecutor().log("Creating indexes...");
|
||||
createIndexIfAbsent("whitelisted", "whitelisted_uuid_1", "`uuid`");
|
||||
createIndexIfAbsent("responses", "responses_ip_1", "`ip`");
|
||||
createIndexIfAbsent("responses", "responses_proxy_1", "`proxy`");
|
||||
createIndexIfAbsent("responses", "responses_inserted_1", "`inserted`");
|
||||
createIndexIfAbsent("whitelisted-ips", "whitelisted_ips_ip_1", "`ip`");
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Failed to update database", e);
|
||||
} finally {
|
||||
MiscUtils.close(toClose.toArray(AutoCloseable[]::new));
|
||||
toClose.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try (var statement = Query.prepare("select * from `database_version` where version = 0")) {
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
private ExecutableStatement closeOnEnd(ExecutableStatement statement) {
|
||||
toClose.add(statement);
|
||||
return statement;
|
||||
}
|
||||
|
||||
protected void createIndexIfAbsent(String tableName, String indexName, String columnList) throws SQLException {
|
||||
if (hasIndex(tableName, indexName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeOnEnd(Query.prepare(String.format(
|
||||
"create index `%s` on `%s` (%s)",
|
||||
indexName,
|
||||
tableName,
|
||||
columnList
|
||||
))).execute();
|
||||
}
|
||||
|
||||
protected void dropIndexIfPresent(String tableName, String indexName) throws SQLException {
|
||||
if (!hasIndex(tableName, indexName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeOnEnd(Query.prepare(String.format(
|
||||
"drop index `%s` on `%s`",
|
||||
indexName,
|
||||
tableName
|
||||
))).execute();
|
||||
}
|
||||
|
||||
protected boolean hasIndex(String tableName, String indexName) throws SQLException {
|
||||
DatabaseMetaData metaData = Query.getConn().getMetaData();
|
||||
|
||||
try (ResultSet indexes = metaData.getIndexInfo(null, null, tableName, false, false)) {
|
||||
while (indexes.next()) {
|
||||
String existingIndexName = indexes.getString("INDEX_NAME");
|
||||
|
||||
if (existingIndexName != null && existingIndexName.equalsIgnoreCase(indexName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try(var statement = Query.prepare("select * from `database_version` where version = 0")) {
|
||||
try(ResultSet set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+107
-122
@@ -26,148 +26,133 @@ import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Second extends First implements Version<VPNDatabase> {
|
||||
private final List<AutoCloseable> toClose = new ArrayList<>();
|
||||
private final List<AutoCloseable> toClose = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
if (database instanceof H2VPN h2VPN && !(database instanceof MySqlVPN)) {
|
||||
h2VPN.backupDatabase();
|
||||
}
|
||||
List<String> whitelistedIps = new ArrayList<>();
|
||||
|
||||
try (var statement = Query.prepare("SELECT * FROM `whitelisted-ips`")) {
|
||||
try (var set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
whitelistedIps.add(set.getString("ip"));
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
if(database instanceof H2VPN h2VPN && !(database instanceof MySqlVPN)) {
|
||||
h2VPN.backupDatabase();
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Could not get whitelisted ips from database!", e);
|
||||
}
|
||||
List<String> whitelistedIps = new ArrayList<>();
|
||||
|
||||
try {
|
||||
closeOnEnd(
|
||||
Query.prepare(
|
||||
"CREATE TABLE IF NOT EXISTS `whitelisted-ranges` "
|
||||
+ "(id INT AUTO_INCREMENT PRIMARY KEY, "
|
||||
+ "cidr_string VARCHAR(45), "
|
||||
+ "ip_start BIGINT NOT NULL, "
|
||||
+ "ip_end BIGINT NOT NULL)"))
|
||||
.execute();
|
||||
createIndexIfAbsent("whitelisted-ranges", "idx_ip_range", "ip_start, ip_end");
|
||||
|
||||
var cidrs =
|
||||
whitelistedIps.stream()
|
||||
.map(
|
||||
ip -> {
|
||||
try {
|
||||
return new CIDRUtils(ip + "/32");
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException("Could not format ip " + ip + " into a CIDR!", e);
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
var insertStatement =
|
||||
Query.prepare(
|
||||
"INSERT INTO `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) VALUES (?, ?, ?)");
|
||||
for (CIDRUtils cidr : cidrs) {
|
||||
insertStatement =
|
||||
insertStatement
|
||||
.append(cidr.toString())
|
||||
.append(cidr.getStartIpInt())
|
||||
.append(cidr.getEndIpInt())
|
||||
.addBatch();
|
||||
}
|
||||
|
||||
int[] updateCounts = insertStatement.executeBatch();
|
||||
|
||||
for (int updateCount : updateCounts) {
|
||||
if (updateCount == 0) {
|
||||
throw new RuntimeException(
|
||||
"Could not insert a CIDR from previous whitelisted lists, attempted to restore previous database!");
|
||||
try (var statement = Query.prepare("SELECT * FROM `whitelisted-ips`")) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
whitelistedIps.add(set.getString("ip"));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Could not get whitelisted ips from database!", e);
|
||||
}
|
||||
}
|
||||
|
||||
dropIndexIfPresent("whitelisted-ips", "ip_1");
|
||||
dropIndexIfPresent("whitelisted-ips", "whitelisted_ips_ip_1");
|
||||
closeOnEnd(Query.prepare("DROP TABLE `whitelisted-ips`")).execute();
|
||||
closeOnEnd(
|
||||
Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)")
|
||||
.append(versionNumber()))
|
||||
.execute();
|
||||
} catch (Throwable e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Failed to update database to version 1: " + e.getMessage());
|
||||
try {
|
||||
rollback(whitelistedIps);
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Failed to rollback database!", e);
|
||||
}
|
||||
throw new DatabaseException("Failed to update to version one, rolling back database!", e);
|
||||
} finally {
|
||||
MiscUtils.close(toClose.toArray(AutoCloseable[]::new));
|
||||
toClose.clear();
|
||||
}
|
||||
}
|
||||
try {
|
||||
closeOnEnd(Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ranges` " +
|
||||
"(id INT AUTO_INCREMENT PRIMARY KEY, " +
|
||||
"cidr_string VARCHAR(45), " +
|
||||
"ip_start BIGINT NOT NULL, " +
|
||||
"ip_end BIGINT NOT NULL)"))
|
||||
.execute();
|
||||
createIndexIfAbsent("whitelisted-ranges", "idx_ip_range", "ip_start, ip_end");
|
||||
|
||||
private ExecutableStatement closeOnEnd(ExecutableStatement statement) {
|
||||
toClose.add(statement);
|
||||
return statement;
|
||||
}
|
||||
var cidrs = whitelistedIps.stream().map(ip -> {
|
||||
try {
|
||||
return new CIDRUtils(ip + "/32");
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException("Could not format ip " + ip + " into a CIDR!", e);
|
||||
}
|
||||
}).toList();
|
||||
var insertStatement = Query.prepare("INSERT INTO `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) VALUES (?, ?, ?)");
|
||||
for (CIDRUtils cidr : cidrs) {
|
||||
insertStatement = insertStatement
|
||||
.append(cidr.toString())
|
||||
.append(cidr.getStartIpInt())
|
||||
.append(cidr.getEndIpInt())
|
||||
.addBatch();
|
||||
}
|
||||
|
||||
int[] updateCounts = insertStatement.executeBatch();
|
||||
|
||||
for (int updateCount : updateCounts) {
|
||||
if(updateCount == 0) {
|
||||
throw new RuntimeException("Could not insert a CIDR from previous whitelisted lists, attempted to restore previous database!");
|
||||
}
|
||||
}
|
||||
|
||||
dropIndexIfPresent("whitelisted-ips", "ip_1");
|
||||
dropIndexIfPresent("whitelisted-ips", "whitelisted_ips_ip_1");
|
||||
closeOnEnd(Query.prepare("DROP TABLE `whitelisted-ips`")).execute();
|
||||
closeOnEnd(Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)").append(versionNumber())).execute();
|
||||
} catch (Throwable e) {
|
||||
AntiVPN.getInstance().getExecutor().log("Failed to update database to version 1: " + e.getMessage());
|
||||
try {
|
||||
rollback(whitelistedIps);
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Failed to rollback database!", e);
|
||||
}
|
||||
throw new DatabaseException("Failed to update to version one, rolling back database!", e);
|
||||
} finally {
|
||||
MiscUtils.close(toClose.toArray(AutoCloseable[]::new));
|
||||
toClose.clear();
|
||||
}
|
||||
|
||||
private void rollback(List<String> ipAddresses) throws SQLException {
|
||||
AntiVPN.getInstance().getExecutor().log("Rolling back to version 0...");
|
||||
dropIndexIfPresent("whitelisted-ranges", "idx_ip_range");
|
||||
try (var statement = Query.prepare("DROP TABLE `whitelisted-ranges`")) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
try (var statement =
|
||||
Query.prepare("DELETE FROM `database_version` WHERE version = ?").append(versionNumber())) {
|
||||
statement.execute();
|
||||
private ExecutableStatement closeOnEnd(ExecutableStatement statement) {
|
||||
toClose.add(statement);
|
||||
return statement;
|
||||
}
|
||||
|
||||
try (var statement =
|
||||
Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ips` (`ip` VARCHAR(45) NOT NULL)")) {
|
||||
statement.execute();
|
||||
private void rollback(List<String> ipAddresses) throws SQLException {
|
||||
AntiVPN.getInstance().getExecutor().log("Rolling back to version 0...");
|
||||
dropIndexIfPresent("whitelisted-ranges", "idx_ip_range");
|
||||
try(var statement = Query.prepare("DROP TABLE `whitelisted-ranges`")) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
try(var statement = Query.prepare("DELETE FROM `database_version` WHERE version = ?").append(versionNumber())) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
try(var statement = Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ips` (`ip` VARCHAR(45) NOT NULL)")) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
createIndexIfAbsent("whitelisted-ips", "whitelisted_ips_ip_1", "`ip`");
|
||||
|
||||
try(var statement = Query.prepare("DELETE FROM `whitelisted-ips`")) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
try(var statement = Query.prepare("INSERT INTO `whitelisted-ips` (`ip`) VALUES (?)")) {
|
||||
for (String ip : ipAddresses) {
|
||||
statement.append(ip);
|
||||
statement.addBatch();
|
||||
}
|
||||
|
||||
statement.executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
createIndexIfAbsent("whitelisted-ips", "whitelisted_ips_ip_1", "`ip`");
|
||||
|
||||
try (var statement = Query.prepare("DELETE FROM `whitelisted-ips`")) {
|
||||
statement.execute();
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
try (var statement = Query.prepare("INSERT INTO `whitelisted-ips` (`ip`) VALUES (?)")) {
|
||||
for (String ip : ipAddresses) {
|
||||
statement.append(ip);
|
||||
statement.addBatch();
|
||||
}
|
||||
|
||||
statement.executeBatch();
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try (var statement = Query.prepare("select * from `database_version` where version = 1")) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try (var statement = Query.prepare("select * from `database_version` where version = 1")) {
|
||||
try (var set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
-120
@@ -23,6 +23,7 @@ import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.UnknownHostException;
|
||||
import java.sql.SQLException;
|
||||
@@ -31,132 +32,84 @@ import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Third implements Version<VPNDatabase> {
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
List<CIDRUtils> ipRanges = new ArrayList<>();
|
||||
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
||||
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
||||
try (var preparedQuery = Query.prepare("select ip_start, ip_end from `whitelisted-ranges`")) {
|
||||
preparedQuery.execute(
|
||||
set -> {
|
||||
BigInteger start = set.getBigDecimal("ip_start").toBigInteger();
|
||||
BigInteger end = set.getBigDecimal("ip_end").toBigInteger();
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
List<CIDRUtils> ipRanges = new ArrayList<>();
|
||||
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
||||
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
||||
try (var preparedQuery = Query.prepare("select ip_start, ip_end from `whitelisted-ranges`")) {
|
||||
preparedQuery.execute(set -> {
|
||||
BigInteger start = set.getBigDecimal("ip_start").toBigInteger();
|
||||
BigInteger end = set.getBigDecimal("ip_end").toBigInteger();
|
||||
|
||||
try {
|
||||
var range = MiscUtils.rangeToCidrs(start, end);
|
||||
try {
|
||||
var range = MiscUtils.rangeToCidrs(start, end);
|
||||
|
||||
if (range.size() > 1) {
|
||||
rangesToRemove.add(new BigInteger[] {start, end});
|
||||
rangesToInsert.addAll(range);
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
Level.WARNING,
|
||||
"Found multiple CIDR ranges for whitelist range for %s, %s!",
|
||||
start,
|
||||
end);
|
||||
} else ipRanges.addAll(range);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException(
|
||||
String.format("Could not convert ip range to CIDR! %s, %s", start, end), e);
|
||||
if(range.size() > 1) {
|
||||
rangesToRemove.add(new BigInteger[]{start, end});
|
||||
rangesToInsert.addAll(range);
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "Found multiple CIDR ranges for whitelist range for %s, %s!", start, end);
|
||||
} else ipRanges.addAll(range);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(
|
||||
String.format("Could not convert ip range to CIDR! %s, %s", start, end), e);
|
||||
}
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted ranges due to SQL error.", e);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Inserting %s new ranges into database...", rangesToInsert.size());
|
||||
|
||||
for (CIDRUtils cidr : rangesToInsert) {
|
||||
try(var statement = Query.prepare("insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
||||
.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not get all whitelisted ranges due to SQL error.", e);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Removing %s old ranges from database...", rangesToRemove.size());
|
||||
|
||||
for (BigInteger[] range : rangesToRemove) {
|
||||
try(var statement = Query.prepare("delete from `whitelisted-ranges` where `ip_start` = ? and `ip_end` = ?")) {
|
||||
statement.append(range[0]).append(range[1]).execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not remove cidr range '" + range[0] + ", " + range[1] + "' from whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Updating %s ranges to proper CIDR notation with the database", ipRanges.size());
|
||||
|
||||
for (CIDRUtils cidr : ipRanges) {
|
||||
try(var statement = Query.prepare("update `whitelisted-ranges` set `cidr_string` = ? where `ip_start` = ? and `ip_end` = ?")) {
|
||||
statement.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt()).execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not update cidr '" + cidr + "' to proper CIDR notation in whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
try (var preparedStatement = Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)").append(versionNumber())) {
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not update database version to 2 due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Inserting %s new ranges into database...", rangesToInsert.size());
|
||||
|
||||
for (CIDRUtils cidr : rangesToInsert) {
|
||||
try (var statement =
|
||||
Query.prepare(
|
||||
"insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
||||
.append(cidr.getCidr())
|
||||
.append(cidr.getStartIpInt())
|
||||
.append(cidr.getEndIpInt())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Removing %s old ranges from database...", rangesToRemove.size());
|
||||
|
||||
for (BigInteger[] range : rangesToRemove) {
|
||||
try (var statement =
|
||||
Query.prepare("delete from `whitelisted-ranges` where `ip_start` = ? and `ip_end` = ?")) {
|
||||
statement.append(range[0]).append(range[1]).execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException(
|
||||
"Could not remove cidr range '"
|
||||
+ range[0]
|
||||
+ ", "
|
||||
+ range[1]
|
||||
+ "' from whitelist due to SQL error.",
|
||||
e);
|
||||
}
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try (var statement = Query.prepare("select * from `database_version` where version = 2")) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Updating %s ranges to proper CIDR notation with the database", ipRanges.size());
|
||||
|
||||
for (CIDRUtils cidr : ipRanges) {
|
||||
try (var statement =
|
||||
Query.prepare(
|
||||
"update `whitelisted-ranges` set `cidr_string` = ? where `ip_start` = ? and `ip_end` = ?")) {
|
||||
statement
|
||||
.append(cidr.getCidr())
|
||||
.append(cidr.getStartIpInt())
|
||||
.append(cidr.getEndIpInt())
|
||||
.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException(
|
||||
"Could not update cidr '"
|
||||
+ cidr
|
||||
+ "' to proper CIDR notation in whitelist due to SQL error.",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
try (var preparedStatement =
|
||||
Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)")
|
||||
.append(versionNumber())) {
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Could not update database version to 2 due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try (var statement = Query.prepare("select * from `database_version` where version = 2")) {
|
||||
try (var set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package dev.brighten.antivpn.database.mongo;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.mongodb.*;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
@@ -28,305 +30,251 @@ import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
public class MongoVPN implements VPNDatabase {
|
||||
|
||||
public MongoCollection<Document> settingsDocument;
|
||||
MongoCollection<Document> cacheDocument;
|
||||
private MongoClient client;
|
||||
public MongoDatabase antivpnDatabase;
|
||||
public MongoCollection<Document> settingsDocument;
|
||||
MongoCollection<Document> cacheDocument;
|
||||
private MongoClient client;
|
||||
public MongoDatabase antivpnDatabase;
|
||||
|
||||
public MongoVPN() {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.scheduleAtFixedRate(
|
||||
() -> {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||
public MongoVPN() {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||
|
||||
// Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
//Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
|
||||
// Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
},
|
||||
2,
|
||||
30,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
|
||||
|
||||
if (rdoc != null) {
|
||||
long lastUpdate = rdoc.get("lastAccess", 0L);
|
||||
|
||||
if (System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> deleteResponse(ip));
|
||||
return null;
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
VPNResponse.builder()
|
||||
.asn(rdoc.getString("asn"))
|
||||
.ip(ip)
|
||||
.countryName(rdoc.getString("countryName"))
|
||||
.countryCode(rdoc.getString("countryCode"))
|
||||
.city(rdoc.getString("city"))
|
||||
.isp(rdoc.getString("isp"))
|
||||
.method(rdoc.getString("method"))
|
||||
.timeZone(rdoc.getString("timeZone"))
|
||||
.proxy(rdoc.getBoolean("proxy"))
|
||||
.cached(rdoc.getBoolean("cached"))
|
||||
.success(true)
|
||||
.latitude(rdoc.getDouble("latitude"))
|
||||
.longitude(rdoc.getDouble("longitude"))
|
||||
.lastAccess(rdoc.get("lastAccess", 0L))
|
||||
.build());
|
||||
//Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
}, 2, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
|
||||
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
if (AntiVPN.getInstance().getVpnConfig().cachedResults()) {
|
||||
Document rdoc = new Document("ip", toCache.getIp());
|
||||
if(rdoc != null) {
|
||||
long lastUpdate = rdoc.get("lastAccess", 0L);
|
||||
|
||||
rdoc.put("asn", toCache.getAsn());
|
||||
rdoc.put("countryName", toCache.getCountryName());
|
||||
rdoc.put("countryCode", toCache.getCountryCode());
|
||||
rdoc.put("city", toCache.getCity());
|
||||
rdoc.put("isp", toCache.getIsp());
|
||||
rdoc.put("method", toCache.getMethod());
|
||||
rdoc.put("timeZone", toCache.getTimeZone());
|
||||
rdoc.put("proxy", toCache.isProxy());
|
||||
rdoc.put("cached", toCache.isCached());
|
||||
rdoc.put("success", toCache.isSuccess());
|
||||
rdoc.put("latitude", toCache.getLatitude());
|
||||
rdoc.put("longitude", toCache.getLongitude());
|
||||
rdoc.put("lastAccess", System.currentTimeMillis());
|
||||
if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> deleteResponse(ip));
|
||||
return null;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.execute(
|
||||
() -> {
|
||||
return Optional.of(VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip)
|
||||
.countryName(rdoc.getString("countryName"))
|
||||
.countryCode(rdoc.getString("countryCode"))
|
||||
.city(rdoc.getString("city"))
|
||||
.isp(rdoc.getString("isp"))
|
||||
.method(rdoc.getString("method"))
|
||||
.timeZone(rdoc.getString("timeZone"))
|
||||
.proxy(rdoc.getBoolean("proxy"))
|
||||
.cached(rdoc.getBoolean("cached"))
|
||||
.success(true)
|
||||
.latitude(rdoc.getDouble("latitude"))
|
||||
.longitude(rdoc.getDouble("longitude"))
|
||||
.lastAccess(rdoc.get("lastAccess", 0L))
|
||||
.build());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
if(AntiVPN.getInstance().getVpnConfig().cachedResults()) {
|
||||
Document rdoc = new Document("ip", toCache.getIp());
|
||||
|
||||
rdoc.put("asn", toCache.getAsn());
|
||||
rdoc.put("countryName", toCache.getCountryName());
|
||||
rdoc.put("countryCode", toCache.getCountryCode());
|
||||
rdoc.put("city", toCache.getCity());
|
||||
rdoc.put("isp", toCache.getIsp());
|
||||
rdoc.put("method", toCache.getMethod());
|
||||
rdoc.put("timeZone", toCache.getTimeZone());
|
||||
rdoc.put("proxy", toCache.isProxy());
|
||||
rdoc.put("cached", toCache.isCached());
|
||||
rdoc.put("success", toCache.isSuccess());
|
||||
rdoc.put("latitude", toCache.getLatitude());
|
||||
rdoc.put("longitude", toCache.getLongitude());
|
||||
rdoc.put("lastAccess", System.currentTimeMillis());
|
||||
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
||||
Bson update = new Document("$set", rdoc);
|
||||
cacheDocument.updateOne(
|
||||
Filters.eq("ip", toCache.getIp()), update, new UpdateOptions().upsert(true));
|
||||
});
|
||||
cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update,
|
||||
new UpdateOptions().upsert(true));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteResponse(String ip) {
|
||||
cacheDocument.deleteMany(Filters.eq("ip", ip));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
return settingsDocument
|
||||
.find(
|
||||
Filters.and(
|
||||
Filters.eq("setting", "whitelist"), Filters.eq("uuid", uuid.toString())))
|
||||
.first()
|
||||
!= null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
try {
|
||||
return isWhitelisted(new CIDRUtils(cidr));
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().log("Failed to check whitelist for IP: " + cidr, e);
|
||||
return false;
|
||||
@Override
|
||||
public void deleteResponse(String ip) {
|
||||
cacheDocument.deleteMany(Filters.eq("ip", ip));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(CIDRUtils cidr) {
|
||||
var start = new Decimal128(new BigDecimal(cidr.getStartIpInt()));
|
||||
var end = new Decimal128(new BigDecimal(cidr.getEndIpInt()));
|
||||
return settingsDocument
|
||||
.find(
|
||||
Filters.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.lte("ip_start", start),
|
||||
Filters.gte("ip_end", end)))
|
||||
.first()
|
||||
!= null;
|
||||
}
|
||||
@Override
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
return settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("uuid", uuid.toString()))).first() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(UUID uuid) {
|
||||
Document wdoc = new Document("setting", "whitelist");
|
||||
wdoc.put("uuid", uuid.toString());
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
settingsDocument.insertOne(wdoc);
|
||||
}
|
||||
@Override
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
try {
|
||||
return isWhitelisted(new CIDRUtils(cidr));
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().log("Failed to check whitelist for IP: " + cidr, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(UUID uuid) {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
settingsDocument.deleteMany(
|
||||
Filters.and(Filters.eq("setting", "whitelist"), Filters.eq("uuid", uuid.toString())));
|
||||
}
|
||||
@Override
|
||||
public boolean isWhitelisted(CIDRUtils cidr) {
|
||||
var start = new Decimal128(new BigDecimal(cidr.getStartIpInt()));
|
||||
var end = new Decimal128(new BigDecimal(cidr.getEndIpInt()));
|
||||
return settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.lte("ip_start", start), Filters.gte("ip_end", end))).first() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(CIDRUtils cidr) {
|
||||
Document doc = new Document("setting", "whitelist");
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.getCidr());
|
||||
@Override
|
||||
public void addWhitelist(UUID uuid) {
|
||||
Document wdoc = new Document("setting", "whitelist");
|
||||
wdoc.put("uuid", uuid.toString());
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
settingsDocument.insertOne(wdoc);
|
||||
}
|
||||
|
||||
settingsDocument.insertOne(doc);
|
||||
}
|
||||
@Override
|
||||
public void removeWhitelist(UUID uuid) {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
settingsDocument.deleteMany(Filters
|
||||
.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("uuid", uuid.toString())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(CIDRUtils cidr) {
|
||||
settingsDocument.deleteMany(
|
||||
Filters.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
||||
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))));
|
||||
}
|
||||
@Override
|
||||
public void addWhitelist(CIDRUtils cidr) {
|
||||
Document doc = new Document("setting", "whitelist");
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.getCidr());
|
||||
|
||||
@Override
|
||||
public List<UUID> getAllWhitelisted() {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("uuid")))
|
||||
.forEach(
|
||||
(Consumer<? super Document>) doc -> uuids.add(UUID.fromString(doc.getString("uuid"))));
|
||||
return uuids;
|
||||
}
|
||||
settingsDocument.insertOne(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CIDRUtils> getAllWhitelistedIps() {
|
||||
List<CIDRUtils> ips = new ArrayList<>();
|
||||
settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("cidr_string")))
|
||||
.forEach(
|
||||
(Consumer<? super Document>)
|
||||
doc -> {
|
||||
try {
|
||||
var cidr = new CIDRUtils(doc.getString("cidr_string"));
|
||||
ips.add(cidr);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException(
|
||||
"Could not format ip " + doc.getString("cidr_string") + " into a CIDR!",
|
||||
e);
|
||||
}
|
||||
@Override
|
||||
public void removeWhitelist(CIDRUtils cidr) {
|
||||
settingsDocument.deleteMany(Filters
|
||||
.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
||||
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAllWhitelisted() {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.exists("uuid")))
|
||||
.forEach((Consumer<? super Document>) doc -> uuids.add(UUID.fromString(doc.getString("uuid"))));
|
||||
return uuids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CIDRUtils> getAllWhitelistedIps() {
|
||||
List<CIDRUtils> ips = new ArrayList<>();
|
||||
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.exists("cidr_string"))).forEach((Consumer<? super Document>) doc -> {
|
||||
try {
|
||||
var cidr = new CIDRUtils(doc.getString("cidr_string"));
|
||||
ips.add(cidr);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not format ip " + doc.getString("cidr_string") + " into a CIDR!", e);
|
||||
}
|
||||
});
|
||||
return ips;
|
||||
}
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.execute(
|
||||
() ->
|
||||
result.accept(
|
||||
settingsDocument
|
||||
.find(
|
||||
Filters.and(
|
||||
Filters.eq("setting", "alerts"),
|
||||
Filters.eq("uuid", uuid.toString())))
|
||||
.first()
|
||||
!= null));
|
||||
}
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "alerts"),
|
||||
Filters.eq("uuid", uuid.toString()))).first() != null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean state) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.execute(
|
||||
() -> {
|
||||
settingsDocument.deleteMany(
|
||||
Filters.and(
|
||||
Filters.eq("setting", "alerts"), Filters.eq("uuid", uuid.toString())));
|
||||
if (state) {
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean state) {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
||||
settingsDocument.deleteMany(Filters.and(Filters.eq("setting", "alerts"),
|
||||
Filters.eq("uuid", uuid.toString())));
|
||||
if(state) {
|
||||
Document adoc = new Document("setting", "alerts");
|
||||
|
||||
adoc.put("uuid", uuid.toString());
|
||||
settingsDocument.insertOne(adoc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearResponses() {
|
||||
cacheDocument.deleteMany(Filters.exists("ip"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL().isEmpty()) { // URL
|
||||
ConnectionString cs =
|
||||
new ConnectionString(AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL());
|
||||
MongoClientSettings settings =
|
||||
MongoClientSettings.builder().applyConnectionString(cs).build();
|
||||
client = MongoClients.create(settings);
|
||||
} else {
|
||||
MongoClientSettings.Builder settingsBld =
|
||||
MongoClientSettings.builder()
|
||||
.readPreference(ReadPreference.nearest())
|
||||
.applyToClusterSettings(
|
||||
builder ->
|
||||
builder.hosts(
|
||||
Collections.singletonList(
|
||||
new ServerAddress(
|
||||
AntiVPN.getInstance().getVpnConfig().getIp(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPort()))));
|
||||
if (AntiVPN.getInstance().getVpnConfig().useDatabaseCreds()) {
|
||||
settingsBld.credential(
|
||||
MongoCredential.createCredential(
|
||||
AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||
AntiVPN.getInstance().getVpnConfig().getDatabaseName(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPassword().toCharArray()));
|
||||
}
|
||||
|
||||
client = MongoClients.create(settingsBld.build());
|
||||
}
|
||||
});
|
||||
}
|
||||
antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getVpnConfig().getDatabaseName());
|
||||
|
||||
settingsDocument = antivpnDatabase.getCollection("settings");
|
||||
|
||||
cacheDocument = antivpnDatabase.getCollection("cache");
|
||||
|
||||
for (Version<MongoVPN> mongoDbVersion : Version.mongoDbVersions) {
|
||||
if (mongoDbVersion.needsUpdate(this)) {
|
||||
mongoDbVersion.update(this);
|
||||
}
|
||||
@Override
|
||||
public void clearResponses() {
|
||||
cacheDocument.deleteMany(Filters.exists("ip"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
settingsDocument = null;
|
||||
cacheDocument = null;
|
||||
client.close();
|
||||
}
|
||||
@Override
|
||||
public void init() {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL().isEmpty()) { //URL
|
||||
ConnectionString cs = new ConnectionString(AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL());
|
||||
MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(cs).build();
|
||||
client = MongoClients.create(settings);
|
||||
} else {
|
||||
MongoClientSettings.Builder settingsBld = MongoClientSettings.builder().readPreference(ReadPreference.nearest())
|
||||
.applyToClusterSettings(builder -> builder.
|
||||
hosts(Collections.singletonList(
|
||||
new ServerAddress(
|
||||
AntiVPN.getInstance().getVpnConfig().getIp(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPort())
|
||||
)));
|
||||
if(AntiVPN.getInstance().getVpnConfig().useDatabaseCreds()) {
|
||||
settingsBld.credential(MongoCredential
|
||||
.createCredential(AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||
AntiVPN.getInstance().getVpnConfig().getDatabaseName(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPassword().toCharArray()));
|
||||
}
|
||||
|
||||
client = MongoClients.create(settingsBld.build());
|
||||
}
|
||||
antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getVpnConfig().getDatabaseName());
|
||||
|
||||
settingsDocument = antivpnDatabase.getCollection("settings");
|
||||
|
||||
cacheDocument = antivpnDatabase.getCollection("cache");
|
||||
|
||||
for (Version<MongoVPN> mongoDbVersion : Version.mongoDbVersions) {
|
||||
if(mongoDbVersion.needsUpdate(this)) {
|
||||
mongoDbVersion.update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
settingsDocument = null;
|
||||
cacheDocument = null;
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
+19
-19
@@ -26,27 +26,27 @@ import org.bson.Document;
|
||||
|
||||
public class MongoFirst implements Version<MongoVPN> {
|
||||
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
if (database.settingsDocument.listIndexes().first() == null) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created index for settings collection!");
|
||||
database.settingsDocument.createIndex(Indexes.ascending("ip"));
|
||||
database.settingsDocument.createIndex(Indexes.ascending("setting"));
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
if(database.settingsDocument.listIndexes().first() == null) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created index for settings collection!");
|
||||
database.settingsDocument.createIndex(Indexes.ascending("ip"));
|
||||
database.settingsDocument.createIndex(Indexes.ascending("setting"));
|
||||
}
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
}
|
||||
|
||||
+41
-47
@@ -23,68 +23,62 @@ import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
public class MongoSecond implements Version<MongoVPN> {
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
List<Document> backup = new ArrayList<>();
|
||||
database
|
||||
.settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("ip")))
|
||||
.forEach(
|
||||
(Consumer<? super Document>)
|
||||
doc -> {
|
||||
backup.add(new Document(doc));
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
List<Document> backup = new ArrayList<>();
|
||||
database.settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.exists("ip")))
|
||||
.forEach((Consumer<? super Document>) doc -> {
|
||||
backup.add(new Document(doc));
|
||||
|
||||
String ip = doc.getString("ip");
|
||||
String ip = doc.getString("ip");
|
||||
|
||||
try {
|
||||
var cidr = new CIDRUtils(ip + "/32");
|
||||
try {
|
||||
var cidr = new CIDRUtils(ip + "/32");
|
||||
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.toString());
|
||||
doc.remove("ip");
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.toString());
|
||||
doc.remove("ip");
|
||||
|
||||
database.settingsDocument.replaceOne(
|
||||
Filters.eq("_id", doc.getObjectId("_id")), doc);
|
||||
} catch (UnknownHostException e) {
|
||||
rollback(backup, database);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
database.settingsDocument.replaceOne(Filters.eq("_id", doc.getObjectId("_id")), doc);
|
||||
} catch (UnknownHostException e) {
|
||||
rollback(backup, database);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
database.settingsDocument.createIndex(
|
||||
Indexes.compoundIndex(Indexes.ascending("ip_start"), Indexes.ascending("ip_end")));
|
||||
database.settingsDocument.createIndex(Indexes.ascending("cidr_string"));
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
database.settingsDocument.createIndex(Indexes.compoundIndex(Indexes.ascending("ip_start"), Indexes.ascending("ip_end")));
|
||||
database.settingsDocument.createIndex(Indexes.ascending("cidr_string"));
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
|
||||
private void rollback(List<Document> toRollback, MongoVPN database) {
|
||||
AntiVPN.getInstance().getExecutor().log("Rolling back to version 0...");
|
||||
toRollback.forEach(
|
||||
doc ->
|
||||
database.settingsDocument.replaceOne(Filters.eq("_id", doc.getObjectId("_id")), doc));
|
||||
toRollback.clear();
|
||||
}
|
||||
private void rollback(List<Document> toRollback, MongoVPN database) {
|
||||
AntiVPN.getInstance().getExecutor().log("Rolling back to version 0...");
|
||||
toRollback.forEach(doc -> database.settingsDocument.replaceOne(Filters.eq("_id", doc.getObjectId("_id")), doc));
|
||||
toRollback.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 1;
|
||||
}
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
}
|
||||
|
||||
+63
-92
@@ -23,6 +23,9 @@ import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.UnknownHostException;
|
||||
@@ -30,108 +33,76 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
public class MongoThird implements Version<MongoVPN> {
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
List<CIDRUtils> ipRanges = new ArrayList<>();
|
||||
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
||||
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
||||
database
|
||||
.settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("cidr_string")))
|
||||
.forEach(
|
||||
(Consumer<? super Document>)
|
||||
doc -> {
|
||||
BigInteger start =
|
||||
doc.get("ip_start", Decimal128.class).bigDecimalValue().toBigInteger();
|
||||
BigInteger end =
|
||||
doc.get("ip_end", Decimal128.class).bigDecimalValue().toBigInteger();
|
||||
public class MongoThird implements Version<MongoVPN> {
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
List<CIDRUtils> ipRanges = new ArrayList<>();
|
||||
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
||||
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
||||
database.settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("cidr_string")))
|
||||
.forEach((Consumer<? super Document>) doc -> {
|
||||
BigInteger start = doc.get("ip_start", Decimal128.class).bigDecimalValue().toBigInteger();
|
||||
BigInteger end = doc.get("ip_end", Decimal128.class).bigDecimalValue().toBigInteger();
|
||||
|
||||
try {
|
||||
var range = MiscUtils.rangeToCidrs(start, end);
|
||||
try {
|
||||
var range = MiscUtils.rangeToCidrs(start, end);
|
||||
|
||||
if (range.size() > 1) {
|
||||
rangesToRemove.add(new BigInteger[] {start, end});
|
||||
rangesToInsert.addAll(range);
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
Level.WARNING,
|
||||
"Found multiple CIDR ranges for whitelist range for %s, %s!",
|
||||
start,
|
||||
end);
|
||||
} else ipRanges.addAll(range);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException(
|
||||
String.format("Could not convert ip range to CIDR! %s, %s", start, end),
|
||||
e);
|
||||
}
|
||||
if(range.size() > 1) {
|
||||
rangesToRemove.add(new BigInteger[]{start, end});
|
||||
rangesToInsert.addAll(range);
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "Found multiple CIDR ranges for whitelist range for %s, %s!", start, end);
|
||||
} else ipRanges.addAll(range);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(
|
||||
String.format("Could not convert ip range to CIDR! %s, %s", start, end), e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!rangesToInsert.isEmpty()) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Inserting %s new ranges into database...", rangesToInsert.size());
|
||||
var documentsToInsert =
|
||||
rangesToInsert.stream()
|
||||
.map(
|
||||
cidr -> {
|
||||
Document doc = new Document("setting", "whitelist");
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.getCidr());
|
||||
if(!rangesToInsert.isEmpty()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Inserting %s new ranges into database...", rangesToInsert.size());
|
||||
var documentsToInsert = rangesToInsert.stream().map(cidr -> {
|
||||
Document doc = new Document("setting", "whitelist");
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.getCidr());
|
||||
|
||||
return doc;
|
||||
})
|
||||
.toList();
|
||||
return doc;
|
||||
}).toList();
|
||||
|
||||
database.settingsDocument.insertMany(documentsToInsert);
|
||||
}
|
||||
if (!rangesToRemove.isEmpty()) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Removing %s old ranges from database...", rangesToRemove.size());
|
||||
rangesToRemove.forEach(
|
||||
range ->
|
||||
database.settingsDocument.deleteMany(
|
||||
Filters.and(
|
||||
Filters.gte("ip_start", new Decimal128(new BigDecimal(range[0]))),
|
||||
Filters.lte("ip_end", new Decimal128(new BigDecimal(range[1]))))));
|
||||
database.settingsDocument.insertMany(documentsToInsert);
|
||||
}
|
||||
if(!rangesToRemove.isEmpty()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Removing %s old ranges from database...", rangesToRemove.size());
|
||||
rangesToRemove.forEach(range -> database.settingsDocument
|
||||
.deleteMany(Filters.and(
|
||||
Filters.gte("ip_start", new Decimal128(new BigDecimal(range[0]))),
|
||||
Filters.lte("ip_end", new Decimal128(new BigDecimal(range[1]))))));
|
||||
}
|
||||
|
||||
if(!ipRanges.isEmpty()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Updating %s CIDRs in database with proper notation...", ipRanges.size());
|
||||
|
||||
ipRanges.forEach(cidr -> database.settingsDocument
|
||||
.updateMany(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
||||
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))),
|
||||
new Document("$set", new Document("cidr_string", cidr.getCidr()))));
|
||||
}
|
||||
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
|
||||
if (!ipRanges.isEmpty()) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Updating %s CIDRs in database with proper notation...", ipRanges.size());
|
||||
|
||||
ipRanges.forEach(
|
||||
cidr ->
|
||||
database.settingsDocument.updateMany(
|
||||
Filters.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
||||
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))),
|
||||
new Document("$set", new Document("cidr_string", cidr.getCidr()))));
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,55 +20,45 @@ import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MySqlVPN extends H2VPN {
|
||||
|
||||
public MySqlVPN() {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getThreadExecutor()
|
||||
.scheduleAtFixedRate(
|
||||
() -> {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
public MySqlVPN() {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
// Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
//Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
|
||||
// Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
},
|
||||
2,
|
||||
30,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||
AntiVPN.getInstance().getExecutor().log("Initializing MySQL...");
|
||||
MySQL.init();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Checking for updates...");
|
||||
|
||||
// Running check for old table types to update
|
||||
try {
|
||||
for (Version<MySqlVPN> version : Version.mysqlVersions) {
|
||||
if (version.needsUpdate(this)) {
|
||||
version.update(this);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not complete version setup due to SQL error", e);
|
||||
//Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
}, 2, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
AntiVPN.getInstance().getExecutor().log("Initializing MySQL...");
|
||||
MySQL.init();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Checking for updates...");
|
||||
|
||||
//Running check for old table types to update
|
||||
try {
|
||||
for (Version<MySqlVPN> version : Version.mysqlVersions) {
|
||||
if(version.needsUpdate(this)) {
|
||||
version.update(this);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not complete version setup due to SQL error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+104
-102
@@ -16,130 +16,132 @@
|
||||
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ExecutableStatement implements AutoCloseable {
|
||||
@Getter private final PreparedStatement statement;
|
||||
private int pos = 1;
|
||||
@Getter
|
||||
private final PreparedStatement statement;
|
||||
private int pos = 1;
|
||||
|
||||
public ExecutableStatement(PreparedStatement statement) {
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
public int execute() throws SQLException {
|
||||
return statement.executeUpdate();
|
||||
}
|
||||
|
||||
public void execute(ResultSetIterator iterator) throws SQLException {
|
||||
try (var rs = statement.executeQuery()) {
|
||||
while (rs.next()) iterator.next(rs);
|
||||
public ExecutableStatement(PreparedStatement statement) {
|
||||
this.statement = statement;
|
||||
}
|
||||
}
|
||||
|
||||
public int[] executeBatch() throws SQLException {
|
||||
return statement.executeBatch();
|
||||
}
|
||||
public int execute() throws SQLException {
|
||||
return statement.executeUpdate();
|
||||
}
|
||||
|
||||
public ResultSet executeQuery() throws SQLException {
|
||||
return statement.executeQuery();
|
||||
}
|
||||
public void execute(ResultSetIterator iterator) throws SQLException {
|
||||
try(var rs = statement.executeQuery()) {
|
||||
while (rs.next()) iterator.next(rs);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Object obj) {
|
||||
statement.setObject(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
public int[] executeBatch() throws SQLException {
|
||||
return statement.executeBatch();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(String obj) {
|
||||
statement.setString(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
public ResultSet executeQuery() throws SQLException {
|
||||
return statement.executeQuery();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(UUID uuid) {
|
||||
if (uuid != null) statement.setString(pos++, uuid.toString().replace("-", ""));
|
||||
else statement.setString(pos++, null);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Object obj) {
|
||||
statement.setObject(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Array obj) {
|
||||
statement.setArray(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(String obj) {
|
||||
statement.setString(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Integer obj) {
|
||||
statement.setInt(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(UUID uuid) {
|
||||
if (uuid != null) statement.setString(pos++, uuid.toString().replace("-", ""));
|
||||
else statement.setString(pos++, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Short obj) {
|
||||
statement.setShort(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Array obj) {
|
||||
statement.setArray(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Long obj) {
|
||||
statement.setLong(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Integer obj) {
|
||||
statement.setInt(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Float obj) {
|
||||
statement.setFloat(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Short obj) {
|
||||
statement.setShort(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Double obj) {
|
||||
statement.setDouble(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Long obj) {
|
||||
statement.setLong(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Date obj) {
|
||||
statement.setDate(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Float obj) {
|
||||
statement.setFloat(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Timestamp obj) {
|
||||
statement.setTimestamp(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Double obj) {
|
||||
statement.setDouble(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Time obj) {
|
||||
statement.setTime(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Date obj) {
|
||||
statement.setDate(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Blob obj) {
|
||||
statement.setBlob(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Timestamp obj) {
|
||||
statement.setTimestamp(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(byte[] obj) {
|
||||
statement.setBytes(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Time obj) {
|
||||
statement.setTime(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement addBatch() {
|
||||
statement.addBatch();
|
||||
return this;
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Blob obj) {
|
||||
statement.setBlob(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
statement.close();
|
||||
}
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(byte[] obj) {
|
||||
statement.setBytes(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement addBatch() {
|
||||
statement.addBatch();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import com.mysql.cj.jdbc.Driver;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import org.h2.jdbc.JdbcSQLFeatureNotSupportedException;
|
||||
import org.h2.jdbc.JdbcSQLNonTransientConnectionException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@@ -26,194 +29,167 @@ import java.sql.SQLException;
|
||||
import java.sql.SQLSyntaxErrorException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
import org.h2.jdbc.JdbcSQLFeatureNotSupportedException;
|
||||
import org.h2.jdbc.JdbcSQLNonTransientConnectionException;
|
||||
|
||||
public class MySQL {
|
||||
private static Connection conn;
|
||||
private static Connection conn;
|
||||
|
||||
public static void init() {
|
||||
try {
|
||||
if (conn == null || conn.isClosed()) {
|
||||
String url =
|
||||
"jdbc:mysql://"
|
||||
+ AntiVPN.getInstance().getVpnConfig().getIp()
|
||||
+ ":"
|
||||
+ AntiVPN.getInstance().getVpnConfig().getPort()
|
||||
+ "/?useSSL=true&autoReconnect=true";
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("user", AntiVPN.getInstance().getVpnConfig().getUsername());
|
||||
properties.setProperty("password", AntiVPN.getInstance().getVpnConfig().getPassword());
|
||||
public static void init() {
|
||||
try {
|
||||
if (conn == null || conn.isClosed()) {
|
||||
String url = "jdbc:mysql://" + AntiVPN.getInstance().getVpnConfig().getIp()
|
||||
+ ":" + AntiVPN.getInstance().getVpnConfig().getPort()
|
||||
+ "/?useSSL=true&autoReconnect=true";
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("user", AntiVPN.getInstance().getVpnConfig().getUsername());
|
||||
properties.setProperty("password", AntiVPN.getInstance().getVpnConfig().getPassword());
|
||||
|
||||
conn = new Driver().connect(url, properties);
|
||||
if (conn == null) {
|
||||
throw new SQLException("MySQL driver did not accept URL: " + url);
|
||||
conn = new Driver().connect(url, properties);
|
||||
if (conn == null) {
|
||||
throw new SQLException("MySQL driver did not accept URL: " + url);
|
||||
}
|
||||
conn.setAutoCommit(true);
|
||||
Query.use(conn);
|
||||
String databaseName = AntiVPN.getInstance().getVpnConfig().getDatabaseName();
|
||||
|
||||
try {
|
||||
Query.prepare("CREATE DATABASE IF NOT EXISTS `" + databaseName + "`").execute();
|
||||
} catch (SQLException ex) {
|
||||
if (!isDatabaseCreationPermissionIssue(ex)) {
|
||||
throw ex;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(
|
||||
"No permission to create MySQL database `" + databaseName
|
||||
+ "`. Attempting to use the existing database instead.");
|
||||
}
|
||||
|
||||
Query.prepare("USE `" + databaseName + "`").execute();
|
||||
AntiVPN.getInstance().getExecutor().log("Connection to MySQL has been established.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to load mysql: " + e.getMessage(), e);
|
||||
throw new RuntimeException("Could not initialize MySQL connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDatabaseCreationPermissionIssue(SQLException ex) {
|
||||
return ex instanceof SQLSyntaxErrorException
|
||||
&& ex.getMessage() != null
|
||||
&& ex.getMessage().contains("Access denied");
|
||||
}
|
||||
|
||||
public static void initH2() {
|
||||
initH2(true);
|
||||
}
|
||||
|
||||
private static void initH2(boolean allowRetry) {
|
||||
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases");
|
||||
if (!dataFolder.exists() && dataFolder.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created database directory");
|
||||
}
|
||||
|
||||
File dbFile = new File(dataFolder, "database.mv.db");
|
||||
|
||||
File databaseFile = new File(dataFolder, "database");
|
||||
try {
|
||||
conn = new NonClosableConnection(new org.h2.jdbc.JdbcConnection("jdbc:h2:file:" +
|
||||
databaseFile.getAbsolutePath(),
|
||||
new Properties(), AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPassword(), false));
|
||||
conn.setAutoCommit(true);
|
||||
Query.use(conn);
|
||||
AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established.");
|
||||
} catch (SQLException ex) {
|
||||
AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize", ex);
|
||||
if(ex instanceof JdbcSQLFeatureNotSupportedException
|
||||
|| ex instanceof JdbcSQLNonTransientConnectionException) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.log("H2 database file is incompatible with this version of AntiVPN. " +
|
||||
"Backing up old database file...");
|
||||
shutdown();
|
||||
if (allowRetry && backupOldDB(dbFile, dataFolder)) {
|
||||
initH2(false);
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log(
|
||||
"Could not back up and remove the incompatible H2 database file automatically.");
|
||||
}
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + ex.getCause().toString(), ex);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + e.getMessage(), e);
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "TIP: Try deleting the plugin folder and restarting your server!");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean backupOldDB(File dbFile, File dataFolder) {
|
||||
if (!dbFile.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!dbFile.isFile()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Skipping backup for non-file path: " + dbFile.getAbsolutePath());
|
||||
return false;
|
||||
}
|
||||
conn.setAutoCommit(true);
|
||||
Query.use(conn);
|
||||
String databaseName = AntiVPN.getInstance().getVpnConfig().getDatabaseName();
|
||||
|
||||
try {
|
||||
Query.prepare("CREATE DATABASE IF NOT EXISTS `" + databaseName + "`").execute();
|
||||
} catch (SQLException ex) {
|
||||
if (!isDatabaseCreationPermissionIssue(ex)) {
|
||||
throw ex;
|
||||
}
|
||||
File backupDir = new File(dataFolder, "backups");
|
||||
if(backupDir.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created backup directory");
|
||||
} else if (backupDir.exists()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Backup directory already exists");
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not create backup directory");
|
||||
return false;
|
||||
}
|
||||
File backupFile = new File(backupDir, dbFile.getName() + ".backup_" + System.currentTimeMillis());
|
||||
Files.copy(dbFile.toPath(), backupFile.toPath());
|
||||
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
"No permission to create MySQL database `"
|
||||
+ databaseName
|
||||
+ "`. Attempting to use the existing database instead.");
|
||||
if (!dbFile.delete()) {
|
||||
dbFile.deleteOnExit();
|
||||
AntiVPN.getInstance().getExecutor().log("Could not delete database file - will try again on shutdown");
|
||||
return false;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully deleted incompatible database file");
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to handle database file", ex);
|
||||
}
|
||||
|
||||
Query.prepare("USE `" + databaseName + "`").execute();
|
||||
AntiVPN.getInstance().getExecutor().log("Connection to MySQL has been established.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Failed to load mysql: " + e.getMessage(), e);
|
||||
throw new RuntimeException("Could not initialize MySQL connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDatabaseCreationPermissionIssue(SQLException ex) {
|
||||
return ex instanceof SQLSyntaxErrorException
|
||||
&& ex.getMessage() != null
|
||||
&& ex.getMessage().contains("Access denied");
|
||||
}
|
||||
|
||||
public static void initH2() {
|
||||
initH2(true);
|
||||
}
|
||||
|
||||
private static void initH2(boolean allowRetry) {
|
||||
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases");
|
||||
if (!dataFolder.exists() && dataFolder.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created database directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
File dbFile = new File(dataFolder, "database.mv.db");
|
||||
|
||||
File databaseFile = new File(dataFolder, "database");
|
||||
try {
|
||||
conn =
|
||||
new NonClosableConnection(
|
||||
new org.h2.jdbc.JdbcConnection(
|
||||
"jdbc:h2:file:" + databaseFile.getAbsolutePath(),
|
||||
new Properties(),
|
||||
AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPassword(),
|
||||
false));
|
||||
conn.setAutoCommit(true);
|
||||
Query.use(conn);
|
||||
AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established.");
|
||||
} catch (SQLException ex) {
|
||||
AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize", ex);
|
||||
if (ex instanceof JdbcSQLFeatureNotSupportedException
|
||||
|| ex instanceof JdbcSQLNonTransientConnectionException) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
"H2 database file is incompatible with this version of AntiVPN. "
|
||||
+ "Backing up old database file...");
|
||||
shutdown();
|
||||
if (allowRetry && backupOldDB(dbFile, dataFolder)) {
|
||||
initH2(false);
|
||||
} else {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Could not back up and remove the incompatible H2 database file automatically.");
|
||||
public static void use() {
|
||||
try {
|
||||
init();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
} else {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Failed to load H2 database: " + ex.getCause().toString(), ex);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Failed to load H2 database: " + e.getMessage(), e);
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(Level.INFO, "TIP: Try deleting the plugin folder and restarting your server!");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean backupOldDB(File dbFile, File dataFolder) {
|
||||
if (!dbFile.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!dbFile.isFile()) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Skipping backup for non-file path: " + dbFile.getAbsolutePath());
|
||||
return false;
|
||||
public static void shutdown() {
|
||||
try {
|
||||
if(conn != null && !conn.isClosed()) {
|
||||
if(conn instanceof NonClosableConnection) {
|
||||
((NonClosableConnection)conn).shutdown();
|
||||
} else conn.close();
|
||||
conn = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
File backupDir = new File(dataFolder, "backups");
|
||||
if (backupDir.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created backup directory");
|
||||
} else if (backupDir.exists()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Backup directory already exists");
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not create backup directory");
|
||||
return false;
|
||||
}
|
||||
File backupFile =
|
||||
new File(backupDir, dbFile.getName() + ".backup_" + System.currentTimeMillis());
|
||||
Files.copy(dbFile.toPath(), backupFile.toPath());
|
||||
public static boolean isClosed() {
|
||||
if(conn == null)
|
||||
return true;
|
||||
|
||||
if (!dbFile.delete()) {
|
||||
dbFile.deleteOnExit();
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log("Could not delete database file - will try again on shutdown");
|
||||
return false;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully deleted incompatible database file");
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to handle database file", ex);
|
||||
try {
|
||||
return conn.isClosed();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void use() {
|
||||
try {
|
||||
init();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
try {
|
||||
if (conn != null && !conn.isClosed()) {
|
||||
if (conn instanceof NonClosableConnection) {
|
||||
((NonClosableConnection) conn).shutdown();
|
||||
} else conn.close();
|
||||
conn = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isClosed() {
|
||||
if (conn == null) return true;
|
||||
|
||||
try {
|
||||
return conn.isClosed();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+77
-287
@@ -25,299 +25,89 @@ import java.util.concurrent.Executor;
|
||||
* A wrapper around a {@link Connection} which blocks usage of the default {@link #close()} method.
|
||||
*/
|
||||
public class NonClosableConnection implements Connection {
|
||||
private final Connection delegate;
|
||||
private final Connection delegate;
|
||||
|
||||
public NonClosableConnection(Connection delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/** Actually {@link #close() closes} the underlying connection. */
|
||||
public final void shutdown() throws SQLException {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws SQLException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
return iface.isInstance(this.delegate) || this.delegate.isWrapperFor(iface);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
if (iface.isInstance(this.delegate)) {
|
||||
return (T) this.delegate;
|
||||
public NonClosableConnection(Connection delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
return this.delegate.unwrap(iface);
|
||||
}
|
||||
|
||||
// Forward to the delegate connection
|
||||
@Override
|
||||
public Statement createStatement() throws SQLException {
|
||||
return this.delegate.createStatement();
|
||||
}
|
||||
/**
|
||||
* Actually {@link #close() closes} the underlying connection.
|
||||
*/
|
||||
public final void shutdown() throws SQLException {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql);
|
||||
}
|
||||
@Override
|
||||
public final void close() throws SQLException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String sql) throws SQLException {
|
||||
return this.delegate.prepareCall(sql);
|
||||
}
|
||||
@Override
|
||||
public final boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
return iface.isInstance(this.delegate) || this.delegate.isWrapperFor(iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nativeSQL(String sql) throws SQLException {
|
||||
return this.delegate.nativeSQL(sql);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
if (iface.isInstance(this.delegate)) {
|
||||
return (T) this.delegate;
|
||||
}
|
||||
return this.delegate.unwrap(iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoCommit(boolean autoCommit) throws SQLException {
|
||||
this.delegate.setAutoCommit(autoCommit);
|
||||
}
|
||||
// Forward to the delegate connection
|
||||
@Override public Statement createStatement() throws SQLException { return this.delegate.createStatement(); }
|
||||
@Override public PreparedStatement prepareStatement(String sql) throws SQLException { return this.delegate.prepareStatement(sql); }
|
||||
@Override public CallableStatement prepareCall(String sql) throws SQLException { return this.delegate.prepareCall(sql); }
|
||||
@Override public String nativeSQL(String sql) throws SQLException { return this.delegate.nativeSQL(sql); }
|
||||
@Override public void setAutoCommit(boolean autoCommit) throws SQLException { this.delegate.setAutoCommit(autoCommit); }
|
||||
@Override public boolean getAutoCommit() throws SQLException { return this.delegate.getAutoCommit(); }
|
||||
@Override public void commit() throws SQLException { this.delegate.commit(); }
|
||||
@Override public void rollback() throws SQLException { this.delegate.rollback(); }
|
||||
@Override public boolean isClosed() throws SQLException { return this.delegate.isClosed(); }
|
||||
@Override public DatabaseMetaData getMetaData() throws SQLException { return this.delegate.getMetaData(); }
|
||||
@Override public void setReadOnly(boolean readOnly) throws SQLException { this.delegate.setReadOnly(readOnly); }
|
||||
@Override public boolean isReadOnly() throws SQLException { return this.delegate.isReadOnly(); }
|
||||
@Override public void setCatalog(String catalog) throws SQLException { this.delegate.setCatalog(catalog); }
|
||||
@Override public String getCatalog() throws SQLException { return this.delegate.getCatalog(); }
|
||||
@Override public void setTransactionIsolation(int level) throws SQLException { this.delegate.setTransactionIsolation(level); }
|
||||
@Override public int getTransactionIsolation() throws SQLException { return this.delegate.getTransactionIsolation(); }
|
||||
@Override public SQLWarning getWarnings() throws SQLException { return this.delegate.getWarnings(); }
|
||||
@Override public void clearWarnings() throws SQLException { this.delegate.clearWarnings(); }
|
||||
@Override public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.createStatement(resultSetType, resultSetConcurrency); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); }
|
||||
@Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency); }
|
||||
@Override public Map<String, Class<?>> getTypeMap() throws SQLException { return this.delegate.getTypeMap(); }
|
||||
@Override public void setTypeMap(Map<String, Class<?>> map) throws SQLException { this.delegate.setTypeMap(map); }
|
||||
@Override public void setHoldability(int holdability) throws SQLException { this.delegate.setHoldability(holdability); }
|
||||
@Override public int getHoldability() throws SQLException { return this.delegate.getHoldability(); }
|
||||
@Override public Savepoint setSavepoint() throws SQLException { return this.delegate.setSavepoint(); }
|
||||
@Override public Savepoint setSavepoint(String name) throws SQLException { return this.delegate.setSavepoint(name); }
|
||||
@Override public void rollback(Savepoint savepoint) throws SQLException { this.delegate.rollback(savepoint); }
|
||||
@Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { this.delegate.releaseSavepoint(savepoint); }
|
||||
@Override public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); }
|
||||
@Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { return this.delegate.prepareStatement(sql, autoGeneratedKeys); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { return this.delegate.prepareStatement(sql, columnIndexes); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { return this.delegate.prepareStatement(sql, columnNames); }
|
||||
@Override public Clob createClob() throws SQLException { return this.delegate.createClob(); }
|
||||
@Override public Blob createBlob() throws SQLException { return this.delegate.createBlob(); }
|
||||
@Override public NClob createNClob() throws SQLException { return this.delegate.createNClob(); }
|
||||
@Override public SQLXML createSQLXML() throws SQLException { return this.delegate.createSQLXML(); }
|
||||
@Override public boolean isValid(int timeout) throws SQLException { return this.delegate.isValid(timeout); }
|
||||
@Override public void setClientInfo(String name, String value) throws SQLClientInfoException { this.delegate.setClientInfo(name, value); }
|
||||
@Override public void setClientInfo(Properties properties) throws SQLClientInfoException { this.delegate.setClientInfo(properties); }
|
||||
@Override public String getClientInfo(String name) throws SQLException { return this.delegate.getClientInfo(name); }
|
||||
@Override public Properties getClientInfo() throws SQLException { return this.delegate.getClientInfo(); }
|
||||
@Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { return this.delegate.createArrayOf(typeName, elements); }
|
||||
@Override public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return this.delegate.createStruct(typeName, attributes); }
|
||||
@Override public void setSchema(String schema) throws SQLException { this.delegate.setSchema(schema); }
|
||||
@Override public String getSchema() throws SQLException { return this.delegate.getSchema(); }
|
||||
@Override public void abort(Executor executor) throws SQLException { this.delegate.abort(executor); }
|
||||
@Override public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { this.delegate.setNetworkTimeout(executor, milliseconds); }
|
||||
@Override public int getNetworkTimeout() throws SQLException { return this.delegate.getNetworkTimeout(); }
|
||||
|
||||
@Override
|
||||
public boolean getAutoCommit() throws SQLException {
|
||||
return this.delegate.getAutoCommit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
this.delegate.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
this.delegate.rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
return this.delegate.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseMetaData getMetaData() throws SQLException {
|
||||
return this.delegate.getMetaData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) throws SQLException {
|
||||
this.delegate.setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() throws SQLException {
|
||||
return this.delegate.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCatalog(String catalog) throws SQLException {
|
||||
this.delegate.setCatalog(catalog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCatalog() throws SQLException {
|
||||
return this.delegate.getCatalog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionIsolation(int level) throws SQLException {
|
||||
this.delegate.setTransactionIsolation(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransactionIsolation() throws SQLException {
|
||||
return this.delegate.getTransactionIsolation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
return this.delegate.getWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
this.delegate.clearWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
return this.delegate.createStatement(resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<?>> getTypeMap() throws SQLException {
|
||||
return this.delegate.getTypeMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
|
||||
this.delegate.setTypeMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHoldability(int holdability) throws SQLException {
|
||||
this.delegate.setHoldability(holdability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHoldability() throws SQLException {
|
||||
return this.delegate.getHoldability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint() throws SQLException {
|
||||
return this.delegate.setSavepoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint(String name) throws SQLException {
|
||||
return this.delegate.setSavepoint(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(Savepoint savepoint) throws SQLException {
|
||||
this.delegate.rollback(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
|
||||
this.delegate.releaseSavepoint(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(
|
||||
int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return this.delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(
|
||||
String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
|
||||
throws SQLException {
|
||||
return this.delegate.prepareStatement(
|
||||
sql, resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(
|
||||
String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
|
||||
throws SQLException {
|
||||
return this.delegate.prepareCall(
|
||||
sql, resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, autoGeneratedKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, columnIndexes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, columnNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clob createClob() throws SQLException {
|
||||
return this.delegate.createClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blob createBlob() throws SQLException {
|
||||
return this.delegate.createBlob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NClob createNClob() throws SQLException {
|
||||
return this.delegate.createNClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLXML createSQLXML() throws SQLException {
|
||||
return this.delegate.createSQLXML();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(int timeout) throws SQLException {
|
||||
return this.delegate.isValid(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(String name, String value) throws SQLClientInfoException {
|
||||
this.delegate.setClientInfo(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(Properties properties) throws SQLClientInfoException {
|
||||
this.delegate.setClientInfo(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientInfo(String name) throws SQLException {
|
||||
return this.delegate.getClientInfo(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getClientInfo() throws SQLException {
|
||||
return this.delegate.getClientInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
|
||||
return this.delegate.createArrayOf(typeName, elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
|
||||
return this.delegate.createStruct(typeName, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSchema(String schema) throws SQLException {
|
||||
this.delegate.setSchema(schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSchema() throws SQLException {
|
||||
return this.delegate.getSchema();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(Executor executor) throws SQLException {
|
||||
this.delegate.abort(executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
|
||||
this.delegate.setNetworkTimeout(executor, milliseconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNetworkTimeout() throws SQLException {
|
||||
return this.delegate.getNetworkTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,20 +16,24 @@
|
||||
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import lombok.Getter;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class Query {
|
||||
@Getter private static Connection conn;
|
||||
@Getter
|
||||
private static Connection conn;
|
||||
|
||||
public static void use(Connection conn) {
|
||||
Query.conn = conn;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SqlSourceToSinkFlow")
|
||||
public static ExecutableStatement prepare(@Language("SQL") String sql) throws SQLException {
|
||||
return new ExecutableStatement(conn.prepareStatement(sql));
|
||||
}
|
||||
|
||||
public static void use(Connection conn) {
|
||||
Query.conn = conn;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SqlSourceToSinkFlow")
|
||||
public static ExecutableStatement prepare(@Language("SQL") String sql) throws SQLException {
|
||||
return new ExecutableStatement(conn.prepareStatement(sql));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,5 +20,5 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public interface ResultSetIterator {
|
||||
void next(ResultSet rs) throws SQLException;
|
||||
void next(ResultSet rs) throws SQLException;
|
||||
}
|
||||
|
||||
+21
-24
@@ -21,34 +21,31 @@ import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.version.First;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class MySQLFirst extends First {
|
||||
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
try (var statement =
|
||||
Query.prepare(
|
||||
"select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS "
|
||||
+ "WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';")) {
|
||||
statement.execute(
|
||||
set -> {
|
||||
if (set.getObject("DATA_TYPE").toString().contains("varchar")) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.log(
|
||||
"Using old database format for storing responses! "
|
||||
+ "Dropping table and creating a new one...");
|
||||
try (var state = Query.prepare("drop table `responses`")) {
|
||||
if (state.execute() > 0) {
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
try(var statement = Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " +
|
||||
"WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';")) {
|
||||
statement.execute(set -> {
|
||||
if(set.getObject("DATA_TYPE").toString().contains("varchar")) {
|
||||
AntiVPN.getInstance().getExecutor().log("Using old database format for storing responses! " +
|
||||
"Dropping table and creating a new one...");
|
||||
try(var state = Query.prepare("drop table `responses`")) {
|
||||
if(state.execute() > 0) {
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Could not update MySQL database", e);
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Could not update MySQL database", e);
|
||||
}
|
||||
super.update(database);
|
||||
}
|
||||
super.update(database);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +28,13 @@ import dev.brighten.antivpn.database.mongo.version.MongoThird;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.database.sql.version.MySQLFirst;
|
||||
|
||||
|
||||
public interface Version<DB> {
|
||||
void update(DB database) throws DatabaseException;
|
||||
void update(DB database) throws DatabaseException;
|
||||
int versionNumber();
|
||||
boolean needsUpdate(DB database);
|
||||
|
||||
int versionNumber();
|
||||
|
||||
boolean needsUpdate(DB database);
|
||||
|
||||
Version<MongoVPN>[] mongoDbVersions =
|
||||
new Version[] {new MongoFirst(), new MongoSecond(), new MongoThird()};
|
||||
Version<MySqlVPN>[] mysqlVersions = new Version[] {new MySQLFirst(), new Second(), new Third()};
|
||||
Version<H2VPN>[] h2Versions = new Version[] {new First(), new Second(), new Third()};
|
||||
}
|
||||
Version<MongoVPN>[] mongoDbVersions = new Version[] {new MongoFirst(), new MongoSecond(), new MongoThird()};
|
||||
Version<MySqlVPN>[] mysqlVersions = new Version[] {new MySQLFirst(), new Second(), new Third()};
|
||||
Version<H2VPN>[] h2Versions = new Version[] {new First(), new Second(), new Third()};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,11 +18,14 @@ package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/** Annotation to indicate the required libraries for a class. */
|
||||
/**
|
||||
* Annotation to indicate the required libraries for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibraries {
|
||||
|
||||
MavenLibrary[] value() default {};
|
||||
MavenLibrary[] value() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -18,40 +18,43 @@ package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/** Annotation to indicate a required library for a class. */
|
||||
/**
|
||||
* Annotation to indicate a required library for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Repeatable(MavenLibraries.class)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibrary {
|
||||
|
||||
/**
|
||||
* The group id of the library
|
||||
*
|
||||
* @return the group id of the library
|
||||
*/
|
||||
String groupId();
|
||||
/**
|
||||
* The group id of the library
|
||||
*
|
||||
* @return the group id of the library
|
||||
*/
|
||||
String groupId();
|
||||
|
||||
/**
|
||||
* The artifact id of the library
|
||||
*
|
||||
* @return the artifact id of the library
|
||||
*/
|
||||
String artifactId();
|
||||
/**
|
||||
* The artifact id of the library
|
||||
*
|
||||
* @return the artifact id of the library
|
||||
*/
|
||||
String artifactId();
|
||||
|
||||
/**
|
||||
* The version of the library
|
||||
*
|
||||
* @return the version of the library
|
||||
*/
|
||||
String version();
|
||||
/**
|
||||
* The version of the library
|
||||
*
|
||||
* @return the version of the library
|
||||
*/
|
||||
String version();
|
||||
|
||||
/**
|
||||
* The repo where the library can be obtained from
|
||||
*
|
||||
* @return the repo where the library can be obtained from
|
||||
*/
|
||||
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
|
||||
/**
|
||||
* The repo where the library can be obtained from
|
||||
*
|
||||
* @return the repo where the library can be obtained from
|
||||
*/
|
||||
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
|
||||
|
||||
Relocate[] relocations() default {}; // Add this line
|
||||
|
||||
Relocate[] relocations() default {}; // Add this line
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.lang.annotation.Target;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({})
|
||||
public @interface Relocate {
|
||||
String from();
|
||||
|
||||
String to();
|
||||
}
|
||||
String from();
|
||||
String to();
|
||||
}
|
||||
@@ -18,16 +18,19 @@ package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/** Represents a maven repository. */
|
||||
/**
|
||||
* Represents a maven repository.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.LOCAL_VARIABLE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Repository {
|
||||
|
||||
/**
|
||||
* Gets the base url of the repository.
|
||||
*
|
||||
* @return the base url of the repository
|
||||
*/
|
||||
String url();
|
||||
/**
|
||||
* Gets the base url of the repository.
|
||||
*
|
||||
* @return the base url of the repository
|
||||
*/
|
||||
String url();
|
||||
|
||||
}
|
||||
|
||||
+127
-122
@@ -22,140 +22,145 @@ import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collection;
|
||||
|
||||
/** Provides access to {@link URLClassLoader}#addURL. */
|
||||
/**
|
||||
* Provides access to {@link URLClassLoader}#addURL.
|
||||
*/
|
||||
public abstract class URLClassLoaderAccess {
|
||||
|
||||
/**
|
||||
* Creates a {@link URLClassLoaderAccess} for the given class loader.
|
||||
*
|
||||
* @param classLoader the class loader
|
||||
* @return the access object
|
||||
*/
|
||||
static URLClassLoaderAccess create(URLClassLoader classLoader) {
|
||||
if (Reflection.isSupported()) {
|
||||
return new Reflection(classLoader);
|
||||
} else if (Unsafe.isSupported()) {
|
||||
return new Unsafe(classLoader);
|
||||
} else {
|
||||
return Noop.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
private final URLClassLoader classLoader;
|
||||
|
||||
protected URLClassLoaderAccess(URLClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given URL to the class loader.
|
||||
*
|
||||
* @param url the URL to add
|
||||
*/
|
||||
public abstract void addURL(URL url);
|
||||
|
||||
/** Accesses using reflection, not supported on Java 9+. */
|
||||
private static class Reflection extends URLClassLoaderAccess {
|
||||
private static final Method ADD_URL_METHOD;
|
||||
|
||||
static {
|
||||
Method addUrlMethod;
|
||||
try {
|
||||
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
addUrlMethod.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
addUrlMethod = null;
|
||||
}
|
||||
ADD_URL_METHOD = addUrlMethod;
|
||||
/**
|
||||
* Creates a {@link URLClassLoaderAccess} for the given class loader.
|
||||
*
|
||||
* @param classLoader the class loader
|
||||
* @return the access object
|
||||
*/
|
||||
static URLClassLoaderAccess create(URLClassLoader classLoader) {
|
||||
if (Reflection.isSupported()) {
|
||||
return new Reflection(classLoader);
|
||||
} else if (Unsafe.isSupported()) {
|
||||
return new Unsafe(classLoader);
|
||||
} else {
|
||||
return Noop.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return ADD_URL_METHOD != null;
|
||||
private final URLClassLoader classLoader;
|
||||
|
||||
protected URLClassLoaderAccess(URLClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
Reflection(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
|
||||
/**
|
||||
* Adds the given URL to the class loader.
|
||||
*
|
||||
* @param url the URL to add
|
||||
*/
|
||||
public abstract void addURL(URL url);
|
||||
|
||||
/**
|
||||
* Accesses using reflection, not supported on Java 9+.
|
||||
*/
|
||||
private static class Reflection extends URLClassLoaderAccess {
|
||||
private static final Method ADD_URL_METHOD;
|
||||
|
||||
static {
|
||||
Method addUrlMethod;
|
||||
try {
|
||||
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
addUrlMethod.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
addUrlMethod = null;
|
||||
}
|
||||
ADD_URL_METHOD = addUrlMethod;
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return ADD_URL_METHOD != null;
|
||||
}
|
||||
|
||||
Reflection(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
try {
|
||||
ADD_URL_METHOD.invoke(super.classLoader, url);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
try {
|
||||
ADD_URL_METHOD.invoke(super.classLoader, url);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Accesses using sun.misc.Unsafe, supported on Java 9+.
|
||||
*
|
||||
* @author Vaishnav Anil (<a href="https://github.com/slimjar/slimjar">...</a>)
|
||||
*/
|
||||
private static class Unsafe extends URLClassLoaderAccess {
|
||||
private static final sun.misc.Unsafe UNSAFE;
|
||||
|
||||
/**
|
||||
* Accesses using sun.misc.Unsafe, supported on Java 9+.
|
||||
*
|
||||
* @author Vaishnav Anil (<a href="https://github.com/slimjar/slimjar">...</a>)
|
||||
*/
|
||||
private static class Unsafe extends URLClassLoaderAccess {
|
||||
private static final sun.misc.Unsafe UNSAFE;
|
||||
static {
|
||||
sun.misc.Unsafe unsafe;
|
||||
try {
|
||||
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
|
||||
} catch (Throwable t) {
|
||||
unsafe = null;
|
||||
}
|
||||
UNSAFE = unsafe;
|
||||
}
|
||||
|
||||
static {
|
||||
sun.misc.Unsafe unsafe;
|
||||
try {
|
||||
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
|
||||
} catch (Throwable t) {
|
||||
unsafe = null;
|
||||
}
|
||||
UNSAFE = unsafe;
|
||||
private static boolean isSupported() {
|
||||
return UNSAFE != null;
|
||||
}
|
||||
|
||||
private final Collection<URL> unopenedURLs;
|
||||
private final Collection<URL> pathURLs;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Unsafe(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
|
||||
Collection<URL> unopenedURLs;
|
||||
Collection<URL> pathURLs;
|
||||
try {
|
||||
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
|
||||
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
|
||||
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
|
||||
} catch (Throwable e) {
|
||||
unopenedURLs = null;
|
||||
pathURLs = null;
|
||||
}
|
||||
this.unopenedURLs = unopenedURLs;
|
||||
this.pathURLs = pathURLs;
|
||||
}
|
||||
|
||||
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
|
||||
Field field = clazz.getDeclaredField(name);
|
||||
long offset = UNSAFE.objectFieldOffset(field);
|
||||
return UNSAFE.getObject(object, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
this.unopenedURLs.add(url);
|
||||
this.pathURLs.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return UNSAFE != null;
|
||||
private static class Noop extends URLClassLoaderAccess {
|
||||
private static final Noop INSTANCE = new Noop();
|
||||
|
||||
private Noop() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private final Collection<URL> unopenedURLs;
|
||||
private final Collection<URL> pathURLs;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Unsafe(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
|
||||
Collection<URL> unopenedURLs;
|
||||
Collection<URL> pathURLs;
|
||||
try {
|
||||
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
|
||||
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
|
||||
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
|
||||
} catch (Throwable e) {
|
||||
unopenedURLs = null;
|
||||
pathURLs = null;
|
||||
}
|
||||
this.unopenedURLs = unopenedURLs;
|
||||
this.pathURLs = pathURLs;
|
||||
}
|
||||
|
||||
private static Object fetchField(final Class<?> clazz, final Object object, final String name)
|
||||
throws NoSuchFieldException {
|
||||
Field field = clazz.getDeclaredField(name);
|
||||
long offset = UNSAFE.objectFieldOffset(field);
|
||||
return UNSAFE.getObject(object, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
this.unopenedURLs.add(url);
|
||||
this.pathURLs.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Noop extends URLClassLoaderAccess {
|
||||
private static final Noop INSTANCE = new Noop();
|
||||
|
||||
private Noop() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,51 +17,46 @@
|
||||
package dev.brighten.antivpn.message;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class MessageHandler {
|
||||
private final Map<String, VpnString> messages = new HashMap<>();
|
||||
private final Map<String, VpnString> messages = new HashMap<>();
|
||||
|
||||
public VpnString getString(String key) {
|
||||
if (!messages.containsKey(key)) {
|
||||
throw new NullPointerException("There is no VpnString with the key \"" + key + "\"");
|
||||
public VpnString getString(String key) {
|
||||
if(!messages.containsKey(key)) {
|
||||
throw new NullPointerException("There is no VpnString with the key \"" + key + "\"");
|
||||
}
|
||||
|
||||
return messages.get(key);
|
||||
}
|
||||
|
||||
return messages.get(key);
|
||||
}
|
||||
|
||||
public void reloadStrings() {
|
||||
for (VpnString value : messages.values()) {
|
||||
value.updateString();
|
||||
public void reloadStrings() {
|
||||
for (VpnString value : messages.values()) {
|
||||
value.updateString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clearStrings() {
|
||||
messages.clear();
|
||||
}
|
||||
public void clearStrings() {
|
||||
messages.clear();
|
||||
}
|
||||
|
||||
public void addString(VpnString string, Function<VpnString, String> getter) {
|
||||
string.setConfigStringGetter(getter);
|
||||
getter.apply(string);
|
||||
AntiVPN.getInstance().getExecutor().log("Added string " + string.getKey());
|
||||
messages.put(string.getKey(), string);
|
||||
}
|
||||
public void addString(VpnString string, Function<VpnString, String> getter) {
|
||||
string.setConfigStringGetter(getter);
|
||||
getter.apply(string);
|
||||
AntiVPN.getInstance().getExecutor().log("Added string " + string.getKey());
|
||||
messages.put(string.getKey(), string);
|
||||
}
|
||||
|
||||
public void initStrings(Function<VpnString, String> getter) {
|
||||
addString(
|
||||
new VpnString(
|
||||
"command-misc-playerRequired", "&cYou must be a player to execute this command!"),
|
||||
getter);
|
||||
addString(
|
||||
new VpnString(
|
||||
"command-alerts-toggled",
|
||||
"&7Your player proxy notifications have been set to: &e%state%"),
|
||||
getter);
|
||||
addString(
|
||||
new VpnString("command-reload-complete", "&aSuccessfully reloaded KauriVPN plugin!"),
|
||||
getter);
|
||||
addString(new VpnString("no-permission", "&cNo permission."), getter);
|
||||
}
|
||||
public void initStrings(Function<VpnString, String> getter) {
|
||||
addString(new VpnString("command-misc-playerRequired",
|
||||
"&cYou must be a player to execute this command!"), getter);
|
||||
addString(new VpnString("command-alerts-toggled",
|
||||
"&7Your player proxy notifications have been set to: &e%state%"), getter);
|
||||
addString(new VpnString("command-reload-complete",
|
||||
"&aSuccessfully reloaded KauriVPN plugin!"), getter);
|
||||
addString(new VpnString("no-permission", "&cNo permission."), getter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,59 +17,58 @@
|
||||
package dev.brighten.antivpn.message;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import java.util.function.Function;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Getter
|
||||
public class VpnString {
|
||||
private final String key;
|
||||
private final String defaultMessage;
|
||||
private String message;
|
||||
@Setter private Function<VpnString, String> configStringGetter;
|
||||
|
||||
public VpnString(String key, String defaultMessage) {
|
||||
this.key = key;
|
||||
this.defaultMessage = defaultMessage;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void updateString() {
|
||||
if (configStringGetter == null)
|
||||
throw new Exception("The configStringGetter for string " + key + " is null!");
|
||||
|
||||
message = configStringGetter.apply(this);
|
||||
}
|
||||
|
||||
public String getFormattedMessage(Var<String, Object>... replacements) {
|
||||
String formatted = configStringGetter.apply(this);
|
||||
|
||||
for (Var<String, Object> replacement : replacements) {
|
||||
formatted =
|
||||
formatted.replace(
|
||||
"%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
public void sendMessage(APIPlayer player, Var<String, Object>... replacements) {
|
||||
String formatted = message;
|
||||
|
||||
for (Var<String, Object> replacement : replacements) {
|
||||
formatted =
|
||||
formatted.replace(
|
||||
"%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
||||
}
|
||||
player.sendMessage(formatted);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public static class Var<S, O> {
|
||||
private final String key;
|
||||
private final Object replacement;
|
||||
}
|
||||
private final String defaultMessage;
|
||||
private String message;
|
||||
@Setter
|
||||
private Function<VpnString, String> configStringGetter;
|
||||
|
||||
public VpnString(String key, String defaultMessage) {
|
||||
this.key = key;
|
||||
this.defaultMessage = defaultMessage;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void updateString() {
|
||||
if(configStringGetter == null) throw new Exception("The configStringGetter for string " + key + " is null!");
|
||||
|
||||
message = configStringGetter.apply(this);
|
||||
}
|
||||
|
||||
public String getFormattedMessage(Var<String, Object>... replacements) {
|
||||
String formatted = configStringGetter.apply(this);
|
||||
|
||||
for (Var<String, Object> replacement : replacements) {
|
||||
formatted = formatted
|
||||
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
public void sendMessage(APIPlayer player, Var<String, Object>... replacements) {
|
||||
String formatted = message;
|
||||
|
||||
for (Var<String, Object> replacement : replacements) {
|
||||
formatted = formatted
|
||||
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
||||
}
|
||||
player.sendMessage(formatted);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public static class Var<S, O> {
|
||||
private final String key;
|
||||
private final Object replacement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,106 +16,116 @@
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* A class that enables to get an IP range from CIDR specification. It supports both IPv4 and IPv6.
|
||||
* A class that enables to get an IP range from CIDR specification. It supports
|
||||
* both IPv4 and IPv6.
|
||||
*/
|
||||
@Getter
|
||||
public class CIDRUtils {
|
||||
private final String cidr;
|
||||
private final String cidr;
|
||||
|
||||
private final InetAddress inetAddress;
|
||||
private final InetAddress inetAddress;
|
||||
|
||||
private InetAddress startAddress;
|
||||
private BigInteger startIpInt, endIpInt;
|
||||
private InetAddress endAddress;
|
||||
private final int prefixLength;
|
||||
private InetAddress startAddress;
|
||||
private BigInteger startIpInt, endIpInt;
|
||||
private InetAddress endAddress;
|
||||
private final int prefixLength;
|
||||
|
||||
public CIDRUtils(String cidr) throws UnknownHostException {
|
||||
|
||||
this.cidr = cidr;
|
||||
public CIDRUtils(String cidr) throws UnknownHostException {
|
||||
|
||||
/* split CIDR to address and prefix part */
|
||||
if (this.cidr.contains("/")) {
|
||||
int index = this.cidr.indexOf('/');
|
||||
String addressPart = this.cidr.substring(0, index);
|
||||
String networkPart = this.cidr.substring(index + 1);
|
||||
this.cidr = cidr;
|
||||
|
||||
inetAddress = InetAddress.getByName(addressPart);
|
||||
prefixLength = Integer.parseInt(networkPart);
|
||||
/* split CIDR to address and prefix part */
|
||||
if (this.cidr.contains("/")) {
|
||||
int index = this.cidr.indexOf("/");
|
||||
String addressPart = this.cidr.substring(0, index);
|
||||
String networkPart = this.cidr.substring(index + 1);
|
||||
|
||||
calculate();
|
||||
} else {
|
||||
throw new IllegalArgumentException("not an valid CIDR format!");
|
||||
}
|
||||
}
|
||||
inetAddress = InetAddress.getByName(addressPart);
|
||||
prefixLength = Integer.parseInt(networkPart);
|
||||
|
||||
private void calculate() throws UnknownHostException {
|
||||
|
||||
ByteBuffer maskBuffer;
|
||||
int targetSize;
|
||||
if (inetAddress.getAddress().length == 4) {
|
||||
maskBuffer = ByteBuffer.allocate(4).putInt(-1);
|
||||
targetSize = 4;
|
||||
} else {
|
||||
maskBuffer = ByteBuffer.allocate(16).putLong(-1L).putLong(-1L);
|
||||
targetSize = 16;
|
||||
calculate();
|
||||
} else {
|
||||
throw new IllegalArgumentException("not an valid CIDR format!");
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(prefixLength);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(inetAddress.getAddress());
|
||||
BigInteger ipVal = new BigInteger(1, buffer.array());
|
||||
private void calculate() throws UnknownHostException {
|
||||
|
||||
BigInteger startIp = ipVal.and(mask);
|
||||
this.startIpInt = startIp;
|
||||
BigInteger endIp = startIp.add(mask.not());
|
||||
this.endIpInt = endIp;
|
||||
ByteBuffer maskBuffer;
|
||||
int targetSize;
|
||||
if (inetAddress.getAddress().length == 4) {
|
||||
maskBuffer =
|
||||
ByteBuffer
|
||||
.allocate(4)
|
||||
.putInt(-1);
|
||||
targetSize = 4;
|
||||
} else {
|
||||
maskBuffer = ByteBuffer.allocate(16)
|
||||
.putLong(-1L)
|
||||
.putLong(-1L);
|
||||
targetSize = 16;
|
||||
}
|
||||
|
||||
byte[] startIpArr = toBytes(startIp.toByteArray(), targetSize);
|
||||
byte[] endIpArr = toBytes(endIp.toByteArray(), targetSize);
|
||||
BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(prefixLength);
|
||||
|
||||
this.startAddress = InetAddress.getByAddress(startIpArr);
|
||||
this.endAddress = InetAddress.getByAddress(endIpArr);
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.wrap(inetAddress.getAddress());
|
||||
BigInteger ipVal = new BigInteger(1, buffer.array());
|
||||
|
||||
BigInteger startIp = ipVal.and(mask);
|
||||
this.startIpInt = startIp;
|
||||
BigInteger endIp = startIp.add(mask.not());
|
||||
this.endIpInt = endIp;
|
||||
|
||||
byte[] startIpArr = toBytes(startIp.toByteArray(), targetSize);
|
||||
byte[] endIpArr = toBytes(endIp.toByteArray(), targetSize);
|
||||
|
||||
this.startAddress = InetAddress.getByAddress(startIpArr);
|
||||
this.endAddress = InetAddress.getByAddress(endIpArr);
|
||||
|
||||
private byte[] toBytes(byte[] array, int targetSize) {
|
||||
int counter = 0;
|
||||
List<Byte> newArr = new ArrayList<Byte>();
|
||||
while (counter < targetSize && (array.length - 1 - counter >= 0)) {
|
||||
newArr.add(0, array[array.length - 1 - counter]);
|
||||
counter++;
|
||||
}
|
||||
|
||||
int size = newArr.size();
|
||||
for (int i = 0; i < (targetSize - size); i++) {
|
||||
private byte[] toBytes(byte[] array, int targetSize) {
|
||||
int counter = 0;
|
||||
List<Byte> newArr = new ArrayList<Byte>();
|
||||
while (counter < targetSize && (array.length - 1 - counter >= 0)) {
|
||||
newArr.add(0, array[array.length - 1 - counter]);
|
||||
counter++;
|
||||
}
|
||||
|
||||
newArr.add(0, (byte) 0);
|
||||
int size = newArr.size();
|
||||
for (int i = 0; i < (targetSize - size); i++) {
|
||||
|
||||
newArr.add(0, (byte) 0);
|
||||
}
|
||||
|
||||
byte[] ret = new byte[newArr.size()];
|
||||
for (int i = 0; i < newArr.size(); i++) {
|
||||
ret[i] = newArr.get(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
byte[] ret = new byte[newArr.size()];
|
||||
for (int i = 0; i < newArr.size(); i++) {
|
||||
ret[i] = newArr.get(i);
|
||||
public boolean isInRange(String ipAddress) throws UnknownHostException {
|
||||
InetAddress address = InetAddress.getByName(ipAddress);
|
||||
BigInteger start = new BigInteger(1, this.startAddress.getAddress());
|
||||
BigInteger end = new BigInteger(1, this.endAddress.getAddress());
|
||||
BigInteger target = new BigInteger(1, address.getAddress());
|
||||
|
||||
int st = start.compareTo(target);
|
||||
int te = target.compareTo(end);
|
||||
|
||||
return (st < 0 || st == 0) && (te < 0 || te == 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public boolean isInRange(String ipAddress) throws UnknownHostException {
|
||||
InetAddress address = InetAddress.getByName(ipAddress);
|
||||
BigInteger start = new BigInteger(1, this.startAddress.getAddress());
|
||||
BigInteger end = new BigInteger(1, this.endAddress.getAddress());
|
||||
BigInteger target = new BigInteger(1, address.getAddress());
|
||||
|
||||
int st = start.compareTo(target);
|
||||
int te = target.compareTo(end);
|
||||
|
||||
return (st < 0 || st == 0) && (te < 0 || te == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,22 +22,23 @@ import lombok.AllArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
public class ConfigDefault<A> {
|
||||
|
||||
private final A defaultValue;
|
||||
private final String path;
|
||||
private final AntiVPN plugin;
|
||||
private final A defaultValue;
|
||||
private final String path;
|
||||
private final AntiVPN plugin;
|
||||
|
||||
public A get() {
|
||||
if (plugin.getConfig().get(path) != null) return (A) plugin.getConfig().get(path);
|
||||
else {
|
||||
plugin.getConfig().set(path, defaultValue);
|
||||
plugin.saveConfig();
|
||||
return defaultValue;
|
||||
public A get() {
|
||||
if(plugin.getConfig().get(path) != null)
|
||||
return (A) plugin.getConfig().get(path);
|
||||
else {
|
||||
plugin.getConfig().set(path, defaultValue);
|
||||
plugin.saveConfig();
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public A set(A value) {
|
||||
plugin.getConfig().set(path, value);
|
||||
public A set(A value) {
|
||||
plugin.getConfig().set(path, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,20 @@
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EvictingMap<K, V> extends LinkedHashMap<K, V> {
|
||||
|
||||
@Getter private final int size;
|
||||
@Getter
|
||||
private final int size;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||
return size() >= size;
|
||||
}
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||
return size() >= size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,87 +25,85 @@ import java.net.UnknownHostException;
|
||||
import java.util.Optional;
|
||||
|
||||
public class IpUtils {
|
||||
public static Optional<BigDecimal> getIpDecimal(String address) {
|
||||
try {
|
||||
InetAddress inet = InetAddress.getByName(address);
|
||||
public static Optional<BigDecimal> getIpDecimal(String address) {
|
||||
try {
|
||||
InetAddress inet = InetAddress.getByName(address);
|
||||
|
||||
if (inet instanceof Inet4Address) {
|
||||
return Optional.of(BigDecimal.valueOf(ipv4ToLong(address)));
|
||||
}
|
||||
return Optional.of(new BigDecimal(ipv6ToDecimalFormat(address)));
|
||||
} catch (Exception e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static long ipv4ToLong(String address) {
|
||||
String[] addrArray = address.split("\\.");
|
||||
|
||||
long ipDecimal = 0;
|
||||
|
||||
for (int i = 0; i < addrArray.length; i++) {
|
||||
|
||||
int power = 3 - i;
|
||||
ipDecimal += ((Integer.parseInt(addrArray[i]) % 256 * Math.pow(256, power)));
|
||||
if(inet instanceof Inet4Address) {
|
||||
return Optional.of(BigDecimal.valueOf(ipv4ToLong(address)));
|
||||
} return Optional.of(new BigDecimal(ipv6ToDecimalFormat(address)));
|
||||
} catch(Exception e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
return ipDecimal;
|
||||
}
|
||||
public static long ipv4ToLong(String address) {
|
||||
String[] addrArray = address.split("\\.");
|
||||
|
||||
public static String getIpv4(long ip) {
|
||||
StringBuilder sb = new StringBuilder(15);
|
||||
long ipDecimal = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sb.insert(0, ip & 0xff);
|
||||
for (int i = 0; i < addrArray.length; i++) {
|
||||
|
||||
if (i < 3) {
|
||||
sb.insert(0, '.');
|
||||
}
|
||||
int power = 3 - i;
|
||||
ipDecimal += ((Integer.parseInt(addrArray[i]) % 256 * Math.pow(256, power)));
|
||||
}
|
||||
|
||||
ip >>= 8;
|
||||
return ipDecimal;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
public static String getIpv4(long ip) {
|
||||
StringBuilder sb = new StringBuilder(15);
|
||||
|
||||
public static boolean isIpv4(BigDecimal ip) {
|
||||
return ip.compareTo(BigDecimal.valueOf(4294967295L)) <= 0;
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sb.insert(0, ip & 0xff);
|
||||
|
||||
public static boolean isIpv6(BigDecimal ip) {
|
||||
return ip.compareTo(BigDecimal.valueOf(4294967295L)) > 0;
|
||||
}
|
||||
if (i < 3) {
|
||||
sb.insert(0, '.');
|
||||
}
|
||||
|
||||
public static boolean isIpv4(String ip) {
|
||||
return ip.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$");
|
||||
}
|
||||
ip >>= 8;
|
||||
}
|
||||
|
||||
public static boolean isNotIp(String ip) {
|
||||
return !isIpv4(ip) && !isIpv6(ip);
|
||||
}
|
||||
|
||||
public static boolean isIpv6(String ip) {
|
||||
return ip.matches(
|
||||
"^([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)$|^(([0-9a-fA-F]{1,4}:){0,6}([0-9a-fA-F]{1,4}|:))?(::([0-9a-fA-F]{1,4}:){0,5}([0-9a-fA-F]{1,4}|:))?$");
|
||||
}
|
||||
|
||||
public static String getIpv4(BigDecimal ip) {
|
||||
try {
|
||||
return Inet4Address.getByAddress(ip.toBigInteger().toByteArray()).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
return "Error";
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getIpv6(BigDecimal ip) {
|
||||
try {
|
||||
return Inet6Address.getByAddress(ip.toBigInteger().toByteArray()).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
return "Error";
|
||||
public static boolean isIpv4(BigDecimal ip) {
|
||||
return ip.compareTo(BigDecimal.valueOf(4294967295L)) <= 0;
|
||||
}
|
||||
|
||||
public static boolean isIpv6(BigDecimal ip) {
|
||||
return ip.compareTo(BigDecimal.valueOf(4294967295L)) > 0;
|
||||
}
|
||||
public static boolean isIpv4(String ip) {
|
||||
return ip.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$");
|
||||
}
|
||||
|
||||
public static boolean isNotIp(String ip) {
|
||||
return !isIpv4(ip) && !isIpv6(ip);
|
||||
}
|
||||
|
||||
public static boolean isIpv6(String ip) {
|
||||
return ip.matches("^([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)$|^(([0-9a-fA-F]{1,4}:){0,6}([0-9a-fA-F]{1,4}|:))?(::([0-9a-fA-F]{1,4}:){0,5}([0-9a-fA-F]{1,4}|:))?$");
|
||||
}
|
||||
|
||||
public static String getIpv4(BigDecimal ip) {
|
||||
try {
|
||||
return Inet4Address.getByAddress(ip.toBigInteger().toByteArray()).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getIpv6(BigDecimal ip) {
|
||||
try {
|
||||
return Inet6Address.getByAddress(ip.toBigInteger().toByteArray()).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
public static BigInteger ipv6ToDecimalFormat(String ipAddress) throws UnknownHostException {
|
||||
return new BigInteger(1, Inet6Address.getByName(ipAddress).getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
public static BigInteger ipv6ToDecimalFormat(String ipAddress) throws UnknownHostException {
|
||||
return new BigInteger(1, Inet6Address.getByName(ipAddress).getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,11 @@ import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.utils.json.JSONObject;
|
||||
import dev.brighten.antivpn.utils.json.JsonReader;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.net.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -31,177 +33,118 @@ import java.util.regex.Pattern;
|
||||
|
||||
public class MiscUtils {
|
||||
|
||||
private static final Pattern ipv4 =
|
||||
Pattern.compile("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
|
||||
private static final String DEFAULT_FUNKEMUNKY_UUID_ENDPOINT =
|
||||
"https://funkemunky.cc/mojang/uuid?name=";
|
||||
private static final String DEFAULT_MOJANG_UUID_ENDPOINT =
|
||||
"https://api.mojang.com/users/profiles/minecraft/";
|
||||
private static volatile String funkemunkyUuidEndpoint = DEFAULT_FUNKEMUNKY_UUID_ENDPOINT;
|
||||
private static volatile String mojangUuidEndpoint = DEFAULT_MOJANG_UUID_ENDPOINT;
|
||||
private static final Pattern ipv4 = Pattern.compile("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
|
||||
|
||||
public static void close(Closeable... closeables) {
|
||||
try {
|
||||
for (Closeable closeable : closeables) if (closeable != null) closeable.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void close(AutoCloseable... closeables) {
|
||||
try {
|
||||
for (AutoCloseable closeable : closeables) if (closeable != null) closeable.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copy(InputStream in, File file) {
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
int lenght;
|
||||
byte[] buf = new byte[1024];
|
||||
|
||||
while ((lenght = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, lenght);
|
||||
}
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ThreadFactory createThreadFactory(String threadName) {
|
||||
return r -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName(threadName);
|
||||
return thread;
|
||||
};
|
||||
}
|
||||
|
||||
public static List<CIDRUtils> rangeToCidrs(BigInteger start, BigInteger end)
|
||||
throws UnknownHostException {
|
||||
List<CIDRUtils> cidrs = new ArrayList<>();
|
||||
|
||||
while (start.compareTo(end) <= 0) {
|
||||
// Find the number of trailing zero bits — this determines max block size alignment
|
||||
int trailingZeros =
|
||||
start.equals(BigInteger.ZERO)
|
||||
? 128 // handle the edge case
|
||||
: start.getLowestSetBit();
|
||||
|
||||
// Find the largest block that fits
|
||||
BigInteger remaining = end.subtract(start).add(BigInteger.ONE);
|
||||
int maxBits = remaining.bitLength() - 1;
|
||||
|
||||
int blockBits = Math.min(trailingZeros, maxBits);
|
||||
int prefixLen = 32 - blockBits; // use 128 for IPv6
|
||||
|
||||
// Build the CIDR string
|
||||
byte[] addrBytes = toFixedLengthBytes(start); // use 16 for IPv6
|
||||
String cidr = InetAddress.getByAddress(addrBytes).getHostAddress() + "/" + prefixLen;
|
||||
cidrs.add(new CIDRUtils(cidr));
|
||||
|
||||
// Advance past this block
|
||||
start = start.add(BigInteger.ONE.shiftLeft(blockBits));
|
||||
public static void close(Closeable... closeables) {
|
||||
try {
|
||||
for (Closeable closeable : closeables) if (closeable != null) closeable.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return cidrs;
|
||||
}
|
||||
|
||||
private static byte[] toFixedLengthBytes(BigInteger value) {
|
||||
byte[] raw = value.toByteArray();
|
||||
byte[] result = new byte[4];
|
||||
int srcPos = Math.max(0, raw.length - 4);
|
||||
int destPos = Math.max(0, 4 - raw.length);
|
||||
System.arraycopy(raw, srcPos, result, destPos, Math.min(raw.length, 4));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static UUID lookupUUID(String playername) {
|
||||
try {
|
||||
UUID uuid = lookupUuidFromUrl(funkemunkyUuidEndpoint + playername);
|
||||
if (uuid != null) {
|
||||
return uuid;
|
||||
}
|
||||
} catch (IOException | JSONException | URISyntaxException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException(
|
||||
"Error while looking up UUID for " + playername + "! Falling back to Mojang API", e);
|
||||
return lookupMojangUuid(playername);
|
||||
public static void close(AutoCloseable... closeables) {
|
||||
try {
|
||||
for (AutoCloseable closeable : closeables) if (closeable != null) closeable.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static void copy(InputStream in, File file) {
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
int lenght;
|
||||
byte[] buf = new byte[1024];
|
||||
|
||||
private static UUID lookupUuidFromUrl(String url)
|
||||
throws IOException, JSONException, URISyntaxException {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URI(url).toURL().openConnection();
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.setInstanceFollowRedirects(true);
|
||||
while ((lenght = in.read(buf)) > 0)
|
||||
{
|
||||
out.write(buf, 0, lenght);
|
||||
}
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode >= 500) {
|
||||
throw new IOException("Server returned HTTP " + responseCode + " for " + url);
|
||||
}
|
||||
if (responseCode != HttpURLConnection.HTTP_OK) {
|
||||
return null;
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream()) {
|
||||
JSONObject object =
|
||||
new JSONObject(
|
||||
JsonReader.readAll(
|
||||
new InputStreamReader(inputStream, java.nio.charset.StandardCharsets.UTF_8)));
|
||||
if (object.has("uuid")) {
|
||||
return parseUuid(object.getString("uuid"));
|
||||
}
|
||||
if (object.has("id")) {
|
||||
return parseUuid(object.getString("id"));
|
||||
}
|
||||
public static ThreadFactory createThreadFactory(String threadName) {
|
||||
return r -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName(threadName);
|
||||
return thread;
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static List<CIDRUtils> rangeToCidrs(BigInteger start, BigInteger end) throws UnknownHostException {
|
||||
List<CIDRUtils> cidrs = new ArrayList<>();
|
||||
|
||||
private static UUID parseUuid(String value) {
|
||||
if (value.length() == 32) {
|
||||
value =
|
||||
value.replaceFirst(
|
||||
"([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{12})",
|
||||
"$1-$2-$3-$4-$5");
|
||||
while (start.compareTo(end) <= 0) {
|
||||
// Find the number of trailing zero bits — this determines max block size alignment
|
||||
int trailingZeros = start.equals(BigInteger.ZERO)
|
||||
? 128 // handle the edge case
|
||||
: start.getLowestSetBit();
|
||||
|
||||
// Find the largest block that fits
|
||||
BigInteger remaining = end.subtract(start).add(BigInteger.ONE);
|
||||
int maxBits = remaining.bitLength() - 1;
|
||||
|
||||
int blockBits = Math.min(trailingZeros, maxBits);
|
||||
int prefixLen = 32 - blockBits; // use 128 for IPv6
|
||||
|
||||
// Build the CIDR string
|
||||
byte[] addrBytes = toFixedLengthBytes(start, 4); // use 16 for IPv6
|
||||
String cidr = InetAddress.getByAddress(addrBytes).getHostAddress() + "/" + prefixLen;
|
||||
cidrs.add(new CIDRUtils(cidr));
|
||||
|
||||
// Advance past this block
|
||||
start = start.add(BigInteger.ONE.shiftLeft(blockBits));
|
||||
}
|
||||
|
||||
return cidrs;
|
||||
}
|
||||
|
||||
return UUID.fromString(value);
|
||||
}
|
||||
|
||||
private static UUID lookupMojangUuid(String playerName) {
|
||||
try {
|
||||
return lookupUuidFromUrl(mojangUuidEndpoint + playerName);
|
||||
} catch (IOException | JSONException | URISyntaxException e) {
|
||||
AntiVPN.getInstance()
|
||||
.getExecutor()
|
||||
.logException("Error while looking up UUID for " + playerName + " from Mojang!:", e);
|
||||
private static byte[] toFixedLengthBytes(BigInteger value, int length) {
|
||||
byte[] raw = value.toByteArray();
|
||||
byte[] result = new byte[length];
|
||||
int srcPos = Math.max(0, raw.length - length);
|
||||
int destPos = Math.max(0, length - raw.length);
|
||||
System.arraycopy(raw, srcPos, result, destPos, Math.min(raw.length, length));
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static UUID lookupUUID(String playername) {
|
||||
try {
|
||||
JSONObject object = JsonReader
|
||||
.readJsonFromUrl("https://funkemunky.cc/mojang/uuid?name=" + playername);
|
||||
|
||||
static void setLookupEndpointsForTesting(String funkemunkyEndpoint, String mojangEndpoint) {
|
||||
funkemunkyUuidEndpoint = funkemunkyEndpoint;
|
||||
mojangUuidEndpoint = mojangEndpoint;
|
||||
}
|
||||
if(object.has("uuid")) {
|
||||
return UUID.fromString(object.getString("uuid"));
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playername + "! Falling back to Mojang API", e);
|
||||
return lookupMojangUuid(playername);
|
||||
}
|
||||
|
||||
static void resetLookupEndpointsForTesting() {
|
||||
funkemunkyUuidEndpoint = DEFAULT_FUNKEMUNKY_UUID_ENDPOINT;
|
||||
mojangUuidEndpoint = DEFAULT_MOJANG_UUID_ENDPOINT;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isIpv4(String ip) {
|
||||
return ipv4.matcher(ip).matches();
|
||||
}
|
||||
private static UUID lookupMojangUuid(String playerName) {
|
||||
try {
|
||||
JSONObject object = JsonReader.readJsonFromUrl("https://api.mojang.com/users/profiles/minecraft/" + playerName);
|
||||
|
||||
if(object.has("id")) {
|
||||
return UUID.fromString(object.getString("id"));
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playerName + " from Mojang!:", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static boolean isIpv4(String ip)
|
||||
{
|
||||
return ipv4.matcher(ip).matches();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface NonnullByDefault {}
|
||||
public @interface NonnullByDefault {
|
||||
|
||||
}
|
||||
|
||||
@@ -22,241 +22,239 @@
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
public final class Preconditions {
|
||||
private Preconditions() {}
|
||||
|
||||
public static <T> T checkNotNull(T reference) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException();
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T reference, Object errorMessage) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(String.valueOf(errorMessage));
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(
|
||||
T reference, String errorMessageTemplate, Object... errorMessageArgs) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(
|
||||
T obj, String errorMessageTemplate, Object p1, Object p2, Object p3) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(
|
||||
T obj, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3, p4));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
static String format(String template, Object... args) {
|
||||
template = String.valueOf(template);
|
||||
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
|
||||
int templateStart = 0;
|
||||
|
||||
int i;
|
||||
int placeholderStart;
|
||||
for (i = 0; i < args.length; templateStart = placeholderStart + 2) {
|
||||
placeholderStart = template.indexOf("%s", templateStart);
|
||||
if (placeholderStart == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
builder.append(template, templateStart, placeholderStart);
|
||||
builder.append(args[i++]);
|
||||
private Preconditions() {
|
||||
}
|
||||
|
||||
builder.append(template, templateStart, template.length());
|
||||
if (i < args.length) {
|
||||
builder.append(" [");
|
||||
builder.append(args[i++]);
|
||||
|
||||
while (i < args.length) {
|
||||
builder.append(", ");
|
||||
builder.append(args[i++]);
|
||||
}
|
||||
|
||||
builder.append(']');
|
||||
public static <T> T checkNotNull(T reference) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException();
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
public static <T> T checkNotNull(T reference, Object errorMessage) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(String.valueOf(errorMessage));
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3, p4));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
static String format(String template, Object... args) {
|
||||
template = String.valueOf(template);
|
||||
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
|
||||
int templateStart = 0;
|
||||
|
||||
int i;
|
||||
int placeholderStart;
|
||||
for(i = 0; i < args.length; templateStart = placeholderStart + 2) {
|
||||
placeholderStart = template.indexOf("%s", templateStart);
|
||||
if (placeholderStart == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
builder.append(template, templateStart, placeholderStart);
|
||||
builder.append(args[i++]);
|
||||
}
|
||||
|
||||
builder.append(template, templateStart, template.length());
|
||||
if (i < args.length) {
|
||||
builder.append(" [");
|
||||
builder.append(args[i++]);
|
||||
|
||||
while(i < args.length) {
|
||||
builder.append(", ");
|
||||
builder.append(args[i++]);
|
||||
}
|
||||
|
||||
builder.append(']');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,34 +20,31 @@ import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
public class StringUtil {
|
||||
public static String line(String color) {
|
||||
return color + "&m-----------------------------------------------------";
|
||||
}
|
||||
|
||||
public static String line() {
|
||||
return "&m-----------------------------------------------------";
|
||||
}
|
||||
|
||||
public static String varReplace(String input, APIPlayer player, VPNResponse result) {
|
||||
return translateAlternateColorCodes(
|
||||
'&',
|
||||
input
|
||||
.replace("%player%", player.getName())
|
||||
.replace("%reason%", result.getMethod())
|
||||
.replace("%country%", result.getCountryName())
|
||||
.replace("%city%", result.getCity()));
|
||||
}
|
||||
|
||||
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
|
||||
for (int i = 0; i < b.length - 1; ++i) {
|
||||
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) {
|
||||
b[i] = 167;
|
||||
b[i + 1] = Character.toLowerCase(b[i + 1]);
|
||||
}
|
||||
public static String line(String color) {
|
||||
return color + "&m-----------------------------------------------------";
|
||||
}
|
||||
|
||||
return new String(b);
|
||||
}
|
||||
public static String line() {
|
||||
return "&m-----------------------------------------------------";
|
||||
}
|
||||
|
||||
public static String varReplace(String input, APIPlayer player, VPNResponse result) {
|
||||
return translateAlternateColorCodes('&', input.replace("%player%", player.getName())
|
||||
.replace("%reason%", result.getMethod())
|
||||
.replace("%country%", result.getCountryName())
|
||||
.replace("%city%", result.getCity()));
|
||||
}
|
||||
|
||||
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
|
||||
for(int i = 0; i < b.length - 1; ++i) {
|
||||
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) {
|
||||
b[i] = 167;
|
||||
b[i + 1] = Character.toLowerCase(b[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return new String(b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@ package dev.brighten.antivpn.utils;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Supplier<T> extends java.util.function.Supplier<T> {
|
||||
T get();
|
||||
T get();
|
||||
}
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
|
||||
import static dev.brighten.antivpn.utils.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Useful suppliers.
|
||||
*
|
||||
@@ -33,114 +32,114 @@ import java.io.Serializable;
|
||||
* @since 2.0
|
||||
*/
|
||||
public final class Suppliers {
|
||||
private Suppliers() {}
|
||||
private Suppliers() {}
|
||||
|
||||
/**
|
||||
* Returns a supplier which caches the instance retrieved during the first call to {@code get()}
|
||||
* and returns that value on later calls to {@code get()}. See: <a
|
||||
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
|
||||
*
|
||||
* <p>The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at
|
||||
* most once unless the underlying {@code get()} throws an exception. The supplier's serialized
|
||||
* form does not contain the cached value, which will be recalculated when {@code get()} is called
|
||||
* on the reserialized instance.
|
||||
*
|
||||
* <p>When the underlying delegate throws an exception then this memorizing supplier will keep
|
||||
* delegating calls until it returns valid data.
|
||||
*
|
||||
* <p>If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
|
||||
* returned directly.
|
||||
*/
|
||||
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
|
||||
if (delegate instanceof NonSerializableMemoizingSupplier
|
||||
|| delegate instanceof MemoizingSupplier) {
|
||||
return delegate;
|
||||
}
|
||||
return delegate instanceof Serializable
|
||||
? new MemoizingSupplier<>(delegate)
|
||||
: new NonSerializableMemoizingSupplier<>(delegate);
|
||||
}
|
||||
|
||||
static class MemoizingSupplier<T> implements Supplier<T>, Serializable {
|
||||
final Supplier<T> delegate;
|
||||
transient volatile boolean initialized;
|
||||
// "value" does not need to be volatile; visibility piggy-backs
|
||||
// on volatile read of "initialized".
|
||||
transient T value;
|
||||
|
||||
MemoizingSupplier(Supplier<T> delegate) {
|
||||
this.delegate = checkNotNull(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
// A 2-field variant of Double Checked Locking.
|
||||
if (!initialized) {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
T t = delegate.get();
|
||||
value = t;
|
||||
initialized = true;
|
||||
return t;
|
||||
}
|
||||
/**
|
||||
* Returns a supplier which caches the instance retrieved during the first call to {@code get()}
|
||||
* and returns that value on subsequent calls to {@code get()}. See: <a
|
||||
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
|
||||
*
|
||||
* <p>The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at
|
||||
* most once unless the underlying {@code get()} throws an exception. The supplier's serialized
|
||||
* form does not contain the cached value, which will be recalculated when {@code get()} is called
|
||||
* on the reserialized instance.
|
||||
*
|
||||
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
|
||||
* delegating calls until it returns valid data.
|
||||
*
|
||||
* <p>If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
|
||||
* returned directly.
|
||||
*/
|
||||
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
|
||||
if (delegate instanceof NonSerializableMemoizingSupplier
|
||||
|| delegate instanceof MemoizingSupplier) {
|
||||
return delegate;
|
||||
}
|
||||
}
|
||||
// This is safe because we checked `initialized.`
|
||||
return uncheckedCastNullableTToT(value);
|
||||
return delegate instanceof Serializable
|
||||
? new MemoizingSupplier<>(delegate)
|
||||
: new NonSerializableMemoizingSupplier<>(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Suppliers.memoize("
|
||||
+ (initialized ? "<supplier that returned " + value + ">" : delegate)
|
||||
+ ")";
|
||||
}
|
||||
static class MemoizingSupplier<T> implements Supplier<T>, Serializable {
|
||||
final Supplier<T> delegate;
|
||||
transient volatile boolean initialized;
|
||||
// "value" does not need to be volatile; visibility piggy-backs
|
||||
// on volatile read of "initialized".
|
||||
transient T value;
|
||||
|
||||
@Serial private static final long serialVersionUID = 0;
|
||||
}
|
||||
|
||||
static class NonSerializableMemoizingSupplier<T> implements Supplier<T> {
|
||||
volatile Supplier<T> delegate;
|
||||
volatile boolean initialized;
|
||||
// "value" does not need to be volatile; visibility piggy-backs
|
||||
// on volatile read of "initialized".
|
||||
T value;
|
||||
|
||||
NonSerializableMemoizingSupplier(Supplier<T> delegate) {
|
||||
this.delegate = checkNotNull(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
// A 2-field variant of Double Checked Locking.
|
||||
if (!initialized) {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
/*
|
||||
* requireNonNull is safe because we read and write `delegate` under synchronization.
|
||||
*
|
||||
* TODO(cpovirk): To avoid having to check for null, replace `delegate` with a singleton
|
||||
* `Supplier` that always throws an exception.
|
||||
*/
|
||||
T t = requireNonNull(delegate).get();
|
||||
value = t;
|
||||
initialized = true;
|
||||
// Release the delegate to GC.
|
||||
delegate = null;
|
||||
return t;
|
||||
}
|
||||
MemoizingSupplier(Supplier<T> delegate) {
|
||||
this.delegate = checkNotNull(delegate);
|
||||
}
|
||||
}
|
||||
// This is safe because we checked `initialized.`
|
||||
return uncheckedCastNullableTToT(value);
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
// A 2-field variant of Double Checked Locking.
|
||||
if (!initialized) {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
T t = delegate.get();
|
||||
value = t;
|
||||
initialized = true;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is safe because we checked `initialized.`
|
||||
return uncheckedCastNullableTToT(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Suppliers.memoize("
|
||||
+ (initialized ? "<supplier that returned " + value + ">" : delegate)
|
||||
+ ")";
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Supplier<T> delegate = this.delegate;
|
||||
return "Suppliers.memoize("
|
||||
+ (delegate == null ? "<supplier that returned " + value + ">" : delegate)
|
||||
+ ")";
|
||||
static class NonSerializableMemoizingSupplier<T> implements Supplier<T> {
|
||||
volatile Supplier<T> delegate;
|
||||
volatile boolean initialized;
|
||||
// "value" does not need to be volatile; visibility piggy-backs
|
||||
// on volatile read of "initialized".
|
||||
T value;
|
||||
|
||||
NonSerializableMemoizingSupplier(Supplier<T> delegate) {
|
||||
this.delegate = checkNotNull(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
// A 2-field variant of Double Checked Locking.
|
||||
if (!initialized) {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
/*
|
||||
* requireNonNull is safe because we read and write `delegate` under synchronization.
|
||||
*
|
||||
* TODO(cpovirk): To avoid having to check for null, replace `delegate` with a singleton
|
||||
* `Supplier` that always throws an exception.
|
||||
*/
|
||||
T t = requireNonNull(delegate).get();
|
||||
value = t;
|
||||
initialized = true;
|
||||
// Release the delegate to GC.
|
||||
delegate = null;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is safe because we checked `initialized.`
|
||||
return uncheckedCastNullableTToT(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Supplier<T> delegate = this.delegate;
|
||||
return "Suppliers.memoize("
|
||||
+ (delegate == null ? "<supplier that returned " + value + ">" : delegate)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,6 @@
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
public record Tuple<F, S>(F first, S second) {}
|
||||
public record Tuple<F, S>(F first, S second) {
|
||||
|
||||
}
|
||||
|
||||
+449
-381
@@ -18,438 +18,506 @@ package dev.brighten.antivpn.utils.config;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public final class Configuration {
|
||||
public final class Configuration
|
||||
{
|
||||
|
||||
private static final char SEPARATOR = '.';
|
||||
final Map<String, Object> self;
|
||||
final Map<String, List<String>> comments;
|
||||
private final Configuration defaults;
|
||||
private static final char SEPARATOR = '.';
|
||||
final Map<String, Object> self;
|
||||
final Map<String, List<String>> comments;
|
||||
private final Configuration defaults;
|
||||
|
||||
public Configuration() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public Configuration(Configuration defaults) {
|
||||
this(new LinkedHashMap<String, Object>(), defaults);
|
||||
}
|
||||
|
||||
Configuration(Map<?, ?> map, Configuration defaults) {
|
||||
this.self = new LinkedHashMap<>();
|
||||
this.defaults = defaults;
|
||||
comments = new HashMap<>();
|
||||
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
|
||||
|
||||
if (entry.getValue() instanceof Map) {
|
||||
this.self.put(
|
||||
key,
|
||||
new Configuration(
|
||||
(Map) entry.getValue(), (defaults == null) ? null : defaults.getSection(key)));
|
||||
} else {
|
||||
this.self.put(key, entry.getValue());
|
||||
}
|
||||
public Configuration()
|
||||
{
|
||||
this( null );
|
||||
}
|
||||
}
|
||||
|
||||
public void loadFromString(String contents) {
|
||||
public Configuration(Configuration defaults)
|
||||
{
|
||||
this( new LinkedHashMap<String, Object>(), defaults );
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
Collections.addAll(list, contents.split("\n"));
|
||||
Configuration(Map<?, ?> map, Configuration defaults)
|
||||
{
|
||||
this.self = new LinkedHashMap<>();
|
||||
this.defaults = defaults;
|
||||
comments = new HashMap<>();
|
||||
|
||||
int currentLayer = 0;
|
||||
String currentPath = "";
|
||||
for ( Map.Entry<?, ?> entry : map.entrySet() )
|
||||
{
|
||||
String key = ( entry.getKey() == null ) ? "null" : entry.getKey().toString();
|
||||
|
||||
int lineNumber = 0;
|
||||
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); lineNumber++) {
|
||||
String line = iterator.next();
|
||||
|
||||
String trimmed = line.trim();
|
||||
if (trimmed.startsWith("#") || trimmed.isEmpty()) {
|
||||
addCommentLine(currentPath, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!line.isEmpty()) {
|
||||
if (line.contains(":")) {
|
||||
|
||||
int layerFromLine = getLayerFromLine(line, lineNumber);
|
||||
|
||||
if (layerFromLine < currentLayer) {
|
||||
currentPath = regressPathBy(currentLayer - layerFromLine, currentPath);
|
||||
}
|
||||
|
||||
String key = getKeyFromLine(line);
|
||||
|
||||
if (currentLayer == 0) {
|
||||
currentPath = key;
|
||||
} else {
|
||||
currentPath += "." + key;
|
||||
}
|
||||
if ( entry.getValue() instanceof Map )
|
||||
{
|
||||
this.self.put( key, new Configuration( (Map) entry.getValue(), ( defaults == null ) ? null : defaults.getSection( key ) ) );
|
||||
} else
|
||||
{
|
||||
this.self.put( key, entry.getValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addCommentLine(String currentPath, String line) {
|
||||
|
||||
List<String> list = comments.get(currentPath);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
list.add(line);
|
||||
|
||||
comments.put(currentPath, list);
|
||||
}
|
||||
|
||||
String getKeyFromLine(String line) {
|
||||
String key = null;
|
||||
|
||||
for (int i = 0; i < line.length(); i++) {
|
||||
if (line.charAt(i) == ':') {
|
||||
key = line.substring(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return key == null ? null : key.trim();
|
||||
}
|
||||
public void loadFromString(String contents) {
|
||||
|
||||
String regressPathBy(int i, String currentPath) {
|
||||
if (i <= 0) {
|
||||
return currentPath;
|
||||
}
|
||||
String[] split = currentPath.split("\\.");
|
||||
List<String> list = new ArrayList<>();
|
||||
Collections.addAll(list, contents.split("\n"));
|
||||
|
||||
String rebuild = "";
|
||||
for (int j = 0; j < split.length - i; j++) {
|
||||
rebuild += split[j];
|
||||
if (j <= (split.length - j)) {
|
||||
rebuild += ".";
|
||||
}
|
||||
int currentLayer = 0;
|
||||
String currentPath = "";
|
||||
|
||||
int lineNumber = 0;
|
||||
for(Iterator<String> iterator = list.iterator(); iterator.hasNext(); lineNumber++) {
|
||||
String line = iterator.next();
|
||||
|
||||
String trimmed = line.trim();
|
||||
if(trimmed.startsWith("#") || trimmed.isEmpty()) {
|
||||
addCommentLine(currentPath, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!line.isEmpty()) {
|
||||
if(line.contains(":")) {
|
||||
|
||||
int layerFromLine = getLayerFromLine(line, lineNumber);
|
||||
|
||||
if(layerFromLine < currentLayer) {
|
||||
currentPath = regressPathBy(currentLayer - layerFromLine, currentPath);
|
||||
}
|
||||
|
||||
String key = getKeyFromLine(line);
|
||||
|
||||
if(currentLayer == 0) {
|
||||
currentPath = key;
|
||||
}
|
||||
else {
|
||||
currentPath += "." + key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rebuild;
|
||||
}
|
||||
private void addCommentLine(String currentPath, String line) {
|
||||
|
||||
int getLayerFromLine(String line, int lineNumber) {
|
||||
List<String> list = comments.get(currentPath);
|
||||
if(list == null) {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
list.add(line);
|
||||
|
||||
double d = 0;
|
||||
for (int i = 0; i < line.length(); i++) {
|
||||
if (line.charAt(i) == ' ') {
|
||||
d += 0.5;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
comments.put(currentPath, list);
|
||||
}
|
||||
|
||||
return (int) d;
|
||||
}
|
||||
String getKeyFromLine(String line) {
|
||||
String key = null;
|
||||
|
||||
private Configuration getSectionFor(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
if (index == -1) {
|
||||
return this;
|
||||
for(int i = 0; i < line.length(); i++) {
|
||||
if(line.charAt(i) == ':') {
|
||||
key = line.substring(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return key == null ? null : key.trim();
|
||||
}
|
||||
|
||||
String root = path.substring(0, index);
|
||||
Object section = self.get(root);
|
||||
if (section == null) {
|
||||
section = new Configuration((defaults == null) ? null : defaults.getSection(root));
|
||||
self.put(root, section);
|
||||
String regressPathBy(int i, String currentPath) {
|
||||
if(i <= 0) {
|
||||
return currentPath;
|
||||
}
|
||||
String[] split = currentPath.split("\\.");
|
||||
|
||||
String rebuild = "";
|
||||
for(int j = 0; j < split.length - i; j++) {
|
||||
rebuild += split[j];
|
||||
if(j <= (split.length - j)) {
|
||||
rebuild += ".";
|
||||
}
|
||||
}
|
||||
|
||||
return rebuild;
|
||||
}
|
||||
|
||||
return (Configuration) section;
|
||||
}
|
||||
int getLayerFromLine(String line, int lineNumber) {
|
||||
|
||||
private String getChild(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
return (index == -1) ? path : path.substring(index + 1);
|
||||
}
|
||||
double d = 0;
|
||||
for(int i = 0; i < line.length(); i++) {
|
||||
if(line.charAt(i) == ' ') {
|
||||
d += 0.5;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) d;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String path, T def) {
|
||||
Configuration section = getSectionFor(path);
|
||||
Object val;
|
||||
if (section == this) {
|
||||
val = self.get(path);
|
||||
} else {
|
||||
val = section.get(getChild(path), def);
|
||||
}
|
||||
|
||||
if (val == null && def instanceof Configuration) {
|
||||
self.put(path, def);
|
||||
private Configuration getSectionFor(String path)
|
||||
{
|
||||
int index = path.indexOf( SEPARATOR );
|
||||
if ( index == -1 )
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
String root = path.substring( 0, index );
|
||||
Object section = self.get( root );
|
||||
if ( section == null )
|
||||
{
|
||||
section = new Configuration( ( defaults == null ) ? null : defaults.getSection( root ) );
|
||||
self.put( root, section );
|
||||
}
|
||||
|
||||
return (Configuration) section;
|
||||
}
|
||||
|
||||
return (val != null) ? (T) val : def;
|
||||
}
|
||||
|
||||
public boolean contains(String path) {
|
||||
return get(path, null) != null;
|
||||
}
|
||||
|
||||
public Object get(String path) {
|
||||
return get(path, getDefault(path));
|
||||
}
|
||||
|
||||
public Object getDefault(String path) {
|
||||
return (defaults == null) ? null : defaults.get(path);
|
||||
}
|
||||
|
||||
public void set(String path, Object value) {
|
||||
if (value instanceof Map) {
|
||||
value = new Configuration((Map) value, (defaults == null) ? null : defaults.getSection(path));
|
||||
private String getChild(String path)
|
||||
{
|
||||
int index = path.indexOf( SEPARATOR );
|
||||
return ( index == -1 ) ? path : path.substring( index + 1 );
|
||||
}
|
||||
|
||||
Configuration section = getSectionFor(path);
|
||||
if (section == this) {
|
||||
if (value == null) {
|
||||
self.remove(path);
|
||||
} else {
|
||||
self.put(path, value);
|
||||
}
|
||||
} else {
|
||||
section.set(getChild(path), value);
|
||||
}
|
||||
}
|
||||
/*------------------------------------------------------------------------*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String path, T def)
|
||||
{
|
||||
Configuration section = getSectionFor( path );
|
||||
Object val;
|
||||
if ( section == this )
|
||||
{
|
||||
val = self.get( path );
|
||||
} else
|
||||
{
|
||||
val = section.get( getChild( path ), def );
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public Configuration getSection(String path) {
|
||||
Object def = getDefault(path);
|
||||
return (Configuration)
|
||||
get(
|
||||
path,
|
||||
(def instanceof Configuration)
|
||||
? def
|
||||
: new Configuration((defaults == null) ? null : defaults.getSection(path)));
|
||||
}
|
||||
if ( val == null && def instanceof Configuration )
|
||||
{
|
||||
self.put( path, def );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets keys, not deep by default.
|
||||
*
|
||||
* @return top level keys for this section
|
||||
*/
|
||||
public Collection<String> getKeys() {
|
||||
return new LinkedHashSet<>(self.keySet());
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public byte getByte(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getByte(path, (def instanceof Number) ? ((Number) def).byteValue() : 0);
|
||||
}
|
||||
|
||||
public byte getByte(String path, byte def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof Number) ? ((Number) val).byteValue() : def;
|
||||
}
|
||||
|
||||
public List<Byte> getByteList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<Byte> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof Number) {
|
||||
result.add(((Number) object).byteValue());
|
||||
}
|
||||
return ( val != null ) ? (T) val : def;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public short getShort(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getShort(path, (def instanceof Number) ? ((Number) def).shortValue() : 0);
|
||||
}
|
||||
|
||||
public short getShort(String path, short def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof Number) ? ((Number) val).shortValue() : def;
|
||||
}
|
||||
|
||||
public List<Short> getShortList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<Short> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof Number) {
|
||||
result.add(((Number) object).shortValue());
|
||||
}
|
||||
public boolean contains(String path)
|
||||
{
|
||||
return get( path, null ) != null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getInt(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getInt(path, (def instanceof Number) ? ((Number) def).intValue() : 0);
|
||||
}
|
||||
|
||||
public int getInt(String path, int def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof Number) ? ((Number) val).intValue() : def;
|
||||
}
|
||||
|
||||
public List<Integer> getIntList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<Integer> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof Number) {
|
||||
result.add(((Number) object).intValue());
|
||||
}
|
||||
public Object get(String path)
|
||||
{
|
||||
return get( path, getDefault( path ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public long getLong(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getLong(path, (def instanceof Number) ? ((Number) def).longValue() : 0);
|
||||
}
|
||||
|
||||
public long getLong(String path, long def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof Number) ? ((Number) val).longValue() : def;
|
||||
}
|
||||
|
||||
public List<Long> getLongList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<Long> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof Number) {
|
||||
result.add(((Number) object).longValue());
|
||||
}
|
||||
public Object getDefault(String path)
|
||||
{
|
||||
return ( defaults == null ) ? null : defaults.get( path );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public void set(String path, Object value)
|
||||
{
|
||||
if ( value instanceof Map )
|
||||
{
|
||||
value = new Configuration( (Map) value, ( defaults == null ) ? null : defaults.getSection( path ) );
|
||||
}
|
||||
|
||||
public float getFloat(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getFloat(path, (def instanceof Number) ? ((Number) def).floatValue() : 0);
|
||||
}
|
||||
|
||||
public float getFloat(String path, float def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof Number) ? ((Number) val).floatValue() : def;
|
||||
}
|
||||
|
||||
public List<Float> getFloatList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<Float> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof Number) {
|
||||
result.add(((Number) object).floatValue());
|
||||
}
|
||||
Configuration section = getSectionFor( path );
|
||||
if ( section == this )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
self.remove( path );
|
||||
} else
|
||||
{
|
||||
self.put( path, value );
|
||||
}
|
||||
} else
|
||||
{
|
||||
section.set( getChild( path ), value );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public double getDouble(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getDouble(path, (def instanceof Number) ? ((Number) def).doubleValue() : 0);
|
||||
}
|
||||
|
||||
public double getDouble(String path, double def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof Number) ? ((Number) val).doubleValue() : def;
|
||||
}
|
||||
|
||||
public List<Double> getDoubleList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<Double> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof Number) {
|
||||
result.add(((Number) object).doubleValue());
|
||||
}
|
||||
/*------------------------------------------------------------------------*/
|
||||
public Configuration getSection(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return (Configuration) get( path, ( def instanceof Configuration ) ? def : new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean getBoolean(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getBoolean(path, (def instanceof Boolean) ? (Boolean) def : false);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String path, boolean def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof Boolean) ? (Boolean) val : def;
|
||||
}
|
||||
|
||||
public List<Boolean> getBooleanList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<Boolean> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof Boolean) {
|
||||
result.add((Boolean) object);
|
||||
}
|
||||
/**
|
||||
* Gets keys, not deep by default.
|
||||
*
|
||||
* @return top level keys for this section
|
||||
*/
|
||||
public Collection<String> getKeys()
|
||||
{
|
||||
return new LinkedHashSet<>( self.keySet() );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public char getChar(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getChar(path, (def instanceof Character) ? (Character) def : '\u0000');
|
||||
}
|
||||
|
||||
public char getChar(String path, char def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof Character) ? (Character) val : def;
|
||||
}
|
||||
|
||||
public List<Character> getCharList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<Character> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof Character) {
|
||||
result.add((Character) object);
|
||||
}
|
||||
/*------------------------------------------------------------------------*/
|
||||
public byte getByte(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getByte( path, ( def instanceof Number ) ? ( (Number) def ).byteValue() : 0 );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getString(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getString(path, (def instanceof String) ? (String) def : "");
|
||||
}
|
||||
|
||||
public String getString(String path, String def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof String) ? (String) val : def;
|
||||
}
|
||||
|
||||
public List<String> getStringList(String path) {
|
||||
List<?> list = getList(path);
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
for (Object object : list) {
|
||||
if (object instanceof String) {
|
||||
result.add((String) object);
|
||||
}
|
||||
public byte getByte(String path, byte def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).byteValue() : def;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public List<Byte> getByteList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Byte> result = new ArrayList<>();
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public List<?> getList(String path) {
|
||||
Object def = getDefault(path);
|
||||
return getList(path, (def instanceof List<?>) ? (List<?>) def : Collections.EMPTY_LIST);
|
||||
}
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).byteValue() );
|
||||
}
|
||||
}
|
||||
|
||||
public List<?> getList(String path, List<?> def) {
|
||||
Object val = get(path, def);
|
||||
return (val instanceof List<?>) ? (List<?>) val : def;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public short getShort(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getShort( path, ( def instanceof Number ) ? ( (Number) def ).shortValue() : 0 );
|
||||
}
|
||||
|
||||
public short getShort(String path, short def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).shortValue() : def;
|
||||
}
|
||||
|
||||
public List<Short> getShortList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Short> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).shortValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getInt(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getInt( path, ( def instanceof Number ) ? ( (Number) def ).intValue() : 0 );
|
||||
}
|
||||
|
||||
public int getInt(String path, int def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).intValue() : def;
|
||||
}
|
||||
|
||||
public List<Integer> getIntList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Integer> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).intValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public long getLong(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getLong( path, ( def instanceof Number ) ? ( (Number) def ).longValue() : 0 );
|
||||
}
|
||||
|
||||
public long getLong(String path, long def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).longValue() : def;
|
||||
}
|
||||
|
||||
public List<Long> getLongList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Long> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).longValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public float getFloat(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getFloat( path, ( def instanceof Number ) ? ( (Number) def ).floatValue() : 0 );
|
||||
}
|
||||
|
||||
public float getFloat(String path, float def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).floatValue() : def;
|
||||
}
|
||||
|
||||
public List<Float> getFloatList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Float> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).floatValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public double getDouble(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getDouble( path, ( def instanceof Number ) ? ( (Number) def ).doubleValue() : 0 );
|
||||
}
|
||||
|
||||
public double getDouble(String path, double def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).doubleValue() : def;
|
||||
}
|
||||
|
||||
public List<Double> getDoubleList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Double> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).doubleValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean getBoolean(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getBoolean( path, ( def instanceof Boolean ) ? (Boolean) def : false );
|
||||
}
|
||||
|
||||
public boolean getBoolean(String path, boolean def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Boolean ) ? (Boolean) val : def;
|
||||
}
|
||||
|
||||
public List<Boolean> getBooleanList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Boolean> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Boolean )
|
||||
{
|
||||
result.add( (Boolean) object );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public char getChar(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getChar( path, ( def instanceof Character ) ? (Character) def : '\u0000' );
|
||||
}
|
||||
|
||||
public char getChar(String path, char def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Character ) ? (Character) val : def;
|
||||
}
|
||||
|
||||
public List<Character> getCharList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Character> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Character )
|
||||
{
|
||||
result.add( (Character) object );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getString(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getString( path, ( def instanceof String ) ? (String) def : "" );
|
||||
}
|
||||
|
||||
public String getString(String path, String def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof String ) ? (String) val : def;
|
||||
}
|
||||
|
||||
public List<String> getStringList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof String )
|
||||
{
|
||||
result.add( (String) object );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public List<?> getList(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getList( path, ( def instanceof List<?> ) ? (List<?>) def : Collections.EMPTY_LIST );
|
||||
}
|
||||
|
||||
public List<?> getList(String path, List<?> def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof List<?> ) ? (List<?>) val : def;
|
||||
}
|
||||
}
|
||||
+29
-25
@@ -20,42 +20,46 @@ import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class ConfigurationProvider {
|
||||
public abstract class ConfigurationProvider
|
||||
{
|
||||
|
||||
public static final Map<Class<? extends ConfigurationProvider>, ConfigurationProvider> providers =
|
||||
new HashMap<>();
|
||||
public static final Map<Class<? extends ConfigurationProvider>, ConfigurationProvider> providers = new HashMap<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
providers.put(YamlConfiguration.class, new YamlConfiguration());
|
||||
} catch (NoClassDefFoundError ex) {
|
||||
ex.printStackTrace();
|
||||
// Ignore, no SnakeYAML
|
||||
static
|
||||
{
|
||||
try
|
||||
{
|
||||
providers.put( YamlConfiguration.class, new YamlConfiguration() );
|
||||
} catch ( NoClassDefFoundError ex )
|
||||
{
|
||||
ex.printStackTrace();
|
||||
// Ignore, no SnakeYAML
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigurationProvider getProvider(Class<? extends ConfigurationProvider> provider) {
|
||||
return providers.get(provider);
|
||||
}
|
||||
public static ConfigurationProvider getProvider(Class<? extends ConfigurationProvider> provider)
|
||||
{
|
||||
return providers.get( provider );
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public abstract void save(Configuration config, File file) throws IOException;
|
||||
/*------------------------------------------------------------------------*/
|
||||
public abstract void save(Configuration config, File file) throws IOException;
|
||||
|
||||
public abstract void save(Configuration config, Writer writer);
|
||||
public abstract void save(Configuration config, Writer writer);
|
||||
|
||||
public abstract Configuration load(File file) throws IOException;
|
||||
public abstract Configuration load(File file) throws IOException;
|
||||
|
||||
public abstract Configuration load(File file, Configuration defaults) throws IOException;
|
||||
public abstract Configuration load(File file, Configuration defaults) throws IOException;
|
||||
|
||||
public abstract Configuration load(Reader reader);
|
||||
public abstract Configuration load(Reader reader);
|
||||
|
||||
public abstract Configuration load(Reader reader, Configuration defaults);
|
||||
public abstract Configuration load(Reader reader, Configuration defaults);
|
||||
|
||||
public abstract Configuration load(InputStream is);
|
||||
public abstract Configuration load(InputStream is);
|
||||
|
||||
public abstract Configuration load(InputStream is, Configuration defaults);
|
||||
public abstract Configuration load(InputStream is, Configuration defaults);
|
||||
|
||||
public abstract Configuration load(String string);
|
||||
public abstract Configuration load(String string);
|
||||
|
||||
public abstract Configuration load(String string, Configuration defaults);
|
||||
}
|
||||
public abstract Configuration load(String string, Configuration defaults);
|
||||
}
|
||||
+150
-142
@@ -16,10 +16,6 @@
|
||||
|
||||
package dev.brighten.antivpn.utils.config;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
@@ -29,161 +25,173 @@ import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class YamlConfiguration extends ConfigurationProvider {
|
||||
public class YamlConfiguration extends ConfigurationProvider
|
||||
{
|
||||
|
||||
private final ThreadLocal<Yaml> yaml =
|
||||
new ThreadLocal<Yaml>() {
|
||||
private final ThreadLocal<Yaml> yaml = new ThreadLocal<Yaml>()
|
||||
{
|
||||
@Override
|
||||
protected Yaml initialValue() {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
Representer representer =
|
||||
new Representer(options) {
|
||||
protected Yaml initialValue()
|
||||
{
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK );
|
||||
Representer representer = new Representer(options)
|
||||
{
|
||||
{
|
||||
representers.put(
|
||||
Configuration.class, data -> represent(((Configuration) data).self));
|
||||
representers.put( Configuration.class, data -> represent( ( (Configuration) data ).self ));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
return new Yaml(new Constructor(new LoaderOptions()), representer, options);
|
||||
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
return new Yaml( new Constructor(new LoaderOptions()), representer, options );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@Override
|
||||
public void save(Configuration config, File file) throws IOException {
|
||||
try (Writer writer =
|
||||
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
|
||||
save(config, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Configuration config, Writer writer) {
|
||||
String contents = this.yaml.get().dump(config.self);
|
||||
if (contents.equals("{}\n")) {
|
||||
contents = "";
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
Collections.addAll(list, contents.split("\n"));
|
||||
|
||||
int currentLayer = 0;
|
||||
StringBuilder currentPath = new StringBuilder();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int lineNumber = 0;
|
||||
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); lineNumber++) {
|
||||
String line = iterator.next();
|
||||
sb.append(line);
|
||||
sb.append('\n');
|
||||
|
||||
if (!line.isEmpty()) {
|
||||
if (line.contains(":")) {
|
||||
|
||||
int layerFromLine = config.getLayerFromLine(line, lineNumber);
|
||||
|
||||
if (layerFromLine < currentLayer) {
|
||||
currentPath =
|
||||
new StringBuilder(
|
||||
config.regressPathBy(currentLayer - layerFromLine, currentPath.toString()));
|
||||
}
|
||||
|
||||
String key = config.getKeyFromLine(line);
|
||||
|
||||
if (currentLayer == 0) {
|
||||
currentPath = new StringBuilder(key);
|
||||
} else {
|
||||
currentPath.append("." + key);
|
||||
}
|
||||
|
||||
String path = currentPath.toString();
|
||||
if (config.comments.containsKey(path)) {
|
||||
config
|
||||
.comments
|
||||
.get(path)
|
||||
.forEach(
|
||||
string -> {
|
||||
sb.append(string);
|
||||
sb.append('\n');
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void save(Configuration config, File file) throws IOException
|
||||
{
|
||||
try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), StandardCharsets.UTF_8 ) )
|
||||
{
|
||||
save( config, writer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
writer.write(sb.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@Override
|
||||
public void save(Configuration config, Writer writer)
|
||||
{
|
||||
String contents = this.yaml.get().dump(config.self);
|
||||
if (contents.equals("{}\n")) {
|
||||
contents = "";
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
Collections.addAll(list, contents.split("\n"));
|
||||
|
||||
int currentLayer = 0;
|
||||
StringBuilder currentPath = new StringBuilder();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int lineNumber = 0;
|
||||
for(Iterator<String> iterator = list.iterator(); iterator.hasNext(); lineNumber++) {
|
||||
String line = iterator.next();
|
||||
sb.append(line);
|
||||
sb.append('\n');
|
||||
|
||||
if (!line.isEmpty()) {
|
||||
if (line.contains(":")) {
|
||||
|
||||
int layerFromLine = config.getLayerFromLine(line, lineNumber);
|
||||
|
||||
if (layerFromLine < currentLayer) {
|
||||
currentPath = new StringBuilder(config.regressPathBy(currentLayer - layerFromLine, currentPath.toString()));
|
||||
}
|
||||
|
||||
String key = config.getKeyFromLine(line);
|
||||
|
||||
if (currentLayer == 0) {
|
||||
currentPath = new StringBuilder(key);
|
||||
} else {
|
||||
currentPath.append("." + key);
|
||||
}
|
||||
|
||||
String path = currentPath.toString();
|
||||
if (config.comments.containsKey(path)) {
|
||||
config.comments.get(path).forEach(string -> {
|
||||
sb.append(string);
|
||||
sb.append('\n');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
writer.write(sb.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(File file) throws IOException {
|
||||
return load(file, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(File file, Configuration defaults) throws IOException {
|
||||
try (FileInputStream is = new FileInputStream(file)) {
|
||||
return load(is, defaults);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(Reader reader) {
|
||||
return load(reader, null);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Configuration load(Reader reader, Configuration defaults) {
|
||||
BufferedReader input =
|
||||
reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String line;
|
||||
try {
|
||||
while ((line = input.readLine()) != null) {
|
||||
builder.append(line);
|
||||
builder.append('\n');
|
||||
}
|
||||
} finally {
|
||||
input.close();
|
||||
@Override
|
||||
public Configuration load(File file) throws IOException
|
||||
{
|
||||
return load( file, null );
|
||||
}
|
||||
|
||||
return load(builder.toString(), defaults);
|
||||
}
|
||||
@Override
|
||||
public Configuration load(File file, Configuration defaults) throws IOException
|
||||
{
|
||||
try ( FileInputStream is = new FileInputStream( file ) )
|
||||
{
|
||||
return load( is, defaults );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(InputStream is) {
|
||||
return this.load(new InputStreamReader(is, Charset.defaultCharset()));
|
||||
}
|
||||
@Override
|
||||
public Configuration load(Reader reader)
|
||||
{
|
||||
return load( reader, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(InputStream is, Configuration defaults) {
|
||||
return this.load(new InputStreamReader(is, Charset.defaultCharset()), defaults);
|
||||
}
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Configuration load(Reader reader, Configuration defaults)
|
||||
{
|
||||
BufferedReader input = reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(reader);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public Configuration load(String string) {
|
||||
return load(string, null);
|
||||
}
|
||||
String line;
|
||||
try {
|
||||
while((line = input.readLine()) != null) {
|
||||
builder.append(line);
|
||||
builder.append('\n');
|
||||
}
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Configuration load(String contents, Configuration defaults) {
|
||||
Map<String, Object> map;
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setMaxAliasesForCollections(2147483647);
|
||||
map = this.yaml.get().loadAs(contents, LinkedHashMap.class);
|
||||
|
||||
Configuration config = new Configuration(map, defaults);
|
||||
config.loadFromString(contents);
|
||||
return load(builder.toString(), defaults);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Configuration load(InputStream is)
|
||||
{
|
||||
return this.load(new InputStreamReader(is, Charset.defaultCharset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(InputStream is, Configuration defaults)
|
||||
{
|
||||
return this.load(new InputStreamReader(is, Charset.defaultCharset()), defaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(String string)
|
||||
{
|
||||
return load( string, null );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Configuration load(String contents, Configuration defaults)
|
||||
{
|
||||
Map<String, Object> map;
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setMaxAliasesForCollections(2147483647);
|
||||
map = this.yaml.get().loadAs(contents, LinkedHashMap.class);
|
||||
|
||||
Configuration config = new Configuration( map, defaults );
|
||||
config.loadFromString(contents);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,254 +17,266 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
* This provides static methods to convert comma delimited text into a JSONArray, and to covert a
|
||||
* JSONArray into comma delimited text. Comma delimited text is a very popular format for data
|
||||
* interchange. It is understood by most database, spreadsheet, and organizer programs.
|
||||
*
|
||||
* <p>Each row of text represents a row in a table or a data record. Each row ends with a NEWLINE
|
||||
* character. Each row contains one or more values. Values are separated by commas. A value can
|
||||
* contain any character except for comma, unless is is wrapped in single quotes or double quotes.
|
||||
*
|
||||
* <p>The first row usually contains the names of the columns.
|
||||
*
|
||||
* <p>A comma delimited list can be converted into a JSONArray of JSONObjects. The names for the
|
||||
* elements in the JSONObjects can be taken from the names in the first row.
|
||||
* This provides static methods to convert comma delimited text into a
|
||||
* JSONArray, and to covert a JSONArray into comma delimited text. Comma
|
||||
* delimited text is a very popular format for data interchange. It is
|
||||
* understood by most database, spreadsheet, and organizer programs.
|
||||
* <p>
|
||||
* Each row of text represents a row in a table or a data record. Each row
|
||||
* ends with a NEWLINE character. Each row contains one or more values.
|
||||
* Values are separated by commas. A value can contain any character except
|
||||
* for comma, unless is is wrapped in single quotes or double quotes.
|
||||
* <p>
|
||||
* The first row usually contains the names of the columns.
|
||||
* <p>
|
||||
* A comma delimited list can be converted into a JSONArray of JSONObjects.
|
||||
* The names for the elements in the JSONObjects can be taken from the names
|
||||
* in the first row.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class CDL {
|
||||
|
||||
/**
|
||||
* Get the next value. The value can be wrapped in quotes. The value can be empty.
|
||||
*
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return The value string, or null if empty.
|
||||
* @throws JSONException if the quoted string is badly formed.
|
||||
*/
|
||||
private static String getValue(JSONTokener x) throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuffer sb;
|
||||
do {
|
||||
c = x.next();
|
||||
} while (c == ' ' || c == '\t');
|
||||
switch (c) {
|
||||
case 0:
|
||||
return null;
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
sb = new StringBuffer();
|
||||
/**
|
||||
* Get the next value. The value can be wrapped in quotes. The value can
|
||||
* be empty.
|
||||
*
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return The value string, or null if empty.
|
||||
* @throws JSONException if the quoted string is badly formed.
|
||||
*/
|
||||
private static String getValue(JSONTokener x) throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuffer sb;
|
||||
do {
|
||||
c = x.next();
|
||||
} while (c == ' ' || c == '\t');
|
||||
switch (c) {
|
||||
case 0:
|
||||
return null;
|
||||
case '"':
|
||||
case '\'':
|
||||
q = c;
|
||||
sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
c = x.next();
|
||||
if (c == q) {
|
||||
break;
|
||||
}
|
||||
if (c == 0 || c == '\n' || c == '\r') {
|
||||
throw x.syntaxError("Missing close quote '" + q + "'.");
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
case ',':
|
||||
x.back();
|
||||
return "";
|
||||
default:
|
||||
x.back();
|
||||
return x.nextTo(',');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of strings from a row of comma delimited values.
|
||||
*
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONArray of strings.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
|
||||
JSONArray ja = new JSONArray();
|
||||
for (; ; ) {
|
||||
c = x.next();
|
||||
if (c == q) {
|
||||
break;
|
||||
}
|
||||
if (c == 0 || c == '\n' || c == '\r') {
|
||||
throw x.syntaxError("Missing close quote '" + q + "'.");
|
||||
}
|
||||
sb.append(c);
|
||||
String value = getValue(x);
|
||||
char c = x.next();
|
||||
if (value == null ||
|
||||
(ja.length() == 0 && value.length() == 0 && c != ',')) {
|
||||
return null;
|
||||
}
|
||||
ja.put(value);
|
||||
for (; ; ) {
|
||||
if (c == ',') {
|
||||
break;
|
||||
}
|
||||
if (c != ' ') {
|
||||
if (c == '\n' || c == '\r' || c == 0) {
|
||||
return ja;
|
||||
}
|
||||
throw x.syntaxError("Bad character '" + c + "' (" +
|
||||
(int) c + ").");
|
||||
}
|
||||
c = x.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONObject from a row of comma delimited text, using a
|
||||
* parallel JSONArray of strings to provides the names of the elements.
|
||||
*
|
||||
* @param names A JSONArray of names. This is commonly obtained from the
|
||||
* first row of a comma delimited text file using the rowToJSONArray
|
||||
* method.
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONObject combining the names and values.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
|
||||
throws JSONException {
|
||||
JSONArray ja = rowToJSONArray(x);
|
||||
return ja != null ? ja.toJSONObject(names) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text row from a JSONArray. Values containing
|
||||
* the comma character will be quoted. Troublesome characters may be
|
||||
* removed.
|
||||
*
|
||||
* @param ja A JSONArray of strings.
|
||||
* @return A string ending in NEWLINE.
|
||||
*/
|
||||
public static String rowToString(JSONArray ja) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < ja.length(); i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
Object object = ja.opt(i);
|
||||
if (object != null) {
|
||||
String string = object.toString();
|
||||
if (string.length() > 0 && (string.indexOf(',') >= 0 ||
|
||||
string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 ||
|
||||
string.indexOf(0) >= 0 || string.charAt(0) == '"')) {
|
||||
sb.append('"');
|
||||
int length = string.length();
|
||||
for (int j = 0; j < length; j += 1) {
|
||||
char c = string.charAt(j);
|
||||
if (c >= ' ' && c != '"') {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
sb.append('"');
|
||||
} else {
|
||||
sb.append(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string,
|
||||
* using the first row as a source of names.
|
||||
*
|
||||
* @param string The comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return toJSONArray(new JSONTokener(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string,
|
||||
* using the first row as a source of names.
|
||||
*
|
||||
* @param x The JSONTokener containing the comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
|
||||
return toJSONArray(rowToJSONArray(x), x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string
|
||||
* using a supplied JSONArray as the source of element names.
|
||||
*
|
||||
* @param names A JSONArray of strings.
|
||||
* @param string The comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONArray names, String string)
|
||||
throws JSONException {
|
||||
return toJSONArray(names, new JSONTokener(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string
|
||||
* using a supplied JSONArray as the source of element names.
|
||||
*
|
||||
* @param names A JSONArray of strings.
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
|
||||
throws JSONException {
|
||||
if (names == null || names.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONArray ja = new JSONArray();
|
||||
for (; ; ) {
|
||||
JSONObject jo = rowToJSONObject(names, x);
|
||||
if (jo == null) {
|
||||
break;
|
||||
}
|
||||
ja.put(jo);
|
||||
}
|
||||
if (ja.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return ja;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text from a JSONArray of JSONObjects. The
|
||||
* first row will be a list of names obtained by inspecting the first
|
||||
* JSONObject.
|
||||
*
|
||||
* @param ja A JSONArray of JSONObjects.
|
||||
* @return A comma delimited text.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray ja) throws JSONException {
|
||||
JSONObject jo = ja.optJSONObject(0);
|
||||
if (jo != null) {
|
||||
JSONArray names = jo.names();
|
||||
if (names != null) {
|
||||
return rowToString(names) + toString(names, ja);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text from a JSONArray of JSONObjects using
|
||||
* a provided list of names. The list of names is not included in the
|
||||
* output.
|
||||
*
|
||||
* @param names A JSONArray of strings.
|
||||
* @param ja A JSONArray of JSONObjects.
|
||||
* @return A comma delimited text.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray names, JSONArray ja)
|
||||
throws JSONException {
|
||||
if (names == null || names.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < ja.length(); i += 1) {
|
||||
JSONObject jo = ja.optJSONObject(i);
|
||||
if (jo != null) {
|
||||
sb.append(rowToString(jo.toJSONArray(names)));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
case ',':
|
||||
x.back();
|
||||
return "";
|
||||
default:
|
||||
x.back();
|
||||
return x.nextTo(',');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of strings from a row of comma delimited values.
|
||||
*
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONArray of strings.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
|
||||
JSONArray ja = new JSONArray();
|
||||
for (; ; ) {
|
||||
String value = getValue(x);
|
||||
char c = x.next();
|
||||
if (value == null || (ja.length() == 0 && value.isEmpty() && c != ',')) {
|
||||
return null;
|
||||
}
|
||||
ja.put(value);
|
||||
for (; ; ) {
|
||||
if (c == ',') {
|
||||
break;
|
||||
}
|
||||
if (c != ' ') {
|
||||
if (c == '\n' || c == '\r' || c == 0) {
|
||||
return ja;
|
||||
}
|
||||
throw x.syntaxError("Bad character '" + c + "' (" + (int) c + ").");
|
||||
}
|
||||
c = x.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONObject from a row of comma delimited text, using a parallel JSONArray of strings
|
||||
* to provides the names of the elements.
|
||||
*
|
||||
* @param names A JSONArray of names. This is commonly obtained from the first row of a comma
|
||||
* delimited text file using the rowToJSONArray method.
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONObject combining the names and values.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) throws JSONException {
|
||||
JSONArray ja = rowToJSONArray(x);
|
||||
return ja != null ? ja.toJSONObject(names) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text row from a JSONArray. Values containing the comma character will
|
||||
* be quoted. Troublesome characters may be removed.
|
||||
*
|
||||
* @param ja A JSONArray of strings.
|
||||
* @return A string ending in NEWLINE.
|
||||
*/
|
||||
public static String rowToString(JSONArray ja) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < ja.length(); i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
Object object = ja.opt(i);
|
||||
if (object != null) {
|
||||
String string = object.toString();
|
||||
if (string.length() > 0
|
||||
&& (string.indexOf(',') >= 0
|
||||
|| string.indexOf('\n') >= 0
|
||||
|| string.indexOf('\r') >= 0
|
||||
|| string.indexOf(0) >= 0
|
||||
|| string.charAt(0) == '"')) {
|
||||
sb.append('"');
|
||||
int length = string.length();
|
||||
for (int j = 0; j < length; j += 1) {
|
||||
char c = string.charAt(j);
|
||||
if (c >= ' ' && c != '"') {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
sb.append('"');
|
||||
} else {
|
||||
sb.append(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string, using the first row as a
|
||||
* source of names.
|
||||
*
|
||||
* @param string The comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return toJSONArray(new JSONTokener(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string, using the first row as a
|
||||
* source of names.
|
||||
*
|
||||
* @param x The JSONTokener containing the comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
|
||||
return toJSONArray(rowToJSONArray(x), x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string using a supplied
|
||||
* JSONArray as the source of element names.
|
||||
*
|
||||
* @param names A JSONArray of strings.
|
||||
* @param string The comma delimited text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONArray names, String string) throws JSONException {
|
||||
return toJSONArray(names, new JSONTokener(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONArray of JSONObjects from a comma delimited text string using a supplied
|
||||
* JSONArray as the source of element names.
|
||||
*
|
||||
* @param names A JSONArray of strings.
|
||||
* @param x A JSONTokener of the source text.
|
||||
* @return A JSONArray of JSONObjects.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(JSONArray names, JSONTokener x) throws JSONException {
|
||||
if (names == null || names.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONArray ja = new JSONArray();
|
||||
for (; ; ) {
|
||||
JSONObject jo = rowToJSONObject(names, x);
|
||||
if (jo == null) {
|
||||
break;
|
||||
}
|
||||
ja.put(jo);
|
||||
}
|
||||
if (ja.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return ja;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text from a JSONArray of JSONObjects. The first row will be a list of
|
||||
* names obtained by inspecting the first JSONObject.
|
||||
*
|
||||
* @param ja A JSONArray of JSONObjects.
|
||||
* @return A comma delimited text.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray ja) throws JSONException {
|
||||
JSONObject jo = ja.optJSONObject(0);
|
||||
if (jo != null) {
|
||||
JSONArray names = jo.names();
|
||||
if (names != null) {
|
||||
return rowToString(names) + toString(names, ja);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a comma delimited text from a JSONArray of JSONObjects using a provided list of names.
|
||||
* The list of names is not included in the output.
|
||||
*
|
||||
* @param names A JSONArray of strings.
|
||||
* @param ja A JSONArray of JSONObjects.
|
||||
* @return A comma delimited text.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray names, JSONArray ja) throws JSONException {
|
||||
if (names == null || names.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < ja.length(); i += 1) {
|
||||
JSONObject jo = ja.optJSONObject(i);
|
||||
if (jo != null) {
|
||||
sb.append(rowToString(jo.toJSONArray(names)));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,139 +17,150 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
* Convert a web browser cookie specification to a JSONObject and back. JSON and Cookies are both
|
||||
* notations for name/value pairs.
|
||||
* Convert a web browser cookie specification to a JSONObject and back.
|
||||
* JSON and Cookies are both notations for name/value pairs.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class Cookie {
|
||||
|
||||
/**
|
||||
* Produce a copy of a string in which the characters '+', '%', '=', ';' and control characters
|
||||
* are replaced with "%hh". This is a gentle form of URL encoding, attempting to cause as little
|
||||
* distortion to the string as possible. The characters '=' and ';' are meta characters in
|
||||
* cookies. By convention, they are escaped using the URL-encoding. This is only a convention, not
|
||||
* a standard. Often, cookies are expected to have encoded values. We encode '=' and ';' because
|
||||
* we must. We encode '%' and '+' because they are meta characters in URL encoding.
|
||||
*
|
||||
* @param string The source string.
|
||||
* @return The escaped result.
|
||||
*/
|
||||
public static String escape(String string) {
|
||||
char c;
|
||||
String s = string.trim();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int length = s.length();
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
c = s.charAt(i);
|
||||
if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
|
||||
sb.append('%');
|
||||
sb.append(Character.forDigit((char) ((c >>> 4) & 0x0f), 16));
|
||||
sb.append(Character.forDigit((char) (c & 0x0f), 16));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a cookie specification string into a JSONObject. The string will contain a name value
|
||||
* pair separated by '='. The name and the value will be unescaped, possibly converting '+' and
|
||||
* '%' sequences. The cookie properties may follow, separated by ';', also represented as
|
||||
* name=value (except the secure property, which does not have a value). The name will be stored
|
||||
* under the key "name", and the value will be stored under the key "value". This method does not
|
||||
* do checking or validation of the parameters. It only converts the cookie string into a
|
||||
* JSONObject.
|
||||
*
|
||||
* @param string The cookie specification string.
|
||||
* @return A JSONObject containing "name", "value", and possibly other members.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
String name;
|
||||
JSONObject jo = new JSONObject();
|
||||
Object value;
|
||||
JSONTokener x = new JSONTokener(string);
|
||||
jo.put("name", x.nextTo('='));
|
||||
x.next('=');
|
||||
jo.put("value", x.nextTo(';'));
|
||||
x.next();
|
||||
while (x.more()) {
|
||||
name = unescape(x.nextTo("=;"));
|
||||
if (x.next() != '=') {
|
||||
if (name.equals("secure")) {
|
||||
value = Boolean.TRUE;
|
||||
} else {
|
||||
throw x.syntaxError("Missing '=' in cookie parameter.");
|
||||
/**
|
||||
* Produce a copy of a string in which the characters '+', '%', '=', ';'
|
||||
* and control characters are replaced with "%hh". This is a gentle form
|
||||
* of URL encoding, attempting to cause as little distortion to the
|
||||
* string as possible. The characters '=' and ';' are meta characters in
|
||||
* cookies. By convention, they are escaped using the URL-encoding. This is
|
||||
* only a convention, not a standard. Often, cookies are expected to have
|
||||
* encoded values. We encode '=' and ';' because we must. We encode '%' and
|
||||
* '+' because they are meta characters in URL encoding.
|
||||
*
|
||||
* @param string The source string.
|
||||
* @return The escaped result.
|
||||
*/
|
||||
public static String escape(String string) {
|
||||
char c;
|
||||
String s = string.trim();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int length = s.length();
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
c = s.charAt(i);
|
||||
if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
|
||||
sb.append('%');
|
||||
sb.append(Character.forDigit((char) ((c >>> 4) & 0x0f), 16));
|
||||
sb.append(Character.forDigit((char) (c & 0x0f), 16));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = unescape(x.nextTo(';'));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a cookie specification string into a JSONObject. The string
|
||||
* will contain a name value pair separated by '='. The name and the value
|
||||
* will be unescaped, possibly converting '+' and '%' sequences. The
|
||||
* cookie properties may follow, separated by ';', also represented as
|
||||
* name=value (except the secure property, which does not have a value).
|
||||
* The name will be stored under the key "name", and the value will be
|
||||
* stored under the key "value". This method does not do checking or
|
||||
* validation of the parameters. It only converts the cookie string into
|
||||
* a JSONObject.
|
||||
*
|
||||
* @param string The cookie specification string.
|
||||
* @return A JSONObject containing "name", "value", and possibly other
|
||||
* members.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
String name;
|
||||
JSONObject jo = new JSONObject();
|
||||
Object value;
|
||||
JSONTokener x = new JSONTokener(string);
|
||||
jo.put("name", x.nextTo('='));
|
||||
x.next('=');
|
||||
jo.put("value", x.nextTo(';'));
|
||||
x.next();
|
||||
}
|
||||
jo.put(name, value);
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into a cookie specification string. The JSONObject must contain "name" and
|
||||
* "value" members. If the JSONObject contains "expires", "domain", "path", or "secure" members,
|
||||
* they will be appended to the cookie specification string. All other members are ignored.
|
||||
*
|
||||
* @param jo A JSONObject
|
||||
* @return A cookie specification string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
sb.append(escape(jo.getString("name")));
|
||||
sb.append("=");
|
||||
sb.append(escape(jo.getString("value")));
|
||||
if (jo.has("expires")) {
|
||||
sb.append(";expires=");
|
||||
sb.append(jo.getString("expires"));
|
||||
}
|
||||
if (jo.has("domain")) {
|
||||
sb.append(";domain=");
|
||||
sb.append(escape(jo.getString("domain")));
|
||||
}
|
||||
if (jo.has("path")) {
|
||||
sb.append(";path=");
|
||||
sb.append(escape(jo.getString("path")));
|
||||
}
|
||||
if (jo.optBoolean("secure")) {
|
||||
sb.append(";secure");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert <code>%</code><i>hh</i> sequences to single characters, and convert plus to space.
|
||||
*
|
||||
* @param string A string that may contain <code>+</code> <small>(plus)</small> and <code>%
|
||||
* </code><i>hh</i> sequences.
|
||||
* @return The unescaped string.
|
||||
*/
|
||||
public static String unescape(String string) {
|
||||
int length = string.length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
char c = string.charAt(i);
|
||||
if (c == '+') {
|
||||
c = ' ';
|
||||
} else if (c == '%' && i + 2 < length) {
|
||||
int d = JSONTokener.dehexchar(string.charAt(i + 1));
|
||||
int e = JSONTokener.dehexchar(string.charAt(i + 2));
|
||||
if (d >= 0 && e >= 0) {
|
||||
c = (char) (d * 16 + e);
|
||||
i += 2;
|
||||
while (x.more()) {
|
||||
name = unescape(x.nextTo("=;"));
|
||||
if (x.next() != '=') {
|
||||
if (name.equals("secure")) {
|
||||
value = Boolean.TRUE;
|
||||
} else {
|
||||
throw x.syntaxError("Missing '=' in cookie parameter.");
|
||||
}
|
||||
} else {
|
||||
value = unescape(x.nextTo(';'));
|
||||
x.next();
|
||||
}
|
||||
jo.put(name, value);
|
||||
}
|
||||
}
|
||||
sb.append(c);
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into a cookie specification string. The JSONObject
|
||||
* must contain "name" and "value" members.
|
||||
* If the JSONObject contains "expires", "domain", "path", or "secure"
|
||||
* members, they will be appended to the cookie specification string.
|
||||
* All other members are ignored.
|
||||
*
|
||||
* @param jo A JSONObject
|
||||
* @return A cookie specification string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
sb.append(escape(jo.getString("name")));
|
||||
sb.append("=");
|
||||
sb.append(escape(jo.getString("value")));
|
||||
if (jo.has("expires")) {
|
||||
sb.append(";expires=");
|
||||
sb.append(jo.getString("expires"));
|
||||
}
|
||||
if (jo.has("domain")) {
|
||||
sb.append(";domain=");
|
||||
sb.append(escape(jo.getString("domain")));
|
||||
}
|
||||
if (jo.has("path")) {
|
||||
sb.append(";path=");
|
||||
sb.append(escape(jo.getString("path")));
|
||||
}
|
||||
if (jo.optBoolean("secure")) {
|
||||
sb.append(";secure");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert <code>%</code><i>hh</i> sequences to single characters, and
|
||||
* convert plus to space.
|
||||
*
|
||||
* @param string A string that may contain
|
||||
* <code>+</code> <small>(plus)</small> and
|
||||
* <code>%</code><i>hh</i> sequences.
|
||||
* @return The unescaped string.
|
||||
*/
|
||||
public static String unescape(String string) {
|
||||
int length = string.length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
char c = string.charAt(i);
|
||||
if (c == '+') {
|
||||
c = ' ';
|
||||
} else if (c == '%' && i + 2 < length) {
|
||||
int d = JSONTokener.dehexchar(string.charAt(i + 1));
|
||||
int e = JSONTokener.dehexchar(string.charAt(i + 2));
|
||||
if (d >= 0 && e >= 0) {
|
||||
c = (char) (d * 16 + e);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,56 +26,60 @@ import java.util.Iterator;
|
||||
*/
|
||||
public class CookieList {
|
||||
|
||||
/**
|
||||
* Convert a cookie list into a JSONObject. A cookie list is a sequence of name/value pairs. The
|
||||
* names are separated from the values by '='. The pairs are separated by ';'. The names and the
|
||||
* values will be unescaped, possibly converting '+' and '%' sequences.
|
||||
*
|
||||
* <p>To add a cookie to a cooklist, cookielistJSONObject.put(cookieJSONObject.getString("name"),
|
||||
* cookieJSONObject.getString("value"));
|
||||
*
|
||||
* @param string A cookie list string
|
||||
* @return A JSONObject
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
JSONTokener x = new JSONTokener(string);
|
||||
while (x.more()) {
|
||||
String name = Cookie.unescape(x.nextTo('='));
|
||||
x.next('=');
|
||||
jo.put(name, Cookie.unescape(x.nextTo(';')));
|
||||
x.next();
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into a cookie list. A cookie list is a sequence of name/value pairs. The
|
||||
* names are separated from the values by '='. The pairs are separated by ';'. The characters '%',
|
||||
* '+', '=', and ';' in the names and values are replaced by "%hh".
|
||||
*
|
||||
* @param jo A JSONObject
|
||||
* @return A cookie list string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
boolean b = false;
|
||||
Iterator keys = jo.keys();
|
||||
String string;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (keys.hasNext()) {
|
||||
string = keys.next().toString();
|
||||
if (!jo.isNull(string)) {
|
||||
if (b) {
|
||||
sb.append(';');
|
||||
/**
|
||||
* Convert a cookie list into a JSONObject. A cookie list is a sequence
|
||||
* of name/value pairs. The names are separated from the values by '='.
|
||||
* The pairs are separated by ';'. The names and the values
|
||||
* will be unescaped, possibly converting '+' and '%' sequences.
|
||||
* <p>
|
||||
* To add a cookie to a cooklist,
|
||||
* cookielistJSONObject.put(cookieJSONObject.getString("name"),
|
||||
* cookieJSONObject.getString("value"));
|
||||
*
|
||||
* @param string A cookie list string
|
||||
* @return A JSONObject
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
JSONTokener x = new JSONTokener(string);
|
||||
while (x.more()) {
|
||||
String name = Cookie.unescape(x.nextTo('='));
|
||||
x.next('=');
|
||||
jo.put(name, Cookie.unescape(x.nextTo(';')));
|
||||
x.next();
|
||||
}
|
||||
sb.append(Cookie.escape(string));
|
||||
sb.append("=");
|
||||
sb.append(Cookie.escape(jo.getString(string)));
|
||||
b = true;
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into a cookie list. A cookie list is a sequence
|
||||
* of name/value pairs. The names are separated from the values by '='.
|
||||
* The pairs are separated by ';'. The characters '%', '+', '=', and ';'
|
||||
* in the names and values are replaced by "%hh".
|
||||
*
|
||||
* @param jo A JSONObject
|
||||
* @return A cookie list string
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
boolean b = false;
|
||||
Iterator keys = jo.keys();
|
||||
String string;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (keys.hasNext()) {
|
||||
string = keys.next().toString();
|
||||
if (!jo.isNull(string)) {
|
||||
if (b) {
|
||||
sb.append(';');
|
||||
}
|
||||
sb.append(Cookie.escape(string));
|
||||
sb.append("=");
|
||||
sb.append(Cookie.escape(jo.getString(string)));
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,146 +26,135 @@ import java.util.Iterator;
|
||||
*/
|
||||
public class HTTP {
|
||||
|
||||
/** Carriage return/line feed. */
|
||||
public static final String CRLF = "\r\n";
|
||||
/**
|
||||
* Carriage return/line feed.
|
||||
*/
|
||||
public static final String CRLF = "\r\n";
|
||||
|
||||
/**
|
||||
* Convert an HTTP header string into a JSONObject. It can be a request header or a response
|
||||
* header. A request header will contain
|
||||
*
|
||||
* <pre>{
|
||||
* Method: "POST" (for example),
|
||||
* "Request-URI": "/" (for example),
|
||||
* "HTTP-Version": "HTTP/1.1" (for example)
|
||||
* }</pre>
|
||||
*
|
||||
* A response header will contain
|
||||
*
|
||||
* <pre>{
|
||||
* "HTTP-Version": "HTTP/1.1" (for example),
|
||||
* "Fixes-Code": "200" (for example),
|
||||
* "Reason-Phrase": "OK" (for example)
|
||||
* }</pre>
|
||||
*
|
||||
* In addition, the other parameters in the header will be captured, using the HTTP field names as
|
||||
* JSON names, so that
|
||||
*
|
||||
* <pre>
|
||||
* Date: Sun, 26 May 2002 18:06:04 GMT
|
||||
* Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
|
||||
* Cache-Control: no-cache</pre>
|
||||
*
|
||||
* become
|
||||
*
|
||||
* <pre>{...
|
||||
* Date: "Sun, 26 May 2002 18:06:04 GMT",
|
||||
* Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
|
||||
* "Cache-Control": "no-cache",
|
||||
* ...}</pre>
|
||||
*
|
||||
* It does no further checking or conversion. It does not parse dates. It does not do '%'
|
||||
* transforms on URLs.
|
||||
*
|
||||
* @param string An HTTP header string.
|
||||
* @return A JSONObject containing the elements and attributes of the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
HTTPTokener x = new HTTPTokener(string);
|
||||
String token;
|
||||
/**
|
||||
* Convert an HTTP header string into a JSONObject. It can be a request
|
||||
* header or a response header. A request header will contain
|
||||
* <pre>{
|
||||
* Method: "POST" (for example),
|
||||
* "Request-URI": "/" (for example),
|
||||
* "HTTP-Version": "HTTP/1.1" (for example)
|
||||
* }</pre>
|
||||
* A response header will contain
|
||||
* <pre>{
|
||||
* "HTTP-Version": "HTTP/1.1" (for example),
|
||||
* "Fixes-Code": "200" (for example),
|
||||
* "Reason-Phrase": "OK" (for example)
|
||||
* }</pre>
|
||||
* In addition, the other parameters in the header will be captured, using
|
||||
* the HTTP field names as JSON names, so that <pre>
|
||||
* Date: Sun, 26 May 2002 18:06:04 GMT
|
||||
* Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
|
||||
* Cache-Control: no-cache</pre>
|
||||
* become
|
||||
* <pre>{...
|
||||
* Date: "Sun, 26 May 2002 18:06:04 GMT",
|
||||
* Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
|
||||
* "Cache-Control": "no-cache",
|
||||
* ...}</pre>
|
||||
* It does no further checking or conversion. It does not parse dates.
|
||||
* It does not do '%' transforms on URLs.
|
||||
*
|
||||
* @param string An HTTP header string.
|
||||
* @return A JSONObject containing the elements and attributes
|
||||
* of the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
JSONObject jo = new JSONObject();
|
||||
HTTPTokener x = new HTTPTokener(string);
|
||||
String token;
|
||||
|
||||
token = x.nextToken();
|
||||
if (token.toUpperCase().startsWith("HTTP")) {
|
||||
token = x.nextToken();
|
||||
if (token.toUpperCase().startsWith("HTTP")) {
|
||||
|
||||
// Response
|
||||
// Response
|
||||
|
||||
jo.put("HTTP-Version", token);
|
||||
jo.put("Fixes-Code", x.nextToken());
|
||||
jo.put("Reason-Phrase", x.nextTo('\0'));
|
||||
x.next();
|
||||
jo.put("HTTP-Version", token);
|
||||
jo.put("Fixes-Code", x.nextToken());
|
||||
jo.put("Reason-Phrase", x.nextTo('\0'));
|
||||
x.next();
|
||||
|
||||
} else {
|
||||
} else {
|
||||
|
||||
// Request
|
||||
// Request
|
||||
|
||||
jo.put("Method", token);
|
||||
jo.put("Request-URI", x.nextToken());
|
||||
jo.put("HTTP-Version", x.nextToken());
|
||||
jo.put("Method", token);
|
||||
jo.put("Request-URI", x.nextToken());
|
||||
jo.put("HTTP-Version", x.nextToken());
|
||||
}
|
||||
|
||||
// Fields
|
||||
|
||||
while (x.more()) {
|
||||
String name = x.nextTo(':');
|
||||
x.next(':');
|
||||
jo.put(name, x.nextTo('\0'));
|
||||
x.next();
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
// Fields
|
||||
|
||||
while (x.more()) {
|
||||
String name = x.nextTo(':');
|
||||
x.next(':');
|
||||
jo.put(name, x.nextTo('\0'));
|
||||
x.next();
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a JSONObject into an HTTP header. A request header must contain
|
||||
*
|
||||
* <pre>{
|
||||
* Method: "POST" (for example),
|
||||
* "Request-URI": "/" (for example),
|
||||
* "HTTP-Version": "HTTP/1.1" (for example)
|
||||
* }</pre>
|
||||
*
|
||||
* A response header must contain
|
||||
*
|
||||
* <pre>{
|
||||
* "HTTP-Version": "HTTP/1.1" (for example),
|
||||
* "Fixes-Code": "200" (for example),
|
||||
* "Reason-Phrase": "OK" (for example)
|
||||
* }</pre>
|
||||
*
|
||||
* Any other members of the JSONObject will be output as HTTP fields. The result will end with two
|
||||
* CRLF pairs.
|
||||
*
|
||||
* @param jo A JSONObject
|
||||
* @return An HTTP header string.
|
||||
* @throws JSONException if the object does not contain enough information.
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
Iterator keys = jo.keys();
|
||||
String string;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (jo.has("Fixes-Code") && jo.has("Reason-Phrase")) {
|
||||
sb.append(jo.getString("HTTP-Version"));
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("Fixes-Code"));
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("Reason-Phrase"));
|
||||
} else if (jo.has("Method") && jo.has("Request-URI")) {
|
||||
sb.append(jo.getString("Method"));
|
||||
sb.append(' ');
|
||||
sb.append('"');
|
||||
sb.append(jo.getString("Request-URI"));
|
||||
sb.append('"');
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("HTTP-Version"));
|
||||
} else {
|
||||
throw new JSONException("Not enough material for an HTTP header.");
|
||||
}
|
||||
sb.append(CRLF);
|
||||
while (keys.hasNext()) {
|
||||
string = keys.next().toString();
|
||||
if (!string.equals("HTTP-Version")
|
||||
&& !string.equals("Fixes-Code")
|
||||
&& !string.equals("Reason-Phrase")
|
||||
&& !string.equals("Method")
|
||||
&& !string.equals("Request-URI")
|
||||
&& !jo.isNull(string)) {
|
||||
sb.append(string);
|
||||
sb.append(": ");
|
||||
sb.append(jo.getString(string));
|
||||
/**
|
||||
* Convert a JSONObject into an HTTP header. A request header must contain
|
||||
* <pre>{
|
||||
* Method: "POST" (for example),
|
||||
* "Request-URI": "/" (for example),
|
||||
* "HTTP-Version": "HTTP/1.1" (for example)
|
||||
* }</pre>
|
||||
* A response header must contain
|
||||
* <pre>{
|
||||
* "HTTP-Version": "HTTP/1.1" (for example),
|
||||
* "Fixes-Code": "200" (for example),
|
||||
* "Reason-Phrase": "OK" (for example)
|
||||
* }</pre>
|
||||
* Any other members of the JSONObject will be output as HTTP fields.
|
||||
* The result will end with two CRLF pairs.
|
||||
*
|
||||
* @param jo A JSONObject
|
||||
* @return An HTTP header string.
|
||||
* @throws JSONException if the object does not contain enough
|
||||
* information.
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
Iterator keys = jo.keys();
|
||||
String string;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
if (jo.has("Fixes-Code") && jo.has("Reason-Phrase")) {
|
||||
sb.append(jo.getString("HTTP-Version"));
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("Fixes-Code"));
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("Reason-Phrase"));
|
||||
} else if (jo.has("Method") && jo.has("Request-URI")) {
|
||||
sb.append(jo.getString("Method"));
|
||||
sb.append(' ');
|
||||
sb.append('"');
|
||||
sb.append(jo.getString("Request-URI"));
|
||||
sb.append('"');
|
||||
sb.append(' ');
|
||||
sb.append(jo.getString("HTTP-Version"));
|
||||
} else {
|
||||
throw new JSONException("Not enough material for an HTTP header.");
|
||||
}
|
||||
sb.append(CRLF);
|
||||
}
|
||||
while (keys.hasNext()) {
|
||||
string = keys.next().toString();
|
||||
if (!string.equals("HTTP-Version") && !string.equals("Fixes-Code") &&
|
||||
!string.equals("Reason-Phrase") && !string.equals("Method") &&
|
||||
!string.equals("Request-URI") && !jo.isNull(string)) {
|
||||
sb.append(string);
|
||||
sb.append(": ");
|
||||
sb.append(jo.getString(string));
|
||||
sb.append(CRLF);
|
||||
}
|
||||
}
|
||||
sb.append(CRLF);
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(CRLF);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,55 +17,56 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
* The HTTPTokener extends the JSONTokener to provide additional methods for the parsing of HTTP
|
||||
* headers.
|
||||
* The HTTPTokener extends the JSONTokener to provide additional methods
|
||||
* for the parsing of HTTP headers.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class HTTPTokener extends JSONTokener {
|
||||
|
||||
/**
|
||||
* Construct an HTTPTokener from a string.
|
||||
*
|
||||
* @param string A source string.
|
||||
*/
|
||||
public HTTPTokener(String string) {
|
||||
super(string);
|
||||
}
|
||||
/**
|
||||
* Construct an HTTPTokener from a string.
|
||||
*
|
||||
* @param string A source string.
|
||||
*/
|
||||
public HTTPTokener(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next token or string. This is used in parsing HTTP headers.
|
||||
*
|
||||
* @return A String.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String nextToken() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
if (c == '"' || c == '\'') {
|
||||
q = c;
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (c < ' ') {
|
||||
throw syntaxError("Unterminated string.");
|
||||
|
||||
/**
|
||||
* Get the next token or string. This is used in parsing HTTP headers.
|
||||
*
|
||||
* @return A String.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String nextToken() throws JSONException {
|
||||
char c;
|
||||
char q;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
do {
|
||||
c = next();
|
||||
} while (Character.isWhitespace(c));
|
||||
if (c == '"' || c == '\'') {
|
||||
q = c;
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (c < ' ') {
|
||||
throw syntaxError("Unterminated string.");
|
||||
}
|
||||
if (c == q) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
if (c == q) {
|
||||
return sb.toString();
|
||||
for (; ; ) {
|
||||
if (c == 0 || Character.isWhitespace(c)) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
c = next();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
for (; ; ) {
|
||||
if (c == 0 || Character.isWhitespace(c)) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
c = next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,24 +23,24 @@ package dev.brighten.antivpn.utils.json;
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class JSONException extends Exception {
|
||||
private static final long serialVersionUID = 0;
|
||||
private Throwable cause;
|
||||
private static final long serialVersionUID = 0;
|
||||
private Throwable cause;
|
||||
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message.
|
||||
*
|
||||
* @param message Detail about the reason for the exception.
|
||||
*/
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message.
|
||||
*
|
||||
* @param message Detail about the reason for the exception.
|
||||
*/
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(Throwable cause) {
|
||||
super(cause.getMessage());
|
||||
this.cause = cause;
|
||||
}
|
||||
public JSONException(Throwable cause) {
|
||||
super(cause.getMessage());
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
public Throwable getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
public Throwable getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,420 +18,439 @@ package dev.brighten.antivpn.utils.json;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* This provides static methods to convert an XML text into a JSONArray or JSONObject, and to covert
|
||||
* a JSONArray or JSONObject into an XML text using the JsonML transform.
|
||||
* This provides static methods to convert an XML text into a JSONArray or
|
||||
* JSONObject, and to covert a JSONArray or JSONObject into an XML text using
|
||||
* the JsonML transform.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2010-12-23
|
||||
*/
|
||||
public class JSONML {
|
||||
|
||||
/**
|
||||
* Parse XML values and store them in a JSONArray.
|
||||
*
|
||||
* @param x The XMLTokener containing the source string.
|
||||
* @param arrayForm true if array form, false if object form.
|
||||
* @param ja The JSONArray that is containing the current tag or null if we are at the outermost
|
||||
* level.
|
||||
* @return A JSONArray if the value is the outermost tag, otherwise null.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private static Object parse(XMLTokener x, boolean arrayForm, JSONArray ja) throws JSONException {
|
||||
String attribute;
|
||||
char c;
|
||||
String closeTag = null;
|
||||
int i;
|
||||
JSONArray newja = null;
|
||||
JSONObject newjo = null;
|
||||
Object token;
|
||||
String tagName = null;
|
||||
/**
|
||||
* Parse XML values and store them in a JSONArray.
|
||||
*
|
||||
* @param x The XMLTokener containing the source string.
|
||||
* @param arrayForm true if array form, false if object form.
|
||||
* @param ja The JSONArray that is containing the current tag or null
|
||||
* if we are at the outermost level.
|
||||
* @return A JSONArray if the value is the outermost tag, otherwise null.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private static Object parse(XMLTokener x, boolean arrayForm,
|
||||
JSONArray ja) throws JSONException {
|
||||
String attribute;
|
||||
char c;
|
||||
String closeTag = null;
|
||||
int i;
|
||||
JSONArray newja = null;
|
||||
JSONObject newjo = null;
|
||||
Object token;
|
||||
String tagName = null;
|
||||
|
||||
// Test for and skip past these forms:
|
||||
// <!-- ... -->
|
||||
// <![ ... ]]>
|
||||
// <! ... >
|
||||
// <? ... ?>
|
||||
// Test for and skip past these forms:
|
||||
// <!-- ... -->
|
||||
// <![ ... ]]>
|
||||
// <! ... >
|
||||
// <? ... ?>
|
||||
|
||||
while (true) {
|
||||
token = x.nextContent();
|
||||
if (token == XML.LT) {
|
||||
token = x.nextToken();
|
||||
if (token instanceof Character) {
|
||||
if (token == XML.SLASH) {
|
||||
while (true) {
|
||||
token = x.nextContent();
|
||||
if (token == XML.LT) {
|
||||
token = x.nextToken();
|
||||
if (token instanceof Character) {
|
||||
if (token == XML.SLASH) {
|
||||
|
||||
// Close tag </
|
||||
// Close tag </
|
||||
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw new JSONException("Expected a closing name instead of '" + token + "'.");
|
||||
}
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped close tag");
|
||||
}
|
||||
return token;
|
||||
} else if (token == XML.BANG) {
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw new JSONException(
|
||||
"Expected a closing name instead of '" +
|
||||
token + "'.");
|
||||
}
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped close tag");
|
||||
}
|
||||
return token;
|
||||
} else if (token == XML.BANG) {
|
||||
|
||||
// <!
|
||||
// <!
|
||||
|
||||
c = x.next();
|
||||
if (c == '-') {
|
||||
if (x.next() == '-') {
|
||||
x.skipPast("-->");
|
||||
}
|
||||
x.back();
|
||||
} else if (c == '[') {
|
||||
token = x.nextToken();
|
||||
if (token.equals("CDATA") && x.next() == '[') {
|
||||
if (ja != null) {
|
||||
ja.put(x.nextCDATA());
|
||||
}
|
||||
} else {
|
||||
throw x.syntaxError("Expected 'CDATA['");
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
do {
|
||||
token = x.nextMeta();
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Missing '>' after '<!'.");
|
||||
} else if (token == XML.LT) {
|
||||
i += 1;
|
||||
} else if (token == XML.GT) {
|
||||
i -= 1;
|
||||
}
|
||||
} while (i > 0);
|
||||
}
|
||||
} else if (token == XML.QUEST) {
|
||||
c = x.next();
|
||||
if (c == '-') {
|
||||
if (x.next() == '-') {
|
||||
x.skipPast("-->");
|
||||
}
|
||||
x.back();
|
||||
} else if (c == '[') {
|
||||
token = x.nextToken();
|
||||
if (token.equals("CDATA") && x.next() == '[') {
|
||||
if (ja != null) {
|
||||
ja.put(x.nextCDATA());
|
||||
}
|
||||
} else {
|
||||
throw x.syntaxError("Expected 'CDATA['");
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
do {
|
||||
token = x.nextMeta();
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Missing '>' after '<!'.");
|
||||
} else if (token == XML.LT) {
|
||||
i += 1;
|
||||
} else if (token == XML.GT) {
|
||||
i -= 1;
|
||||
}
|
||||
} while (i > 0);
|
||||
}
|
||||
} else if (token == XML.QUEST) {
|
||||
|
||||
// <?
|
||||
// <?
|
||||
|
||||
x.skipPast("?>");
|
||||
} else {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
x.skipPast("?>");
|
||||
} else {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
|
||||
// Open tag <
|
||||
// Open tag <
|
||||
|
||||
} else {
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Bad tagName '" + token + "'.");
|
||||
}
|
||||
tagName = (String) token;
|
||||
newja = new JSONArray();
|
||||
newjo = new JSONObject();
|
||||
if (arrayForm) {
|
||||
newja.put(tagName);
|
||||
if (ja != null) {
|
||||
ja.put(newja);
|
||||
}
|
||||
} else {
|
||||
newjo.put("tagName", tagName);
|
||||
if (ja != null) {
|
||||
ja.put(newjo);
|
||||
}
|
||||
}
|
||||
token = null;
|
||||
for (; ; ) {
|
||||
if (token == null) {
|
||||
token = x.nextToken();
|
||||
}
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (!(token instanceof String)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// attribute = value
|
||||
|
||||
attribute = (String) token;
|
||||
if (!arrayForm && (attribute == "tagName" || attribute == "childNode")) {
|
||||
throw x.syntaxError("Reserved attribute.");
|
||||
}
|
||||
token = x.nextToken();
|
||||
if (token == XML.EQ) {
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Missing value");
|
||||
}
|
||||
newjo.accumulate(attribute, XML.stringToValue((String) token));
|
||||
token = null;
|
||||
} else {
|
||||
newjo.accumulate(attribute, "");
|
||||
}
|
||||
}
|
||||
if (arrayForm && newjo.length() > 0) {
|
||||
newja.put(newjo);
|
||||
}
|
||||
|
||||
// Empty tag <.../>
|
||||
|
||||
if (token == XML.SLASH) {
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (ja == null) {
|
||||
if (arrayForm) {
|
||||
return newja;
|
||||
} else {
|
||||
return newjo;
|
||||
}
|
||||
}
|
||||
|
||||
// Content, between <...> and </...>
|
||||
|
||||
} else {
|
||||
if (token != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
closeTag = (String) parse(x, arrayForm, newja);
|
||||
if (closeTag != null) {
|
||||
if (!closeTag.equals(tagName)) {
|
||||
throw x.syntaxError("Mismatched '" + tagName + "' and '" + closeTag + "'");
|
||||
}
|
||||
tagName = null;
|
||||
if (!arrayForm && newja.length() > 0) {
|
||||
newjo.put("childNodes", newja);
|
||||
}
|
||||
if (ja == null) {
|
||||
if (arrayForm) {
|
||||
return newja;
|
||||
} else {
|
||||
return newjo;
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Bad tagName '" + token + "'.");
|
||||
}
|
||||
tagName = (String) token;
|
||||
newja = new JSONArray();
|
||||
newjo = new JSONObject();
|
||||
if (arrayForm) {
|
||||
newja.put(tagName);
|
||||
if (ja != null) {
|
||||
ja.put(newja);
|
||||
}
|
||||
} else {
|
||||
newjo.put("tagName", tagName);
|
||||
if (ja != null) {
|
||||
ja.put(newjo);
|
||||
}
|
||||
}
|
||||
token = null;
|
||||
for (; ; ) {
|
||||
if (token == null) {
|
||||
token = x.nextToken();
|
||||
}
|
||||
if (token == null) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (!(token instanceof String)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// attribute = value
|
||||
|
||||
attribute = (String) token;
|
||||
if (!arrayForm && (attribute == "tagName" || attribute == "childNode")) {
|
||||
throw x.syntaxError("Reserved attribute.");
|
||||
}
|
||||
token = x.nextToken();
|
||||
if (token == XML.EQ) {
|
||||
token = x.nextToken();
|
||||
if (!(token instanceof String)) {
|
||||
throw x.syntaxError("Missing value");
|
||||
}
|
||||
newjo.accumulate(attribute, XML.stringToValue((String) token));
|
||||
token = null;
|
||||
} else {
|
||||
newjo.accumulate(attribute, "");
|
||||
}
|
||||
}
|
||||
if (arrayForm && newjo.length() > 0) {
|
||||
newja.put(newjo);
|
||||
}
|
||||
|
||||
// Empty tag <.../>
|
||||
|
||||
if (token == XML.SLASH) {
|
||||
if (x.nextToken() != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
if (ja == null) {
|
||||
if (arrayForm) {
|
||||
return newja;
|
||||
} else {
|
||||
return newjo;
|
||||
}
|
||||
}
|
||||
|
||||
// Content, between <...> and </...>
|
||||
|
||||
} else {
|
||||
if (token != XML.GT) {
|
||||
throw x.syntaxError("Misshaped tag");
|
||||
}
|
||||
closeTag = (String) parse(x, arrayForm, newja);
|
||||
if (closeTag != null) {
|
||||
if (!closeTag.equals(tagName)) {
|
||||
throw x.syntaxError("Mismatched '" + tagName +
|
||||
"' and '" + closeTag + "'");
|
||||
}
|
||||
tagName = null;
|
||||
if (!arrayForm && newja.length() > 0) {
|
||||
newjo.put("childNodes", newja);
|
||||
}
|
||||
if (ja == null) {
|
||||
if (arrayForm) {
|
||||
return newja;
|
||||
} else {
|
||||
return newjo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ja != null) {
|
||||
ja.put(token instanceof String ?
|
||||
XML.stringToValue((String) token) : token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ja != null) {
|
||||
ja.put(token instanceof String ? XML.stringToValue((String) token) : token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a JSONArray using the JsonML
|
||||
* transform. Each XML tag is represented as a JSONArray in which the first element is the tag
|
||||
* name. If the tag has attributes, then the second element will be JSONObject containing the
|
||||
* name/value pairs. If the tag contains children, then strings and JSONArrays will represent the
|
||||
* child tags. Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
*
|
||||
* @param string The source string.
|
||||
* @return A JSONArray containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return toJSONArray(new XMLTokener(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a JSONArray using the JsonML
|
||||
* transform. Each XML tag is represented as a JSONArray in which the first element is the tag
|
||||
* name. If the tag has attributes, then the second element will be JSONObject containing the
|
||||
* name/value pairs. If the tag contains children, then strings and JSONArrays will represent the
|
||||
* child content and tags. Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
*
|
||||
* @param x An XMLTokener.
|
||||
* @return A JSONArray containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
|
||||
return (JSONArray) parse(x, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a JSONObject using the JsonML
|
||||
* transform. Each XML tag is represented as a JSONObject with a "tagName" property. If the tag
|
||||
* has attributes, then the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which will be an array of
|
||||
* strings and JsonML JSONObjects.
|
||||
*
|
||||
* <p>Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
*
|
||||
* @param x An XMLTokener of the XML source text.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
|
||||
return (JSONObject) parse(x, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a JSONObject using the JsonML
|
||||
* transform. Each XML tag is represented as a JSONObject with a "tagName" property. If the tag
|
||||
* has attributes, then the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which will be an array of
|
||||
* strings and JsonML JSONObjects.
|
||||
*
|
||||
* <p>Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
*
|
||||
* @param string The XML source text.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
return toJSONObject(new XMLTokener(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the JSONML transformation, making an XML text from a JSONArray.
|
||||
*
|
||||
* @param ja A JSONArray.
|
||||
* @return An XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray ja) throws JSONException {
|
||||
int i;
|
||||
JSONObject jo;
|
||||
String key;
|
||||
Iterator keys;
|
||||
int length;
|
||||
Object object;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String tagName;
|
||||
String value;
|
||||
|
||||
// Emit <tagName
|
||||
|
||||
tagName = ja.getString(0);
|
||||
XML.noSpace(tagName);
|
||||
tagName = XML.escape(tagName);
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
|
||||
object = ja.opt(1);
|
||||
if (object instanceof JSONObject) {
|
||||
i = 2;
|
||||
jo = (JSONObject) object;
|
||||
|
||||
// Emit the attributes
|
||||
|
||||
keys = jo.keys();
|
||||
while (keys.hasNext()) {
|
||||
key = keys.next().toString();
|
||||
XML.noSpace(key);
|
||||
value = jo.optString(key);
|
||||
if (value != null) {
|
||||
sb.append(' ');
|
||||
sb.append(XML.escape(key));
|
||||
sb.append('=');
|
||||
sb.append('"');
|
||||
sb.append(XML.escape(value));
|
||||
sb.append('"');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
}
|
||||
|
||||
// Emit content in body
|
||||
|
||||
length = ja.length();
|
||||
if (i >= length) {
|
||||
sb.append('/');
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append('>');
|
||||
do {
|
||||
object = ja.get(i);
|
||||
i += 1;
|
||||
if (object != null) {
|
||||
if (object instanceof String) {
|
||||
sb.append(XML.escape(object.toString()));
|
||||
} else if (object instanceof JSONObject) {
|
||||
sb.append(toString((JSONObject) object));
|
||||
} else if (object instanceof JSONArray) {
|
||||
sb.append(toString((JSONArray) object));
|
||||
}
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONArray using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONArray in which the first element is the tag name. If the tag has
|
||||
* attributes, then the second element will be JSONObject containing the
|
||||
* name/value pairs. If the tag contains children, then strings and
|
||||
* JSONArrays will represent the child tags.
|
||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
*
|
||||
* @param string The source string.
|
||||
* @return A JSONArray containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(String string) throws JSONException {
|
||||
return toJSONArray(new XMLTokener(string));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONArray using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONArray in which the first element is the tag name. If the tag has
|
||||
* attributes, then the second element will be JSONObject containing the
|
||||
* name/value pairs. If the tag contains children, then strings and
|
||||
* JSONArrays will represent the child content and tags.
|
||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
*
|
||||
* @param x An XMLTokener.
|
||||
* @return A JSONArray containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
|
||||
return (JSONArray) parse(x, true, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
||||
* the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which
|
||||
* will be an array of strings and JsonML JSONObjects.
|
||||
* <p>
|
||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
*
|
||||
* @param x An XMLTokener of the XML source text.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
|
||||
return (JSONObject) parse(x, false, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
||||
* the attributes will be in the JSONObject as properties. If the tag
|
||||
* contains children, the object will have a "childNodes" property which
|
||||
* will be an array of strings and JsonML JSONObjects.
|
||||
* <p>
|
||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||
*
|
||||
* @param string The XML source text.
|
||||
* @return A JSONObject containing the structured data from the XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||
return toJSONObject(new XMLTokener(string));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse the JSONML transformation, making an XML text from a JSONArray.
|
||||
*
|
||||
* @param ja A JSONArray.
|
||||
* @return An XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONArray ja) throws JSONException {
|
||||
int i;
|
||||
JSONObject jo;
|
||||
String key;
|
||||
Iterator keys;
|
||||
int length;
|
||||
Object object;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String tagName;
|
||||
String value;
|
||||
|
||||
// Emit <tagName
|
||||
|
||||
tagName = ja.getString(0);
|
||||
XML.noSpace(tagName);
|
||||
tagName = XML.escape(tagName);
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
|
||||
object = ja.opt(1);
|
||||
if (object instanceof JSONObject) {
|
||||
i = 2;
|
||||
jo = (JSONObject) object;
|
||||
|
||||
// Emit the attributes
|
||||
|
||||
keys = jo.keys();
|
||||
while (keys.hasNext()) {
|
||||
key = keys.next().toString();
|
||||
XML.noSpace(key);
|
||||
value = jo.optString(key);
|
||||
if (value != null) {
|
||||
sb.append(' ');
|
||||
sb.append(XML.escape(key));
|
||||
sb.append('=');
|
||||
sb.append('"');
|
||||
sb.append(XML.escape(value));
|
||||
sb.append('"');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
}
|
||||
} while (i < length);
|
||||
sb.append('<');
|
||||
sb.append('/');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the JSONML transformation, making an XML text from a JSONObject. The JSONObject must
|
||||
* contain a "tagName" property. If it has children, then it must have a "childNodes" property
|
||||
* containing an array of objects. The other properties are attributes with string values.
|
||||
*
|
||||
* @param jo A JSONObject.
|
||||
* @return An XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int i;
|
||||
JSONArray ja;
|
||||
String key;
|
||||
Iterator keys;
|
||||
int length;
|
||||
Object object;
|
||||
String tagName;
|
||||
String value;
|
||||
//Emit content in body
|
||||
|
||||
// Emit <tagName
|
||||
|
||||
tagName = jo.optString("tagName");
|
||||
if (tagName == null) {
|
||||
return XML.escape(jo.toString());
|
||||
}
|
||||
XML.noSpace(tagName);
|
||||
tagName = XML.escape(tagName);
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
|
||||
// Emit the attributes
|
||||
|
||||
keys = jo.keys();
|
||||
while (keys.hasNext()) {
|
||||
key = keys.next().toString();
|
||||
if (!key.equals("tagName") && !key.equals("childNodes")) {
|
||||
XML.noSpace(key);
|
||||
value = jo.optString(key);
|
||||
if (value != null) {
|
||||
sb.append(' ');
|
||||
sb.append(XML.escape(key));
|
||||
sb.append('=');
|
||||
sb.append('"');
|
||||
sb.append(XML.escape(value));
|
||||
sb.append('"');
|
||||
length = ja.length();
|
||||
if (i >= length) {
|
||||
sb.append('/');
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append('>');
|
||||
do {
|
||||
object = ja.get(i);
|
||||
i += 1;
|
||||
if (object != null) {
|
||||
if (object instanceof String) {
|
||||
sb.append(XML.escape(object.toString()));
|
||||
} else if (object instanceof JSONObject) {
|
||||
sb.append(toString((JSONObject) object));
|
||||
} else if (object instanceof JSONArray) {
|
||||
sb.append(toString((JSONArray) object));
|
||||
}
|
||||
}
|
||||
} while (i < length);
|
||||
sb.append('<');
|
||||
sb.append('/');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Emit content in body
|
||||
/**
|
||||
* Reverse the JSONML transformation, making an XML text from a JSONObject.
|
||||
* The JSONObject must contain a "tagName" property. If it has children,
|
||||
* then it must have a "childNodes" property containing an array of objects.
|
||||
* The other properties are attributes with string values.
|
||||
*
|
||||
* @param jo A JSONObject.
|
||||
* @return An XML string.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String toString(JSONObject jo) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int i;
|
||||
JSONArray ja;
|
||||
String key;
|
||||
Iterator keys;
|
||||
int length;
|
||||
Object object;
|
||||
String tagName;
|
||||
String value;
|
||||
|
||||
ja = jo.optJSONArray("childNodes");
|
||||
if (ja == null) {
|
||||
sb.append('/');
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append('>');
|
||||
length = ja.length();
|
||||
for (i = 0; i < length; i += 1) {
|
||||
object = ja.get(i);
|
||||
if (object != null) {
|
||||
if (object instanceof String) {
|
||||
sb.append(XML.escape(object.toString()));
|
||||
} else if (object instanceof JSONObject) {
|
||||
sb.append(toString((JSONObject) object));
|
||||
} else if (object instanceof JSONArray) {
|
||||
sb.append(toString((JSONArray) object));
|
||||
}
|
||||
//Emit <tagName
|
||||
|
||||
tagName = jo.optString("tagName");
|
||||
if (tagName == null) {
|
||||
return XML.escape(jo.toString());
|
||||
}
|
||||
}
|
||||
sb.append('<');
|
||||
sb.append('/');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
XML.noSpace(tagName);
|
||||
tagName = XML.escape(tagName);
|
||||
sb.append('<');
|
||||
sb.append(tagName);
|
||||
|
||||
//Emit the attributes
|
||||
|
||||
keys = jo.keys();
|
||||
while (keys.hasNext()) {
|
||||
key = keys.next().toString();
|
||||
if (!key.equals("tagName") && !key.equals("childNodes")) {
|
||||
XML.noSpace(key);
|
||||
value = jo.optString(key);
|
||||
if (value != null) {
|
||||
sb.append(' ');
|
||||
sb.append(XML.escape(key));
|
||||
sb.append('=');
|
||||
sb.append('"');
|
||||
sb.append(XML.escape(value));
|
||||
sb.append('"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Emit content in body
|
||||
|
||||
ja = jo.optJSONArray("childNodes");
|
||||
if (ja == null) {
|
||||
sb.append('/');
|
||||
sb.append('>');
|
||||
} else {
|
||||
sb.append('>');
|
||||
length = ja.length();
|
||||
for (i = 0; i < length; i += 1) {
|
||||
object = ja.get(i);
|
||||
if (object != null) {
|
||||
if (object instanceof String) {
|
||||
sb.append(XML.escape(object.toString()));
|
||||
} else if (object instanceof JSONObject) {
|
||||
sb.append(toString((JSONObject) object));
|
||||
} else if (object instanceof JSONArray) {
|
||||
sb.append(toString((JSONArray) object));
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append('<');
|
||||
sb.append('/');
|
||||
sb.append(tagName);
|
||||
sb.append('>');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,17 +17,19 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
* The <code>JSONString</code> interface allows a <code>toJSONString()</code> method so that a class
|
||||
* can change the behavior of <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
|
||||
* and <code>JSONWriter.value(</code>Object<code>)</code>. The <code>toJSONString</code> method will
|
||||
* be used instead of the default behavior of using the Object's <code>toString()</code> method and
|
||||
* quoting the result.
|
||||
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
|
||||
* method so that a class can change the behavior of
|
||||
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
|
||||
* and <code>JSONWriter.value(</code>Object<code>)</code>. The
|
||||
* <code>toJSONString</code> method will be used instead of the default behavior
|
||||
* of using the Object's <code>toString()</code> method and quoting the result.
|
||||
*/
|
||||
public interface JSONString {
|
||||
/**
|
||||
* The <code>toJSONString</code> method allows a class to produce its own JSON serialization.
|
||||
*
|
||||
* @return A strictly syntactically correct JSON text.
|
||||
*/
|
||||
String toJSONString();
|
||||
/**
|
||||
* The <code>toJSONString</code> method allows a class to produce its own JSON
|
||||
* serialization.
|
||||
*
|
||||
* @return A strictly syntactically correct JSON text.
|
||||
*/
|
||||
public String toJSONString();
|
||||
}
|
||||
|
||||
@@ -19,53 +19,54 @@ package dev.brighten.antivpn.utils.json;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* JSONStringer provides a quick and convenient way of producing JSON text. The texts produced
|
||||
* strictly conform to JSON syntax rules. No whitespace is added, so the results are ready for
|
||||
* transmission or storage. Each instance of JSONStringer can produce one JSON text.
|
||||
*
|
||||
* <p>A JSONStringer instance provides a <code>value</code> method for appending values to the text,
|
||||
* and a <code>key</code> method for adding keys before values in objects. There are <code>array
|
||||
* </code> and <code>endArray</code> methods that make and bound array values, and <code>object
|
||||
* </code> and <code>endObject</code> methods which make and bound object values. All of these
|
||||
* methods return the JSONWriter instance, permitting cascade style. For example,
|
||||
*
|
||||
* <pre>
|
||||
* JSONStringer provides a quick and convenient way of producing JSON text.
|
||||
* The texts produced strictly conform to JSON syntax rules. No whitespace is
|
||||
* added, so the results are ready for transmission or storage. Each instance of
|
||||
* JSONStringer can produce one JSON text.
|
||||
* <p>
|
||||
* A JSONStringer instance provides a <code>value</code> method for appending
|
||||
* values to the
|
||||
* text, and a <code>key</code>
|
||||
* method for adding keys before values in objects. There are <code>array</code>
|
||||
* and <code>endArray</code> methods that make and bound array values, and
|
||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
||||
* object values. All of these methods return the JSONWriter instance,
|
||||
* permitting cascade style. For example, <pre>
|
||||
* myString = new JSONStringer()
|
||||
* .object()
|
||||
* .key("JSON")
|
||||
* .value("Hello, World!")
|
||||
* .endObject()
|
||||
* .toString();</pre>
|
||||
*
|
||||
* which produces the string
|
||||
*
|
||||
* <pre>
|
||||
* .toString();</pre> which produces the string <pre>
|
||||
* {"JSON":"Hello, World!"}</pre>
|
||||
*
|
||||
* <p>The first method called must be <code>array</code> or <code>object</code>. There are no
|
||||
* methods for adding commas or colons. JSONStringer adds them for you. Objects and arrays can be
|
||||
* nested up to 20 levels deep.
|
||||
*
|
||||
* <p>This can sometimes be easier than using a JSONObject to build a string.
|
||||
* <p>
|
||||
* The first method called must be <code>array</code> or <code>object</code>.
|
||||
* There are no methods for adding commas or colons. JSONStringer adds them for
|
||||
* you. Objects and arrays can be nested up to 20 levels deep.
|
||||
* <p>
|
||||
* This can sometimes be easier than using a JSONObject to build a string.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2008-09-18
|
||||
*/
|
||||
public class JSONStringer extends JSONWriter {
|
||||
/** Make a fresh JSONStringer. It can be used to build one JSON text. */
|
||||
public JSONStringer() {
|
||||
super(new StringWriter());
|
||||
}
|
||||
/**
|
||||
* Make a fresh JSONStringer. It can be used to build one JSON text.
|
||||
*/
|
||||
public JSONStringer() {
|
||||
super(new StringWriter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JSON text. This method is used to obtain the product of the JSONStringer instance.
|
||||
* It will return <code>null</code> if there was a problem in the construction of the JSON text
|
||||
* (such as the calls to <code>array</code> were not properly balanced with calls to <code>
|
||||
* endArray</code>).
|
||||
*
|
||||
* @return The JSON text.
|
||||
*/
|
||||
public String toString() {
|
||||
return this.mode == 'd' ? this.writer.toString() : null;
|
||||
}
|
||||
/**
|
||||
* Return the JSON text. This method is used to obtain the product of the
|
||||
* JSONStringer instance. It will return <code>null</code> if there was a
|
||||
* problem in the construction of the JSON text (such as the calls to
|
||||
* <code>array</code> were not properly balanced with calls to
|
||||
* <code>endArray</code>).
|
||||
*
|
||||
* @return The JSON text.
|
||||
*/
|
||||
public String toString() {
|
||||
return this.mode == 'd' ? this.writer.toString() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,391 +19,420 @@ package dev.brighten.antivpn.utils.json;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A JSONTokener takes a source string and extracts characters and tokens from it. It is used by the
|
||||
* JSONObject and JSONArray constructors to parse JSON source strings.
|
||||
* A JSONTokener takes a source string and extracts characters and tokens from
|
||||
* it. It is used by the JSONObject and JSONArray constructors to parse
|
||||
* JSON source strings.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2010-12-24
|
||||
*/
|
||||
public class JSONTokener {
|
||||
|
||||
private int character;
|
||||
private boolean eof;
|
||||
private int index;
|
||||
private int line;
|
||||
private char previous;
|
||||
private Reader reader;
|
||||
private boolean usePrevious;
|
||||
private int character;
|
||||
private boolean eof;
|
||||
private int index;
|
||||
private int line;
|
||||
private char previous;
|
||||
private Reader reader;
|
||||
private boolean usePrevious;
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a Reader.
|
||||
*
|
||||
* @param reader A reader.
|
||||
*/
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.line = 1;
|
||||
}
|
||||
|
||||
/** Construct a JSONTokener from an InputStream. */
|
||||
public JSONTokener(InputStream inputStream) throws JSONException {
|
||||
this(new InputStreamReader(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a string.
|
||||
*
|
||||
* @param s A source string.
|
||||
*/
|
||||
public JSONTokener(String s) {
|
||||
this(new StringReader(s));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hex value of a character (base16).
|
||||
*
|
||||
* @param c A character between '0' and '9' or between 'A' and 'F' or between 'a' and 'f'.
|
||||
* @return An int between 0 and 15, or -1 if c was not a hex digit.
|
||||
*/
|
||||
public static int dehexchar(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - ('A' - 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - ('a' - 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Back up one character. This provides a sort of lookahead capability, so that you can test for a
|
||||
* digit or letter before attempting to parse the next number or identifier.
|
||||
*/
|
||||
public void back() throws JSONException {
|
||||
if (usePrevious || index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
this.index -= 1;
|
||||
this.character -= 1;
|
||||
this.usePrevious = true;
|
||||
this.eof = false;
|
||||
}
|
||||
|
||||
public boolean end() {
|
||||
return eof && !usePrevious;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the source string still contains characters that next() can consume.
|
||||
*
|
||||
* @return true if not yet at the end of the source.
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
next();
|
||||
if (end()) {
|
||||
return false;
|
||||
}
|
||||
back();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next character in the source string.
|
||||
*
|
||||
* @return The next character, or 0 if past the end of the source string.
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
this.index += 1;
|
||||
if (this.previous == '\r') {
|
||||
this.line += 1;
|
||||
this.character = c == '\n' ? 0 : 1;
|
||||
} else if (c == '\n') {
|
||||
this.line += 1;
|
||||
this.character = 0;
|
||||
} else {
|
||||
this.character += 1;
|
||||
}
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume the next character, and check that it matches a specified character.
|
||||
*
|
||||
* @param c The character to match.
|
||||
* @return The character.
|
||||
* @throws JSONException if the character does not match.
|
||||
*/
|
||||
public char next(char c) throws JSONException {
|
||||
char n = next();
|
||||
if (n != c) {
|
||||
throw syntaxError("Expected '" + c + "' and instead saw '" + n + "'");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next n characters.
|
||||
*
|
||||
* @param n The number of characters to take.
|
||||
* @return A string of n characters.
|
||||
* @throws JSONException Substring bounds error if there are not n characters remaining in the
|
||||
* source string.
|
||||
*/
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
/**
|
||||
* Construct a JSONTokener from a Reader.
|
||||
*
|
||||
* @param reader A reader.
|
||||
*/
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported() ?
|
||||
reader : new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.line = 1;
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
while (pos < n) {
|
||||
chars[pos] = next();
|
||||
if (end()) {
|
||||
throw syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
/**
|
||||
* Construct a JSONTokener from an InputStream.
|
||||
*/
|
||||
public JSONTokener(InputStream inputStream) throws JSONException {
|
||||
this(new InputStreamReader(inputStream));
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next char in the string, skipping whitespace.
|
||||
*
|
||||
* @return A character, or 0 if there are no more characters.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public char nextClean() throws JSONException {
|
||||
for (; ; ) {
|
||||
char c = next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a string.
|
||||
*
|
||||
* @param s A source string.
|
||||
*/
|
||||
public JSONTokener(String s) {
|
||||
this(new StringReader(s));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the characters up to the next close quote character. Backslash processing is done. The
|
||||
* formal JSON format does not allow strings in single quotes, but an implementation is allowed to
|
||||
* accept them.
|
||||
*
|
||||
* @param quote The quoting character, either <code>"</code> <small>(double quote)</small> or
|
||||
* <code>'</code> <small>(single quote)</small>.
|
||||
* @return A String.
|
||||
* @throws JSONException Unterminated string.
|
||||
*/
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw syntaxError("Unterminated string");
|
||||
case '\\':
|
||||
c = next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':
|
||||
sb.append((char) Integer.parseInt(next(4), 16));
|
||||
break;
|
||||
/**
|
||||
* Get the hex value of a character (base16).
|
||||
*
|
||||
* @param c A character between '0' and '9' or between 'A' and 'F' or
|
||||
* between 'a' and 'f'.
|
||||
* @return An int between 0 and 15, or -1 if c was not a hex digit.
|
||||
*/
|
||||
public static int dehexchar(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - ('A' - 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - ('a' - 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Back up one character. This provides a sort of lookahead capability,
|
||||
* so that you can test for a digit or letter before attempting to parse
|
||||
* the next number or identifier.
|
||||
*/
|
||||
public void back() throws JSONException {
|
||||
if (usePrevious || index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
this.index -= 1;
|
||||
this.character -= 1;
|
||||
this.usePrevious = true;
|
||||
this.eof = false;
|
||||
}
|
||||
|
||||
public boolean end() {
|
||||
return eof && !usePrevious;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the source string still contains characters that next()
|
||||
* can consume.
|
||||
*
|
||||
* @return true if not yet at the end of the source.
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
next();
|
||||
if (end()) {
|
||||
return false;
|
||||
}
|
||||
back();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next character in the source string.
|
||||
*
|
||||
* @return The next character, or 0 if past the end of the source string.
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
this.index += 1;
|
||||
if (this.previous == '\r') {
|
||||
this.line += 1;
|
||||
this.character = c == '\n' ? 0 : 1;
|
||||
} else if (c == '\n') {
|
||||
this.line += 1;
|
||||
this.character = 0;
|
||||
} else {
|
||||
this.character += 1;
|
||||
}
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Consume the next character, and check that it matches a specified
|
||||
* character.
|
||||
*
|
||||
* @param c The character to match.
|
||||
* @return The character.
|
||||
* @throws JSONException if the character does not match.
|
||||
*/
|
||||
public char next(char c) throws JSONException {
|
||||
char n = next();
|
||||
if (n != c) {
|
||||
throw syntaxError("Expected '" + c + "' and instead saw '" +
|
||||
n + "'");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next n characters.
|
||||
*
|
||||
* @param n The number of characters to take.
|
||||
* @return A string of n characters.
|
||||
* @throws JSONException Substring bounds error if there are not
|
||||
* n characters remaining in the source string.
|
||||
*/
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
while (pos < n) {
|
||||
chars[pos] = next();
|
||||
if (end()) {
|
||||
throw syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next char in the string, skipping whitespace.
|
||||
*
|
||||
* @return A character, or 0 if there are no more characters.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public char nextClean() throws JSONException {
|
||||
for (; ; ) {
|
||||
char c = next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the characters up to the next close quote character.
|
||||
* Backslash processing is done. The formal JSON format does not
|
||||
* allow strings in single quotes, but an implementation is allowed to
|
||||
* accept them.
|
||||
*
|
||||
* @param quote The quoting character, either
|
||||
* <code>"</code> <small>(double quote)</small> or
|
||||
* <code>'</code> <small>(single quote)</small>.
|
||||
* @return A String.
|
||||
* @throws JSONException Unterminated string.
|
||||
*/
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw syntaxError("Unterminated string");
|
||||
case '\\':
|
||||
c = next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':
|
||||
sb.append((char) Integer.parseInt(next(4), 16));
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw syntaxError("Illegal escape.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the text up but not including the specified character or the
|
||||
* end of line, whichever comes first.
|
||||
*
|
||||
* @param delimiter A delimiter character.
|
||||
* @return A string.
|
||||
*/
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
char c = next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the text up but not including one of the specified delimiter
|
||||
* characters or the end of line, whichever comes first.
|
||||
*
|
||||
* @param delimiters A set of delimiter characters.
|
||||
* @return A string, trimmed.
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 ||
|
||||
c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next value. The value can be a Boolean, Double, Integer,
|
||||
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||
*
|
||||
* @return An object.
|
||||
* @throws JSONException If syntax error.
|
||||
*/
|
||||
public Object nextValue() throws JSONException {
|
||||
char c = nextClean();
|
||||
String string;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw syntaxError("Illegal escape.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including the specified character or the end of line, whichever comes
|
||||
* first.
|
||||
*
|
||||
* @param delimiter A delimiter character.
|
||||
* @return A string.
|
||||
*/
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
char c = next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
back();
|
||||
return nextString(c);
|
||||
case '{':
|
||||
back();
|
||||
return new JSONObject(this);
|
||||
case '[':
|
||||
back();
|
||||
return new JSONArray(this);
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including one of the specified delimiter characters or the end of line,
|
||||
* whichever comes first.
|
||||
*
|
||||
* @param delimiters A set of delimiter characters.
|
||||
* @return A string, trimmed.
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
back();
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or
|
||||
* null, or it can be a number. An implementation (such as this one)
|
||||
* is allowed to also accept non-standard forms.
|
||||
*
|
||||
* Accumulate characters until we reach the end of the text or a
|
||||
* formatting character.
|
||||
*/
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = next();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next value. The value can be a Boolean, Double, Integer, JSONArray, JSONObject, Long,
|
||||
* or String, or the JSONObject.NULL object.
|
||||
*
|
||||
* @return An object.
|
||||
* @throws JSONException If syntax error.
|
||||
*/
|
||||
public Object nextValue() throws JSONException {
|
||||
char c = nextClean();
|
||||
String string;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
return nextString(c);
|
||||
case '{':
|
||||
back();
|
||||
return new JSONObject(this);
|
||||
case '[':
|
||||
back();
|
||||
return new JSONArray(this);
|
||||
|
||||
string = sb.toString().trim();
|
||||
if (string.equals("")) {
|
||||
throw syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(string);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or
|
||||
* null, or it can be a number. An implementation (such as this one)
|
||||
* is allowed to also accept non-standard forms.
|
||||
|
||||
/**
|
||||
* Skip characters until the next character is the requested character.
|
||||
* If the requested character is not found, no characters are skipped.
|
||||
*
|
||||
* Accumulate characters until we reach the end of the text or a
|
||||
* formatting character.
|
||||
* @param to A character to skip to.
|
||||
* @return The requested character, or zero if the requested character
|
||||
* is not found.
|
||||
*/
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = next();
|
||||
}
|
||||
back();
|
||||
|
||||
string = sb.toString().trim();
|
||||
if (string.isEmpty()) {
|
||||
throw syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip characters until the next character is the requested character. If the requested character
|
||||
* is not found, no characters are skipped.
|
||||
*
|
||||
* @param to A character to skip to.
|
||||
* @return The requested character, or zero if the requested character is not found.
|
||||
*/
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
int startIndex = this.index;
|
||||
int startCharacter = this.character;
|
||||
int startLine = this.line;
|
||||
reader.mark(Integer.MAX_VALUE);
|
||||
do {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return c;
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
int startIndex = this.index;
|
||||
int startCharacter = this.character;
|
||||
int startLine = this.line;
|
||||
reader.mark(Integer.MAX_VALUE);
|
||||
do {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return c;
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
|
||||
back();
|
||||
return c;
|
||||
}
|
||||
|
||||
back();
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error.
|
||||
*
|
||||
* @param message The error message.
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + toString());
|
||||
}
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error.
|
||||
*
|
||||
* @param message The error message.
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at {index} [character {character} line {line}]"
|
||||
*/
|
||||
public String toString() {
|
||||
return " at " + index + " [character " + this.character + " line " + this.line + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at {index} [character {character} line {line}]"
|
||||
*/
|
||||
public String toString() {
|
||||
return " at " + index + " [character " + this.character + " line " +
|
||||
this.line + "]";
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user