mirror of
https://github.com/funkemunky/AntiVPN.git
synced 2026-05-31 09:31:54 +00:00
Merge pull request #92 from funkemunky/bugfix/spam-kick
Fixing player spam kick
This commit is contained in:
+1288
File diff suppressed because it is too large
Load Diff
@@ -31,4 +31,4 @@ jobs:
|
|||||||
name: AntiVPN-Universal
|
name: AntiVPN-Universal
|
||||||
path: build/libs/AntiVPN-*-universal.jar
|
path: build/libs/AntiVPN-*-universal.jar
|
||||||
- name: Test
|
- name: Test
|
||||||
run: gradle test --no-daemon
|
run: gradle test --no-daemon --parallel
|
||||||
|
|||||||
@@ -30,4 +30,8 @@ tasks.named('shadowJar') {
|
|||||||
dependsOn(':Bukkit:Plugin:shadowJar')
|
dependsOn(':Bukkit:Plugin:shadowJar')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.named('compileJava') {
|
||||||
|
dependsOn(':Bukkit:Plugin:shadowJar')
|
||||||
|
}
|
||||||
|
|
||||||
tasks.build.dependsOn shadowJar
|
tasks.build.dependsOn shadowJar
|
||||||
|
|||||||
+2
-3
@@ -29,7 +29,8 @@ public class BukkitLoaderPlugin extends JavaPlugin {
|
|||||||
private final LoaderBootstrap plugin;
|
private final LoaderBootstrap plugin;
|
||||||
|
|
||||||
public BukkitLoaderPlugin() {
|
public BukkitLoaderPlugin() {
|
||||||
JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
JarInJarClassLoader loader =
|
||||||
|
new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
||||||
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, JavaPlugin.class, this);
|
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, JavaPlugin.class, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +48,4 @@ public class BukkitLoaderPlugin extends JavaPlugin {
|
|||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
this.plugin.onDisable();
|
this.plugin.onDisable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ dependencies {
|
|||||||
testImplementation 'org.mockito:mockito-core:5.11.0'
|
testImplementation 'org.mockito:mockito-core:5.11.0'
|
||||||
testImplementation 'org.mockito:mockito-subclass:5.11.0'
|
testImplementation 'org.mockito:mockito-subclass:5.11.0'
|
||||||
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
|
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
|
||||||
|
testImplementation testFixtures(project(':Common:Source'))
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ package dev.brighten.antivpn.bukkit;
|
|||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.api.APIPlayer;
|
import dev.brighten.antivpn.api.APIPlayer;
|
||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class BukkitCommandExecutor implements CommandExecutor {
|
public class BukkitCommandExecutor implements CommandExecutor {
|
||||||
|
|
||||||
@@ -33,8 +32,8 @@ public class BukkitCommandExecutor implements CommandExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(String message, Object... objects) {
|
public void sendMessage(String message, Object... objects) {
|
||||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
sender.sendMessage(
|
||||||
String.format(message, objects)));
|
ChatColor.translateAlternateColorCodes('&', String.format(message, objects)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import dev.brighten.antivpn.api.CheckResult;
|
|||||||
import dev.brighten.antivpn.api.OfflinePlayer;
|
import dev.brighten.antivpn.api.OfflinePlayer;
|
||||||
import dev.brighten.antivpn.api.VPNExecutor;
|
import dev.brighten.antivpn.api.VPNExecutor;
|
||||||
import dev.brighten.antivpn.utils.StringUtil;
|
import dev.brighten.antivpn.utils.StringUtil;
|
||||||
|
import java.util.logging.Level;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
@@ -33,14 +34,11 @@ import org.bukkit.event.player.PlayerLoginEvent;
|
|||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class BukkitListener extends VPNExecutor implements Listener {
|
public class BukkitListener extends VPNExecutor implements Listener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerListeners() {
|
public void registerListeners() {
|
||||||
Bukkit.getPluginManager()
|
Bukkit.getPluginManager().registerEvents(this, BukkitPlugin.pluginInstance.getPlugin());
|
||||||
.registerEvents(this, BukkitPlugin.pluginInstance.getPlugin());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -62,8 +60,9 @@ public class BukkitListener extends VPNExecutor implements Listener {
|
|||||||
public void runCommand(String command) {
|
public void runCommand(String command) {
|
||||||
new BukkitRunnable() {
|
new BukkitRunnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
|
Bukkit.getServer()
|
||||||
ChatColor.translateAlternateColorCodes('&', command));
|
.dispatchCommand(
|
||||||
|
Bukkit.getConsoleSender(), ChatColor.translateAlternateColorCodes('&', command));
|
||||||
}
|
}
|
||||||
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||||
}
|
}
|
||||||
@@ -76,12 +75,15 @@ public class BukkitListener extends VPNExecutor implements Listener {
|
|||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGH)
|
@EventHandler(priority = EventPriority.HIGH)
|
||||||
public void onLogin(final PlayerLoginEvent event) {
|
public void onLogin(final PlayerLoginEvent event) {
|
||||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
APIPlayer player =
|
||||||
.orElse(new OfflinePlayer(
|
AntiVPN.getInstance()
|
||||||
|
.getPlayerExecutor()
|
||||||
|
.getPlayer(event.getPlayer().getUniqueId())
|
||||||
|
.orElse(
|
||||||
|
new OfflinePlayer(
|
||||||
event.getPlayer().getUniqueId(),
|
event.getPlayer().getUniqueId(),
|
||||||
event.getPlayer().getName(),
|
event.getPlayer().getName(),
|
||||||
event.getAddress()
|
event.getAddress()));
|
||||||
));
|
|
||||||
|
|
||||||
CheckResult result = player.checkPlayer();
|
CheckResult result = player.checkPlayer();
|
||||||
|
|
||||||
@@ -92,25 +94,25 @@ public class BukkitListener extends VPNExecutor implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
event.setResult(PlayerLoginEvent.Result.KICK_BANNED);
|
event.setResult(PlayerLoginEvent.Result.KICK_BANNED);
|
||||||
event.setKickMessage(switch (result.resultType()) {
|
event.setKickMessage(
|
||||||
case DENIED_COUNTRY -> StringUtil.varReplace(
|
switch (result.resultType()) {
|
||||||
|
case DENIED_COUNTRY ->
|
||||||
|
StringUtil.varReplace(
|
||||||
AntiVPN.getInstance().getVpnConfig().getCountryVanillaKickReason(),
|
AntiVPN.getInstance().getVpnConfig().getCountryVanillaKickReason(),
|
||||||
player,
|
player,
|
||||||
result.response()
|
result.response());
|
||||||
);
|
|
||||||
case DENIED_PROXY ->
|
case DENIED_PROXY ->
|
||||||
StringUtil.varReplace(
|
StringUtil.varReplace(
|
||||||
AntiVPN.getInstance().getVpnConfig().getKickMessage(),
|
AntiVPN.getInstance().getVpnConfig().getKickMessage(), player, result.response());
|
||||||
player,
|
|
||||||
result.response()
|
|
||||||
);
|
|
||||||
default -> "You were kicked by KauriVPN for an unknown reason!";
|
default -> "You were kicked by KauriVPN for an unknown reason!";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
public void onJoin(final PlayerJoinEvent event) {
|
public void onJoin(final PlayerJoinEvent event) {
|
||||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
AntiVPN.getInstance()
|
||||||
|
.getPlayerExecutor()
|
||||||
|
.getPlayer(event.getPlayer().getUniqueId())
|
||||||
.ifPresent(APIPlayer::checkAlertsState);
|
.ifPresent(APIPlayer::checkAlertsState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,12 @@ import org.bukkit.scheduler.BukkitRunnable;
|
|||||||
public class BukkitPlayer extends APIPlayer {
|
public class BukkitPlayer extends APIPlayer {
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
|
|
||||||
public BukkitPlayer(Player player) {
|
public BukkitPlayer(Player player) {
|
||||||
super(player.getUniqueId(), player.getName(), player.getAddress() != null ? player.getAddress().getAddress() : null);
|
super(
|
||||||
|
player.getUniqueId(),
|
||||||
|
player.getName(),
|
||||||
|
player.getAddress() != null ? player.getAddress().getAddress() : null);
|
||||||
|
|
||||||
this.player = player;
|
this.player = player;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,10 @@ package dev.brighten.antivpn.bukkit;
|
|||||||
|
|
||||||
import dev.brighten.antivpn.api.APIPlayer;
|
import dev.brighten.antivpn.api.APIPlayer;
|
||||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
public class BukkitPlayerExecutor implements PlayerExecutor {
|
public class BukkitPlayerExecutor implements PlayerExecutor {
|
||||||
|
|
||||||
@@ -36,7 +35,8 @@ public class BukkitPlayerExecutor implements PlayerExecutor {
|
|||||||
return Optional.empty();
|
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
|
@Override
|
||||||
@@ -47,7 +47,8 @@ public class BukkitPlayerExecutor implements PlayerExecutor {
|
|||||||
return Optional.empty();
|
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
|
@Override
|
||||||
@@ -55,12 +56,10 @@ public class BukkitPlayerExecutor implements PlayerExecutor {
|
|||||||
cachedPlayers.remove(uuid);
|
cachedPlayers.remove(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<APIPlayer> getOnlinePlayers() {
|
public List<APIPlayer> getOnlinePlayers() {
|
||||||
return Bukkit.getOnlinePlayers().stream()
|
return Bukkit.getOnlinePlayers().stream()
|
||||||
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), k -> new BukkitPlayer(pl)))
|
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), k -> new BukkitPlayer(pl)))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ import dev.brighten.antivpn.database.local.H2VPN;
|
|||||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||||
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
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 lombok.Getter;
|
||||||
import org.bstats.bukkit.Metrics;
|
import org.bstats.bukkit.Metrics;
|
||||||
import org.bstats.charts.SimplePie;
|
import org.bstats.charts.SimplePie;
|
||||||
@@ -34,28 +39,19 @@ import org.bukkit.plugin.SimplePluginManager;
|
|||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
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 class BukkitPlugin implements LoaderBootstrap {
|
||||||
|
|
||||||
public static BukkitPlugin pluginInstance;
|
public static BukkitPlugin pluginInstance;
|
||||||
private SimpleCommandMap commandMap;
|
private SimpleCommandMap commandMap;
|
||||||
@Getter
|
@Getter private File dataFolder;
|
||||||
private File dataFolder;
|
|
||||||
private final List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
|
private final List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
|
||||||
@Getter
|
@Getter private final JavaPlugin plugin;
|
||||||
private final JavaPlugin plugin;
|
|
||||||
|
|
||||||
public BukkitPlugin(JavaPlugin plugin) {
|
public BukkitPlugin(JavaPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter private PlayerCommandRunner playerCommandRunner;
|
||||||
private PlayerCommandRunner playerCommandRunner;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad(File dataFolder) {
|
public void onLoad(File dataFolder) {
|
||||||
@@ -90,7 +86,10 @@ public class BukkitPlugin implements LoaderBootstrap {
|
|||||||
Field field = SimplePluginManager.class.getDeclaredField("commandMap");
|
Field field = SimplePluginManager.class.getDeclaredField("commandMap");
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
commandMap = (SimpleCommandMap) field.get(manager);
|
commandMap = (SimpleCommandMap) field.get(manager);
|
||||||
} catch (IllegalArgumentException | SecurityException | NoSuchFieldException | IllegalAccessException e) {
|
} catch (IllegalArgumentException
|
||||||
|
| SecurityException
|
||||||
|
| NoSuchFieldException
|
||||||
|
| IllegalAccessException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException(e);
|
AntiVPN.getInstance().getExecutor().logException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +129,8 @@ public class BukkitPlugin implements LoaderBootstrap {
|
|||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
|
|
||||||
if (field.get(commandMap) instanceof Map<?, ?> knownCommands) {
|
if (field.get(commandMap) instanceof Map<?, ?> knownCommands) {
|
||||||
Map<String, org.bukkit.command.Command> casted = (Map<String, org.bukkit.command.Command>) knownCommands;
|
Map<String, org.bukkit.command.Command> casted =
|
||||||
|
(Map<String, org.bukkit.command.Command>) knownCommands;
|
||||||
casted.values().removeAll(registeredCommands);
|
casted.values().removeAll(registeredCommands);
|
||||||
registeredCommands.clear();
|
registeredCommands.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,29 +17,29 @@
|
|||||||
package dev.brighten.antivpn.bukkit;
|
package dev.brighten.antivpn.bukkit;
|
||||||
|
|
||||||
import dev.brighten.antivpn.utils.MiscUtils;
|
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.Queue;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
public class PlayerCommandRunner {
|
public class PlayerCommandRunner {
|
||||||
private final ScheduledExecutorService executorService;
|
private final ScheduledExecutorService executorService;
|
||||||
private final Queue<PlayerAction> playerActions = new ArrayBlockingQueue<>(10000);
|
private final Queue<PlayerAction> playerActions = new ArrayBlockingQueue<>(10000);
|
||||||
|
|
||||||
public PlayerCommandRunner() {
|
public PlayerCommandRunner() {
|
||||||
executorService = Executors.newSingleThreadScheduledExecutor(
|
executorService =
|
||||||
MiscUtils.createThreadFactory("AntiVPN:PlayerCommandRunner")
|
Executors.newSingleThreadScheduledExecutor(
|
||||||
);
|
MiscUtils.createThreadFactory("AntiVPN:PlayerCommandRunner"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() {
|
void start() {
|
||||||
executorService.scheduleAtFixedRate(() -> {
|
executorService.scheduleAtFixedRate(
|
||||||
|
() -> {
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
while (!playerActions.isEmpty()) {
|
while (!playerActions.isEmpty()) {
|
||||||
PlayerAction action = playerActions.peek();
|
PlayerAction action = playerActions.peek();
|
||||||
@@ -56,7 +56,10 @@ public class PlayerCommandRunner {
|
|||||||
playerActions.poll();
|
playerActions.poll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 1000, 100, TimeUnit.MILLISECONDS);
|
},
|
||||||
|
1000,
|
||||||
|
100,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
|
|||||||
+36
-21
@@ -19,17 +19,17 @@ package dev.brighten.antivpn.bukkit.command;
|
|||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.bukkit.BukkitCommandExecutor;
|
import dev.brighten.antivpn.bukkit.BukkitCommandExecutor;
|
||||||
import dev.brighten.antivpn.command.Command;
|
import dev.brighten.antivpn.command.Command;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import org.bukkit.command.CommandSender;
|
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 {
|
public class BukkitCommand extends org.bukkit.command.Command {
|
||||||
|
|
||||||
private final Command command;
|
private final Command command;
|
||||||
|
|
||||||
public BukkitCommand(Command command) {
|
public BukkitCommand(Command command) {
|
||||||
super(command.name(), command.description(), command.usage(), Arrays.asList(command.aliases()));
|
super(command.name(), command.description(), command.usage(), Arrays.asList(command.aliases()));
|
||||||
|
|
||||||
@@ -43,11 +43,15 @@ public class BukkitCommand extends org.bukkit.command.Command {
|
|||||||
|
|
||||||
if (children.length > 0 && args.length > 0) {
|
if (children.length > 0 && args.length > 0) {
|
||||||
for (Command child : children) {
|
for (Command child : children) {
|
||||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
if (child.name().equalsIgnoreCase(args[0])
|
||||||
|
|| Arrays.stream(child.aliases())
|
||||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||||
return child.tabComplete(new BukkitCommandExecutor(sender), alias, IntStream
|
return child.tabComplete(
|
||||||
.range(0, args.length - 1)
|
new BukkitCommandExecutor(sender),
|
||||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
|
alias,
|
||||||
|
IntStream.range(0, args.length - 1)
|
||||||
|
.mapToObj(i -> args[i + 1])
|
||||||
|
.toArray(String[]::new));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,9 +60,10 @@ public class BukkitCommand extends org.bukkit.command.Command {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(CommandSender sender, String s, String[] args) {
|
public boolean execute(CommandSender sender, String s, String[] args) {
|
||||||
if(!sender.hasPermission("antivpn.command.*")
|
if (!sender.hasPermission("antivpn.command.*") && !sender.hasPermission(command.permission())) {
|
||||||
&& !sender.hasPermission(command.permission())) {
|
sender.sendMessage(
|
||||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
ChatColor.translateAlternateColorCodes(
|
||||||
|
'&',
|
||||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
|
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -67,26 +72,36 @@ public class BukkitCommand extends org.bukkit.command.Command {
|
|||||||
|
|
||||||
if (children.length > 0 && args.length > 0) {
|
if (children.length > 0 && args.length > 0) {
|
||||||
for (Command child : children) {
|
for (Command child : children) {
|
||||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
if (child.name().equalsIgnoreCase(args[0])
|
||||||
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
|| Arrays.stream(child.aliases()).anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||||
if (!sender.hasPermission("antivpn.command.*")
|
if (!sender.hasPermission("antivpn.command.*")
|
||||||
&& !sender.hasPermission(child.permission())) {
|
&& !sender.hasPermission(child.permission())) {
|
||||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
sender.sendMessage(
|
||||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
|
ChatColor.translateAlternateColorCodes(
|
||||||
|
'&',
|
||||||
|
AntiVPN.getInstance()
|
||||||
|
.getMessageHandler()
|
||||||
|
.getString("no-permission")
|
||||||
|
.getMessage()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
sender.sendMessage(
|
||||||
child.execute(new BukkitCommandExecutor(sender), IntStream
|
ChatColor.translateAlternateColorCodes(
|
||||||
.range(0, args.length - 1)
|
'&',
|
||||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
|
child.execute(
|
||||||
|
new BukkitCommandExecutor(sender),
|
||||||
|
IntStream.range(0, args.length - 1)
|
||||||
|
.mapToObj(i -> args[i + 1])
|
||||||
|
.toArray(String[]::new))));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
sender.sendMessage(
|
||||||
command.execute(new BukkitCommandExecutor(sender), args)));
|
ChatColor.translateAlternateColorCodes(
|
||||||
|
'&', command.execute(new BukkitCommandExecutor(sender), args)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
package dev.brighten.antivpn.bukkit;
|
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.MockBukkit;
|
||||||
import be.seeseemelk.mockbukkit.ServerMock;
|
import be.seeseemelk.mockbukkit.ServerMock;
|
||||||
import be.seeseemelk.mockbukkit.entity.PlayerMock;
|
import be.seeseemelk.mockbukkit.entity.PlayerMock;
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
import dev.brighten.antivpn.StandardTest;
|
||||||
import dev.brighten.antivpn.api.VPNConfig;
|
import dev.brighten.antivpn.api.*;
|
||||||
import dev.brighten.antivpn.api.VPNExecutor;
|
|
||||||
import dev.brighten.antivpn.message.MessageHandler;
|
import dev.brighten.antivpn.message.MessageHandler;
|
||||||
import dev.brighten.antivpn.message.VpnString;
|
import dev.brighten.antivpn.message.VpnString;
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||||
import org.bukkit.event.player.PlayerLoginEvent;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -29,14 +25,16 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
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;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
public class BukkitListenerTest extends StandardTest {
|
||||||
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.*;
|
|
||||||
|
|
||||||
public class BukkitListenerTest {
|
|
||||||
|
|
||||||
private ServerMock server;
|
private ServerMock server;
|
||||||
private BukkitListener listener;
|
private BukkitListener listener;
|
||||||
@@ -45,10 +43,10 @@ public class BukkitListenerTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
server = MockBukkit.mock(new RecordingServerMock());
|
server = MockBukkit.mock(new RecordingServerMock());
|
||||||
JavaPlugin plugin = MockBukkit.loadWith(
|
JavaPlugin plugin =
|
||||||
|
MockBukkit.loadWith(
|
||||||
TestPlugin.class,
|
TestPlugin.class,
|
||||||
new PluginDescriptionFile("AntiVPNTest", "1.0.0", TestPlugin.class.getName())
|
new PluginDescriptionFile("AntiVPNTest", "1.0.0", TestPlugin.class.getName()));
|
||||||
);
|
|
||||||
BukkitPlugin.pluginInstance = new BukkitPlugin(plugin);
|
BukkitPlugin.pluginInstance = new BukkitPlugin(plugin);
|
||||||
|
|
||||||
AntiVPN antiVPN = mock(AntiVPN.class);
|
AntiVPN antiVPN = mock(AntiVPN.class);
|
||||||
@@ -72,10 +70,17 @@ public class BukkitListenerTest {
|
|||||||
when(mockVpnString.getFormattedMessage(any())).thenReturn("Blocked!");
|
when(mockVpnString.getFormattedMessage(any())).thenReturn("Blocked!");
|
||||||
when(messageHandler.getString(anyString())).thenReturn(mockVpnString);
|
when(messageHandler.getString(anyString())).thenReturn(mockVpnString);
|
||||||
|
|
||||||
when(vpnExecutor.checkIp(anyString())).thenReturn(CompletableFuture.completedFuture(
|
when(vpnExecutor.checkIp(anyString()))
|
||||||
VPNResponse.builder().success(true).proxy(false).ip("127.0.0.1")
|
.thenReturn(
|
||||||
.method("N/A").countryName("N/A").city("N/A").build()
|
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
|
// Use reflection to set the private static INSTANCE field
|
||||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||||
@@ -101,6 +106,21 @@ public class BukkitListenerTest {
|
|||||||
PlayerMock player = server.addPlayer("TestPlayer");
|
PlayerMock player = server.addPlayer("TestPlayer");
|
||||||
InetAddress address = InetAddress.getByName("127.0.0.1");
|
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);
|
PlayerLoginEvent event = new PlayerLoginEvent(player, "localhost", address);
|
||||||
|
|
||||||
listener.onLogin(event);
|
listener.onLogin(event);
|
||||||
@@ -108,41 +128,21 @@ public class BukkitListenerTest {
|
|||||||
assertEquals(PlayerLoginEvent.Result.ALLOWED, event.getResult());
|
assertEquals(PlayerLoginEvent.Result.ALLOWED, event.getResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
@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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoginPipelineProxyPlayerIsKickedWithoutErrors() throws Exception {
|
public void testLoginPipelineProxyPlayerIsKickedWithoutErrors() throws Exception {
|
||||||
PlayerMock player = server.addPlayer("PipelineProxyPlayer");
|
PlayerMock player = server.addPlayer("PipelineProxyPlayer");
|
||||||
InetAddress address = InetAddress.getByName("1.1.1.1");
|
InetAddress address = InetAddress.getByName("1.1.1.1");
|
||||||
|
|
||||||
when(vpnExecutor.checkIp("1.1.1.1")).thenReturn(CompletableFuture.completedFuture(
|
mockCache();
|
||||||
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);
|
PlayerLoginEvent event = new PlayerLoginEvent(player, "localhost", address);
|
||||||
|
|
||||||
assertDoesNotThrow(() -> listener.onLogin(event));
|
assertDoesNotThrow(() -> listener.onLogin(event));
|
||||||
|
|
||||||
assertEquals(PlayerLoginEvent.Result.KICK_BANNED, event.getResult());
|
assertEquals(PlayerLoginEvent.Result.KICK_BANNED, event.getResult());
|
||||||
assertEquals("Blocked!", net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(event.kickMessage()));
|
assertEquals(
|
||||||
|
"Blocked!",
|
||||||
|
net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection()
|
||||||
|
.serialize(event.kickMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -151,26 +151,29 @@ public class BukkitListenerTest {
|
|||||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
CompletableFuture<Void> asyncCall = CompletableFuture.runAsync(
|
CompletableFuture<Void> asyncCall =
|
||||||
() -> listener.runCommand("antivpn-test &aok"),
|
CompletableFuture.runAsync(() -> listener.runCommand("antivpn-test &aok"), executor);
|
||||||
executor
|
|
||||||
);
|
|
||||||
|
|
||||||
assertDoesNotThrow(() -> asyncCall.get(5, TimeUnit.SECONDS));
|
assertDoesNotThrow(() -> asyncCall.get(5, TimeUnit.SECONDS));
|
||||||
assertFalse(recordingServer.commandDispatched(), "Command should be scheduled, not dispatched asynchronously");
|
assertFalse(
|
||||||
|
recordingServer.commandDispatched(),
|
||||||
|
"Command should be scheduled, not dispatched asynchronously");
|
||||||
|
|
||||||
server.getScheduler().performOneTick();
|
server.getScheduler().performOneTick();
|
||||||
|
|
||||||
assertTrue(recordingServer.commandDispatched(), "Scheduled command should be dispatched on the next server tick");
|
assertTrue(
|
||||||
assertTrue(recordingServer.dispatchedOnPrimaryThread(), "Command dispatch must happen on Bukkit's primary thread");
|
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());
|
assertEquals("antivpn-test §aok", recordingServer.dispatchedCommand());
|
||||||
} finally {
|
} finally {
|
||||||
executor.shutdownNow();
|
executor.shutdownNow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TestPlugin extends JavaPlugin {
|
public static class TestPlugin extends JavaPlugin {}
|
||||||
}
|
|
||||||
|
|
||||||
private static class RecordingServerMock extends ServerMock {
|
private static class RecordingServerMock extends ServerMock {
|
||||||
private final AtomicBoolean commandDispatched = new AtomicBoolean();
|
private final AtomicBoolean commandDispatched = new AtomicBoolean();
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
package dev.brighten.antivpn.bukkit;
|
package dev.brighten.antivpn.bukkit;
|
||||||
|
|
||||||
import be.seeseemelk.mockbukkit.MockBukkit;
|
|
||||||
import be.seeseemelk.mockbukkit.ServerMock;
|
|
||||||
import be.seeseemelk.mockbukkit.entity.PlayerMock;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
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 {
|
class BukkitPlayerTest {
|
||||||
|
|
||||||
private ServerMock server;
|
private ServerMock server;
|
||||||
@@ -44,7 +43,8 @@ class BukkitPlayerTest {
|
|||||||
|
|
||||||
assertTrue(player.isOnline());
|
assertTrue(player.isOnline());
|
||||||
|
|
||||||
CompletableFuture<Void> asyncKick = CompletableFuture.runAsync(() -> bukkitPlayer.kickPlayer("&cBlocked!"));
|
CompletableFuture<Void> asyncKick =
|
||||||
|
CompletableFuture.runAsync(() -> bukkitPlayer.kickPlayer("&cBlocked!"));
|
||||||
assertDoesNotThrow(() -> asyncKick.get(1, TimeUnit.SECONDS));
|
assertDoesNotThrow(() -> asyncKick.get(1, TimeUnit.SECONDS));
|
||||||
|
|
||||||
assertTrue(player.isOnline(), "Kick should be deferred to the server scheduler");
|
assertTrue(player.isOnline(), "Kick should be deferred to the server scheduler");
|
||||||
|
|||||||
@@ -29,4 +29,8 @@ tasks.named('shadowJar') {
|
|||||||
dependsOn(':Bungee:BungeePlugin:shadowJar')
|
dependsOn(':Bungee:BungeePlugin:shadowJar')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.named('compileJava') {
|
||||||
|
dependsOn(':Bungee:BungeePlugin:shadowJar')
|
||||||
|
}
|
||||||
|
|
||||||
tasks.build.dependsOn shadowJar
|
tasks.build.dependsOn shadowJar
|
||||||
|
|||||||
+2
-3
@@ -29,7 +29,8 @@ public class BungeeLoaderPlugin extends Plugin {
|
|||||||
private final LoaderBootstrap plugin;
|
private final LoaderBootstrap plugin;
|
||||||
|
|
||||||
public BungeeLoaderPlugin() {
|
public BungeeLoaderPlugin() {
|
||||||
JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
JarInJarClassLoader loader =
|
||||||
|
new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
||||||
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, Plugin.class, this);
|
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, Plugin.class, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +48,4 @@ public class BungeeLoaderPlugin extends Plugin {
|
|||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
this.plugin.onDisable();
|
this.plugin.onDisable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ dependencies {
|
|||||||
testImplementation 'org.mockito:mockito-subclass:5.11.0'
|
testImplementation 'org.mockito:mockito-subclass:5.11.0'
|
||||||
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
|
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
|
||||||
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
||||||
|
testImplementation testFixtures(project(':Common:Source'))
|
||||||
}
|
}
|
||||||
tasks.compileJava.dependsOn(':Common:Source:jar')
|
tasks.compileJava.dependsOn(':Common:Source:jar')
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ import dev.brighten.antivpn.AntiVPN;
|
|||||||
import dev.brighten.antivpn.api.*;
|
import dev.brighten.antivpn.api.*;
|
||||||
import dev.brighten.antivpn.utils.MiscUtils;
|
import dev.brighten.antivpn.utils.MiscUtils;
|
||||||
import dev.brighten.antivpn.utils.StringUtil;
|
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.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.event.LoginEvent;
|
import net.md_5.bungee.api.event.LoginEvent;
|
||||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||||
@@ -29,17 +32,15 @@ import net.md_5.bungee.api.scheduler.ScheduledTask;
|
|||||||
import net.md_5.bungee.event.EventHandler;
|
import net.md_5.bungee.event.EventHandler;
|
||||||
import net.md_5.bungee.event.EventPriority;
|
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 {
|
public class BungeeListener extends VPNExecutor implements Listener {
|
||||||
|
|
||||||
private ScheduledTask cacheResetTask;
|
private ScheduledTask cacheResetTask;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerListeners() {
|
public void registerListeners() {
|
||||||
BungeePlugin.pluginInstance.getProxy().getPluginManager()
|
BungeePlugin.pluginInstance
|
||||||
|
.getProxy()
|
||||||
|
.getPluginManager()
|
||||||
.registerListener(BungeePlugin.pluginInstance.getPlugin(), this);
|
.registerListener(BungeePlugin.pluginInstance.getPlugin(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,30 +61,49 @@ public class BungeeListener extends VPNExecutor implements Listener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void runCommand(String command) {
|
public void runCommand(String command) {
|
||||||
BungeePlugin.pluginInstance.getProxy().getPluginManager()
|
BungeePlugin.pluginInstance
|
||||||
|
.getProxy()
|
||||||
|
.getPluginManager()
|
||||||
.dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), command);
|
.dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disablePlugin() {
|
public void disablePlugin() {
|
||||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance.getPlugin());
|
BungeePlugin.pluginInstance
|
||||||
|
.getProxy()
|
||||||
|
.getPluginManager()
|
||||||
|
.unregisterListeners(BungeePlugin.pluginInstance.getPlugin());
|
||||||
if (cacheResetTask != null) {
|
if (cacheResetTask != null) {
|
||||||
cacheResetTask.cancel();
|
cacheResetTask.cancel();
|
||||||
cacheResetTask = null;
|
cacheResetTask = null;
|
||||||
}
|
}
|
||||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance.getPlugin());
|
BungeePlugin.pluginInstance
|
||||||
|
.getProxy()
|
||||||
|
.getPluginManager()
|
||||||
|
.unregisterCommands(BungeePlugin.pluginInstance.getPlugin());
|
||||||
BungeePlugin.pluginInstance.onDisable();
|
BungeePlugin.pluginInstance.onDisable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGH)
|
@EventHandler(priority = EventPriority.HIGH)
|
||||||
public void onListener(final PreLoginEvent event) {
|
public void onListener(final PreLoginEvent event) {
|
||||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getConnection().getUniqueId())
|
APIPlayer player =
|
||||||
.orElseGet(() -> {
|
AntiVPN.getInstance()
|
||||||
|
.getPlayerExecutor()
|
||||||
|
.getPlayer(event.getConnection().getUniqueId())
|
||||||
|
.orElseGet(
|
||||||
|
() -> {
|
||||||
UUID uuid = MiscUtils.lookupUUID(event.getConnection().getName());
|
UUID uuid = MiscUtils.lookupUUID(event.getConnection().getName());
|
||||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "Getting offline player for %s with name %s",
|
AntiVPN.getInstance()
|
||||||
event.getConnection().getUniqueId(), uuid);
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
Level.INFO,
|
||||||
|
"Getting offline player for %s with name %s",
|
||||||
|
event.getConnection().getUniqueId(),
|
||||||
|
uuid);
|
||||||
|
|
||||||
return new OfflinePlayer(uuid, event.getConnection().getName(),
|
return new OfflinePlayer(
|
||||||
|
uuid,
|
||||||
|
event.getConnection().getName(),
|
||||||
((InetSocketAddress) event.getConnection().getSocketAddress()).getAddress());
|
((InetSocketAddress) event.getConnection().getSocketAddress()).getAddress());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -96,13 +116,24 @@ public class BungeeListener extends VPNExecutor implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
event.setReason(TextComponent.fromLegacy(StringUtil.varReplace(switch (result.resultType()) {
|
event.setReason(
|
||||||
case DENIED_PROXY -> StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
TextComponent.fromLegacy(
|
||||||
.getKickMessage(), player, result.response());
|
StringUtil.varReplace(
|
||||||
case DENIED_COUNTRY -> StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
switch (result.resultType()) {
|
||||||
.getCountryVanillaKickReason(), player, result.response());
|
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!";
|
default -> "You were kicked by KauriVPN for an unknown reason!";
|
||||||
}, player, result.response())));
|
},
|
||||||
|
player,
|
||||||
|
result.response())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGH)
|
@EventHandler(priority = EventPriority.HIGH)
|
||||||
@@ -110,7 +141,9 @@ public class BungeeListener extends VPNExecutor implements Listener {
|
|||||||
if (event.isCancelled()) return;
|
if (event.isCancelled()) return;
|
||||||
|
|
||||||
// Handling player alerts on join
|
// Handling player alerts on join
|
||||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getConnection().getUniqueId())
|
AntiVPN.getInstance()
|
||||||
|
.getPlayerExecutor()
|
||||||
|
.getPlayer(event.getConnection().getUniqueId())
|
||||||
.ifPresent(APIPlayer::checkAlertsState);
|
.ifPresent(APIPlayer::checkAlertsState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,23 +24,23 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|||||||
public class BungeePlayer extends APIPlayer {
|
public class BungeePlayer extends APIPlayer {
|
||||||
|
|
||||||
private final ProxiedPlayer player;
|
private final ProxiedPlayer player;
|
||||||
|
|
||||||
public BungeePlayer(ProxiedPlayer player) {
|
public BungeePlayer(ProxiedPlayer player) {
|
||||||
super(player.getUniqueId(), player.getName(), player.getAddress().getAddress());
|
super(player.getUniqueId(), player.getName(), player.getAddress().getAddress());
|
||||||
|
|
||||||
this.player = player;
|
this.player = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(String message) {
|
public void sendMessage(String message) {
|
||||||
player.sendMessage(TextComponent.fromLegacyText(ChatColor
|
player.sendMessage(
|
||||||
.translateAlternateColorCodes('&', message)));
|
TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void kickPlayer(String reason) {
|
public void kickPlayer(String reason) {
|
||||||
player.disconnect(TextComponent.fromLegacyText(ChatColor
|
player.disconnect(
|
||||||
.translateAlternateColorCodes('&', reason)));
|
TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', reason)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+3
-3
@@ -18,10 +18,9 @@ package dev.brighten.antivpn.bungee;
|
|||||||
|
|
||||||
import dev.brighten.antivpn.api.APIPlayer;
|
import dev.brighten.antivpn.api.APIPlayer;
|
||||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
public class BungeePlayerExecutor implements PlayerExecutor {
|
public class BungeePlayerExecutor implements PlayerExecutor {
|
||||||
|
|
||||||
@@ -33,7 +32,8 @@ public class BungeePlayerExecutor implements PlayerExecutor {
|
|||||||
|
|
||||||
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
|
@Override
|
||||||
|
|||||||
@@ -24,24 +24,21 @@ import dev.brighten.antivpn.database.local.H2VPN;
|
|||||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||||
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import org.bstats.bungeecord.Metrics;
|
import org.bstats.bungeecord.Metrics;
|
||||||
import org.bstats.charts.SimplePie;
|
import org.bstats.charts.SimplePie;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class BungeePlugin implements LoaderBootstrap {
|
public class BungeePlugin implements LoaderBootstrap {
|
||||||
|
|
||||||
public static BungeePlugin pluginInstance;
|
public static BungeePlugin pluginInstance;
|
||||||
|
|
||||||
@Getter
|
@Getter private File dataFolder;
|
||||||
private File dataFolder;
|
|
||||||
|
|
||||||
@Getter
|
@Getter private final Plugin plugin;
|
||||||
private final Plugin plugin;
|
|
||||||
|
|
||||||
public BungeePlugin(Plugin plugin) {
|
public BungeePlugin(Plugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@@ -59,7 +56,6 @@ public class BungeePlugin implements LoaderBootstrap {
|
|||||||
// Setting up config
|
// Setting up config
|
||||||
ProxyServer.getInstance().getLogger().info("Loading config...");
|
ProxyServer.getInstance().getLogger().info("Loading config...");
|
||||||
|
|
||||||
|
|
||||||
// Loading plugin
|
// Loading plugin
|
||||||
ProxyServer.getInstance().getLogger().info("Starting AntiVPN services...");
|
ProxyServer.getInstance().getLogger().info("Starting AntiVPN services...");
|
||||||
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
|
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
|
||||||
@@ -68,13 +64,20 @@ public class BungeePlugin implements LoaderBootstrap {
|
|||||||
ProxyServer.getInstance().getLogger().info("Starting bStats metrics...");
|
ProxyServer.getInstance().getLogger().info("Starting bStats metrics...");
|
||||||
Metrics metrics = new Metrics(getPlugin(), 12616);
|
Metrics metrics = new Metrics(getPlugin(), 12616);
|
||||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||||
ProxyServer.getInstance().getScheduler().schedule(getPlugin(),
|
ProxyServer.getInstance()
|
||||||
|
.getScheduler()
|
||||||
|
.schedule(
|
||||||
|
getPlugin(),
|
||||||
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
|
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
|
||||||
10, 10, TimeUnit.MINUTES);
|
10,
|
||||||
|
10,
|
||||||
|
TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||||
ProxyServer.getInstance().getPluginManager().registerCommand(getPlugin(), new BungeeCommand(command));
|
ProxyServer.getInstance()
|
||||||
|
.getPluginManager()
|
||||||
|
.registerCommand(getPlugin(), new BungeeCommand(command));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-26
@@ -17,6 +17,8 @@
|
|||||||
package dev.brighten.antivpn.bungee.command;
|
package dev.brighten.antivpn.bungee.command;
|
||||||
|
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
@@ -24,12 +26,10 @@ import net.md_5.bungee.api.chat.TextComponent;
|
|||||||
import net.md_5.bungee.api.plugin.Command;
|
import net.md_5.bungee.api.plugin.Command;
|
||||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
public class BungeeCommand extends Command implements TabExecutor {
|
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) {
|
public BungeeCommand(dev.brighten.antivpn.command.Command command) {
|
||||||
super(command.name(), command.permission(), command.aliases());
|
super(command.name(), command.permission(), command.aliases());
|
||||||
|
|
||||||
@@ -38,10 +38,15 @@ public class BungeeCommand extends Command implements TabExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(CommandSender sender, String[] args) {
|
public void execute(CommandSender sender, String[] args) {
|
||||||
if(!sender.hasPermission("antivpn.command.*")
|
if (!sender.hasPermission("antivpn.command.*") && !sender.hasPermission(command.permission())) {
|
||||||
&& !sender.hasPermission(command.permission())) {
|
sender.sendMessage(
|
||||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&',
|
TextComponent.fromLegacyText(
|
||||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
|
ChatColor.translateAlternateColorCodes(
|
||||||
|
'&',
|
||||||
|
AntiVPN.getInstance()
|
||||||
|
.getMessageHandler()
|
||||||
|
.getString("no-permission")
|
||||||
|
.getMessage())));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,31 +54,39 @@ public class BungeeCommand extends Command implements TabExecutor {
|
|||||||
|
|
||||||
if (children.length > 0 && args.length > 0) {
|
if (children.length > 0 && args.length > 0) {
|
||||||
for (dev.brighten.antivpn.command.Command child : children) {
|
for (dev.brighten.antivpn.command.Command child : children) {
|
||||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
if (child.name().equalsIgnoreCase(args[0])
|
||||||
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
|| Arrays.stream(child.aliases()).anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||||
if (!sender.hasPermission("antivpn.command.*")
|
if (!sender.hasPermission("antivpn.command.*")
|
||||||
&& !sender.hasPermission(child.permission())) {
|
&& !sender.hasPermission(child.permission())) {
|
||||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&',
|
sender.sendMessage(
|
||||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
|
TextComponent.fromLegacyText(
|
||||||
|
ChatColor.translateAlternateColorCodes(
|
||||||
|
'&',
|
||||||
|
AntiVPN.getInstance()
|
||||||
|
.getMessageHandler()
|
||||||
|
.getString("no-permission")
|
||||||
|
.getMessage())));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sender.sendMessage(TextComponent
|
sender.sendMessage(
|
||||||
.fromLegacyText(ChatColor
|
TextComponent.fromLegacyText(
|
||||||
.translateAlternateColorCodes('&',
|
ChatColor.translateAlternateColorCodes(
|
||||||
child.execute(new BungeeCommandExecutor(sender), IntStream
|
'&',
|
||||||
.range(0, args.length - 1)
|
child.execute(
|
||||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new)))));
|
new BungeeCommandExecutor(sender),
|
||||||
|
IntStream.range(0, args.length - 1)
|
||||||
|
.mapToObj(i -> args[i + 1])
|
||||||
|
.toArray(String[]::new)))));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sender.sendMessage(
|
||||||
sender.sendMessage(TextComponent
|
TextComponent.fromLegacyText(
|
||||||
.fromLegacyText(ChatColor
|
ChatColor.translateAlternateColorCodes(
|
||||||
.translateAlternateColorCodes('&',
|
'&', command.execute(new BungeeCommandExecutor(sender), args))));
|
||||||
command.execute(new BungeeCommandExecutor(sender), args))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,11 +95,15 @@ public class BungeeCommand extends Command implements TabExecutor {
|
|||||||
|
|
||||||
if (children.length > 0 && args.length > 0) {
|
if (children.length > 0 && args.length > 0) {
|
||||||
for (dev.brighten.antivpn.command.Command child : children) {
|
for (dev.brighten.antivpn.command.Command child : children) {
|
||||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
if (child.name().equalsIgnoreCase(args[0])
|
||||||
|
|| Arrays.stream(child.aliases())
|
||||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||||
return child.tabComplete(new BungeeCommandExecutor(sender), "alias", IntStream
|
return child.tabComplete(
|
||||||
.range(0, args.length - 1)
|
new BungeeCommandExecutor(sender),
|
||||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
|
"alias",
|
||||||
|
IntStream.range(0, args.length - 1)
|
||||||
|
.mapToObj(i -> args[i + 1])
|
||||||
|
.toArray(String[]::new));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-5
@@ -19,14 +19,13 @@ package dev.brighten.antivpn.bungee.command;
|
|||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.api.APIPlayer;
|
import dev.brighten.antivpn.api.APIPlayer;
|
||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class BungeeCommandExecutor implements CommandExecutor {
|
public class BungeeCommandExecutor implements CommandExecutor {
|
||||||
|
|
||||||
@@ -34,8 +33,9 @@ public class BungeeCommandExecutor implements CommandExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(String message, Object... objects) {
|
public void sendMessage(String message, Object... objects) {
|
||||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor
|
sender.sendMessage(
|
||||||
.translateAlternateColorCodes('&', String.format(message, objects))));
|
TextComponent.fromLegacyText(
|
||||||
|
ChatColor.translateAlternateColorCodes('&', String.format(message, objects))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,7 +47,9 @@ public class BungeeCommandExecutor implements CommandExecutor {
|
|||||||
public Optional<APIPlayer> getPlayer() {
|
public Optional<APIPlayer> getPlayer() {
|
||||||
if (!isPlayer()) return Optional.empty();
|
if (!isPlayer()) return Optional.empty();
|
||||||
|
|
||||||
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((ProxiedPlayer) sender).getUniqueId());
|
return AntiVPN.getInstance()
|
||||||
|
.getPlayerExecutor()
|
||||||
|
.getPlayer(((ProxiedPlayer) sender).getUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+22
-18
@@ -1,27 +1,27 @@
|
|||||||
package dev.brighten.antivpn.bungee;
|
package dev.brighten.antivpn.bungee;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
|
import dev.brighten.antivpn.StandardTest;
|
||||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||||
import dev.brighten.antivpn.api.VPNConfig;
|
import dev.brighten.antivpn.api.VPNConfig;
|
||||||
import dev.brighten.antivpn.api.VPNExecutor;
|
import dev.brighten.antivpn.api.VPNExecutor;
|
||||||
import dev.brighten.antivpn.message.MessageHandler;
|
import dev.brighten.antivpn.message.MessageHandler;
|
||||||
import dev.brighten.antivpn.message.VpnString;
|
import dev.brighten.antivpn.message.VpnString;
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
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.connection.PendingConnection;
|
||||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
public class BungeeListenerTest extends StandardTest {
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
public class BungeeListenerTest {
|
|
||||||
|
|
||||||
private BungeeListener listener;
|
private BungeeListener listener;
|
||||||
private VPNExecutor vpnExecutor;
|
private VPNExecutor vpnExecutor;
|
||||||
@@ -49,10 +49,17 @@ public class BungeeListenerTest {
|
|||||||
when(mockVpnString.getFormattedMessage(any())).thenReturn("Blocked!");
|
when(mockVpnString.getFormattedMessage(any())).thenReturn("Blocked!");
|
||||||
when(messageHandler.getString(anyString())).thenReturn(mockVpnString);
|
when(messageHandler.getString(anyString())).thenReturn(mockVpnString);
|
||||||
|
|
||||||
when(vpnExecutor.checkIp(anyString())).thenReturn(CompletableFuture.completedFuture(
|
when(vpnExecutor.checkIp(anyString()))
|
||||||
VPNResponse.builder().success(true).proxy(false).ip("127.0.0.1")
|
.thenReturn(
|
||||||
.method("N/A").countryName("N/A").city("N/A").build()
|
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
|
// Use reflection to set the private static INSTANCE field
|
||||||
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
|
||||||
@@ -86,7 +93,7 @@ public class BungeeListenerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPreLoginEventBlocked() {
|
public void testPreLoginEventBlocked() throws NoSuchFieldException, IllegalAccessException {
|
||||||
PreLoginEvent event = mock(PreLoginEvent.class);
|
PreLoginEvent event = mock(PreLoginEvent.class);
|
||||||
PendingConnection connection = mock(PendingConnection.class);
|
PendingConnection connection = mock(PendingConnection.class);
|
||||||
|
|
||||||
@@ -97,10 +104,7 @@ public class BungeeListenerTest {
|
|||||||
when(connection.getSocketAddress()).thenReturn(new InetSocketAddress("1.1.1.1", 12345));
|
when(connection.getSocketAddress()).thenReturn(new InetSocketAddress("1.1.1.1", 12345));
|
||||||
|
|
||||||
// Mock proxy response
|
// Mock proxy response
|
||||||
when(vpnExecutor.checkIp("1.1.1.1")).thenReturn(CompletableFuture.completedFuture(
|
mockCache();
|
||||||
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);
|
listener.onListener(event);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ 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/),
|
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).
|
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
|
## [1.10.1.1] 2026-04-29
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.gradleup.shadow'
|
id 'com.gradleup.shadow'
|
||||||
|
id 'java-test-fixtures'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -24,6 +25,7 @@ dependencies {
|
|||||||
testImplementation 'com.h2database:h2:2.4.240'
|
testImplementation 'com.h2database:h2:2.4.240'
|
||||||
testImplementation 'org.mongodb:mongo-java-driver:3.12.14'
|
testImplementation 'org.mongodb:mongo-java-driver:3.12.14'
|
||||||
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
||||||
|
testFixturesImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
|
|||||||
@@ -33,10 +33,6 @@ import dev.brighten.antivpn.utils.MiscUtils;
|
|||||||
import dev.brighten.antivpn.utils.config.Configuration;
|
import dev.brighten.antivpn.utils.config.Configuration;
|
||||||
import dev.brighten.antivpn.utils.config.ConfigurationProvider;
|
import dev.brighten.antivpn.utils.config.ConfigurationProvider;
|
||||||
import dev.brighten.antivpn.utils.config.YamlConfiguration;
|
import dev.brighten.antivpn.utils.config.YamlConfiguration;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -44,16 +40,15 @@ import java.net.URL;
|
|||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter(AccessLevel.PRIVATE)
|
@Setter(AccessLevel.PRIVATE)
|
||||||
@MavenLibrary(groupId = "com.h2database", artifactId = "h2", version = "2.4.240")
|
@MavenLibrary(groupId = "com.h2database", artifactId = "h2", version = "2.4.240")
|
||||||
@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14")
|
@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14")
|
||||||
@MavenLibrary(
|
@MavenLibrary(groupId = "com.mysql", artifactId = "mysql-connector-j", version = "9.3.0")
|
||||||
groupId = "com.mysql",
|
|
||||||
artifactId = "mysql-connector-j",
|
|
||||||
version = "9.3.0"
|
|
||||||
)
|
|
||||||
public class AntiVPN {
|
public class AntiVPN {
|
||||||
|
|
||||||
private static AntiVPN INSTANCE;
|
private static AntiVPN INSTANCE;
|
||||||
@@ -86,10 +81,11 @@ public class AntiVPN {
|
|||||||
}
|
}
|
||||||
MiscUtils.copy(INSTANCE.getResource("config.yml"), configFile);
|
MiscUtils.copy(INSTANCE.getResource("config.yml"), configFile);
|
||||||
}
|
}
|
||||||
INSTANCE.config = ConfigurationProvider.getProvider(YamlConfiguration.class)
|
INSTANCE.config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
|
||||||
.load(configFile);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not load config.yml, plugin disabling...", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not load config.yml, plugin disabling...", e);
|
||||||
executor.disablePlugin();
|
executor.disablePlugin();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -105,14 +101,16 @@ public class AntiVPN {
|
|||||||
switch (INSTANCE.vpnConfig.getDatabaseType().toLowerCase()) {
|
switch (INSTANCE.vpnConfig.getDatabaseType().toLowerCase()) {
|
||||||
case "h2":
|
case "h2":
|
||||||
case "local":
|
case "local":
|
||||||
case "flatfile": {
|
case "flatfile":
|
||||||
|
{
|
||||||
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
||||||
INSTANCE.database = new H2VPN();
|
INSTANCE.database = new H2VPN();
|
||||||
INSTANCE.database.init();
|
INSTANCE.database.init();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "mysql":
|
case "mysql":
|
||||||
case "sql": {
|
case "sql":
|
||||||
|
{
|
||||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||||
INSTANCE.database = new MySqlVPN();
|
INSTANCE.database = new MySqlVPN();
|
||||||
INSTANCE.database.init();
|
INSTANCE.database.init();
|
||||||
@@ -120,19 +118,28 @@ public class AntiVPN {
|
|||||||
}
|
}
|
||||||
case "mongo":
|
case "mongo":
|
||||||
case "mongodb":
|
case "mongodb":
|
||||||
case "mongod": {
|
case "mongod":
|
||||||
|
{
|
||||||
INSTANCE.database = new MongoVPN();
|
INSTANCE.database = new MongoVPN();
|
||||||
INSTANCE.database.init();
|
INSTANCE.database.init();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default:
|
||||||
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " +
|
{
|
||||||
"Options: [MySQL]");
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
"Could not find database type \""
|
||||||
|
+ INSTANCE.vpnConfig.getDatabaseType()
|
||||||
|
+ "\". "
|
||||||
|
+ "Options: [MySQL]");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not initialize database, plugin disabling...", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not initialize database, plugin disabling...", e);
|
||||||
executor.disablePlugin();
|
executor.disablePlugin();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -141,7 +148,10 @@ public class AntiVPN {
|
|||||||
INSTANCE.registerCommands();
|
INSTANCE.registerCommands();
|
||||||
|
|
||||||
// Turning on alerts of players who are already online.
|
// Turning on alerts of players who are already online.
|
||||||
playerExecutor.getOnlinePlayers().forEach(player -> {
|
playerExecutor
|
||||||
|
.getOnlinePlayers()
|
||||||
|
.forEach(
|
||||||
|
player -> {
|
||||||
// We want to make sure they even have permission to see alerts before we make a bunch
|
// We want to make sure they even have permission to see alerts before we make a bunch
|
||||||
// of unnecessary database queries.
|
// of unnecessary database queries.
|
||||||
if (player.hasPermission("antivpn.command.alerts")) {
|
if (player.hasPermission("antivpn.command.alerts")) {
|
||||||
@@ -150,8 +160,14 @@ public class AntiVPN {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
|
AntiVPN.getInstance()
|
||||||
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), AntiVPN.getInstance())
|
.getMessageHandler()
|
||||||
|
.initStrings(
|
||||||
|
vpnString ->
|
||||||
|
new ConfigDefault<>(
|
||||||
|
vpnString.getDefaultMessage(),
|
||||||
|
"messages." + vpnString.getKey(),
|
||||||
|
AntiVPN.getInstance())
|
||||||
.get());
|
.get());
|
||||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();
|
AntiVPN.getInstance().getMessageHandler().reloadStrings();
|
||||||
|
|
||||||
@@ -207,14 +223,16 @@ public class AntiVPN {
|
|||||||
switch (AntiVPN.getInstance().getVpnConfig().getDatabaseType().toLowerCase()) {
|
switch (AntiVPN.getInstance().getVpnConfig().getDatabaseType().toLowerCase()) {
|
||||||
case "h2":
|
case "h2":
|
||||||
case "local":
|
case "local":
|
||||||
case "flatfile": {
|
case "flatfile":
|
||||||
|
{
|
||||||
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
||||||
INSTANCE.database = new H2VPN();
|
INSTANCE.database = new H2VPN();
|
||||||
INSTANCE.database.init();
|
INSTANCE.database.init();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "mysql":
|
case "mysql":
|
||||||
case "sql":{
|
case "sql":
|
||||||
|
{
|
||||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||||
INSTANCE.database = new MySqlVPN();
|
INSTANCE.database = new MySqlVPN();
|
||||||
INSTANCE.database.init();
|
INSTANCE.database.init();
|
||||||
@@ -222,14 +240,21 @@ public class AntiVPN {
|
|||||||
}
|
}
|
||||||
case "mongo":
|
case "mongo":
|
||||||
case "mongodb":
|
case "mongodb":
|
||||||
case "mongod": {
|
case "mongod":
|
||||||
|
{
|
||||||
INSTANCE.database = new MongoVPN();
|
INSTANCE.database = new MongoVPN();
|
||||||
INSTANCE.database.init();
|
INSTANCE.database.init();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default:
|
||||||
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " +
|
{
|
||||||
"Options: [MySQL]");
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
"Could not find database type \""
|
||||||
|
+ INSTANCE.vpnConfig.getDatabaseType()
|
||||||
|
+ "\". "
|
||||||
|
+ "Options: [MySQL]");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,7 +278,8 @@ public class AntiVPN {
|
|||||||
public void reloadConfig() {
|
public void reloadConfig() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
config = ConfigurationProvider.getProvider(YamlConfiguration.class)
|
config =
|
||||||
|
ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||||
.load(new File(pluginFolder.getPath() + File.separator + "config.yml"));
|
.load(new File(pluginFolder.getPath() + File.separator + "config.yml"));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|||||||
@@ -20,26 +20,22 @@ import com.github.benmanes.caffeine.cache.Cache;
|
|||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.message.VpnString;
|
import dev.brighten.antivpn.message.VpnString;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public abstract class APIPlayer {
|
public abstract class APIPlayer {
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final InetAddress ip;
|
private final InetAddress ip;
|
||||||
@Setter
|
@Setter private boolean alertsEnabled;
|
||||||
private boolean alertsEnabled;
|
|
||||||
|
|
||||||
private static final Cache<String, CheckResult> checkResultCache = Caffeine.newBuilder()
|
public static final Cache<String, CheckResult> checkResultCache =
|
||||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).maximumSize(2000).build();
|
||||||
.maximumSize(2000)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public APIPlayer(UUID uuid, String name, InetAddress ip) {
|
public APIPlayer(UUID uuid, String name, InetAddress ip) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
@@ -57,20 +53,29 @@ public abstract class APIPlayer {
|
|||||||
// Updating into database so its synced across servers and saved on logout.
|
// Updating into database so its synced across servers and saved on logout.
|
||||||
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
|
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
|
||||||
|
|
||||||
sendMessage(AntiVPN.getInstance().getMessageHandler()
|
sendMessage(
|
||||||
|
AntiVPN.getInstance()
|
||||||
|
.getMessageHandler()
|
||||||
.getString("command-alerts-toggled")
|
.getString("command-alerts-toggled")
|
||||||
.getFormattedMessage(new VpnString.Var<>("state", alertsEnabled)));
|
.getFormattedMessage(new VpnString.Var<>("state", alertsEnabled)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkAlertsState() {
|
public void checkAlertsState() {
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() ->
|
AntiVPN.getInstance()
|
||||||
AntiVPN.getInstance().getDatabase().alertsState(uuid, state -> {
|
.getExecutor()
|
||||||
|
.getThreadExecutor()
|
||||||
|
.execute(
|
||||||
|
() ->
|
||||||
|
AntiVPN.getInstance()
|
||||||
|
.getDatabase()
|
||||||
|
.alertsState(
|
||||||
|
uuid,
|
||||||
|
state -> {
|
||||||
if (state) {
|
if (state) {
|
||||||
alertsEnabled = true;
|
alertsEnabled = true;
|
||||||
updateAlertsState();
|
updateAlertsState();
|
||||||
}
|
}
|
||||||
})
|
}));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@@ -93,7 +98,11 @@ public abstract class APIPlayer {
|
|||||||
|
|
||||||
if (cachedResult != null) {
|
if (cachedResult != null) {
|
||||||
if (cachedResult.response().getIp().equals(ip.getHostAddress())) {
|
if (cachedResult.response().getIp().equals(ip.getHostAddress())) {
|
||||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Cached result for " + ip.getHostAddress() + " is " + cachedResult.resultType());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
Level.FINE,
|
||||||
|
"Cached result for " + ip.getHostAddress() + " is " + cachedResult.resultType());
|
||||||
if (cachedResult.resultType().isShouldBlock()) {
|
if (cachedResult.resultType().isShouldBlock()) {
|
||||||
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(cachedResult, this);
|
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(cachedResult, this);
|
||||||
}
|
}
|
||||||
@@ -101,27 +110,38 @@ public abstract class APIPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AntiVPN.getInstance().getExecutor().checkIp(ip.getHostAddress())
|
AntiVPN.getInstance()
|
||||||
.thenAccept(result -> {
|
.getExecutor()
|
||||||
|
.checkIp(ip.getHostAddress())
|
||||||
|
.thenAccept(
|
||||||
|
result -> {
|
||||||
if (!result.isSuccess()) {
|
if (!result.isSuccess()) {
|
||||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "The API query was not a success! " +
|
AntiVPN.getInstance()
|
||||||
"You may need to upgrade your license on " +
|
.getExecutor()
|
||||||
"https://funkemunky.cc/shop");
|
.log(
|
||||||
|
Level.WARNING,
|
||||||
|
"The API query was not a success! "
|
||||||
|
+ "You may need to upgrade your license on "
|
||||||
|
+ "https://funkemunky.cc/shop");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If the countryList() size is zero, no need to check.
|
// If the countryList() size is zero, no need to check.
|
||||||
// Running country check first
|
// Running country check first
|
||||||
CheckResult checkResult;
|
CheckResult checkResult;
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().getCountryList().isEmpty()
|
if (!AntiVPN.getInstance().getVpnConfig().getCountryList().isEmpty()
|
||||||
&& !((uuid != null && AntiVPN.getInstance().getExecutor()
|
&& !((uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
|
||||||
.isWhitelisted(uuid))
|
// Or has a name that starts with a certain prefix. This is for Bedrock
|
||||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
// exempting.
|
||||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress() + "/32"))
|
|| AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.isWhitelisted(ip.getHostAddress() + "/32"))
|
||||||
// This bit of code will decide whether or not to kick the player
|
// 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
|
// 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
|
// as they are equal and vise versa. However, if the contains does not match
|
||||||
// the state, it will kick.
|
// the state, it will kick.
|
||||||
&& AntiVPN.getInstance().getVpnConfig().getCountryList()
|
&& AntiVPN.getInstance()
|
||||||
|
.getVpnConfig()
|
||||||
|
.getCountryList()
|
||||||
.contains(result.getCountryCode())
|
.contains(result.getCountryCode())
|
||||||
!= AntiVPN.getInstance().getVpnConfig().getWhitelistCountries()) {
|
!= AntiVPN.getInstance().getVpnConfig().getWhitelistCountries()) {
|
||||||
// Using our built in kicking system if no commands are configured
|
// Using our built in kicking system if no commands are configured
|
||||||
@@ -132,9 +152,15 @@ public abstract class APIPlayer {
|
|||||||
checkResult = new CheckResult(result, ResultType.ALLOWED, false);
|
checkResult = new CheckResult(result, ResultType.ALLOWED, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Result for " + ip.getHostAddress() + " is " + checkResult.resultType());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
Level.FINE,
|
||||||
|
"Result for " + ip.getHostAddress() + " is " + checkResult.resultType());
|
||||||
|
|
||||||
checkResultCache.put(ip.getHostAddress(), new CheckResult(checkResult.response(), checkResult.resultType(), true));
|
checkResultCache.put(
|
||||||
|
ip.getHostAddress(),
|
||||||
|
new CheckResult(checkResult.response(), checkResult.resultType(), true));
|
||||||
if (checkResult.resultType().isShouldBlock()) {
|
if (checkResult.resultType().isShouldBlock()) {
|
||||||
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(checkResult, this);
|
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(checkResult, this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,4 @@ package dev.brighten.antivpn.api;
|
|||||||
|
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
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) {}
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,14 +26,10 @@ public class OfflinePlayer extends APIPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(String message) {
|
public void sendMessage(String message) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void kickPlayer(String reason) {
|
public void kickPlayer(String reason) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(String permission) {
|
public boolean hasPermission(String permission) {
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ public enum ResultType {
|
|||||||
API_FAILURE(false),
|
API_FAILURE(false),
|
||||||
UNKNOWN(false);
|
UNKNOWN(false);
|
||||||
|
|
||||||
@Getter
|
@Getter private final boolean shouldBlock;
|
||||||
private final boolean shouldBlock;
|
|
||||||
|
|
||||||
ResultType(boolean shouldBlock) {
|
ResultType(boolean shouldBlock) {
|
||||||
this.shouldBlock = shouldBlock;
|
this.shouldBlock = shouldBlock;
|
||||||
|
|||||||
@@ -18,97 +18,87 @@ package dev.brighten.antivpn.api;
|
|||||||
|
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.utils.ConfigDefault;
|
import dev.brighten.antivpn.utils.ConfigDefault;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
public class VPNConfig {
|
public class VPNConfig {
|
||||||
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
|
private final ConfigDefault<String>
|
||||||
"license", AntiVPN.getInstance()), kickStringDefault =
|
licenseDefault = new ConfigDefault<>("", "license", AntiVPN.getInstance()),
|
||||||
new ConfigDefault<>("Proxies are not allowed on our server",
|
kickStringDefault =
|
||||||
"kickMessage", AntiVPN.getInstance()),
|
new ConfigDefault<>(
|
||||||
defaultDatabaseType = new ConfigDefault<>("H2",
|
"Proxies are not allowed on our server", "kickMessage", AntiVPN.getInstance()),
|
||||||
"database.type", AntiVPN.getInstance()),
|
defaultDatabaseType = new ConfigDefault<>("H2", "database.type", AntiVPN.getInstance()),
|
||||||
defaultDatabaseName = new ConfigDefault<>("kaurivpn",
|
defaultDatabaseName =
|
||||||
"database.database", AntiVPN.getInstance()),
|
new ConfigDefault<>("kaurivpn", "database.database", AntiVPN.getInstance()),
|
||||||
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", AntiVPN.getInstance()),
|
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", AntiVPN.getInstance()),
|
||||||
defaultUsername = new ConfigDefault<>("root",
|
defaultUsername = new ConfigDefault<>("root", "database.username", AntiVPN.getInstance()),
|
||||||
"database.username", AntiVPN.getInstance()),
|
defaultPassword = new ConfigDefault<>("password", "database.password", AntiVPN.getInstance()),
|
||||||
defaultPassword = new ConfigDefault<>("password",
|
defaultCountryKickReason =
|
||||||
"database.password", AntiVPN.getInstance()),
|
new ConfigDefault<>(
|
||||||
defaultCountryKickReason = new ConfigDefault<>(
|
|
||||||
"&cSorry, but our server does not allow connections from\n&f%country%",
|
"&cSorry, but our server does not allow connections from\n&f%country%",
|
||||||
"countries.vanillaKickReason", AntiVPN.getInstance()),
|
"countries.vanillaKickReason", AntiVPN.getInstance()),
|
||||||
defaultIp = new ConfigDefault<>("localhost", "database.ip", AntiVPN.getInstance()),
|
defaultIp = new ConfigDefault<>("localhost", "database.ip", AntiVPN.getInstance()),
|
||||||
defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" +
|
defaultAlertMsg =
|
||||||
" &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message",
|
new ConfigDefault<>(
|
||||||
AntiVPN.getInstance());
|
"&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy"
|
||||||
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
|
+ " &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)",
|
||||||
"cachedResults", AntiVPN.getInstance()),
|
"alerts.message", AntiVPN.getInstance());
|
||||||
defaultUseCredentials = new ConfigDefault<>(true,
|
private final ConfigDefault<Boolean>
|
||||||
"database.useCredentials", AntiVPN.getInstance()),
|
cacheResultsDefault = new ConfigDefault<>(true, "cachedResults", AntiVPN.getInstance()),
|
||||||
defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled",
|
defaultUseCredentials =
|
||||||
AntiVPN.getInstance()), defaultCommandsEnable = new ConfigDefault<>(false,
|
new ConfigDefault<>(true, "database.useCredentials", AntiVPN.getInstance()),
|
||||||
"commands.enabled", AntiVPN.getInstance()), defaultKickPlayers
|
defaultDatabaseEnabled =
|
||||||
= new ConfigDefault<>(true, "kickPlayers", AntiVPN.getInstance()),
|
new ConfigDefault<>(false, "database.enabled", AntiVPN.getInstance()),
|
||||||
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled",
|
defaultCommandsEnable = new ConfigDefault<>(false, "commands.enabled", AntiVPN.getInstance()),
|
||||||
AntiVPN.getInstance()),
|
defaultKickPlayers = new ConfigDefault<>(true, "kickPlayers", AntiVPN.getInstance()),
|
||||||
defaultWhitelistCountries = new ConfigDefault<>(true, "countries.whitelist",
|
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled", AntiVPN.getInstance()),
|
||||||
AntiVPN.getInstance()),
|
defaultWhitelistCountries =
|
||||||
|
new ConfigDefault<>(true, "countries.whitelist", AntiVPN.getInstance()),
|
||||||
defaultMetrics = new ConfigDefault<>(true, "bstats", AntiVPN.getInstance());
|
defaultMetrics = new ConfigDefault<>(true, "bstats", AntiVPN.getInstance());
|
||||||
private final ConfigDefault<Integer>
|
private final ConfigDefault<Integer> defaultPort =
|
||||||
defaultPort = new ConfigDefault<>(-1, "database.port", AntiVPN.getInstance());
|
new ConfigDefault<>(-1, "database.port", AntiVPN.getInstance());
|
||||||
private final ConfigDefault<List<String>> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(),
|
private final ConfigDefault<List<String>>
|
||||||
"prefixWhitelists", AntiVPN.getInstance()), defaultCommands = new ConfigDefault<>(
|
prefixWhitelistsDefault =
|
||||||
Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute",
|
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()),
|
AntiVPN.getInstance()),
|
||||||
defCountryKickCommands = new ConfigDefault<>(Collections.emptyList(),
|
defCountryKickCommands =
|
||||||
"countries.commands", AntiVPN.getInstance()),
|
new ConfigDefault<>(Collections.emptyList(), "countries.commands", AntiVPN.getInstance()),
|
||||||
defCountrylist = new ConfigDefault<>(new ArrayList<>(), "countries.list",
|
defCountrylist =
|
||||||
AntiVPN.getInstance());
|
new ConfigDefault<>(new ArrayList<>(), "countries.list", AntiVPN.getInstance());
|
||||||
|
|
||||||
@Getter
|
@Getter private String license;
|
||||||
private String license;
|
@Getter private String kickMessage;
|
||||||
@Getter
|
@Getter private String databaseType;
|
||||||
private String kickMessage;
|
@Getter private String databaseName;
|
||||||
@Getter
|
|
||||||
private String databaseType;
|
|
||||||
@Getter
|
|
||||||
private String databaseName;
|
|
||||||
private String mongoURL;
|
private String mongoURL;
|
||||||
@Getter
|
@Getter private String username;
|
||||||
private String username;
|
@Getter private String password;
|
||||||
@Getter
|
@Getter private String ip;
|
||||||
private String password;
|
@Getter private String alertMsg;
|
||||||
@Getter
|
@Getter private String countryVanillaKickReason;
|
||||||
private String ip;
|
@Getter private List<String> prefixWhitelists;
|
||||||
@Getter
|
|
||||||
private String alertMsg;
|
|
||||||
@Getter
|
|
||||||
private String countryVanillaKickReason;
|
|
||||||
@Getter
|
|
||||||
private List<String> prefixWhitelists;
|
|
||||||
private List<String> commands;
|
private List<String> commands;
|
||||||
@Getter
|
@Getter private List<String> countryList;
|
||||||
private List<String> countryList;
|
|
||||||
private List<String> countryKickCommands;
|
private List<String> countryKickCommands;
|
||||||
private int port;
|
private int port;
|
||||||
private boolean cacheResults;
|
private boolean cacheResults;
|
||||||
@Getter
|
@Getter private boolean databaseEnabled;
|
||||||
private boolean databaseEnabled;
|
|
||||||
private boolean useCredentials;
|
private boolean useCredentials;
|
||||||
@Getter
|
@Getter private boolean commandsEnabled;
|
||||||
private boolean commandsEnabled;
|
@Getter private boolean kickPlayers;
|
||||||
@Getter
|
|
||||||
private boolean kickPlayers;
|
|
||||||
private boolean alertToStaff;
|
private boolean alertToStaff;
|
||||||
private boolean metrics;
|
private boolean metrics;
|
||||||
private boolean whitelistCountries;
|
private boolean whitelistCountries;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, results will be cached to reduce queries to <a href="https://funkemunky.cc">...</a>
|
* If true, results will be cached to reduce queries to <a href="https://funkemunky.cc">...</a>
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public boolean cachedResults() {
|
public boolean cachedResults() {
|
||||||
@@ -117,6 +107,7 @@ public class VPNConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, staff will be alerted on proxy detection.
|
* If true, staff will be alerted on proxy detection.
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public boolean isAlertToSTaff() {
|
public boolean isAlertToSTaff() {
|
||||||
@@ -125,6 +116,7 @@ public class VPNConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Commands to run on proxy detection.
|
* Commands to run on proxy detection.
|
||||||
|
*
|
||||||
* @return List
|
* @return List
|
||||||
*/
|
*/
|
||||||
public List<String> commands() {
|
public List<String> commands() {
|
||||||
@@ -133,6 +125,7 @@ public class VPNConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the database we want to connect to requires credentials.
|
* Whether or not the database we want to connect to requires credentials.
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public boolean useDatabaseCreds() {
|
public boolean useDatabaseCreds() {
|
||||||
@@ -141,6 +134,7 @@ public class VPNConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Only for Mongo only. URL used for connecting to database. Overrides other fields
|
* Only for Mongo only. URL used for connecting to database. Overrides other fields
|
||||||
|
*
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
public String mongoDatabaseURL() {
|
public String mongoDatabaseURL() {
|
||||||
@@ -148,7 +142,9 @@ public class VPNConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist them.
|
* If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist
|
||||||
|
* them.
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public boolean getWhitelistCountries() {
|
public boolean getWhitelistCountries() {
|
||||||
@@ -157,6 +153,7 @@ public class VPNConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns our configured commands to run on player country detection.
|
* Returns our configured commands to run on player country detection.
|
||||||
|
*
|
||||||
* @return List
|
* @return List
|
||||||
*/
|
*/
|
||||||
public List<String> countryKickCommands() {
|
public List<String> countryKickCommands() {
|
||||||
@@ -166,6 +163,7 @@ public class VPNConfig {
|
|||||||
/**
|
/**
|
||||||
* Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port
|
* Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port
|
||||||
* based on {@link VPNConfig#getDatabaseType()} lowerCase().
|
* based on {@link VPNConfig#getDatabaseType()} lowerCase().
|
||||||
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
@@ -184,18 +182,16 @@ public class VPNConfig {
|
|||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, <a href="https://bstats.org">...</a> metrics will be collected to improve KauriVPN.
|
* If true, <a href="https://bstats.org">...</a> metrics will be collected to improve KauriVPN.
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public boolean metrics() {
|
public boolean metrics() {
|
||||||
return metrics;
|
return metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Grabs all information from the config.yml */
|
||||||
* Grabs all information from the config.yml
|
|
||||||
*/
|
|
||||||
public void update() {
|
public void update() {
|
||||||
license = licenseDefault.get();
|
license = licenseDefault.get();
|
||||||
kickMessage = kickStringDefault.get();
|
kickMessage = kickStringDefault.get();
|
||||||
@@ -221,5 +217,4 @@ public class VPNConfig {
|
|||||||
countryKickCommands = defCountryKickCommands.get();
|
countryKickCommands = defCountryKickCommands.get();
|
||||||
countryVanillaKickReason = defaultCountryKickReason.get();
|
countryVanillaKickReason = defaultCountryKickReason.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -25,13 +25,12 @@ import dev.brighten.antivpn.utils.Tuple;
|
|||||||
import dev.brighten.antivpn.utils.json.JSONException;
|
import dev.brighten.antivpn.utils.json.JSONException;
|
||||||
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public abstract class VPNExecutor {
|
public abstract class VPNExecutor {
|
||||||
@@ -42,7 +41,6 @@ public abstract class VPNExecutor {
|
|||||||
private final Queue<APIPlayer> playersToRecheck = new LinkedBlockingQueue<>();
|
private final Queue<APIPlayer> playersToRecheck = new LinkedBlockingQueue<>();
|
||||||
private ScheduledFuture<?> kickTask = null;
|
private ScheduledFuture<?> kickTask = null;
|
||||||
|
|
||||||
|
|
||||||
public abstract void registerListeners();
|
public abstract void registerListeners();
|
||||||
|
|
||||||
public abstract void log(Level level, String log, Object... objects);
|
public abstract void log(Level level, String log, Object... objects);
|
||||||
@@ -58,14 +56,17 @@ public abstract class VPNExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startKickChecks() {
|
public void startKickChecks() {
|
||||||
kickTask = threadExecutor.scheduleAtFixedRate(() -> {
|
kickTask =
|
||||||
|
threadExecutor.scheduleAtFixedRate(
|
||||||
|
() -> {
|
||||||
synchronized (toKick) {
|
synchronized (toKick) {
|
||||||
if (toKick.isEmpty()) return;
|
if (toKick.isEmpty()) return;
|
||||||
|
|
||||||
Tuple<CheckResult, UUID> toCheck;
|
Tuple<CheckResult, UUID> toCheck;
|
||||||
|
|
||||||
while ((toCheck = toKick.poll()) != null) {
|
while ((toCheck = toKick.poll()) != null) {
|
||||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second());
|
Optional<APIPlayer> player =
|
||||||
|
AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second());
|
||||||
|
|
||||||
if (player.isEmpty()) {
|
if (player.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -74,7 +75,10 @@ public abstract class VPNExecutor {
|
|||||||
handleKickingOfPlayer(toCheck.first(), player.get());
|
handleKickingOfPlayer(toCheck.first(), player.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 8, 2, TimeUnit.SECONDS);
|
},
|
||||||
|
8,
|
||||||
|
2,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleKickingOfPlayer(CheckResult result, APIPlayer player) {
|
public void handleKickingOfPlayer(CheckResult result, APIPlayer player) {
|
||||||
@@ -84,27 +88,42 @@ public abstract class VPNExecutor {
|
|||||||
startKickChecks();
|
startKickChecks();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AntiVPN.getInstance().getVpnConfig().isAlertToSTaff()) AntiVPN.getInstance().getPlayerExecutor()
|
if (AntiVPN.getInstance().getVpnConfig().isAlertToSTaff())
|
||||||
.getOnlinePlayers()
|
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||||
.stream()
|
|
||||||
.filter(APIPlayer::isAlertsEnabled)
|
.filter(APIPlayer::isAlertsEnabled)
|
||||||
.forEach(pl ->
|
.forEach(
|
||||||
pl.sendMessage(StringUtil.translateAlternateColorCodes('&',
|
pl ->
|
||||||
StringUtil.varReplace(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig()
|
pl.sendMessage(
|
||||||
.getAlertMsg(), player, result.response()))));
|
StringUtil.translateAlternateColorCodes(
|
||||||
|
'&',
|
||||||
|
StringUtil.varReplace(
|
||||||
|
dev.brighten.antivpn.AntiVPN.getInstance()
|
||||||
|
.getVpnConfig()
|
||||||
|
.getAlertMsg(),
|
||||||
|
player,
|
||||||
|
result.response()))));
|
||||||
|
|
||||||
if (AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
if (AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||||
switch (result.resultType()) {
|
switch (result.resultType()) {
|
||||||
case DENIED_PROXY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
case DENIED_PROXY ->
|
||||||
.getKickMessage(), player, result.response()));
|
player.kickPlayer(
|
||||||
case DENIED_COUNTRY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
StringUtil.varReplace(
|
||||||
.getCountryVanillaKickReason(), player, result.response()));
|
AntiVPN.getInstance().getVpnConfig().getKickMessage(),
|
||||||
|
player,
|
||||||
|
result.response()));
|
||||||
|
case DENIED_COUNTRY ->
|
||||||
|
player.kickPlayer(
|
||||||
|
StringUtil.varReplace(
|
||||||
|
AntiVPN.getInstance().getVpnConfig().getCountryVanillaKickReason(),
|
||||||
|
player,
|
||||||
|
result.response()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isCommandsEnabled()) return;
|
if (!AntiVPN.getInstance().getVpnConfig().isCommandsEnabled()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Runnable runCommands = () -> {
|
Runnable runCommands =
|
||||||
|
() -> {
|
||||||
switch (result.resultType()) {
|
switch (result.resultType()) {
|
||||||
case DENIED_PROXY -> {
|
case DENIED_PROXY -> {
|
||||||
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
||||||
@@ -119,15 +138,22 @@ public abstract class VPNExecutor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fixes the commands running too fast and causing messaging errors by any downstream plugins like LiteBans
|
// Fixes the commands running too fast and causing messaging errors by any downstream plugins
|
||||||
var scheduleResult = threadExecutor.schedule(runCommands, 1, TimeUnit.SECONDS);
|
// like LiteBans
|
||||||
|
var scheduleResult = threadExecutor.schedule(runCommands, 200, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
if (scheduleResult.isCancelled()) {
|
if (scheduleResult.isCancelled()) {
|
||||||
runCommands.run();
|
runCommands.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var toAdd = new Tuple<>(result, player.getUuid());
|
||||||
// Ensuring players are actually kicked as they are supposed to be.
|
// Ensuring players are actually kicked as they are supposed to be.
|
||||||
toKick.add(new Tuple<>(result, player.getUuid()));
|
threadExecutor.schedule(
|
||||||
|
() -> {
|
||||||
|
toKick.add(toAdd);
|
||||||
|
},
|
||||||
|
500,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWhitelisted(UUID uuid) {
|
public boolean isWhitelisted(UUID uuid) {
|
||||||
@@ -148,10 +174,8 @@ public abstract class VPNExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
private final Cache<String, VPNResponse> cachedResponses =
|
||||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
Caffeine.newBuilder().expireAfterWrite(20, TimeUnit.MINUTES).maximumSize(4000).build();
|
||||||
.maximumSize(4000)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public CompletableFuture<VPNResponse> checkIp(String ip) {
|
public CompletableFuture<VPNResponse> checkIp(String ip) {
|
||||||
VPNResponse cached = cachedResponses.getIfPresent(ip);
|
VPNResponse cached = cachedResponses.getIfPresent(ip);
|
||||||
@@ -160,16 +184,18 @@ public abstract class VPNExecutor {
|
|||||||
return CompletableFuture.completedFuture(cached);
|
return CompletableFuture.completedFuture(cached);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
return CompletableFuture.supplyAsync(
|
||||||
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
() -> {
|
||||||
|
Optional<VPNResponse> cachedRes =
|
||||||
|
AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
||||||
|
|
||||||
if (cachedRes.isPresent()) {
|
if (cachedRes.isPresent()) {
|
||||||
return cachedRes.get();
|
return cachedRes.get();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
try {
|
try {
|
||||||
VPNResponse response = FunkemunkyAPI
|
VPNResponse response =
|
||||||
.getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
FunkemunkyAPI.getVPNResponse(
|
||||||
|
ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
||||||
|
|
||||||
if (response.isSuccess()) {
|
if (response.isSuccess()) {
|
||||||
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
||||||
@@ -183,7 +209,8 @@ public abstract class VPNExecutor {
|
|||||||
return VPNResponse.FAILED_RESPONSE;
|
return VPNResponse.FAILED_RESPONSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, threadExecutor);
|
},
|
||||||
|
threadExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void disablePlugin();
|
public abstract void disablePlugin();
|
||||||
|
|||||||
@@ -17,14 +17,15 @@
|
|||||||
package dev.brighten.antivpn.command;
|
package dev.brighten.antivpn.command;
|
||||||
|
|
||||||
import dev.brighten.antivpn.api.APIPlayer;
|
import dev.brighten.antivpn.api.APIPlayer;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface CommandExecutor {
|
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,7 +21,6 @@ import dev.brighten.antivpn.api.APIPlayer;
|
|||||||
import dev.brighten.antivpn.command.Command;
|
import dev.brighten.antivpn.command.Command;
|
||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
import dev.brighten.antivpn.message.VpnString;
|
import dev.brighten.antivpn.message.VpnString;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -65,15 +64,20 @@ public class AlertsCommand extends Command {
|
|||||||
@Override
|
@Override
|
||||||
public String execute(CommandExecutor executor, String[] args) {
|
public String execute(CommandExecutor executor, String[] args) {
|
||||||
Optional<APIPlayer> pgetter = executor.getPlayer();
|
Optional<APIPlayer> pgetter = executor.getPlayer();
|
||||||
if(!pgetter.isPresent()) return AntiVPN.getInstance().getMessageHandler()
|
if (!pgetter.isPresent())
|
||||||
.getString("command-misc-playerRequired").getMessage();
|
return AntiVPN.getInstance()
|
||||||
|
.getMessageHandler()
|
||||||
|
.getString("command-misc-playerRequired")
|
||||||
|
.getMessage();
|
||||||
|
|
||||||
APIPlayer player = pgetter.get();
|
APIPlayer player = pgetter.get();
|
||||||
|
|
||||||
player.setAlertsEnabled(!player.isAlertsEnabled());
|
player.setAlertsEnabled(!player.isAlertsEnabled());
|
||||||
player.updateAlertsState();
|
player.updateAlertsState();
|
||||||
|
|
||||||
return AntiVPN.getInstance().getMessageHandler().getString("command-alerts-toggled")
|
return AntiVPN.getInstance()
|
||||||
|
.getMessageHandler()
|
||||||
|
.getString("command-alerts-toggled")
|
||||||
.getFormattedMessage(new VpnString.Var<>("state", player.isAlertsEnabled()));
|
.getFormattedMessage(new VpnString.Var<>("state", player.isAlertsEnabled()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+62
-28
@@ -22,7 +22,6 @@ import dev.brighten.antivpn.command.Command;
|
|||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import dev.brighten.antivpn.utils.MiscUtils;
|
import dev.brighten.antivpn.utils.MiscUtils;
|
||||||
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -68,7 +67,8 @@ public class AllowlistCommand extends Command {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(CommandExecutor executor, String[] args) {
|
public String execute(CommandExecutor executor, String[] args) {
|
||||||
if(args.length == 0 || Arrays.stream(secondArgs).noneMatch(arg -> arg.equalsIgnoreCase(args[0]))) {
|
if (args.length == 0
|
||||||
|
|| Arrays.stream(secondArgs).noneMatch(arg -> arg.equalsIgnoreCase(args[0]))) {
|
||||||
return "&cUsage: /antivpn allowlist " + usage();
|
return "&cUsage: /antivpn allowlist " + usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,10 +86,12 @@ public class AllowlistCommand extends Command {
|
|||||||
|
|
||||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||||
|
|
||||||
List<UUID> uuids = databaseEnabled
|
List<UUID> uuids =
|
||||||
|
databaseEnabled
|
||||||
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
||||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
||||||
List<CIDRUtils> ips = databaseEnabled
|
List<CIDRUtils> ips =
|
||||||
|
databaseEnabled
|
||||||
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
||||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
||||||
|
|
||||||
@@ -119,7 +121,8 @@ public class AllowlistCommand extends Command {
|
|||||||
page = candidate;
|
page = candidate;
|
||||||
queryEnd = args.length - 1;
|
queryEnd = args.length - 1;
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
String search = String.join(" ", Arrays.copyOfRange(args, 1, queryEnd)).toLowerCase();
|
String search = String.join(" ", Arrays.copyOfRange(args, 1, queryEnd)).toLowerCase();
|
||||||
// Strip color code characters to prevent formatting injection in output
|
// Strip color code characters to prevent formatting injection in output
|
||||||
@@ -131,10 +134,12 @@ public class AllowlistCommand extends Command {
|
|||||||
|
|
||||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||||
|
|
||||||
List<UUID> uuids = databaseEnabled
|
List<UUID> uuids =
|
||||||
|
databaseEnabled
|
||||||
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
||||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
||||||
List<CIDRUtils> ips = databaseEnabled
|
List<CIDRUtils> ips =
|
||||||
|
databaseEnabled
|
||||||
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
||||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
||||||
|
|
||||||
@@ -155,13 +160,14 @@ public class AllowlistCommand extends Command {
|
|||||||
return buildPage(entries, page, safeSearch, "search " + safeSearch);
|
return buildPage(entries, page, safeSearch, "search " + safeSearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(args.length == 1)
|
if (args.length == 1) return "&cYou have to provide a player to allow or deny exemption.";
|
||||||
return "&cYou have to provide a player to allow or deny exemption.";
|
|
||||||
|
|
||||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||||
|
|
||||||
if(!databaseEnabled) executor.sendMessage("&cThe database is currently not setup, " +
|
if (!databaseEnabled)
|
||||||
"so any changes here will disappear after a restart.");
|
executor.sendMessage(
|
||||||
|
"&cThe database is currently not setup, "
|
||||||
|
+ "so any changes here will disappear after a restart.");
|
||||||
|
|
||||||
CIDRUtils cidrUtils;
|
CIDRUtils cidrUtils;
|
||||||
|
|
||||||
@@ -180,11 +186,13 @@ public class AllowlistCommand extends Command {
|
|||||||
}
|
}
|
||||||
case "remove", "delete" -> {
|
case "remove", "delete" -> {
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
||||||
yield String.format("&cRemoved &%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
yield String.format(
|
||||||
|
"&cRemoved &%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
||||||
}
|
}
|
||||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||||
};
|
};
|
||||||
} else return switch (args[0].toLowerCase()) {
|
} else
|
||||||
|
return switch (args[0].toLowerCase()) {
|
||||||
case "add", "insert" -> {
|
case "add", "insert" -> {
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(cidrUtils);
|
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(cidrUtils);
|
||||||
AntiVPN.getInstance().getDatabase().addWhitelist(cidrUtils);
|
AntiVPN.getInstance().getDatabase().addWhitelist(cidrUtils);
|
||||||
@@ -193,7 +201,8 @@ public class AllowlistCommand extends Command {
|
|||||||
case "remove", "delete" -> {
|
case "remove", "delete" -> {
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
||||||
AntiVPN.getInstance().getDatabase().removeWhitelist(cidrUtils);
|
AntiVPN.getInstance().getDatabase().removeWhitelist(cidrUtils);
|
||||||
yield String.format("&cRemoved &6%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
yield String.format(
|
||||||
|
"&cRemoved &6%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
||||||
}
|
}
|
||||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||||
};
|
};
|
||||||
@@ -203,19 +212,28 @@ public class AllowlistCommand extends Command {
|
|||||||
try {
|
try {
|
||||||
return switch (args[0].toLowerCase()) {
|
return switch (args[0].toLowerCase()) {
|
||||||
case "add", "insert" -> {
|
case "add", "insert" -> {
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(new CIDRUtils(args[1] + "/32"));
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getWhitelistedIps()
|
||||||
|
.add(new CIDRUtils(args[1] + "/32"));
|
||||||
AntiVPN.getInstance().getDatabase().addWhitelist(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");
|
yield String.format("&aAdded &6%s &ato the exemption allowlist.", args[1] + "/32");
|
||||||
}
|
}
|
||||||
case "remove", "delete" -> {
|
case "remove", "delete" -> {
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(new CIDRUtils(args[1] + "/32"));
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getWhitelistedIps()
|
||||||
|
.remove(new CIDRUtils(args[1] + "/32"));
|
||||||
AntiVPN.getInstance().getDatabase().removeWhitelist(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");
|
yield String.format(
|
||||||
|
"&cRemoved &6%s &cfrom the exemption allowlist.", args[1] + "/32");
|
||||||
}
|
}
|
||||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||||
};
|
};
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Invalid IP format for allowlist command", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Invalid IP format for allowlist command", e);
|
||||||
return "&cInvalid IP format for allowlist command";
|
return "&cInvalid IP format for allowlist command";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -227,12 +245,15 @@ public class AllowlistCommand extends Command {
|
|||||||
}
|
}
|
||||||
case "remove", "delete" -> {
|
case "remove", "delete" -> {
|
||||||
AntiVPN.getInstance().getDatabase().removeWhitelist(new CIDRUtils(args[1] + "/32"));
|
AntiVPN.getInstance().getDatabase().removeWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||||
yield String.format("&cRemoved &6%s &c from the exemption allowlist.", 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";
|
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||||
};
|
};
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Invalid IP format for allowlist command", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Invalid IP format for allowlist command", e);
|
||||||
return "&cInvalid IP format for allowlist command";
|
return "&cInvalid IP format for allowlist command";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +268,9 @@ public class AllowlistCommand extends Command {
|
|||||||
} else {
|
} else {
|
||||||
uuid = MiscUtils.lookupUUID(args[1]);
|
uuid = MiscUtils.lookupUUID(args[1]);
|
||||||
if (uuid == null) {
|
if (uuid == null) {
|
||||||
return "&cCould not find a UUID for \"" + args[1] + "\". They might not have provided a valid username.";
|
return "&cCould not find a UUID for \""
|
||||||
|
+ args[1]
|
||||||
|
+ "\". They might not have provided a valid username.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,7 +283,8 @@ public class AllowlistCommand extends Command {
|
|||||||
}
|
}
|
||||||
case "remove", "delete" -> {
|
case "remove", "delete" -> {
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||||
yield String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
yield String.format(
|
||||||
|
"&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||||
}
|
}
|
||||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||||
};
|
};
|
||||||
@@ -272,7 +296,8 @@ public class AllowlistCommand extends Command {
|
|||||||
}
|
}
|
||||||
case "remove", "delete" -> {
|
case "remove", "delete" -> {
|
||||||
AntiVPN.getInstance().getDatabase().removeWhitelist(uuid);
|
AntiVPN.getInstance().getDatabase().removeWhitelist(uuid);
|
||||||
yield String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
yield String.format(
|
||||||
|
"&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||||
}
|
}
|
||||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||||
};
|
};
|
||||||
@@ -283,7 +308,8 @@ public class AllowlistCommand extends Command {
|
|||||||
@Override
|
@Override
|
||||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||||
return switch (args.length) {
|
return switch (args.length) {
|
||||||
case 1 -> Arrays.stream(secondArgs)
|
case 1 ->
|
||||||
|
Arrays.stream(secondArgs)
|
||||||
.filter(narg -> narg.toLowerCase().startsWith(args[0].toLowerCase()))
|
.filter(narg -> narg.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
case 2 -> {
|
case 2 -> {
|
||||||
@@ -299,19 +325,26 @@ public class AllowlistCommand extends Command {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildPage(List<String> entries, int page, String safeSearch, String subcommandPrefix) {
|
private String buildPage(
|
||||||
|
List<String> entries, int page, String safeSearch, String subcommandPrefix) {
|
||||||
int pageSize = 10;
|
int pageSize = 10;
|
||||||
int totalPages = Math.max(1, (entries.size() + pageSize - 1) / pageSize);
|
int totalPages = Math.max(1, (entries.size() + pageSize - 1) / pageSize);
|
||||||
if (page > totalPages) page = totalPages;
|
if (page > totalPages) page = totalPages;
|
||||||
|
|
||||||
List<String> messages = new ArrayList<>();
|
List<String> messages = new ArrayList<>();
|
||||||
messages.add("&8&m-----------------------------------------------------");
|
messages.add("&8&m-----------------------------------------------------");
|
||||||
messages.add("&6&lAllowlist Entries &8(&7Page &f" + page + "&7/&f" + totalPages + "&8)"
|
messages.add(
|
||||||
|
"&6&lAllowlist Entries &8(&7Page &f"
|
||||||
|
+ page
|
||||||
|
+ "&7/&f"
|
||||||
|
+ totalPages
|
||||||
|
+ "&8)"
|
||||||
+ (safeSearch != null ? " &7(search: &f" + safeSearch + "&7)" : ""));
|
+ (safeSearch != null ? " &7(search: &f" + safeSearch + "&7)" : ""));
|
||||||
messages.add("");
|
messages.add("");
|
||||||
|
|
||||||
if (entries.isEmpty()) {
|
if (entries.isEmpty()) {
|
||||||
messages.add(safeSearch != null
|
messages.add(
|
||||||
|
safeSearch != null
|
||||||
? "&cNo allowlist entries matching &f\"" + safeSearch + "&c\" were found."
|
? "&cNo allowlist entries matching &f\"" + safeSearch + "&c\" were found."
|
||||||
: "&cThe allowlist is empty.");
|
: "&cThe allowlist is empty.");
|
||||||
} else {
|
} else {
|
||||||
@@ -323,7 +356,8 @@ public class AllowlistCommand extends Command {
|
|||||||
if (totalPages > 1) {
|
if (totalPages > 1) {
|
||||||
messages.add("");
|
messages.add("");
|
||||||
if (page > 1) {
|
if (page > 1) {
|
||||||
messages.add("&7Previous page: &f/antivpn allowlist " + subcommandPrefix + " " + (page - 1));
|
messages.add(
|
||||||
|
"&7Previous page: &f/antivpn allowlist " + subcommandPrefix + " " + (page - 1));
|
||||||
}
|
}
|
||||||
if (page < totalPages) {
|
if (page < totalPages) {
|
||||||
messages.add("&7Next page: &f/antivpn allowlist " + subcommandPrefix + " " + (page + 1));
|
messages.add("&7Next page: &f/antivpn allowlist " + subcommandPrefix + " " + (page + 1));
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import dev.brighten.antivpn.AntiVPN;
|
|||||||
import dev.brighten.antivpn.command.Command;
|
import dev.brighten.antivpn.command.Command;
|
||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
import dev.brighten.antivpn.utils.StringUtil;
|
import dev.brighten.antivpn.utils.StringUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -60,8 +59,14 @@ public class AntiVPNCommand extends Command {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Command[] children() {
|
public Command[] children() {
|
||||||
return new Command[] {new LookupCommand(), new AllowlistCommand(), new AlertsCommand(),
|
return new Command[] {
|
||||||
new ClearCacheCommand(), new PlanCommand(), new ReloadCommand()};
|
new LookupCommand(),
|
||||||
|
new AllowlistCommand(),
|
||||||
|
new AlertsCommand(),
|
||||||
|
new ClearCacheCommand(),
|
||||||
|
new PlanCommand(),
|
||||||
|
new ReloadCommand()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -72,14 +77,30 @@ public class AntiVPNCommand extends Command {
|
|||||||
messages.add("&6&lAntiVPN Help Page");
|
messages.add("&6&lAntiVPN Help Page");
|
||||||
messages.add("");
|
messages.add("");
|
||||||
for (Command cmd : AntiVPN.getInstance().getCommands()) {
|
for (Command cmd : AntiVPN.getInstance().getCommands()) {
|
||||||
messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + cmd.parent()
|
messages.add(
|
||||||
+ (cmd.parent().length() > 0 ? " " : "") + "&f" + cmd.name() + " &7"
|
String.format(
|
||||||
+ cmd.usage(), cmd.description()));
|
"&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()) {
|
for (Command child : children()) {
|
||||||
messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + child.parent()
|
messages.add(
|
||||||
+ (child.parent().length() > 0 ? " " : "") + "&f" + child.name() + " &7"
|
String.format(
|
||||||
+ child.usage(), child.description()));
|
"&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"));
|
messages.add(StringUtil.line("&8"));
|
||||||
|
|||||||
+4
-3
@@ -17,10 +17,8 @@
|
|||||||
package dev.brighten.antivpn.command.impl;
|
package dev.brighten.antivpn.command.impl;
|
||||||
|
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.api.VPNExecutor;
|
|
||||||
import dev.brighten.antivpn.command.Command;
|
import dev.brighten.antivpn.command.Command;
|
||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -62,7 +60,10 @@ public class ClearCacheCommand extends Command {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(CommandExecutor executor, String[] args) {
|
public String execute(CommandExecutor executor, String[] args) {
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> AntiVPN.getInstance().getDatabase().clearResponses());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getThreadExecutor()
|
||||||
|
.execute(() -> AntiVPN.getInstance().getDatabase().clearResponses());
|
||||||
return "&aCleared all cached API response information!";
|
return "&aCleared all cached API response information!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import dev.brighten.antivpn.api.APIPlayer;
|
|||||||
import dev.brighten.antivpn.command.Command;
|
import dev.brighten.antivpn.command.Command;
|
||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
import dev.brighten.antivpn.utils.StringUtil;
|
import dev.brighten.antivpn.utils.StringUtil;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -75,36 +74,40 @@ public class LookupCommand extends Command {
|
|||||||
return String.format("&cNo player found with the name \"%s\"", args[0]);
|
return String.format("&cNo player found with the name \"%s\"", args[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
AntiVPN.getInstance().getExecutor()
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
.checkIp(player.get().getIp().getHostAddress())
|
.checkIp(player.get().getIp().getHostAddress())
|
||||||
.thenAccept(result -> {
|
.thenAccept(
|
||||||
|
result -> {
|
||||||
if (!result.isSuccess()) {
|
if (!result.isSuccess()) {
|
||||||
executor.sendMessage("&cThere was an error trying to find the " +
|
executor.sendMessage(
|
||||||
"information of this player.");
|
"&cThere was an error trying to find the " + "information of this player.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.sendMessage(StringUtil.line("&8"));
|
executor.sendMessage(StringUtil.line("&8"));
|
||||||
executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information");
|
executor.sendMessage(
|
||||||
|
"&6&l" + player.get().getName() + "&7&l's Connection Information");
|
||||||
executor.sendMessage("");
|
executor.sendMessage("");
|
||||||
executor.sendMessage("&e%s&8: &f%s", "Proxy", result.isProxy()
|
executor.sendMessage(
|
||||||
? "&a" + result.getMethod() : "&cNo");
|
"&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", "ISP", result.getIsp());
|
||||||
executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName());
|
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", "City", result.getCity());
|
||||||
executor.sendMessage("&e%s&8: &f%s", "Coordinates", result.getLatitude()
|
executor.sendMessage(
|
||||||
+ "&7/&f" + result.getLongitude());
|
"&e%s&8: &f%s",
|
||||||
|
"Coordinates", result.getLatitude() + "&7/&f" + result.getLongitude());
|
||||||
executor.sendMessage(StringUtil.line("&8"));
|
executor.sendMessage(StringUtil.line("&8"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return "&7Looking up the IP information for player " + player.get().getName() + "...";
|
return "&7Looking up the IP information for player " + player.get().getName() + "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||||
|
|
||||||
if(args.length == 1) return AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
if (args.length == 1)
|
||||||
|
return AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||||
.map(APIPlayer::getName)
|
.map(APIPlayer::getName)
|
||||||
.filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase()))
|
.filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|||||||
@@ -17,14 +17,12 @@
|
|||||||
package dev.brighten.antivpn.command.impl;
|
package dev.brighten.antivpn.command.impl;
|
||||||
|
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.api.VPNExecutor;
|
|
||||||
import dev.brighten.antivpn.command.Command;
|
import dev.brighten.antivpn.command.Command;
|
||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
import dev.brighten.antivpn.utils.StringUtil;
|
import dev.brighten.antivpn.utils.StringUtil;
|
||||||
import dev.brighten.antivpn.utils.json.JSONException;
|
import dev.brighten.antivpn.utils.json.JSONException;
|
||||||
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
||||||
import dev.brighten.antivpn.web.objects.QueryResponse;
|
import dev.brighten.antivpn.web.objects.QueryResponse;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -67,17 +65,24 @@ public class PlanCommand extends Command {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(CommandExecutor executor, String[] args) {
|
public String execute(CommandExecutor executor, String[] args) {
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getThreadExecutor()
|
||||||
|
.execute(
|
||||||
|
() -> {
|
||||||
QueryResponse result;
|
QueryResponse result;
|
||||||
try {
|
try {
|
||||||
if (AntiVPN.getInstance().getVpnConfig().getLicense().isEmpty()) {
|
if (AntiVPN.getInstance().getVpnConfig().getLicense().isEmpty()) {
|
||||||
result = FunkemunkyAPI.getQueryResponse();
|
result = FunkemunkyAPI.getQueryResponse();
|
||||||
} else {
|
} else {
|
||||||
result = FunkemunkyAPI.getQueryResponse(AntiVPN.getInstance().getVpnConfig().getLicense());
|
result =
|
||||||
|
FunkemunkyAPI.getQueryResponse(
|
||||||
|
AntiVPN.getInstance().getVpnConfig().getLicense());
|
||||||
|
|
||||||
if (!result.isValidPlan()) {
|
if (!result.isValidPlan()) {
|
||||||
executor.sendMessage("&cThe license &f%s &cis not a valid license, " +
|
executor.sendMessage(
|
||||||
"checking your Free plan information...",
|
"&cThe license &f%s &cis not a valid license, "
|
||||||
|
+ "checking your Free plan information...",
|
||||||
AntiVPN.getInstance().getVpnConfig().getLicense());
|
AntiVPN.getInstance().getVpnConfig().getLicense());
|
||||||
|
|
||||||
result = FunkemunkyAPI.getQueryResponse();
|
result = FunkemunkyAPI.getQueryResponse();
|
||||||
@@ -87,24 +92,28 @@ public class PlanCommand extends Command {
|
|||||||
String plan = result.getPlanType();
|
String plan = result.getPlanType();
|
||||||
if (plan.equals("IP")) plan += " (Free)";
|
if (plan.equals("IP")) plan += " (Free)";
|
||||||
|
|
||||||
String queryMax = result.getQueriesMax() == Long.MAX_VALUE
|
String queryMax =
|
||||||
? "Unlimited" : String.valueOf(result.getQueriesMax());
|
result.getQueriesMax() == Long.MAX_VALUE
|
||||||
|
? "Unlimited"
|
||||||
|
: String.valueOf(result.getQueriesMax());
|
||||||
|
|
||||||
executor.sendMessage(StringUtil.line("&8"));
|
executor.sendMessage(StringUtil.line("&8"));
|
||||||
executor.sendMessage("&6&lKauriVPN Plan Information");
|
executor.sendMessage("&6&lKauriVPN Plan Information");
|
||||||
executor.sendMessage("");
|
executor.sendMessage("");
|
||||||
executor.sendMessage("&e%s&8: &f%s", "Plan", plan);
|
executor.sendMessage("&e%s&8: &f%s", "Plan", plan);
|
||||||
executor.sendMessage("&e%s&8: &f%s&7/&f%s", "Queries Used",
|
executor.sendMessage(
|
||||||
result.getQueries(), queryMax);
|
"&e%s&8: &f%s&7/&f%s", "Queries Used", result.getQueries(), queryMax);
|
||||||
executor.sendMessage(StringUtil.line("&8"));
|
executor.sendMessage(StringUtil.line("&8"));
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException(e);
|
AntiVPN.getInstance().getExecutor().logException(e);
|
||||||
executor.sendMessage("&cThere was a JSONException thrown while looking up your query " +
|
executor.sendMessage(
|
||||||
"information. Check console for more details.");
|
"&cThere was a JSONException thrown while looking up your query "
|
||||||
|
+ "information. Check console for more details.");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException(e);
|
AntiVPN.getInstance().getExecutor().logException(e);
|
||||||
executor.sendMessage("&cThere was a IOException thrown while looking up your query " +
|
executor.sendMessage(
|
||||||
"information. Check console for more details.");
|
"&cThere was a IOException thrown while looking up your query "
|
||||||
|
+ "information. Check console for more details.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return "&7Looking up your query information...";
|
return "&7Looking up your query information...";
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package dev.brighten.antivpn.command.impl;
|
|||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.command.Command;
|
import dev.brighten.antivpn.command.Command;
|
||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -71,7 +70,10 @@ public class ReloadCommand extends Command {
|
|||||||
|
|
||||||
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
|
@Override
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package dev.brighten.antivpn.database;
|
|||||||
|
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ import dev.brighten.antivpn.database.sql.utils.Query;
|
|||||||
import dev.brighten.antivpn.database.version.Version;
|
import dev.brighten.antivpn.database.version.Version;
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@@ -35,24 +33,36 @@ import java.sql.Timestamp;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
public class H2VPN implements VPNDatabase {
|
public class H2VPN implements VPNDatabase {
|
||||||
|
|
||||||
|
|
||||||
public H2VPN() {
|
public H2VPN() {
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
AntiVPN.getInstance()
|
||||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
.getExecutor()
|
||||||
|
.getThreadExecutor()
|
||||||
|
.scheduleAtFixedRate(
|
||||||
|
() -> {
|
||||||
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||||
|
return;
|
||||||
|
|
||||||
// Refreshing whitelisted players
|
// Refreshing whitelisted players
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getWhitelisted()
|
||||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||||
|
|
||||||
// Refreshing whitlisted IPs
|
// Refreshing whitlisted IPs
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getWhitelistedIps()
|
||||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||||
}, 2, 30, TimeUnit.SECONDS);
|
},
|
||||||
|
2,
|
||||||
|
30,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,21 +70,34 @@ public class H2VPN implements VPNDatabase {
|
|||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
|
||||||
try(ExecutableStatement statement = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip)) {
|
try (ExecutableStatement statement =
|
||||||
|
Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip)) {
|
||||||
try (ResultSet rs = statement.executeQuery()) {
|
try (ResultSet rs = statement.executeQuery()) {
|
||||||
if (rs != null && rs.next()) {
|
if (rs != null && rs.next()) {
|
||||||
return Optional.of(new VPNResponse(rs.getString("asn"), rs.getString("ip"),
|
return Optional.of(
|
||||||
rs.getString("countryName"), rs.getString("countryCode"),
|
new VPNResponse(
|
||||||
rs.getString("city"), rs.getString("timeZone"),
|
rs.getString("asn"),
|
||||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
rs.getString("ip"),
|
||||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
rs.getString("countryName"),
|
||||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
rs.getString("countryCode"),
|
||||||
rs.getTimestamp("inserted").getTime(), -1));
|
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) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting a response for "
|
AntiVPN.getInstance()
|
||||||
+ ip, e);
|
.getExecutor()
|
||||||
|
.logException("There was a problem getting a response for " + ip, e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -92,46 +115,60 @@ public class H2VPN implements VPNDatabase {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void cacheResponse(VPNResponse toCache) {
|
public void cacheResponse(VPNResponse toCache) {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
try(var statement = Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
try (var statement =
|
||||||
|
Query.prepare(
|
||||||
|
"insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
||||||
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||||
.append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName())
|
.append(toCache.getIp())
|
||||||
.append(toCache.getCountryCode()).append(toCache.getCity()).append(toCache.getTimeZone())
|
.append(toCache.getAsn())
|
||||||
.append(toCache.getMethod()).append(toCache.getIsp()).append(toCache.isProxy())
|
.append(toCache.getCountryName())
|
||||||
.append(toCache.isCached()).append(new Timestamp(System.currentTimeMillis()))
|
.append(toCache.getCountryCode())
|
||||||
.append(toCache.getLatitude()).append(toCache.getLongitude())) {
|
.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();
|
statement.execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not cache response for IP: " + toCache.getIp(), e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not cache response for IP: " + toCache.getIp(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteResponse(String ip) {
|
public void deleteResponse(String ip) {
|
||||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
try (var statement = Query.prepare("delete from `responses` where `ip` = ?").append(ip)) {
|
try (var statement = Query.prepare("delete from `responses` where `ip` = ?").append(ip)) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not delete response from IP: " + ip, e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not delete response from IP: " + ip, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWhitelisted(UUID uuid) {
|
public boolean isWhitelisted(UUID uuid) {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
try(var statement = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
try (var statement =
|
||||||
|
Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
||||||
.append(uuid.toString())) {
|
.append(uuid.toString())) {
|
||||||
try (var set = statement.executeQuery()) {
|
try (var set = statement.executeQuery()) {
|
||||||
return set != null && set.next() && set.getString("uuid") != null;
|
return set != null && set.next() && set.getString("uuid") != null;
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not check whitelist for uuid '" + uuid + "' due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not check whitelist for uuid '" + uuid + "' due to SQL error.", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,73 +181,88 @@ public class H2VPN implements VPNDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWhitelisted(CIDRUtils cidr) {
|
public boolean isWhitelisted(CIDRUtils cidr) {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return false;
|
||||||
return false;
|
|
||||||
|
|
||||||
BigInteger start = cidr.getStartIpInt();
|
BigInteger start = cidr.getStartIpInt();
|
||||||
BigInteger end = cidr.getEndIpInt();
|
BigInteger end = cidr.getEndIpInt();
|
||||||
|
|
||||||
try(var statement = Query.prepare("SELECT * FROM `whitelisted-ranges` WHERE ip_start <= ? AND ip_end >= ?")
|
try (var statement =
|
||||||
.append(start).append(end)) {
|
Query.prepare("SELECT * FROM `whitelisted-ranges` WHERE ip_start <= ? AND ip_end >= ?")
|
||||||
|
.append(start)
|
||||||
|
.append(end)) {
|
||||||
|
|
||||||
try (var result = statement.executeQuery()) {
|
try (var result = statement.executeQuery()) {
|
||||||
return result.next();
|
return result.next();
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not check whitelist for cidr '" + cidr + "' due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not check whitelist for cidr '" + cidr + "' due to SQL error.", e);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addWhitelist(UUID uuid) {
|
public void addWhitelist(UUID uuid) {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
try(var statement = Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString())) {
|
try (var statement =
|
||||||
|
Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString())) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not add uuid '" + uuid + "' to whitelist due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not add uuid '" + uuid + "' to whitelist due to SQL error.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeWhitelist(UUID uuid) {
|
public void removeWhitelist(UUID uuid) {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||||
return;
|
try (var statement =
|
||||||
try(var statement = Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString())) {
|
Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString())) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not remove uuid '" + uuid + "' from whitelist due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not remove uuid '" + uuid + "' from whitelist due to SQL error.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addWhitelist(CIDRUtils cidr) {
|
public void addWhitelist(CIDRUtils cidr) {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
try(var statement = Query.prepare("insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
try (var statement =
|
||||||
.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) {
|
Query.prepare(
|
||||||
|
"insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
||||||
|
.append(cidr.getCidr())
|
||||||
|
.append(cidr.getStartIpInt())
|
||||||
|
.append(cidr.getEndIpInt())) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeWhitelist(CIDRUtils cidr) {
|
public void removeWhitelist(CIDRUtils cidr) {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
try(var statement = Query.prepare("delete from `whitelisted-ranges` where `cidr_string` = ?").append(cidr.getCidr())) {
|
try (var statement =
|
||||||
|
Query.prepare("delete from `whitelisted-ranges` where `cidr_string` = ?")
|
||||||
|
.append(cidr.getCidr())) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not remove cidr '" + cidr + "' from whitelist due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not remove cidr '" + cidr + "' from whitelist due to SQL error.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,13 +270,14 @@ public class H2VPN implements VPNDatabase {
|
|||||||
public List<UUID> getAllWhitelisted() {
|
public List<UUID> getAllWhitelisted() {
|
||||||
List<UUID> uuids = new ArrayList<>();
|
List<UUID> uuids = new ArrayList<>();
|
||||||
|
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return uuids;
|
||||||
return uuids;
|
|
||||||
|
|
||||||
try (var statement = Query.prepare("select uuid from `whitelisted`")) {
|
try (var statement = Query.prepare("select uuid from `whitelisted`")) {
|
||||||
statement.execute(set -> uuids.add(UUID.fromString(set.getString("uuid"))));
|
statement.execute(set -> uuids.add(UUID.fromString(set.getString("uuid"))));
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted players due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not get all whitelisted players due to SQL error.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return uuids;
|
return uuids;
|
||||||
@@ -234,23 +287,27 @@ public class H2VPN implements VPNDatabase {
|
|||||||
public List<CIDRUtils> getAllWhitelistedIps() {
|
public List<CIDRUtils> getAllWhitelistedIps() {
|
||||||
List<CIDRUtils> ips = new ArrayList<>();
|
List<CIDRUtils> ips = new ArrayList<>();
|
||||||
|
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return ips;
|
||||||
return ips;
|
try (var statement =
|
||||||
try(var statement = Query.prepare("select `cidr_string`, `ip_start`, `ip_end` from `whitelisted-ranges`")) {
|
Query.prepare("select `cidr_string`, `ip_start`, `ip_end` from `whitelisted-ranges`")) {
|
||||||
statement.execute(set -> {
|
statement.execute(
|
||||||
|
set -> {
|
||||||
try {
|
try {
|
||||||
String cidrString = set.getString("cidr_string");
|
String cidrString = set.getString("cidr_string");
|
||||||
|
|
||||||
ips.add(new CIDRUtils(cidrString));
|
ips.add(new CIDRUtils(cidrString));
|
||||||
|
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
AntiVPN.getInstance().getExecutor()
|
AntiVPN.getInstance()
|
||||||
.logException("Could not format ip "
|
.getExecutor()
|
||||||
+ set.getString("cidr_string") + " into a CIDR!", e);
|
.logException(
|
||||||
|
"Could not format ip " + set.getString("cidr_string") + " into a CIDR!", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted ips due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not get all whitelisted ips due to SQL error.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ips;
|
return ips;
|
||||||
@@ -259,15 +316,21 @@ public class H2VPN implements VPNDatabase {
|
|||||||
@Override
|
@Override
|
||||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||||
if (MySQL.isClosed()) return;
|
if (MySQL.isClosed()) return;
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
try(var statement = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
.getThreadExecutor()
|
||||||
|
.execute(
|
||||||
|
() -> {
|
||||||
|
try (var statement =
|
||||||
|
Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||||
.append(uuid.toString())) {
|
.append(uuid.toString())) {
|
||||||
try (var set = statement.executeQuery()) {
|
try (var set = statement.executeQuery()) {
|
||||||
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting alerts state for " + uuid, e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("There was a problem getting alerts state for " + uuid, e);
|
||||||
result.accept(false);
|
result.accept(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -279,24 +342,30 @@ public class H2VPN implements VPNDatabase {
|
|||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
// We want to make sure there isn't already a uuid inserted to prevent double insertions
|
// 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
|
alertsState(
|
||||||
|
uuid,
|
||||||
|
alreadyEnabled -> { // No need to make another thread execute, already async
|
||||||
if (!alreadyEnabled) {
|
if (!alreadyEnabled) {
|
||||||
try(var statement = Query.prepare("insert into `alerts` (`uuid`) values (?)")
|
try (var statement =
|
||||||
|
Query.prepare("insert into `alerts` (`uuid`) values (?)")
|
||||||
.append(uuid.toString())) {
|
.append(uuid.toString())) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor()
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
.logException("There was a problem updating alerts state for " + uuid, e);
|
.logException("There was a problem updating alerts state for " + uuid, e);
|
||||||
}
|
}
|
||||||
} // No need to insert again of already enabled
|
} // No need to insert again of already enabled
|
||||||
});
|
});
|
||||||
// Removing any uuid from the alerts table will disable alerts globally.
|
// Removing any uuid from the alerts table will disable alerts globally.
|
||||||
} else {
|
} else {
|
||||||
try(var statement = Query.prepare("delete from `alerts` where `uuid` = ?").append(uuid.toString())) {
|
try (var statement =
|
||||||
|
Query.prepare("delete from `alerts` where `uuid` = ?").append(uuid.toString())) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("There was a problem updating alerts state for "
|
AntiVPN.getInstance()
|
||||||
+ uuid, e);
|
.getExecutor()
|
||||||
|
.logException("There was a problem updating alerts state for " + uuid, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,14 +377,15 @@ public class H2VPN implements VPNDatabase {
|
|||||||
try (var statement = Query.prepare("delete from `responses`")) {
|
try (var statement = Query.prepare("delete from `responses`")) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("There was a problem clearing responses.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("There was a problem clearing responses.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||||
return;
|
|
||||||
AntiVPN.getInstance().getExecutor().log("Initializing H2...");
|
AntiVPN.getInstance().getExecutor().log("Initializing H2...");
|
||||||
MySQL.initH2();
|
MySQL.initH2();
|
||||||
try {
|
try {
|
||||||
@@ -335,8 +405,7 @@ public class H2VPN implements VPNDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
MySQL.shutdown();
|
MySQL.shutdown();
|
||||||
}
|
}
|
||||||
@@ -350,12 +419,15 @@ public class H2VPN implements VPNDatabase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
var connection = Query.getConn();
|
var connection = Query.getConn();
|
||||||
if (connection == null || connection.getMetaData() == null
|
if (connection == null
|
||||||
|
|| connection.getMetaData() == null
|
||||||
|| !connection.getMetaData().getDatabaseProductName().equalsIgnoreCase("H2")) {
|
|| !connection.getMetaData().getDatabaseProductName().equalsIgnoreCase("H2")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not verify database type before H2 backup.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not verify database type before H2 backup.", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,16 +437,17 @@ public class H2VPN implements VPNDatabase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File backupFile = new File(backupDir, "database.h2_backup_" + System.currentTimeMillis() + ".zip");
|
File backupFile =
|
||||||
String backupPath = backupFile.getAbsolutePath()
|
new File(backupDir, "database.h2_backup_" + System.currentTimeMillis() + ".zip");
|
||||||
.replace("\\", "/")
|
String backupPath = backupFile.getAbsolutePath().replace("\\", "/").replace("'", "''");
|
||||||
.replace("'", "''");
|
|
||||||
|
|
||||||
try (var statement = Query.prepare("BACKUP TO '" + backupPath + "'")) {
|
try (var statement = Query.prepare("BACKUP TO '" + backupPath + "'")) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
AntiVPN.getInstance().getExecutor().log("Created H2 backup at " + backupFile.getName());
|
AntiVPN.getInstance().getExecutor().log("Created H2 backup at " + backupFile.getName());
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not create H2 backup before migration.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not create H2 backup before migration.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-22
@@ -23,7 +23,6 @@ import dev.brighten.antivpn.database.sql.utils.ExecutableStatement;
|
|||||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||||
import dev.brighten.antivpn.database.version.Version;
|
import dev.brighten.antivpn.database.version.Version;
|
||||||
import dev.brighten.antivpn.utils.MiscUtils;
|
import dev.brighten.antivpn.utils.MiscUtils;
|
||||||
|
|
||||||
import java.sql.DatabaseMetaData;
|
import java.sql.DatabaseMetaData;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -33,23 +32,33 @@ import java.util.List;
|
|||||||
public class First implements Version<VPNDatabase> {
|
public class First implements Version<VPNDatabase> {
|
||||||
|
|
||||||
private final List<AutoCloseable> toClose = new ArrayList<>();
|
private final List<AutoCloseable> toClose = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(VPNDatabase database) throws DatabaseException {
|
public void update(VPNDatabase database) throws DatabaseException {
|
||||||
try {
|
try {
|
||||||
closeOnEnd(Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)"))
|
closeOnEnd(
|
||||||
|
Query.prepare(
|
||||||
|
"create table if not exists `whitelisted` (`uuid` varchar(36) not null)"))
|
||||||
.execute();
|
.execute();
|
||||||
closeOnEnd(Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)"))
|
closeOnEnd(
|
||||||
|
Query.prepare(
|
||||||
|
"create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)"))
|
||||||
.execute();
|
.execute();
|
||||||
closeOnEnd(Query
|
closeOnEnd(
|
||||||
.prepare("create table if not exists `responses` (`ip` varchar(45) not null, `asn` varchar(12),"
|
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), "
|
+ "`countryName` text, `countryCode` varchar(10), `city` text, `timeZone` varchar(64), "
|
||||||
+ "`method` varchar(32), `isp` text, `proxy` boolean, `cached` boolean, `inserted` timestamp,"
|
+ "`method` varchar(32), `isp` text, `proxy` boolean, `cached` boolean, `inserted` timestamp,"
|
||||||
+ "`latitude` double, `longitude` double)")).execute();
|
+ "`latitude` double, `longitude` double)"))
|
||||||
|
.execute();
|
||||||
closeOnEnd(Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)"))
|
closeOnEnd(Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)"))
|
||||||
.execute();
|
.execute();
|
||||||
closeOnEnd(Query.prepare("create table if not exists `database_version` (`version` int)")).execute();
|
closeOnEnd(Query.prepare("create table if not exists `database_version` (`version` int)"))
|
||||||
closeOnEnd(Query.prepare("insert into `database_version` (`version`) values (?)")
|
.execute();
|
||||||
.append(versionNumber())).execute();
|
closeOnEnd(
|
||||||
|
Query.prepare("insert into `database_version` (`version`) values (?)")
|
||||||
|
.append(versionNumber()))
|
||||||
|
.execute();
|
||||||
|
|
||||||
AntiVPN.getInstance().getExecutor().log("Creating indexes...");
|
AntiVPN.getInstance().getExecutor().log("Creating indexes...");
|
||||||
createIndexIfAbsent("whitelisted", "whitelisted_uuid_1", "`uuid`");
|
createIndexIfAbsent("whitelisted", "whitelisted_uuid_1", "`uuid`");
|
||||||
@@ -70,17 +79,16 @@ public class First implements Version<VPNDatabase> {
|
|||||||
return statement;
|
return statement;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createIndexIfAbsent(String tableName, String indexName, String columnList) throws SQLException {
|
protected void createIndexIfAbsent(String tableName, String indexName, String columnList)
|
||||||
|
throws SQLException {
|
||||||
if (hasIndex(tableName, indexName)) {
|
if (hasIndex(tableName, indexName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOnEnd(Query.prepare(String.format(
|
closeOnEnd(
|
||||||
"create index `%s` on `%s` (%s)",
|
Query.prepare(
|
||||||
indexName,
|
String.format("create index `%s` on `%s` (%s)", indexName, tableName, columnList)))
|
||||||
tableName,
|
.execute();
|
||||||
columnList
|
|
||||||
))).execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void dropIndexIfPresent(String tableName, String indexName) throws SQLException {
|
protected void dropIndexIfPresent(String tableName, String indexName) throws SQLException {
|
||||||
@@ -88,11 +96,8 @@ public class First implements Version<VPNDatabase> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOnEnd(Query.prepare(String.format(
|
closeOnEnd(Query.prepare(String.format("drop index `%s` on `%s`", indexName, tableName)))
|
||||||
"drop index `%s` on `%s`",
|
.execute();
|
||||||
indexName,
|
|
||||||
tableName
|
|
||||||
))).execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean hasIndex(String tableName, String indexName) throws SQLException {
|
protected boolean hasIndex(String tableName, String indexName) throws SQLException {
|
||||||
@@ -125,6 +130,5 @@ public class First implements Version<VPNDatabase> {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-16
@@ -26,7 +26,6 @@ import dev.brighten.antivpn.database.sql.utils.Query;
|
|||||||
import dev.brighten.antivpn.database.version.Version;
|
import dev.brighten.antivpn.database.version.Version;
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import dev.brighten.antivpn.utils.MiscUtils;
|
import dev.brighten.antivpn.utils.MiscUtils;
|
||||||
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -53,24 +52,33 @@ public class Second extends First implements Version<VPNDatabase> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
closeOnEnd(Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ranges` " +
|
closeOnEnd(
|
||||||
"(id INT AUTO_INCREMENT PRIMARY KEY, " +
|
Query.prepare(
|
||||||
"cidr_string VARCHAR(45), " +
|
"CREATE TABLE IF NOT EXISTS `whitelisted-ranges` "
|
||||||
"ip_start BIGINT NOT NULL, " +
|
+ "(id INT AUTO_INCREMENT PRIMARY KEY, "
|
||||||
"ip_end BIGINT NOT NULL)"))
|
+ "cidr_string VARCHAR(45), "
|
||||||
|
+ "ip_start BIGINT NOT NULL, "
|
||||||
|
+ "ip_end BIGINT NOT NULL)"))
|
||||||
.execute();
|
.execute();
|
||||||
createIndexIfAbsent("whitelisted-ranges", "idx_ip_range", "ip_start, ip_end");
|
createIndexIfAbsent("whitelisted-ranges", "idx_ip_range", "ip_start, ip_end");
|
||||||
|
|
||||||
var cidrs = whitelistedIps.stream().map(ip -> {
|
var cidrs =
|
||||||
|
whitelistedIps.stream()
|
||||||
|
.map(
|
||||||
|
ip -> {
|
||||||
try {
|
try {
|
||||||
return new CIDRUtils(ip + "/32");
|
return new CIDRUtils(ip + "/32");
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
throw new RuntimeException("Could not format ip " + ip + " into a CIDR!", 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 (?, ?, ?)");
|
.toList();
|
||||||
|
var insertStatement =
|
||||||
|
Query.prepare(
|
||||||
|
"INSERT INTO `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) VALUES (?, ?, ?)");
|
||||||
for (CIDRUtils cidr : cidrs) {
|
for (CIDRUtils cidr : cidrs) {
|
||||||
insertStatement = insertStatement
|
insertStatement =
|
||||||
|
insertStatement
|
||||||
.append(cidr.toString())
|
.append(cidr.toString())
|
||||||
.append(cidr.getStartIpInt())
|
.append(cidr.getStartIpInt())
|
||||||
.append(cidr.getEndIpInt())
|
.append(cidr.getEndIpInt())
|
||||||
@@ -81,16 +89,22 @@ public class Second extends First implements Version<VPNDatabase> {
|
|||||||
|
|
||||||
for (int updateCount : updateCounts) {
|
for (int updateCount : updateCounts) {
|
||||||
if (updateCount == 0) {
|
if (updateCount == 0) {
|
||||||
throw new RuntimeException("Could not insert a CIDR from previous whitelisted lists, attempted to restore previous database!");
|
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", "ip_1");
|
||||||
dropIndexIfPresent("whitelisted-ips", "whitelisted_ips_ip_1");
|
dropIndexIfPresent("whitelisted-ips", "whitelisted_ips_ip_1");
|
||||||
closeOnEnd(Query.prepare("DROP TABLE `whitelisted-ips`")).execute();
|
closeOnEnd(Query.prepare("DROP TABLE `whitelisted-ips`")).execute();
|
||||||
closeOnEnd(Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)").append(versionNumber())).execute();
|
closeOnEnd(
|
||||||
|
Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)")
|
||||||
|
.append(versionNumber()))
|
||||||
|
.execute();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
AntiVPN.getInstance().getExecutor().log("Failed to update database to version 1: " + e.getMessage());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log("Failed to update database to version 1: " + e.getMessage());
|
||||||
try {
|
try {
|
||||||
rollback(whitelistedIps);
|
rollback(whitelistedIps);
|
||||||
} catch (SQLException ex) {
|
} catch (SQLException ex) {
|
||||||
@@ -101,7 +115,6 @@ public class Second extends First implements Version<VPNDatabase> {
|
|||||||
MiscUtils.close(toClose.toArray(AutoCloseable[]::new));
|
MiscUtils.close(toClose.toArray(AutoCloseable[]::new));
|
||||||
toClose.clear();
|
toClose.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExecutableStatement closeOnEnd(ExecutableStatement statement) {
|
private ExecutableStatement closeOnEnd(ExecutableStatement statement) {
|
||||||
@@ -116,11 +129,13 @@ public class Second extends First implements Version<VPNDatabase> {
|
|||||||
statement.execute();
|
statement.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
try(var statement = Query.prepare("DELETE FROM `database_version` WHERE version = ?").append(versionNumber())) {
|
try (var statement =
|
||||||
|
Query.prepare("DELETE FROM `database_version` WHERE version = ?").append(versionNumber())) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
try(var statement = Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ips` (`ip` VARCHAR(45) NOT NULL)")) {
|
try (var statement =
|
||||||
|
Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ips` (`ip` VARCHAR(45) NOT NULL)")) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+65
-18
@@ -23,7 +23,6 @@ import dev.brighten.antivpn.database.sql.utils.Query;
|
|||||||
import dev.brighten.antivpn.database.version.Version;
|
import dev.brighten.antivpn.database.version.Version;
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import dev.brighten.antivpn.utils.MiscUtils;
|
import dev.brighten.antivpn.utils.MiscUtils;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -38,7 +37,8 @@ public class Third implements Version<VPNDatabase> {
|
|||||||
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
||||||
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
||||||
try (var preparedQuery = Query.prepare("select ip_start, ip_end from `whitelisted-ranges`")) {
|
try (var preparedQuery = Query.prepare("select ip_start, ip_end from `whitelisted-ranges`")) {
|
||||||
preparedQuery.execute(set -> {
|
preparedQuery.execute(
|
||||||
|
set -> {
|
||||||
BigInteger start = set.getBigDecimal("ip_start").toBigInteger();
|
BigInteger start = set.getBigDecimal("ip_start").toBigInteger();
|
||||||
BigInteger end = set.getBigDecimal("ip_end").toBigInteger();
|
BigInteger end = set.getBigDecimal("ip_end").toBigInteger();
|
||||||
|
|
||||||
@@ -48,52 +48,99 @@ public class Third implements Version<VPNDatabase> {
|
|||||||
if (range.size() > 1) {
|
if (range.size() > 1) {
|
||||||
rangesToRemove.add(new BigInteger[] {start, end});
|
rangesToRemove.add(new BigInteger[] {start, end});
|
||||||
rangesToInsert.addAll(range);
|
rangesToInsert.addAll(range);
|
||||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "Found multiple CIDR ranges for whitelist range for %s, %s!", start, end);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
Level.WARNING,
|
||||||
|
"Found multiple CIDR ranges for whitelist range for %s, %s!",
|
||||||
|
start,
|
||||||
|
end);
|
||||||
} else ipRanges.addAll(range);
|
} else ipRanges.addAll(range);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException(
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException(
|
||||||
String.format("Could not convert ip range to CIDR! %s, %s", start, end), e);
|
String.format("Could not convert ip range to CIDR! %s, %s", start, end), e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted ranges due to SQL error.", 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());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log("Inserting %s new ranges into database...", rangesToInsert.size());
|
||||||
|
|
||||||
for (CIDRUtils cidr : rangesToInsert) {
|
for (CIDRUtils cidr : rangesToInsert) {
|
||||||
try(var statement = Query.prepare("insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
try (var statement =
|
||||||
.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) {
|
Query.prepare(
|
||||||
|
"insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
||||||
|
.append(cidr.getCidr())
|
||||||
|
.append(cidr.getStartIpInt())
|
||||||
|
.append(cidr.getEndIpInt())) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AntiVPN.getInstance().getExecutor().log("Removing %s old ranges from database...", rangesToRemove.size());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log("Removing %s old ranges from database...", rangesToRemove.size());
|
||||||
|
|
||||||
for (BigInteger[] range : rangesToRemove) {
|
for (BigInteger[] range : rangesToRemove) {
|
||||||
try(var statement = Query.prepare("delete from `whitelisted-ranges` where `ip_start` = ? and `ip_end` = ?")) {
|
try (var statement =
|
||||||
|
Query.prepare("delete from `whitelisted-ranges` where `ip_start` = ? and `ip_end` = ?")) {
|
||||||
statement.append(range[0]).append(range[1]).execute();
|
statement.append(range[0]).append(range[1]).execute();
|
||||||
} catch (SQLException e) {
|
} 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()
|
||||||
|
.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());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log("Updating %s ranges to proper CIDR notation with the database", ipRanges.size());
|
||||||
|
|
||||||
for (CIDRUtils cidr : ipRanges) {
|
for (CIDRUtils cidr : ipRanges) {
|
||||||
try(var statement = Query.prepare("update `whitelisted-ranges` set `cidr_string` = ? where `ip_start` = ? and `ip_end` = ?")) {
|
try (var statement =
|
||||||
statement.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt()).execute();
|
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) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not update cidr '" + cidr + "' to proper CIDR notation in whitelist due to SQL error.", 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())) {
|
try (var preparedStatement =
|
||||||
|
Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)")
|
||||||
|
.append(versionNumber())) {
|
||||||
preparedStatement.execute();
|
preparedStatement.execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not update database version to 2 due to SQL error.", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Could not update database version to 2 due to SQL error.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package dev.brighten.antivpn.database.mongo;
|
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.*;
|
||||||
import com.mongodb.client.MongoClient;
|
import com.mongodb.client.MongoClient;
|
||||||
import com.mongodb.client.MongoClients;
|
import com.mongodb.client.MongoClients;
|
||||||
@@ -30,15 +28,14 @@ import dev.brighten.antivpn.database.VPNDatabase;
|
|||||||
import dev.brighten.antivpn.database.version.Version;
|
import dev.brighten.antivpn.database.version.Version;
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
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.math.BigDecimal;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
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 class MongoVPN implements VPNDatabase {
|
||||||
|
|
||||||
@@ -48,20 +45,32 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
public MongoDatabase antivpnDatabase;
|
public MongoDatabase antivpnDatabase;
|
||||||
|
|
||||||
public MongoVPN() {
|
public MongoVPN() {
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getThreadExecutor()
|
||||||
|
.scheduleAtFixedRate(
|
||||||
|
() -> {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||||
|
|
||||||
// Refreshing whitelisted players
|
// Refreshing whitelisted players
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getWhitelisted()
|
||||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||||
|
|
||||||
// Refreshing whitlisted IPs
|
// Refreshing whitlisted IPs
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getWhitelistedIps()
|
||||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||||
}, 2, 30, TimeUnit.SECONDS);
|
},
|
||||||
|
2,
|
||||||
|
30,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||||
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
|
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
|
||||||
@@ -74,7 +83,10 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Optional.of(VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip)
|
return Optional.of(
|
||||||
|
VPNResponse.builder()
|
||||||
|
.asn(rdoc.getString("asn"))
|
||||||
|
.ip(ip)
|
||||||
.countryName(rdoc.getString("countryName"))
|
.countryName(rdoc.getString("countryName"))
|
||||||
.countryCode(rdoc.getString("countryCode"))
|
.countryCode(rdoc.getString("countryCode"))
|
||||||
.city(rdoc.getString("city"))
|
.city(rdoc.getString("city"))
|
||||||
@@ -111,10 +123,14 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
rdoc.put("longitude", toCache.getLongitude());
|
rdoc.put("longitude", toCache.getLongitude());
|
||||||
rdoc.put("lastAccess", System.currentTimeMillis());
|
rdoc.put("lastAccess", System.currentTimeMillis());
|
||||||
|
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getThreadExecutor()
|
||||||
|
.execute(
|
||||||
|
() -> {
|
||||||
Bson update = new Document("$set", rdoc);
|
Bson update = new Document("$set", rdoc);
|
||||||
cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update,
|
cacheDocument.updateOne(
|
||||||
new UpdateOptions().upsert(true));
|
Filters.eq("ip", toCache.getIp()), update, new UpdateOptions().upsert(true));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,8 +143,11 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
@Override
|
@Override
|
||||||
public boolean isWhitelisted(UUID uuid) {
|
public boolean isWhitelisted(UUID uuid) {
|
||||||
return settingsDocument
|
return settingsDocument
|
||||||
.find(Filters.and(Filters.eq("setting", "whitelist"),
|
.find(
|
||||||
Filters.eq("uuid", uuid.toString()))).first() != null;
|
Filters.and(
|
||||||
|
Filters.eq("setting", "whitelist"), Filters.eq("uuid", uuid.toString())))
|
||||||
|
.first()
|
||||||
|
!= null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -145,8 +164,14 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
public boolean isWhitelisted(CIDRUtils cidr) {
|
public boolean isWhitelisted(CIDRUtils cidr) {
|
||||||
var start = new Decimal128(new BigDecimal(cidr.getStartIpInt()));
|
var start = new Decimal128(new BigDecimal(cidr.getStartIpInt()));
|
||||||
var end = new Decimal128(new BigDecimal(cidr.getEndIpInt()));
|
var end = new Decimal128(new BigDecimal(cidr.getEndIpInt()));
|
||||||
return settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
return settingsDocument
|
||||||
Filters.lte("ip_start", start), Filters.gte("ip_end", end))).first() != null;
|
.find(
|
||||||
|
Filters.and(
|
||||||
|
Filters.eq("setting", "whitelist"),
|
||||||
|
Filters.lte("ip_start", start),
|
||||||
|
Filters.gte("ip_end", end)))
|
||||||
|
.first()
|
||||||
|
!= null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -160,10 +185,8 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
@Override
|
@Override
|
||||||
public void removeWhitelist(UUID uuid) {
|
public void removeWhitelist(UUID uuid) {
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||||
settingsDocument.deleteMany(Filters
|
settingsDocument.deleteMany(
|
||||||
.and(
|
Filters.and(Filters.eq("setting", "whitelist"), Filters.eq("uuid", uuid.toString())));
|
||||||
Filters.eq("setting", "whitelist"),
|
|
||||||
Filters.eq("uuid", uuid.toString())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -178,8 +201,8 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeWhitelist(CIDRUtils cidr) {
|
public void removeWhitelist(CIDRUtils cidr) {
|
||||||
settingsDocument.deleteMany(Filters
|
settingsDocument.deleteMany(
|
||||||
.and(
|
Filters.and(
|
||||||
Filters.eq("setting", "whitelist"),
|
Filters.eq("setting", "whitelist"),
|
||||||
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
||||||
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))));
|
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))));
|
||||||
@@ -188,22 +211,30 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
@Override
|
@Override
|
||||||
public List<UUID> getAllWhitelisted() {
|
public List<UUID> getAllWhitelisted() {
|
||||||
List<UUID> uuids = new ArrayList<>();
|
List<UUID> uuids = new ArrayList<>();
|
||||||
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
settingsDocument
|
||||||
Filters.exists("uuid")))
|
.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("uuid")))
|
||||||
.forEach((Consumer<? super Document>) doc -> uuids.add(UUID.fromString(doc.getString("uuid"))));
|
.forEach(
|
||||||
|
(Consumer<? super Document>) doc -> uuids.add(UUID.fromString(doc.getString("uuid"))));
|
||||||
return uuids;
|
return uuids;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CIDRUtils> getAllWhitelistedIps() {
|
public List<CIDRUtils> getAllWhitelistedIps() {
|
||||||
List<CIDRUtils> ips = new ArrayList<>();
|
List<CIDRUtils> ips = new ArrayList<>();
|
||||||
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
settingsDocument
|
||||||
Filters.exists("cidr_string"))).forEach((Consumer<? super Document>) doc -> {
|
.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("cidr_string")))
|
||||||
|
.forEach(
|
||||||
|
(Consumer<? super Document>)
|
||||||
|
doc -> {
|
||||||
try {
|
try {
|
||||||
var cidr = new CIDRUtils(doc.getString("cidr_string"));
|
var cidr = new CIDRUtils(doc.getString("cidr_string"));
|
||||||
ips.add(cidr);
|
ips.add(cidr);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Could not format ip " + doc.getString("cidr_string") + " into a CIDR!", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException(
|
||||||
|
"Could not format ip " + doc.getString("cidr_string") + " into a CIDR!",
|
||||||
|
e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return ips;
|
return ips;
|
||||||
@@ -211,16 +242,31 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(settingsDocument
|
AntiVPN.getInstance()
|
||||||
.find(Filters.and(Filters.eq("setting", "alerts"),
|
.getExecutor()
|
||||||
Filters.eq("uuid", uuid.toString()))).first() != null));
|
.getThreadExecutor()
|
||||||
|
.execute(
|
||||||
|
() ->
|
||||||
|
result.accept(
|
||||||
|
settingsDocument
|
||||||
|
.find(
|
||||||
|
Filters.and(
|
||||||
|
Filters.eq("setting", "alerts"),
|
||||||
|
Filters.eq("uuid", uuid.toString())))
|
||||||
|
.first()
|
||||||
|
!= null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateAlertsState(UUID uuid, boolean state) {
|
public void updateAlertsState(UUID uuid, boolean state) {
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
AntiVPN.getInstance()
|
||||||
settingsDocument.deleteMany(Filters.and(Filters.eq("setting", "alerts"),
|
.getExecutor()
|
||||||
Filters.eq("uuid", uuid.toString())));
|
.getThreadExecutor()
|
||||||
|
.execute(
|
||||||
|
() -> {
|
||||||
|
settingsDocument.deleteMany(
|
||||||
|
Filters.and(
|
||||||
|
Filters.eq("setting", "alerts"), Filters.eq("uuid", uuid.toString())));
|
||||||
if (state) {
|
if (state) {
|
||||||
Document adoc = new Document("setting", "alerts");
|
Document adoc = new Document("setting", "alerts");
|
||||||
|
|
||||||
@@ -238,20 +284,26 @@ public class MongoVPN implements VPNDatabase {
|
|||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL().isEmpty()) { // URL
|
if (!AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL().isEmpty()) { // URL
|
||||||
ConnectionString cs = new ConnectionString(AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL());
|
ConnectionString cs =
|
||||||
MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(cs).build();
|
new ConnectionString(AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL());
|
||||||
|
MongoClientSettings settings =
|
||||||
|
MongoClientSettings.builder().applyConnectionString(cs).build();
|
||||||
client = MongoClients.create(settings);
|
client = MongoClients.create(settings);
|
||||||
} else {
|
} else {
|
||||||
MongoClientSettings.Builder settingsBld = MongoClientSettings.builder().readPreference(ReadPreference.nearest())
|
MongoClientSettings.Builder settingsBld =
|
||||||
.applyToClusterSettings(builder -> builder.
|
MongoClientSettings.builder()
|
||||||
hosts(Collections.singletonList(
|
.readPreference(ReadPreference.nearest())
|
||||||
|
.applyToClusterSettings(
|
||||||
|
builder ->
|
||||||
|
builder.hosts(
|
||||||
|
Collections.singletonList(
|
||||||
new ServerAddress(
|
new ServerAddress(
|
||||||
AntiVPN.getInstance().getVpnConfig().getIp(),
|
AntiVPN.getInstance().getVpnConfig().getIp(),
|
||||||
AntiVPN.getInstance().getVpnConfig().getPort())
|
AntiVPN.getInstance().getVpnConfig().getPort()))));
|
||||||
)));
|
|
||||||
if (AntiVPN.getInstance().getVpnConfig().useDatabaseCreds()) {
|
if (AntiVPN.getInstance().getVpnConfig().useDatabaseCreds()) {
|
||||||
settingsBld.credential(MongoCredential
|
settingsBld.credential(
|
||||||
.createCredential(AntiVPN.getInstance().getVpnConfig().getUsername(),
|
MongoCredential.createCredential(
|
||||||
|
AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||||
AntiVPN.getInstance().getVpnConfig().getDatabaseName(),
|
AntiVPN.getInstance().getVpnConfig().getDatabaseName(),
|
||||||
AntiVPN.getInstance().getVpnConfig().getPassword().toCharArray()));
|
AntiVPN.getInstance().getVpnConfig().getPassword().toCharArray()));
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-9
@@ -23,22 +23,24 @@ import dev.brighten.antivpn.database.DatabaseException;
|
|||||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||||
import dev.brighten.antivpn.database.version.Version;
|
import dev.brighten.antivpn.database.version.Version;
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import org.bson.Document;
|
|
||||||
import org.bson.types.Decimal128;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.bson.types.Decimal128;
|
||||||
|
|
||||||
public class MongoSecond implements Version<MongoVPN> {
|
public class MongoSecond implements Version<MongoVPN> {
|
||||||
@Override
|
@Override
|
||||||
public void update(MongoVPN database) throws DatabaseException {
|
public void update(MongoVPN database) throws DatabaseException {
|
||||||
List<Document> backup = new ArrayList<>();
|
List<Document> backup = new ArrayList<>();
|
||||||
database.settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
database
|
||||||
Filters.exists("ip")))
|
.settingsDocument
|
||||||
.forEach((Consumer<? super Document>) doc -> {
|
.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("ip")))
|
||||||
|
.forEach(
|
||||||
|
(Consumer<? super Document>)
|
||||||
|
doc -> {
|
||||||
backup.add(new Document(doc));
|
backup.add(new Document(doc));
|
||||||
|
|
||||||
String ip = doc.getString("ip");
|
String ip = doc.getString("ip");
|
||||||
@@ -51,14 +53,16 @@ public class MongoSecond implements Version<MongoVPN> {
|
|||||||
doc.append("cidr_string", cidr.toString());
|
doc.append("cidr_string", cidr.toString());
|
||||||
doc.remove("ip");
|
doc.remove("ip");
|
||||||
|
|
||||||
database.settingsDocument.replaceOne(Filters.eq("_id", doc.getObjectId("_id")), doc);
|
database.settingsDocument.replaceOne(
|
||||||
|
Filters.eq("_id", doc.getObjectId("_id")), doc);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
rollback(backup, database);
|
rollback(backup, database);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
database.settingsDocument.createIndex(Indexes.compoundIndex(Indexes.ascending("ip_start"), Indexes.ascending("ip_end")));
|
database.settingsDocument.createIndex(
|
||||||
|
Indexes.compoundIndex(Indexes.ascending("ip_start"), Indexes.ascending("ip_end")));
|
||||||
database.settingsDocument.createIndex(Indexes.ascending("cidr_string"));
|
database.settingsDocument.createIndex(Indexes.ascending("cidr_string"));
|
||||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||||
@@ -66,7 +70,9 @@ public class MongoSecond implements Version<MongoVPN> {
|
|||||||
|
|
||||||
private void rollback(List<Document> toRollback, MongoVPN database) {
|
private void rollback(List<Document> toRollback, MongoVPN database) {
|
||||||
AntiVPN.getInstance().getExecutor().log("Rolling back to version 0...");
|
AntiVPN.getInstance().getExecutor().log("Rolling back to version 0...");
|
||||||
toRollback.forEach(doc -> database.settingsDocument.replaceOne(Filters.eq("_id", doc.getObjectId("_id")), doc));
|
toRollback.forEach(
|
||||||
|
doc ->
|
||||||
|
database.settingsDocument.replaceOne(Filters.eq("_id", doc.getObjectId("_id")), doc));
|
||||||
toRollback.clear();
|
toRollback.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+48
-19
@@ -23,9 +23,6 @@ import dev.brighten.antivpn.database.mongo.MongoVPN;
|
|||||||
import dev.brighten.antivpn.database.version.Version;
|
import dev.brighten.antivpn.database.version.Version;
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import dev.brighten.antivpn.utils.MiscUtils;
|
import dev.brighten.antivpn.utils.MiscUtils;
|
||||||
import org.bson.Document;
|
|
||||||
import org.bson.types.Decimal128;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@@ -33,6 +30,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.bson.types.Decimal128;
|
||||||
|
|
||||||
public class MongoThird implements Version<MongoVPN> {
|
public class MongoThird implements Version<MongoVPN> {
|
||||||
@Override
|
@Override
|
||||||
@@ -40,10 +39,16 @@ public class MongoThird implements Version<MongoVPN> {
|
|||||||
List<CIDRUtils> ipRanges = new ArrayList<>();
|
List<CIDRUtils> ipRanges = new ArrayList<>();
|
||||||
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
||||||
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
||||||
database.settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("cidr_string")))
|
database
|
||||||
.forEach((Consumer<? super Document>) doc -> {
|
.settingsDocument
|
||||||
BigInteger start = doc.get("ip_start", Decimal128.class).bigDecimalValue().toBigInteger();
|
.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("cidr_string")))
|
||||||
BigInteger end = doc.get("ip_end", Decimal128.class).bigDecimalValue().toBigInteger();
|
.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 {
|
try {
|
||||||
var range = MiscUtils.rangeToCidrs(start, end);
|
var range = MiscUtils.rangeToCidrs(start, end);
|
||||||
@@ -51,40 +56,64 @@ public class MongoThird implements Version<MongoVPN> {
|
|||||||
if (range.size() > 1) {
|
if (range.size() > 1) {
|
||||||
rangesToRemove.add(new BigInteger[] {start, end});
|
rangesToRemove.add(new BigInteger[] {start, end});
|
||||||
rangesToInsert.addAll(range);
|
rangesToInsert.addAll(range);
|
||||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "Found multiple CIDR ranges for whitelist range for %s, %s!", start, end);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
Level.WARNING,
|
||||||
|
"Found multiple CIDR ranges for whitelist range for %s, %s!",
|
||||||
|
start,
|
||||||
|
end);
|
||||||
} else ipRanges.addAll(range);
|
} else ipRanges.addAll(range);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException(
|
AntiVPN.getInstance()
|
||||||
String.format("Could not convert ip range to CIDR! %s, %s", start, end), e);
|
.getExecutor()
|
||||||
|
.logException(
|
||||||
|
String.format("Could not convert ip range to CIDR! %s, %s", start, end),
|
||||||
|
e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!rangesToInsert.isEmpty()) {
|
if (!rangesToInsert.isEmpty()) {
|
||||||
AntiVPN.getInstance().getExecutor().log("Inserting %s new ranges into database...", rangesToInsert.size());
|
AntiVPN.getInstance()
|
||||||
var documentsToInsert = rangesToInsert.stream().map(cidr -> {
|
.getExecutor()
|
||||||
|
.log("Inserting %s new ranges into database...", rangesToInsert.size());
|
||||||
|
var documentsToInsert =
|
||||||
|
rangesToInsert.stream()
|
||||||
|
.map(
|
||||||
|
cidr -> {
|
||||||
Document doc = new Document("setting", "whitelist");
|
Document doc = new Document("setting", "whitelist");
|
||||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||||
doc.append("cidr_string", cidr.getCidr());
|
doc.append("cidr_string", cidr.getCidr());
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
}).toList();
|
})
|
||||||
|
.toList();
|
||||||
|
|
||||||
database.settingsDocument.insertMany(documentsToInsert);
|
database.settingsDocument.insertMany(documentsToInsert);
|
||||||
}
|
}
|
||||||
if (!rangesToRemove.isEmpty()) {
|
if (!rangesToRemove.isEmpty()) {
|
||||||
AntiVPN.getInstance().getExecutor().log("Removing %s old ranges from database...", rangesToRemove.size());
|
AntiVPN.getInstance()
|
||||||
rangesToRemove.forEach(range -> database.settingsDocument
|
.getExecutor()
|
||||||
.deleteMany(Filters.and(
|
.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.gte("ip_start", new Decimal128(new BigDecimal(range[0]))),
|
||||||
Filters.lte("ip_end", new Decimal128(new BigDecimal(range[1]))))));
|
Filters.lte("ip_end", new Decimal128(new BigDecimal(range[1]))))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ipRanges.isEmpty()) {
|
if (!ipRanges.isEmpty()) {
|
||||||
AntiVPN.getInstance().getExecutor().log("Updating %s CIDRs in database with proper notation...", ipRanges.size());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log("Updating %s CIDRs in database with proper notation...", ipRanges.size());
|
||||||
|
|
||||||
ipRanges.forEach(cidr -> database.settingsDocument
|
ipRanges.forEach(
|
||||||
.updateMany(Filters.and(Filters.eq("setting", "whitelist"),
|
cidr ->
|
||||||
|
database.settingsDocument.updateMany(
|
||||||
|
Filters.and(
|
||||||
|
Filters.eq("setting", "whitelist"),
|
||||||
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
||||||
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))),
|
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))),
|
||||||
new Document("$set", new Document("cidr_string", cidr.getCidr()))));
|
new Document("$set", new Document("cidr_string", cidr.getCidr()))));
|
||||||
|
|||||||
@@ -20,31 +20,41 @@ import dev.brighten.antivpn.AntiVPN;
|
|||||||
import dev.brighten.antivpn.database.local.H2VPN;
|
import dev.brighten.antivpn.database.local.H2VPN;
|
||||||
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
||||||
import dev.brighten.antivpn.database.version.Version;
|
import dev.brighten.antivpn.database.version.Version;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class MySqlVPN extends H2VPN {
|
public class MySqlVPN extends H2VPN {
|
||||||
|
|
||||||
public MySqlVPN() {
|
public MySqlVPN() {
|
||||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
AntiVPN.getInstance()
|
||||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
.getExecutor()
|
||||||
|
.getThreadExecutor()
|
||||||
|
.scheduleAtFixedRate(
|
||||||
|
() -> {
|
||||||
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||||
|
return;
|
||||||
|
|
||||||
// Refreshing whitelisted players
|
// Refreshing whitelisted players
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getWhitelisted()
|
||||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||||
|
|
||||||
// Refreshing whitlisted IPs
|
// Refreshing whitlisted IPs
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.getWhitelistedIps()
|
||||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||||
}, 2, 30, TimeUnit.SECONDS);
|
},
|
||||||
|
2,
|
||||||
|
30,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||||
return;
|
|
||||||
AntiVPN.getInstance().getExecutor().log("Initializing MySQL...");
|
AntiVPN.getInstance().getExecutor().log("Initializing MySQL...");
|
||||||
MySQL.init();
|
MySQL.init();
|
||||||
|
|
||||||
|
|||||||
+3
-5
@@ -16,15 +16,13 @@
|
|||||||
|
|
||||||
package dev.brighten.antivpn.database.sql.utils;
|
package dev.brighten.antivpn.database.sql.utils;
|
||||||
|
|
||||||
|
import java.sql.*;
|
||||||
|
import java.util.UUID;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.sql.*;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class ExecutableStatement implements AutoCloseable {
|
public class ExecutableStatement implements AutoCloseable {
|
||||||
@Getter
|
@Getter private final PreparedStatement statement;
|
||||||
private final PreparedStatement statement;
|
|
||||||
private int pos = 1;
|
private int pos = 1;
|
||||||
|
|
||||||
public ExecutableStatement(PreparedStatement statement) {
|
public ExecutableStatement(PreparedStatement statement) {
|
||||||
|
|||||||
@@ -18,9 +18,6 @@ package dev.brighten.antivpn.database.sql.utils;
|
|||||||
|
|
||||||
import com.mysql.cj.jdbc.Driver;
|
import com.mysql.cj.jdbc.Driver;
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import org.h2.jdbc.JdbcSQLFeatureNotSupportedException;
|
|
||||||
import org.h2.jdbc.JdbcSQLNonTransientConnectionException;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -29,6 +26,8 @@ import java.sql.SQLException;
|
|||||||
import java.sql.SQLSyntaxErrorException;
|
import java.sql.SQLSyntaxErrorException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import org.h2.jdbc.JdbcSQLFeatureNotSupportedException;
|
||||||
|
import org.h2.jdbc.JdbcSQLNonTransientConnectionException;
|
||||||
|
|
||||||
public class MySQL {
|
public class MySQL {
|
||||||
private static Connection conn;
|
private static Connection conn;
|
||||||
@@ -36,8 +35,11 @@ public class MySQL {
|
|||||||
public static void init() {
|
public static void init() {
|
||||||
try {
|
try {
|
||||||
if (conn == null || conn.isClosed()) {
|
if (conn == null || conn.isClosed()) {
|
||||||
String url = "jdbc:mysql://" + AntiVPN.getInstance().getVpnConfig().getIp()
|
String url =
|
||||||
+ ":" + AntiVPN.getInstance().getVpnConfig().getPort()
|
"jdbc:mysql://"
|
||||||
|
+ AntiVPN.getInstance().getVpnConfig().getIp()
|
||||||
|
+ ":"
|
||||||
|
+ AntiVPN.getInstance().getVpnConfig().getPort()
|
||||||
+ "/?useSSL=true&autoReconnect=true";
|
+ "/?useSSL=true&autoReconnect=true";
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
properties.setProperty("user", AntiVPN.getInstance().getVpnConfig().getUsername());
|
properties.setProperty("user", AntiVPN.getInstance().getVpnConfig().getUsername());
|
||||||
@@ -58,8 +60,11 @@ public class MySQL {
|
|||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
AntiVPN.getInstance().getExecutor().log(
|
AntiVPN.getInstance()
|
||||||
"No permission to create MySQL database `" + databaseName
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
"No permission to create MySQL database `"
|
||||||
|
+ databaseName
|
||||||
+ "`. Attempting to use the existing database instead.");
|
+ "`. Attempting to use the existing database instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +72,9 @@ public class MySQL {
|
|||||||
AntiVPN.getInstance().getExecutor().log("Connection to MySQL has been established.");
|
AntiVPN.getInstance().getExecutor().log("Connection to MySQL has been established.");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Failed to load mysql: " + e.getMessage(), e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Failed to load mysql: " + e.getMessage(), e);
|
||||||
throw new RuntimeException("Could not initialize MySQL connection", e);
|
throw new RuntimeException("Could not initialize MySQL connection", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,10 +99,14 @@ public class MySQL {
|
|||||||
|
|
||||||
File databaseFile = new File(dataFolder, "database");
|
File databaseFile = new File(dataFolder, "database");
|
||||||
try {
|
try {
|
||||||
conn = new NonClosableConnection(new org.h2.jdbc.JdbcConnection("jdbc:h2:file:" +
|
conn =
|
||||||
databaseFile.getAbsolutePath(),
|
new NonClosableConnection(
|
||||||
new Properties(), AntiVPN.getInstance().getVpnConfig().getUsername(),
|
new org.h2.jdbc.JdbcConnection(
|
||||||
AntiVPN.getInstance().getVpnConfig().getPassword(), false));
|
"jdbc:h2:file:" + databaseFile.getAbsolutePath(),
|
||||||
|
new Properties(),
|
||||||
|
AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||||
|
AntiVPN.getInstance().getVpnConfig().getPassword(),
|
||||||
|
false));
|
||||||
conn.setAutoCommit(true);
|
conn.setAutoCommit(true);
|
||||||
Query.use(conn);
|
Query.use(conn);
|
||||||
AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established.");
|
AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established.");
|
||||||
@@ -103,22 +114,31 @@ public class MySQL {
|
|||||||
AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize", ex);
|
AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize", ex);
|
||||||
if (ex instanceof JdbcSQLFeatureNotSupportedException
|
if (ex instanceof JdbcSQLFeatureNotSupportedException
|
||||||
|| ex instanceof JdbcSQLNonTransientConnectionException) {
|
|| ex instanceof JdbcSQLNonTransientConnectionException) {
|
||||||
AntiVPN.getInstance().getExecutor()
|
AntiVPN.getInstance()
|
||||||
.log("H2 database file is incompatible with this version of AntiVPN. " +
|
.getExecutor()
|
||||||
"Backing up old database file...");
|
.log(
|
||||||
|
"H2 database file is incompatible with this version of AntiVPN. "
|
||||||
|
+ "Backing up old database file...");
|
||||||
shutdown();
|
shutdown();
|
||||||
if (allowRetry && backupOldDB(dbFile, dataFolder)) {
|
if (allowRetry && backupOldDB(dbFile, dataFolder)) {
|
||||||
initH2(false);
|
initH2(false);
|
||||||
} else {
|
} else {
|
||||||
AntiVPN.getInstance().getExecutor().log(
|
AntiVPN.getInstance()
|
||||||
"Could not back up and remove the incompatible H2 database file automatically.");
|
.getExecutor()
|
||||||
|
.log("Could not back up and remove the incompatible H2 database file automatically.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + ex.getCause().toString(), ex);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Failed to load H2 database: " + ex.getCause().toString(), ex);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + e.getMessage(), e);
|
AntiVPN.getInstance()
|
||||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "TIP: Try deleting the plugin folder and restarting your server!");
|
.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!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +148,9 @@ public class MySQL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!dbFile.isFile()) {
|
if (!dbFile.isFile()) {
|
||||||
AntiVPN.getInstance().getExecutor().log("Skipping backup for non-file path: " + dbFile.getAbsolutePath());
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log("Skipping backup for non-file path: " + dbFile.getAbsolutePath());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,12 +164,15 @@ public class MySQL {
|
|||||||
AntiVPN.getInstance().getExecutor().log("Could not create backup directory");
|
AntiVPN.getInstance().getExecutor().log("Could not create backup directory");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
File backupFile = new File(backupDir, dbFile.getName() + ".backup_" + System.currentTimeMillis());
|
File backupFile =
|
||||||
|
new File(backupDir, dbFile.getName() + ".backup_" + System.currentTimeMillis());
|
||||||
Files.copy(dbFile.toPath(), backupFile.toPath());
|
Files.copy(dbFile.toPath(), backupFile.toPath());
|
||||||
|
|
||||||
if (!dbFile.delete()) {
|
if (!dbFile.delete()) {
|
||||||
dbFile.deleteOnExit();
|
dbFile.deleteOnExit();
|
||||||
AntiVPN.getInstance().getExecutor().log("Could not delete database file - will try again on shutdown");
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.log("Could not delete database file - will try again on shutdown");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,8 +207,7 @@ public class MySQL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isClosed() {
|
public static boolean isClosed() {
|
||||||
if(conn == null)
|
if (conn == null) return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return conn.isClosed();
|
return conn.isClosed();
|
||||||
|
|||||||
+265
-55
@@ -31,9 +31,7 @@ public class NonClosableConnection implements Connection {
|
|||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Actually {@link #close() closes} the underlying connection. */
|
||||||
* Actually {@link #close() closes} the underlying connection.
|
|
||||||
*/
|
|
||||||
public final void shutdown() throws SQLException {
|
public final void shutdown() throws SQLException {
|
||||||
this.delegate.close();
|
this.delegate.close();
|
||||||
}
|
}
|
||||||
@@ -58,56 +56,268 @@ public class NonClosableConnection implements Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Forward to the delegate connection
|
// Forward to the delegate connection
|
||||||
@Override public Statement createStatement() throws SQLException { return this.delegate.createStatement(); }
|
@Override
|
||||||
@Override public PreparedStatement prepareStatement(String sql) throws SQLException { return this.delegate.prepareStatement(sql); }
|
public Statement createStatement() throws SQLException {
|
||||||
@Override public CallableStatement prepareCall(String sql) throws SQLException { return this.delegate.prepareCall(sql); }
|
return this.delegate.createStatement();
|
||||||
@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
|
||||||
@Override public void commit() throws SQLException { this.delegate.commit(); }
|
public PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||||
@Override public void rollback() throws SQLException { this.delegate.rollback(); }
|
return this.delegate.prepareStatement(sql);
|
||||||
@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
|
||||||
@Override public boolean isReadOnly() throws SQLException { return this.delegate.isReadOnly(); }
|
public CallableStatement prepareCall(String sql) throws SQLException {
|
||||||
@Override public void setCatalog(String catalog) throws SQLException { this.delegate.setCatalog(catalog); }
|
return this.delegate.prepareCall(sql);
|
||||||
@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
|
||||||
@Override public SQLWarning getWarnings() throws SQLException { return this.delegate.getWarnings(); }
|
public String nativeSQL(String sql) throws SQLException {
|
||||||
@Override public void clearWarnings() throws SQLException { this.delegate.clearWarnings(); }
|
return this.delegate.nativeSQL(sql);
|
||||||
@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
|
||||||
@Override public Map<String, Class<?>> getTypeMap() throws SQLException { return this.delegate.getTypeMap(); }
|
public void setAutoCommit(boolean autoCommit) throws SQLException {
|
||||||
@Override public void setTypeMap(Map<String, Class<?>> map) throws SQLException { this.delegate.setTypeMap(map); }
|
this.delegate.setAutoCommit(autoCommit);
|
||||||
@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
|
||||||
@Override public Savepoint setSavepoint(String name) throws SQLException { return this.delegate.setSavepoint(name); }
|
public boolean getAutoCommit() throws SQLException {
|
||||||
@Override public void rollback(Savepoint savepoint) throws SQLException { this.delegate.rollback(savepoint); }
|
return this.delegate.getAutoCommit();
|
||||||
@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
|
||||||
@Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); }
|
public void commit() throws SQLException {
|
||||||
@Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { return this.delegate.prepareStatement(sql, autoGeneratedKeys); }
|
this.delegate.commit();
|
||||||
@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
|
||||||
@Override public Blob createBlob() throws SQLException { return this.delegate.createBlob(); }
|
public void rollback() throws SQLException {
|
||||||
@Override public NClob createNClob() throws SQLException { return this.delegate.createNClob(); }
|
this.delegate.rollback();
|
||||||
@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
|
||||||
@Override public void setClientInfo(Properties properties) throws SQLClientInfoException { this.delegate.setClientInfo(properties); }
|
public boolean isClosed() throws SQLException {
|
||||||
@Override public String getClientInfo(String name) throws SQLException { return this.delegate.getClientInfo(name); }
|
return this.delegate.isClosed();
|
||||||
@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
|
||||||
@Override public void setSchema(String schema) throws SQLException { this.delegate.setSchema(schema); }
|
public DatabaseMetaData getMetaData() throws SQLException {
|
||||||
@Override public String getSchema() throws SQLException { return this.delegate.getSchema(); }
|
return this.delegate.getMetaData();
|
||||||
@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 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,15 +16,13 @@
|
|||||||
|
|
||||||
package dev.brighten.antivpn.database.sql.utils;
|
package dev.brighten.antivpn.database.sql.utils;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.intellij.lang.annotations.Language;
|
import org.intellij.lang.annotations.Language;
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
public class Query {
|
public class Query {
|
||||||
@Getter
|
@Getter private static Connection conn;
|
||||||
private static Connection conn;
|
|
||||||
|
|
||||||
public static void use(Connection conn) {
|
public static void use(Connection conn) {
|
||||||
Query.conn = conn;
|
Query.conn = conn;
|
||||||
@@ -34,6 +32,4 @@ public class Query {
|
|||||||
public static ExecutableStatement prepare(@Language("SQL") String sql) throws SQLException {
|
public static ExecutableStatement prepare(@Language("SQL") String sql) throws SQLException {
|
||||||
return new ExecutableStatement(conn.prepareStatement(sql));
|
return new ExecutableStatement(conn.prepareStatement(sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-8
@@ -21,26 +21,29 @@ import dev.brighten.antivpn.database.DatabaseException;
|
|||||||
import dev.brighten.antivpn.database.VPNDatabase;
|
import dev.brighten.antivpn.database.VPNDatabase;
|
||||||
import dev.brighten.antivpn.database.local.version.First;
|
import dev.brighten.antivpn.database.local.version.First;
|
||||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public class MySQLFirst extends First {
|
public class MySQLFirst extends First {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(VPNDatabase database) throws DatabaseException {
|
public void update(VPNDatabase database) throws DatabaseException {
|
||||||
try(var statement = Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " +
|
try (var statement =
|
||||||
"WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';")) {
|
Query.prepare(
|
||||||
statement.execute(set -> {
|
"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")) {
|
if (set.getObject("DATA_TYPE").toString().contains("varchar")) {
|
||||||
AntiVPN.getInstance().getExecutor().log("Using old database format for storing responses! " +
|
AntiVPN.getInstance()
|
||||||
"Dropping table and creating a new one...");
|
.getExecutor()
|
||||||
|
.log(
|
||||||
|
"Using old database format for storing responses! "
|
||||||
|
+ "Dropping table and creating a new one...");
|
||||||
try (var state = Query.prepare("drop table `responses`")) {
|
try (var state = Query.prepare("drop table `responses`")) {
|
||||||
if (state.execute() > 0) {
|
if (state.execute() > 0) {
|
||||||
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
|
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
|||||||
@@ -28,13 +28,15 @@ import dev.brighten.antivpn.database.mongo.version.MongoThird;
|
|||||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||||
import dev.brighten.antivpn.database.sql.version.MySQLFirst;
|
import dev.brighten.antivpn.database.sql.version.MySQLFirst;
|
||||||
|
|
||||||
|
|
||||||
public interface Version<DB> {
|
public interface Version<DB> {
|
||||||
void update(DB database) throws DatabaseException;
|
void update(DB database) throws DatabaseException;
|
||||||
|
|
||||||
int versionNumber();
|
int versionNumber();
|
||||||
|
|
||||||
boolean needsUpdate(DB database);
|
boolean needsUpdate(DB database);
|
||||||
|
|
||||||
Version<MongoVPN>[] mongoDbVersions = new Version[] {new MongoFirst(), new MongoSecond(), new MongoThird()};
|
Version<MongoVPN>[] mongoDbVersions =
|
||||||
|
new Version[] {new MongoFirst(), new MongoSecond(), new MongoThird()};
|
||||||
Version<MySqlVPN>[] mysqlVersions = new Version[] {new MySQLFirst(), new Second(), new Third()};
|
Version<MySqlVPN>[] mysqlVersions = new Version[] {new MySQLFirst(), new Second(), new Third()};
|
||||||
Version<H2VPN>[] h2Versions = new Version[] {new First(), new Second(), new Third()};
|
Version<H2VPN>[] h2Versions = new Version[] {new First(), new Second(), new Third()};
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,16 @@ import dev.brighten.antivpn.AntiVPN;
|
|||||||
import dev.brighten.antivpn.utils.NonnullByDefault;
|
import dev.brighten.antivpn.utils.NonnullByDefault;
|
||||||
import dev.brighten.antivpn.utils.Supplier;
|
import dev.brighten.antivpn.utils.Supplier;
|
||||||
import dev.brighten.antivpn.utils.Suppliers;
|
import dev.brighten.antivpn.utils.Suppliers;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.objectweb.asm.AnnotationVisitor;
|
import org.objectweb.asm.AnnotationVisitor;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
@@ -32,20 +42,9 @@ import org.objectweb.asm.RecordComponentVisitor;
|
|||||||
import org.objectweb.asm.commons.ClassRemapper;
|
import org.objectweb.asm.commons.ClassRemapper;
|
||||||
import org.objectweb.asm.commons.Remapper;
|
import org.objectweb.asm.commons.Remapper;
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
import java.util.jar.JarFile;
|
|
||||||
import java.util.jar.JarOutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves {@link MavenLibrary} annotations for a class, and loads the dependency
|
* Resolves {@link MavenLibrary} annotations for a class, and loads the dependency into the
|
||||||
* into the classloader.
|
* classloader.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("CallToPrintStackTrace")
|
@SuppressWarnings("CallToPrintStackTrace")
|
||||||
@NonnullByDefault
|
@NonnullByDefault
|
||||||
@@ -54,20 +53,21 @@ public final class LibraryLoader {
|
|||||||
private static final String RELOCATION_METADATA_PATH = "META-INF/antivpn-relocation.properties";
|
private static final String RELOCATION_METADATA_PATH = "META-INF/antivpn-relocation.properties";
|
||||||
|
|
||||||
@SuppressWarnings("Guava")
|
@SuppressWarnings("Guava")
|
||||||
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = AntiVPN.getInstance().getClass().getClassLoader() instanceof URLClassLoader ?
|
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR =
|
||||||
Suppliers.memoize(() ->
|
AntiVPN.getInstance().getClass().getClassLoader() instanceof URLClassLoader
|
||||||
URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader()))
|
? Suppliers.memoize(
|
||||||
|
() ->
|
||||||
|
URLClassLoaderAccess.create(
|
||||||
|
(URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader()))
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
public static void loadAll(Object object) {
|
public static void loadAll(Object object) {
|
||||||
if(URL_INJECTOR == null)
|
if (URL_INJECTOR == null) return;
|
||||||
return;
|
|
||||||
loadAll(object.getClass());
|
loadAll(object.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadAll(Class<?> clazz) {
|
public static void loadAll(Class<?> clazz) {
|
||||||
if(URL_INJECTOR == null)
|
if (URL_INJECTOR == null) return;
|
||||||
return;
|
|
||||||
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
|
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
|
||||||
|
|
||||||
for (MavenLibrary lib : libs) {
|
for (MavenLibrary lib : libs) {
|
||||||
@@ -77,17 +77,27 @@ public final class LibraryLoader {
|
|||||||
relocations.put(relocate.from().replace("\\", ""), relocate.to());
|
relocations.put(relocate.from().replace("\\", ""), relocate.to());
|
||||||
}
|
}
|
||||||
|
|
||||||
load(lib.groupId().replace("\\", ""), lib.artifactId(), lib.version(), lib.repo().url(), relocations);
|
load(
|
||||||
|
lib.groupId().replace("\\", ""),
|
||||||
|
lib.artifactId(),
|
||||||
|
lib.version(),
|
||||||
|
lib.repo().url(),
|
||||||
|
relocations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void load(String groupId, String artifactId, String version, String repoUrl,
|
public static void load(
|
||||||
|
String groupId,
|
||||||
|
String artifactId,
|
||||||
|
String version,
|
||||||
|
String repoUrl,
|
||||||
Map<String, String> relocations) {
|
Map<String, String> relocations) {
|
||||||
load(new Dependency(groupId, artifactId, version, repoUrl), relocations);
|
load(new Dependency(groupId, artifactId, version, repoUrl), relocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void load(Dependency d, Map<String, String> relocations) {
|
public static void load(Dependency d, Map<String, String> relocations) {
|
||||||
System.out.printf("Loading dependency %s:%s:%s from %s%n",
|
System.out.printf(
|
||||||
|
"Loading dependency %s:%s:%s from %s%n",
|
||||||
d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl());
|
d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl());
|
||||||
String name = d.getArtifactId() + "-" + d.getVersion();
|
String name = d.getArtifactId() + "-" + d.getVersion();
|
||||||
|
|
||||||
@@ -103,8 +113,10 @@ public final class LibraryLoader {
|
|||||||
// Download the original jar if it doesn't exist
|
// Download the original jar if it doesn't exist
|
||||||
if (!originalJar.exists()) {
|
if (!originalJar.exists()) {
|
||||||
try {
|
try {
|
||||||
System.out.println("Dependency '" + name +
|
System.out.println(
|
||||||
"' is not already in the libraries folder. Attempting to download...");
|
"Dependency '"
|
||||||
|
+ name
|
||||||
|
+ "' is not already in the libraries folder. Attempting to download...");
|
||||||
URL url = d.getUrl();
|
URL url = d.getUrl();
|
||||||
|
|
||||||
try (InputStream is = url.openStream()) {
|
try (InputStream is = url.openStream()) {
|
||||||
@@ -182,7 +194,8 @@ public final class LibraryLoader {
|
|||||||
jos.write(relocatedBytes);
|
jos.write(relocatedBytes);
|
||||||
jos.closeEntry();
|
jos.closeEntry();
|
||||||
} else {
|
} else {
|
||||||
// Relocate package-scoped resources so ResourceBundle lookups follow relocated packages.
|
// Relocate package-scoped resources so ResourceBundle lookups follow relocated
|
||||||
|
// packages.
|
||||||
String relocatedPath = relocateResourcePath(name, relocations);
|
String relocatedPath = relocateResourcePath(name, relocations);
|
||||||
|
|
||||||
JarEntry newEntry = new JarEntry(relocatedPath);
|
JarEntry newEntry = new JarEntry(relocatedPath);
|
||||||
@@ -202,8 +215,8 @@ public final class LibraryLoader {
|
|||||||
jos.closeEntry();
|
jos.closeEntry();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Log but continue with other service files
|
// Log but continue with other service files
|
||||||
System.out.println("Warning: Could not write service file " +
|
System.out.println(
|
||||||
entry.getKey() + ": " + e.getMessage());
|
"Warning: Could not write service file " + entry.getKey() + ": " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +226,8 @@ public final class LibraryLoader {
|
|||||||
validateRelocatedJar(targetJar, relocations);
|
validateRelocatedJar(targetJar, relocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldRebuildRelocatedJar(File relocatedJar, Map<String, String> relocations) {
|
private static boolean shouldRebuildRelocatedJar(
|
||||||
|
File relocatedJar, Map<String, String> relocations) {
|
||||||
if (!relocatedJar.exists()) {
|
if (!relocatedJar.exists()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -229,7 +243,8 @@ public final class LibraryLoader {
|
|||||||
metadata.load(is);
|
metadata.load(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!String.valueOf(RELOCATION_FORMAT_VERSION).equals(metadata.getProperty("formatVersion"))) {
|
if (!String.valueOf(RELOCATION_FORMAT_VERSION)
|
||||||
|
.equals(metadata.getProperty("formatVersion"))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,9 +281,12 @@ public final class LibraryLoader {
|
|||||||
jos.closeEntry();
|
jos.closeEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void processServiceFile(String name, InputStream is,
|
private static void processServiceFile(
|
||||||
|
String name,
|
||||||
|
InputStream is,
|
||||||
Map<String, StringBuilder> serviceFiles,
|
Map<String, StringBuilder> serviceFiles,
|
||||||
Map<String, String> relocations) throws IOException {
|
Map<String, String> relocations)
|
||||||
|
throws IOException {
|
||||||
// Read service file content
|
// Read service file content
|
||||||
String content = new String(readAllBytes(is));
|
String content = new String(readAllBytes(is));
|
||||||
StringBuilder contentBuilder = serviceFiles.computeIfAbsent(name, k -> new StringBuilder());
|
StringBuilder contentBuilder = serviceFiles.computeIfAbsent(name, k -> new StringBuilder());
|
||||||
@@ -279,8 +297,7 @@ public final class LibraryLoader {
|
|||||||
if (!trimmed.isEmpty() && !trimmed.startsWith("#")) {
|
if (!trimmed.isEmpty() && !trimmed.startsWith("#")) {
|
||||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||||
if (trimmed.startsWith(relocation.getKey())) {
|
if (trimmed.startsWith(relocation.getKey())) {
|
||||||
trimmed = relocation.getValue() +
|
trimmed = relocation.getValue() + trimmed.substring(relocation.getKey().length());
|
||||||
trimmed.substring(relocation.getKey().length());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,14 +306,16 @@ public final class LibraryLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] relocateClass(String entryName, byte[] classBytes, Map<String, String> relocations) {
|
private static byte[] relocateClass(
|
||||||
|
String entryName, byte[] classBytes, Map<String, String> relocations) {
|
||||||
try {
|
try {
|
||||||
// Convert to slash notation for ASM
|
// Convert to slash notation for ASM
|
||||||
Remapper prefixRemapper = getPrefixRemapper(relocations);
|
Remapper prefixRemapper = getPrefixRemapper(relocations);
|
||||||
|
|
||||||
// Create custom ClassWriter to handle missing classes
|
// Create custom ClassWriter to handle missing classes
|
||||||
ClassReader reader = new ClassReader(classBytes);
|
ClassReader reader = new ClassReader(classBytes);
|
||||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
ClassWriter writer =
|
||||||
|
new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
||||||
@Override
|
@Override
|
||||||
protected String getCommonSuperClass(String type1, String type2) {
|
protected String getCommonSuperClass(String type1, String type2) {
|
||||||
try {
|
try {
|
||||||
@@ -308,7 +327,8 @@ public final class LibraryLoader {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ClassVisitor visitor = createStringRelocationVisitor(new ClassRemapper(writer, prefixRemapper), relocations);
|
ClassVisitor visitor =
|
||||||
|
createStringRelocationVisitor(new ClassRemapper(writer, prefixRemapper), relocations);
|
||||||
visitor = createMySqlUtilFallbackVisitor(entryName, visitor);
|
visitor = createMySqlUtilFallbackVisitor(entryName, visitor);
|
||||||
|
|
||||||
// Process class with remapper
|
// Process class with remapper
|
||||||
@@ -332,7 +352,8 @@ public final class LibraryLoader {
|
|||||||
return className;
|
return className;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] relocateUtf8Constants(byte[] classBytes, Map<String, String> relocations) throws IOException {
|
private static byte[] relocateUtf8Constants(byte[] classBytes, Map<String, String> relocations)
|
||||||
|
throws IOException {
|
||||||
Map<String, String> dotMappings = new HashMap<>();
|
Map<String, String> dotMappings = new HashMap<>();
|
||||||
Map<String, String> slashMappings = new HashMap<>();
|
Map<String, String> slashMappings = new HashMap<>();
|
||||||
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
||||||
@@ -421,22 +442,24 @@ public final class LibraryLoader {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClassVisitor createMySqlUtilFallbackVisitor(String entryName, ClassVisitor delegate) {
|
private static ClassVisitor createMySqlUtilFallbackVisitor(
|
||||||
|
String entryName, ClassVisitor delegate) {
|
||||||
if (!"com/mysql/cj/util/Util.class".equals(entryName)) {
|
if (!"com/mysql/cj/util/Util.class".equals(entryName)) {
|
||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ClassVisitor(Opcodes.ASM9, delegate) {
|
return new ClassVisitor(Opcodes.ASM9, delegate) {
|
||||||
@Override
|
@Override
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
|
public MethodVisitor visitMethod(
|
||||||
String[] exceptions) {
|
int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||||
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (visitor == null) {
|
if (visitor == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!"getInstance".equals(name)
|
if (!"getInstance".equals(name)
|
||||||
|| !"(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;[Ljava/lang/Object;Lcom/mysql/cj/exceptions/ExceptionInterceptor;)Ljava/lang/Object;".equals(descriptor)) {
|
|| !"(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;[Ljava/lang/Object;Lcom/mysql/cj/exceptions/ExceptionInterceptor;)Ljava/lang/Object;"
|
||||||
|
.equals(descriptor)) {
|
||||||
return visitor;
|
return visitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,7 +468,8 @@ public final class LibraryLoader {
|
|||||||
public void visitCode() {
|
public void visitCode() {
|
||||||
super.visitCode();
|
super.visitCode();
|
||||||
super.visitVarInsn(Opcodes.ALOAD, 1);
|
super.visitVarInsn(Opcodes.ALOAD, 1);
|
||||||
super.visitMethodInsn(Opcodes.INVOKESTATIC,
|
super.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESTATIC,
|
||||||
"dev/brighten/antivpn/depends/LibraryLoader",
|
"dev/brighten/antivpn/depends/LibraryLoader",
|
||||||
"relocateReflectiveClassName",
|
"relocateReflectiveClassName",
|
||||||
"(Ljava/lang/String;)Ljava/lang/String;",
|
"(Ljava/lang/String;)Ljava/lang/String;",
|
||||||
@@ -457,8 +481,8 @@ public final class LibraryLoader {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClassVisitor createStringRelocationVisitor(ClassVisitor delegate,
|
private static ClassVisitor createStringRelocationVisitor(
|
||||||
Map<String, String> relocations) {
|
ClassVisitor delegate, Map<String, String> relocations) {
|
||||||
Map<String, String> dotMappings = new HashMap<>();
|
Map<String, String> dotMappings = new HashMap<>();
|
||||||
Map<String, String> slashMappings = new HashMap<>();
|
Map<String, String> slashMappings = new HashMap<>();
|
||||||
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
||||||
@@ -469,18 +493,22 @@ public final class LibraryLoader {
|
|||||||
return new ClassVisitor(Opcodes.ASM9, delegate) {
|
return new ClassVisitor(Opcodes.ASM9, delegate) {
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||||
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible), dotMappings, slashMappings);
|
return wrapAnnotationVisitor(
|
||||||
|
super.visitAnnotation(descriptor, visible), dotMappings, slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
public AnnotationVisitor visitTypeAnnotation(
|
||||||
String descriptor, boolean visible) {
|
int typeRef, org.objectweb.asm.TypePath typePath, String descriptor, boolean visible) {
|
||||||
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
return wrapAnnotationVisitor(
|
||||||
dotMappings, slashMappings);
|
super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
||||||
|
dotMappings,
|
||||||
|
slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
|
public RecordComponentVisitor visitRecordComponent(
|
||||||
|
String name, String descriptor, String signature) {
|
||||||
RecordComponentVisitor visitor = super.visitRecordComponent(name, descriptor, signature);
|
RecordComponentVisitor visitor = super.visitRecordComponent(name, descriptor, signature);
|
||||||
if (visitor == null) {
|
if (visitor == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -488,22 +516,33 @@ public final class LibraryLoader {
|
|||||||
return new RecordComponentVisitor(Opcodes.ASM9, visitor) {
|
return new RecordComponentVisitor(Opcodes.ASM9, visitor) {
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||||
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
|
return wrapAnnotationVisitor(
|
||||||
dotMappings, slashMappings);
|
super.visitAnnotation(descriptor, visible), dotMappings, slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
public AnnotationVisitor visitTypeAnnotation(
|
||||||
String descriptor, boolean visible) {
|
int typeRef,
|
||||||
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
org.objectweb.asm.TypePath typePath,
|
||||||
dotMappings, slashMappings);
|
String descriptor,
|
||||||
|
boolean visible) {
|
||||||
|
return wrapAnnotationVisitor(
|
||||||
|
super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
||||||
|
dotMappings,
|
||||||
|
slashMappings);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
public FieldVisitor visitField(
|
||||||
FieldVisitor visitor = super.visitField(access, name, descriptor, signature,
|
int access, String name, String descriptor, String signature, Object value) {
|
||||||
|
FieldVisitor visitor =
|
||||||
|
super.visitField(
|
||||||
|
access,
|
||||||
|
name,
|
||||||
|
descriptor,
|
||||||
|
signature,
|
||||||
relocateAsmValue(value, dotMappings, slashMappings));
|
relocateAsmValue(value, dotMappings, slashMappings));
|
||||||
if (visitor == null) {
|
if (visitor == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -511,22 +550,27 @@ public final class LibraryLoader {
|
|||||||
return new FieldVisitor(Opcodes.ASM9, visitor) {
|
return new FieldVisitor(Opcodes.ASM9, visitor) {
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||||
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
|
return wrapAnnotationVisitor(
|
||||||
dotMappings, slashMappings);
|
super.visitAnnotation(descriptor, visible), dotMappings, slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
public AnnotationVisitor visitTypeAnnotation(
|
||||||
String descriptor, boolean visible) {
|
int typeRef,
|
||||||
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
org.objectweb.asm.TypePath typePath,
|
||||||
dotMappings, slashMappings);
|
String descriptor,
|
||||||
|
boolean visible) {
|
||||||
|
return wrapAnnotationVisitor(
|
||||||
|
super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
||||||
|
dotMappings,
|
||||||
|
slashMappings);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
|
public MethodVisitor visitMethod(
|
||||||
String[] exceptions) {
|
int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||||
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
|
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
if (visitor == null) {
|
if (visitor == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -534,53 +578,75 @@ public final class LibraryLoader {
|
|||||||
return new MethodVisitor(Opcodes.ASM9, visitor) {
|
return new MethodVisitor(Opcodes.ASM9, visitor) {
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotationDefault() {
|
public AnnotationVisitor visitAnnotationDefault() {
|
||||||
return wrapAnnotationVisitor(super.visitAnnotationDefault(), dotMappings, slashMappings);
|
return wrapAnnotationVisitor(
|
||||||
|
super.visitAnnotationDefault(), dotMappings, slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||||
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
|
return wrapAnnotationVisitor(
|
||||||
dotMappings, slashMappings);
|
super.visitAnnotation(descriptor, visible), dotMappings, slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
public AnnotationVisitor visitTypeAnnotation(
|
||||||
String descriptor, boolean visible) {
|
int typeRef,
|
||||||
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
org.objectweb.asm.TypePath typePath,
|
||||||
dotMappings, slashMappings);
|
String descriptor,
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor,
|
|
||||||
boolean visible) {
|
boolean visible) {
|
||||||
return wrapAnnotationVisitor(super.visitParameterAnnotation(parameter, descriptor, visible),
|
return wrapAnnotationVisitor(
|
||||||
dotMappings, slashMappings);
|
super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
||||||
|
dotMappings,
|
||||||
|
slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitInsnAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
public AnnotationVisitor visitParameterAnnotation(
|
||||||
String descriptor, boolean visible) {
|
int parameter, String descriptor, boolean visible) {
|
||||||
return wrapAnnotationVisitor(super.visitInsnAnnotation(typeRef, typePath, descriptor, visible),
|
return wrapAnnotationVisitor(
|
||||||
dotMappings, slashMappings);
|
super.visitParameterAnnotation(parameter, descriptor, visible),
|
||||||
|
dotMappings,
|
||||||
|
slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
public AnnotationVisitor visitInsnAnnotation(
|
||||||
String descriptor, boolean visible) {
|
int typeRef,
|
||||||
return wrapAnnotationVisitor(super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible),
|
org.objectweb.asm.TypePath typePath,
|
||||||
dotMappings, slashMappings);
|
String descriptor,
|
||||||
|
boolean visible) {
|
||||||
|
return wrapAnnotationVisitor(
|
||||||
|
super.visitInsnAnnotation(typeRef, typePath, descriptor, visible),
|
||||||
|
dotMappings,
|
||||||
|
slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
|
public AnnotationVisitor visitTryCatchAnnotation(
|
||||||
|
int typeRef,
|
||||||
|
org.objectweb.asm.TypePath typePath,
|
||||||
|
String descriptor,
|
||||||
|
boolean visible) {
|
||||||
|
return wrapAnnotationVisitor(
|
||||||
|
super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible),
|
||||||
|
dotMappings,
|
||||||
|
slashMappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationVisitor visitLocalVariableAnnotation(
|
||||||
|
int typeRef,
|
||||||
org.objectweb.asm.TypePath typePath,
|
org.objectweb.asm.TypePath typePath,
|
||||||
org.objectweb.asm.Label[] start,
|
org.objectweb.asm.Label[] start,
|
||||||
org.objectweb.asm.Label[] end,
|
org.objectweb.asm.Label[] end,
|
||||||
int[] index, String descriptor,
|
int[] index,
|
||||||
|
String descriptor,
|
||||||
boolean visible) {
|
boolean visible) {
|
||||||
return wrapAnnotationVisitor(
|
return wrapAnnotationVisitor(
|
||||||
super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible),
|
super.visitLocalVariableAnnotation(
|
||||||
dotMappings, slashMappings);
|
typeRef, typePath, start, end, index, descriptor, visible),
|
||||||
|
dotMappings,
|
||||||
|
slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -589,11 +655,15 @@ public final class LibraryLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitInvokeDynamicInsn(String name, String descriptor, org.objectweb.asm.Handle bootstrapMethodHandle,
|
public void visitInvokeDynamicInsn(
|
||||||
|
String name,
|
||||||
|
String descriptor,
|
||||||
|
org.objectweb.asm.Handle bootstrapMethodHandle,
|
||||||
Object... bootstrapMethodArguments) {
|
Object... bootstrapMethodArguments) {
|
||||||
Object[] relocatedArgs = new Object[bootstrapMethodArguments.length];
|
Object[] relocatedArgs = new Object[bootstrapMethodArguments.length];
|
||||||
for (int i = 0; i < bootstrapMethodArguments.length; i++) {
|
for (int i = 0; i < bootstrapMethodArguments.length; i++) {
|
||||||
relocatedArgs[i] = relocateAsmValue(bootstrapMethodArguments[i], dotMappings, slashMappings);
|
relocatedArgs[i] =
|
||||||
|
relocateAsmValue(bootstrapMethodArguments[i], dotMappings, slashMappings);
|
||||||
}
|
}
|
||||||
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, relocatedArgs);
|
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, relocatedArgs);
|
||||||
}
|
}
|
||||||
@@ -602,7 +672,8 @@ public final class LibraryLoader {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AnnotationVisitor wrapAnnotationVisitor(AnnotationVisitor delegate,
|
private static AnnotationVisitor wrapAnnotationVisitor(
|
||||||
|
AnnotationVisitor delegate,
|
||||||
Map<String, String> dotMappings,
|
Map<String, String> dotMappings,
|
||||||
Map<String, String> slashMappings) {
|
Map<String, String> slashMappings) {
|
||||||
if (delegate == null) {
|
if (delegate == null) {
|
||||||
@@ -617,7 +688,8 @@ public final class LibraryLoader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
|
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
|
||||||
return wrapAnnotationVisitor(super.visitAnnotation(name, descriptor), dotMappings, slashMappings);
|
return wrapAnnotationVisitor(
|
||||||
|
super.visitAnnotation(name, descriptor), dotMappings, slashMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -627,8 +699,8 @@ public final class LibraryLoader {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object relocateAsmValue(Object value, Map<String, String> dotMappings,
|
private static Object relocateAsmValue(
|
||||||
Map<String, String> slashMappings) {
|
Object value, Map<String, String> dotMappings, Map<String, String> slashMappings) {
|
||||||
if (value instanceof String stringValue) {
|
if (value instanceof String stringValue) {
|
||||||
return relocateStringValue(stringValue, dotMappings, slashMappings);
|
return relocateStringValue(stringValue, dotMappings, slashMappings);
|
||||||
}
|
}
|
||||||
@@ -636,8 +708,8 @@ public final class LibraryLoader {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String relocateStringValue(String value, Map<String, String> dotMappings,
|
private static String relocateStringValue(
|
||||||
Map<String, String> slashMappings) {
|
String value, Map<String, String> dotMappings, Map<String, String> slashMappings) {
|
||||||
for (Map.Entry<String, String> entry : dotMappings.entrySet()) {
|
for (Map.Entry<String, String> entry : dotMappings.entrySet()) {
|
||||||
String from = entry.getKey();
|
String from = entry.getKey();
|
||||||
String relocated = relocateByPrefixes(value, from, entry.getValue(), '.', '$');
|
String relocated = relocateByPrefixes(value, from, entry.getValue(), '.', '$');
|
||||||
@@ -674,7 +746,8 @@ public final class LibraryLoader {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String relocateByPrefixes(String value, String from, String to, char... delimiters) {
|
private static String relocateByPrefixes(
|
||||||
|
String value, String from, String to, char... delimiters) {
|
||||||
if (value.equals(from)) {
|
if (value.equals(from)) {
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
@@ -688,14 +761,16 @@ public final class LibraryLoader {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateRelocatedJar(File targetJar, Map<String, String> relocations) throws IOException {
|
private static void validateRelocatedJar(File targetJar, Map<String, String> relocations)
|
||||||
|
throws IOException {
|
||||||
Set<String> relocatedPrefixes = new HashSet<>();
|
Set<String> relocatedPrefixes = new HashSet<>();
|
||||||
Map<String, String> dotMappings = new HashMap<>();
|
Map<String, String> dotMappings = new HashMap<>();
|
||||||
Map<String, String> slashMappings = new HashMap<>();
|
Map<String, String> slashMappings = new HashMap<>();
|
||||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||||
relocatedPrefixes.add(relocation.getValue().replace('.', '/') + "/");
|
relocatedPrefixes.add(relocation.getValue().replace('.', '/') + "/");
|
||||||
dotMappings.put(relocation.getKey(), relocation.getValue());
|
dotMappings.put(relocation.getKey(), relocation.getValue());
|
||||||
slashMappings.put(relocation.getKey().replace('.', '/'), relocation.getValue().replace('.', '/'));
|
slashMappings.put(
|
||||||
|
relocation.getKey().replace('.', '/'), relocation.getValue().replace('.', '/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
try (JarFile jar = new JarFile(targetJar)) {
|
try (JarFile jar = new JarFile(targetJar)) {
|
||||||
@@ -725,8 +800,12 @@ public final class LibraryLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void findUnrelocatedConstant(String entryName, byte[] classBytes, Map<String, String> dotMappings,
|
private static void findUnrelocatedConstant(
|
||||||
Map<String, String> slashMappings) throws IOException {
|
String entryName,
|
||||||
|
byte[] classBytes,
|
||||||
|
Map<String, String> dotMappings,
|
||||||
|
Map<String, String> slashMappings)
|
||||||
|
throws IOException {
|
||||||
DataInputStream in = new DataInputStream(new ByteArrayInputStream(classBytes));
|
DataInputStream in = new DataInputStream(new ByteArrayInputStream(classBytes));
|
||||||
in.readInt();
|
in.readInt();
|
||||||
in.readUnsignedShort();
|
in.readUnsignedShort();
|
||||||
@@ -740,8 +819,11 @@ public final class LibraryLoader {
|
|||||||
String value = in.readUTF();
|
String value = in.readUTF();
|
||||||
String relocated = relocateStringValue(value, dotMappings, slashMappings);
|
String relocated = relocateStringValue(value, dotMappings, slashMappings);
|
||||||
if (!value.equals(relocated)) {
|
if (!value.equals(relocated)) {
|
||||||
throw new IOException("Relocated jar still contains original reference '" + value
|
throw new IOException(
|
||||||
+ "' in class entry " + entryName);
|
"Relocated jar still contains original reference '"
|
||||||
|
+ value
|
||||||
|
+ "' in class entry "
|
||||||
|
+ entryName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 3, 4 -> in.readInt();
|
case 3, 4 -> in.readInt();
|
||||||
@@ -758,7 +840,9 @@ public final class LibraryLoader {
|
|||||||
in.readUnsignedByte();
|
in.readUnsignedByte();
|
||||||
in.readUnsignedShort();
|
in.readUnsignedShort();
|
||||||
}
|
}
|
||||||
default -> throw new IOException("Unknown constant pool tag " + tag + " while validating " + entryName);
|
default ->
|
||||||
|
throw new IOException(
|
||||||
|
"Unknown constant pool tag " + tag + " while validating " + entryName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,13 +936,17 @@ public final class LibraryLoader {
|
|||||||
repo += "%s/%s/%s/%s-%s.jar";
|
repo += "%s/%s/%s/%s-%s.jar";
|
||||||
|
|
||||||
// Always use original groupId for Maven repository URL
|
// Always use original groupId for Maven repository URL
|
||||||
String url = String.format(repo, this.originalGroupId.replace(".", "/"),
|
String url =
|
||||||
this.originalArtifactId, this.version, this.originalArtifactId, this.version);
|
String.format(
|
||||||
|
repo,
|
||||||
|
this.originalGroupId.replace(".", "/"),
|
||||||
|
this.originalArtifactId,
|
||||||
|
this.version,
|
||||||
|
this.originalArtifactId,
|
||||||
|
this.version);
|
||||||
return new URL(url);
|
return new URL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rest of the class unchanged
|
// Rest of the class unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,11 @@ package dev.brighten.antivpn.depends;
|
|||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/** Annotation to indicate the required libraries for a class. */
|
||||||
* Annotation to indicate the required libraries for a class.
|
|
||||||
*/
|
|
||||||
@Documented
|
@Documented
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface MavenLibraries {
|
public @interface MavenLibraries {
|
||||||
|
|
||||||
MavenLibrary[] value() default {};
|
MavenLibrary[] value() default {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ package dev.brighten.antivpn.depends;
|
|||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/** Annotation to indicate a required library for a class. */
|
||||||
* Annotation to indicate a required library for a class.
|
|
||||||
*/
|
|
||||||
@Documented
|
@Documented
|
||||||
@Repeatable(MavenLibraries.class)
|
@Repeatable(MavenLibraries.class)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@@ -56,5 +54,4 @@ public @interface MavenLibrary {
|
|||||||
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
|
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
|
||||||
|
|
||||||
Relocate[] relocations() default {}; // Add this line
|
Relocate[] relocations() default {}; // Add this line
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,5 +26,6 @@ import java.lang.annotation.Target;
|
|||||||
@Target({})
|
@Target({})
|
||||||
public @interface Relocate {
|
public @interface Relocate {
|
||||||
String from();
|
String from();
|
||||||
|
|
||||||
String to();
|
String to();
|
||||||
}
|
}
|
||||||
@@ -18,9 +18,7 @@ package dev.brighten.antivpn.depends;
|
|||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/** Represents a maven repository. */
|
||||||
* Represents a maven repository.
|
|
||||||
*/
|
|
||||||
@Documented
|
@Documented
|
||||||
@Target(ElementType.LOCAL_VARIABLE)
|
@Target(ElementType.LOCAL_VARIABLE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@@ -32,5 +30,4 @@ public @interface Repository {
|
|||||||
* @return the base url of the repository
|
* @return the base url of the repository
|
||||||
*/
|
*/
|
||||||
String url();
|
String url();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ import java.net.URL;
|
|||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/** Provides access to {@link URLClassLoader}#addURL. */
|
||||||
* Provides access to {@link URLClassLoader}#addURL.
|
|
||||||
*/
|
|
||||||
public abstract class URLClassLoaderAccess {
|
public abstract class URLClassLoaderAccess {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +47,6 @@ public abstract class URLClassLoaderAccess {
|
|||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given URL to the class loader.
|
* Adds the given URL to the class loader.
|
||||||
*
|
*
|
||||||
@@ -57,9 +54,7 @@ public abstract class URLClassLoaderAccess {
|
|||||||
*/
|
*/
|
||||||
public abstract void addURL(URL url);
|
public abstract void addURL(URL url);
|
||||||
|
|
||||||
/**
|
/** Accesses using reflection, not supported on Java 9+. */
|
||||||
* Accesses using reflection, not supported on Java 9+.
|
|
||||||
*/
|
|
||||||
private static class Reflection extends URLClassLoaderAccess {
|
private static class Reflection extends URLClassLoaderAccess {
|
||||||
private static final Method ADD_URL_METHOD;
|
private static final Method ADD_URL_METHOD;
|
||||||
|
|
||||||
@@ -137,7 +132,8 @@ public abstract class URLClassLoaderAccess {
|
|||||||
this.pathURLs = pathURLs;
|
this.pathURLs = pathURLs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
|
private static Object fetchField(final Class<?> clazz, final Object object, final String name)
|
||||||
|
throws NoSuchFieldException {
|
||||||
Field field = clazz.getDeclaredField(name);
|
Field field = clazz.getDeclaredField(name);
|
||||||
long offset = UNSAFE.objectFieldOffset(field);
|
long offset = UNSAFE.objectFieldOffset(field);
|
||||||
return UNSAFE.getObject(object, offset);
|
return UNSAFE.getObject(object, offset);
|
||||||
@@ -162,5 +158,4 @@ public abstract class URLClassLoaderAccess {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package dev.brighten.antivpn.message;
|
package dev.brighten.antivpn.message;
|
||||||
|
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -51,12 +50,18 @@ public class MessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void initStrings(Function<VpnString, String> getter) {
|
public void initStrings(Function<VpnString, String> getter) {
|
||||||
addString(new VpnString("command-misc-playerRequired",
|
addString(
|
||||||
"&cYou must be a player to execute this command!"), getter);
|
new VpnString(
|
||||||
addString(new VpnString("command-alerts-toggled",
|
"command-misc-playerRequired", "&cYou must be a player to execute this command!"),
|
||||||
"&7Your player proxy notifications have been set to: &e%state%"), getter);
|
getter);
|
||||||
addString(new VpnString("command-reload-complete",
|
addString(
|
||||||
"&aSuccessfully reloaded KauriVPN plugin!"), getter);
|
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);
|
addString(new VpnString("no-permission", "&cNo permission."), getter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,18 @@
|
|||||||
package dev.brighten.antivpn.message;
|
package dev.brighten.antivpn.message;
|
||||||
|
|
||||||
import dev.brighten.antivpn.api.APIPlayer;
|
import dev.brighten.antivpn.api.APIPlayer;
|
||||||
|
import java.util.function.Function;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class VpnString {
|
public class VpnString {
|
||||||
private final String key;
|
private final String key;
|
||||||
private final String defaultMessage;
|
private final String defaultMessage;
|
||||||
private String message;
|
private String message;
|
||||||
@Setter
|
@Setter private Function<VpnString, String> configStringGetter;
|
||||||
private Function<VpnString, String> configStringGetter;
|
|
||||||
|
|
||||||
public VpnString(String key, String defaultMessage) {
|
public VpnString(String key, String defaultMessage) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
@@ -39,7 +37,8 @@ public class VpnString {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void updateString() {
|
public void updateString() {
|
||||||
if(configStringGetter == null) throw new Exception("The configStringGetter for string " + key + " is null!");
|
if (configStringGetter == null)
|
||||||
|
throw new Exception("The configStringGetter for string " + key + " is null!");
|
||||||
|
|
||||||
message = configStringGetter.apply(this);
|
message = configStringGetter.apply(this);
|
||||||
}
|
}
|
||||||
@@ -48,8 +47,9 @@ public class VpnString {
|
|||||||
String formatted = configStringGetter.apply(this);
|
String formatted = configStringGetter.apply(this);
|
||||||
|
|
||||||
for (Var<String, Object> replacement : replacements) {
|
for (Var<String, Object> replacement : replacements) {
|
||||||
formatted = formatted
|
formatted =
|
||||||
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
formatted.replace(
|
||||||
|
"%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatted;
|
return formatted;
|
||||||
@@ -59,8 +59,9 @@ public class VpnString {
|
|||||||
String formatted = message;
|
String formatted = message;
|
||||||
|
|
||||||
for (Var<String, Object> replacement : replacements) {
|
for (Var<String, Object> replacement : replacements) {
|
||||||
formatted = formatted
|
formatted =
|
||||||
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
formatted.replace(
|
||||||
|
"%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
||||||
}
|
}
|
||||||
player.sendMessage(formatted);
|
player.sendMessage(formatted);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,18 +16,16 @@
|
|||||||
|
|
||||||
package dev.brighten.antivpn.utils;
|
package dev.brighten.antivpn.utils;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that enables to get an IP range from CIDR specification. It supports
|
* A class that enables to get an IP range from CIDR specification. It supports both IPv4 and IPv6.
|
||||||
* both IPv4 and IPv6.
|
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public class CIDRUtils {
|
public class CIDRUtils {
|
||||||
@@ -40,14 +38,13 @@ public class CIDRUtils {
|
|||||||
private InetAddress endAddress;
|
private InetAddress endAddress;
|
||||||
private final int prefixLength;
|
private final int prefixLength;
|
||||||
|
|
||||||
|
|
||||||
public CIDRUtils(String cidr) throws UnknownHostException {
|
public CIDRUtils(String cidr) throws UnknownHostException {
|
||||||
|
|
||||||
this.cidr = cidr;
|
this.cidr = cidr;
|
||||||
|
|
||||||
/* split CIDR to address and prefix part */
|
/* split CIDR to address and prefix part */
|
||||||
if (this.cidr.contains("/")) {
|
if (this.cidr.contains("/")) {
|
||||||
int index = this.cidr.indexOf("/");
|
int index = this.cidr.indexOf('/');
|
||||||
String addressPart = this.cidr.substring(0, index);
|
String addressPart = this.cidr.substring(0, index);
|
||||||
String networkPart = this.cidr.substring(index + 1);
|
String networkPart = this.cidr.substring(index + 1);
|
||||||
|
|
||||||
@@ -60,21 +57,15 @@ public class CIDRUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void calculate() throws UnknownHostException {
|
private void calculate() throws UnknownHostException {
|
||||||
|
|
||||||
ByteBuffer maskBuffer;
|
ByteBuffer maskBuffer;
|
||||||
int targetSize;
|
int targetSize;
|
||||||
if (inetAddress.getAddress().length == 4) {
|
if (inetAddress.getAddress().length == 4) {
|
||||||
maskBuffer =
|
maskBuffer = ByteBuffer.allocate(4).putInt(-1);
|
||||||
ByteBuffer
|
|
||||||
.allocate(4)
|
|
||||||
.putInt(-1);
|
|
||||||
targetSize = 4;
|
targetSize = 4;
|
||||||
} else {
|
} else {
|
||||||
maskBuffer = ByteBuffer.allocate(16)
|
maskBuffer = ByteBuffer.allocate(16).putLong(-1L).putLong(-1L);
|
||||||
.putLong(-1L)
|
|
||||||
.putLong(-1L);
|
|
||||||
targetSize = 16;
|
targetSize = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +84,6 @@ public class CIDRUtils {
|
|||||||
|
|
||||||
this.startAddress = InetAddress.getByAddress(startIpArr);
|
this.startAddress = InetAddress.getByAddress(startIpArr);
|
||||||
this.endAddress = InetAddress.getByAddress(endIpArr);
|
this.endAddress = InetAddress.getByAddress(endIpArr);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] toBytes(byte[] array, int targetSize) {
|
private byte[] toBytes(byte[] array, int targetSize) {
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ public class ConfigDefault<A> {
|
|||||||
private final AntiVPN plugin;
|
private final AntiVPN plugin;
|
||||||
|
|
||||||
public A get() {
|
public A get() {
|
||||||
if(plugin.getConfig().get(path) != null)
|
if (plugin.getConfig().get(path) != null) return (A) plugin.getConfig().get(path);
|
||||||
return (A) plugin.getConfig().get(path);
|
|
||||||
else {
|
else {
|
||||||
plugin.getConfig().set(path, defaultValue);
|
plugin.getConfig().set(path, defaultValue);
|
||||||
plugin.saveConfig();
|
plugin.saveConfig();
|
||||||
|
|||||||
@@ -16,17 +16,15 @@
|
|||||||
|
|
||||||
package dev.brighten.antivpn.utils;
|
package dev.brighten.antivpn.utils;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class EvictingMap<K, V> extends LinkedHashMap<K, V> {
|
public class EvictingMap<K, V> extends LinkedHashMap<K, V> {
|
||||||
|
|
||||||
@Getter
|
@Getter private final int size;
|
||||||
private final int size;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ public class IpUtils {
|
|||||||
|
|
||||||
if (inet instanceof Inet4Address) {
|
if (inet instanceof Inet4Address) {
|
||||||
return Optional.of(BigDecimal.valueOf(ipv4ToLong(address)));
|
return Optional.of(BigDecimal.valueOf(ipv4ToLong(address)));
|
||||||
} return Optional.of(new BigDecimal(ipv6ToDecimalFormat(address)));
|
}
|
||||||
|
return Optional.of(new BigDecimal(ipv6ToDecimalFormat(address)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
@@ -74,6 +75,7 @@ public class IpUtils {
|
|||||||
public static boolean isIpv6(BigDecimal ip) {
|
public static boolean isIpv6(BigDecimal ip) {
|
||||||
return ip.compareTo(BigDecimal.valueOf(4294967295L)) > 0;
|
return ip.compareTo(BigDecimal.valueOf(4294967295L)) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isIpv4(String ip) {
|
public static boolean isIpv4(String ip) {
|
||||||
return ip.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$");
|
return ip.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$");
|
||||||
}
|
}
|
||||||
@@ -83,7 +85,8 @@ public class IpUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isIpv6(String 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}|:))?$");
|
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) {
|
public static String getIpv4(BigDecimal ip) {
|
||||||
@@ -105,5 +108,4 @@ public class IpUtils {
|
|||||||
public static BigInteger ipv6ToDecimalFormat(String ipAddress) throws UnknownHostException {
|
public static BigInteger ipv6ToDecimalFormat(String ipAddress) throws UnknownHostException {
|
||||||
return new BigInteger(1, Inet6Address.getByName(ipAddress).getAddress());
|
return new BigInteger(1, Inet6Address.getByName(ipAddress).getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import dev.brighten.antivpn.AntiVPN;
|
|||||||
import dev.brighten.antivpn.utils.json.JSONException;
|
import dev.brighten.antivpn.utils.json.JSONException;
|
||||||
import dev.brighten.antivpn.utils.json.JSONObject;
|
import dev.brighten.antivpn.utils.json.JSONObject;
|
||||||
import dev.brighten.antivpn.utils.json.JsonReader;
|
import dev.brighten.antivpn.utils.json.JsonReader;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
@@ -32,9 +31,12 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public class MiscUtils {
|
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 Pattern ipv4 =
|
||||||
private static final String DEFAULT_FUNKEMUNKY_UUID_ENDPOINT = "https://funkemunky.cc/mojang/uuid?name=";
|
Pattern.compile("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
|
||||||
private static final String DEFAULT_MOJANG_UUID_ENDPOINT = "https://api.mojang.com/users/profiles/minecraft/";
|
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 funkemunkyUuidEndpoint = DEFAULT_FUNKEMUNKY_UUID_ENDPOINT;
|
||||||
private static volatile String mojangUuidEndpoint = DEFAULT_MOJANG_UUID_ENDPOINT;
|
private static volatile String mojangUuidEndpoint = DEFAULT_MOJANG_UUID_ENDPOINT;
|
||||||
|
|
||||||
@@ -60,8 +62,7 @@ public class MiscUtils {
|
|||||||
int lenght;
|
int lenght;
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
|
|
||||||
while ((lenght = in.read(buf)) > 0)
|
while ((lenght = in.read(buf)) > 0) {
|
||||||
{
|
|
||||||
out.write(buf, 0, lenght);
|
out.write(buf, 0, lenght);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +81,14 @@ public class MiscUtils {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<CIDRUtils> rangeToCidrs(BigInteger start, BigInteger end) throws UnknownHostException {
|
public static List<CIDRUtils> rangeToCidrs(BigInteger start, BigInteger end)
|
||||||
|
throws UnknownHostException {
|
||||||
List<CIDRUtils> cidrs = new ArrayList<>();
|
List<CIDRUtils> cidrs = new ArrayList<>();
|
||||||
|
|
||||||
while (start.compareTo(end) <= 0) {
|
while (start.compareTo(end) <= 0) {
|
||||||
// Find the number of trailing zero bits — this determines max block size alignment
|
// Find the number of trailing zero bits — this determines max block size alignment
|
||||||
int trailingZeros = start.equals(BigInteger.ZERO)
|
int trailingZeros =
|
||||||
|
start.equals(BigInteger.ZERO)
|
||||||
? 128 // handle the edge case
|
? 128 // handle the edge case
|
||||||
: start.getLowestSetBit();
|
: start.getLowestSetBit();
|
||||||
|
|
||||||
@@ -124,14 +127,18 @@ public class MiscUtils {
|
|||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
} catch (IOException | JSONException | URISyntaxException e) {
|
} catch (IOException | JSONException | URISyntaxException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playername + "! Falling back to Mojang API", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException(
|
||||||
|
"Error while looking up UUID for " + playername + "! Falling back to Mojang API", e);
|
||||||
return lookupMojangUuid(playername);
|
return lookupMojangUuid(playername);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UUID lookupUuidFromUrl(String url) throws IOException, JSONException, URISyntaxException {
|
private static UUID lookupUuidFromUrl(String url)
|
||||||
|
throws IOException, JSONException, URISyntaxException {
|
||||||
HttpURLConnection connection = (HttpURLConnection) new URI(url).toURL().openConnection();
|
HttpURLConnection connection = (HttpURLConnection) new URI(url).toURL().openConnection();
|
||||||
connection.setConnectTimeout(5000);
|
connection.setConnectTimeout(5000);
|
||||||
connection.setReadTimeout(5000);
|
connection.setReadTimeout(5000);
|
||||||
@@ -146,7 +153,10 @@ public class MiscUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try (InputStream inputStream = connection.getInputStream()) {
|
try (InputStream inputStream = connection.getInputStream()) {
|
||||||
JSONObject object = new JSONObject(JsonReader.readAll(new InputStreamReader(inputStream, java.nio.charset.StandardCharsets.UTF_8)));
|
JSONObject object =
|
||||||
|
new JSONObject(
|
||||||
|
JsonReader.readAll(
|
||||||
|
new InputStreamReader(inputStream, java.nio.charset.StandardCharsets.UTF_8)));
|
||||||
if (object.has("uuid")) {
|
if (object.has("uuid")) {
|
||||||
return parseUuid(object.getString("uuid"));
|
return parseUuid(object.getString("uuid"));
|
||||||
}
|
}
|
||||||
@@ -160,10 +170,10 @@ public class MiscUtils {
|
|||||||
|
|
||||||
private static UUID parseUuid(String value) {
|
private static UUID parseUuid(String value) {
|
||||||
if (value.length() == 32) {
|
if (value.length() == 32) {
|
||||||
value = value.replaceFirst(
|
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})",
|
"([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"
|
"$1-$2-$3-$4-$5");
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return UUID.fromString(value);
|
return UUID.fromString(value);
|
||||||
@@ -173,7 +183,9 @@ public class MiscUtils {
|
|||||||
try {
|
try {
|
||||||
return lookupUuidFromUrl(mojangUuidEndpoint + playerName);
|
return lookupUuidFromUrl(mojangUuidEndpoint + playerName);
|
||||||
} catch (IOException | JSONException | URISyntaxException e) {
|
} catch (IOException | JSONException | URISyntaxException e) {
|
||||||
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playerName + " from Mojang!:", e);
|
AntiVPN.getInstance()
|
||||||
|
.getExecutor()
|
||||||
|
.logException("Error while looking up UUID for " + playerName + " from Mojang!:", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -188,8 +200,8 @@ public class MiscUtils {
|
|||||||
funkemunkyUuidEndpoint = DEFAULT_FUNKEMUNKY_UUID_ENDPOINT;
|
funkemunkyUuidEndpoint = DEFAULT_FUNKEMUNKY_UUID_ENDPOINT;
|
||||||
mojangUuidEndpoint = DEFAULT_MOJANG_UUID_ENDPOINT;
|
mojangUuidEndpoint = DEFAULT_MOJANG_UUID_ENDPOINT;
|
||||||
}
|
}
|
||||||
public static boolean isIpv4(String ip)
|
|
||||||
{
|
public static boolean isIpv4(String ip) {
|
||||||
return ipv4.matcher(ip).matches();
|
return ipv4.matcher(ip).matches();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,4 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
|
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface NonnullByDefault {
|
public @interface NonnullByDefault {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,8 +22,7 @@
|
|||||||
package dev.brighten.antivpn.utils;
|
package dev.brighten.antivpn.utils;
|
||||||
|
|
||||||
public final class Preconditions {
|
public final class Preconditions {
|
||||||
private Preconditions() {
|
private Preconditions() {}
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T checkNotNull(T reference) {
|
public static <T> T checkNotNull(T reference) {
|
||||||
if (reference == null) {
|
if (reference == null) {
|
||||||
@@ -41,7 +40,8 @@ public final class Preconditions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) {
|
public static <T> T checkNotNull(
|
||||||
|
T reference, String errorMessageTemplate, Object... errorMessageArgs) {
|
||||||
if (reference == null) {
|
if (reference == null) {
|
||||||
throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
|
throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
|
||||||
} else {
|
} else {
|
||||||
@@ -209,7 +209,8 @@ public final class Preconditions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3) {
|
public static <T> T checkNotNull(
|
||||||
|
T obj, String errorMessageTemplate, Object p1, Object p2, Object p3) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3));
|
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3));
|
||||||
} else {
|
} else {
|
||||||
@@ -217,7 +218,8 @@ public final class Preconditions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
|
public static <T> T checkNotNull(
|
||||||
|
T obj, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3, p4));
|
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3, p4));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ public class StringUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String varReplace(String input, APIPlayer player, VPNResponse result) {
|
public static String varReplace(String input, APIPlayer player, VPNResponse result) {
|
||||||
return translateAlternateColorCodes('&', input.replace("%player%", player.getName())
|
return translateAlternateColorCodes(
|
||||||
|
'&',
|
||||||
|
input
|
||||||
|
.replace("%player%", player.getName())
|
||||||
.replace("%reason%", result.getMethod())
|
.replace("%reason%", result.getMethod())
|
||||||
.replace("%country%", result.getCountryName())
|
.replace("%country%", result.getCountryName())
|
||||||
.replace("%city%", result.getCity()));
|
.replace("%city%", result.getCity()));
|
||||||
|
|||||||
@@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
package dev.brighten.antivpn.utils;
|
package dev.brighten.antivpn.utils;
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
|
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
|
||||||
import static dev.brighten.antivpn.utils.Preconditions.checkNotNull;
|
import static dev.brighten.antivpn.utils.Preconditions.checkNotNull;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Useful suppliers.
|
* Useful suppliers.
|
||||||
*
|
*
|
||||||
@@ -96,8 +96,7 @@ public final class Suppliers {
|
|||||||
+ ")";
|
+ ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serial
|
@Serial private static final long serialVersionUID = 0;
|
||||||
private static final long serialVersionUID = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class NonSerializableMemoizingSupplier<T> implements Supplier<T> {
|
static class NonSerializableMemoizingSupplier<T> implements Supplier<T> {
|
||||||
|
|||||||
@@ -16,6 +16,4 @@
|
|||||||
|
|
||||||
package dev.brighten.antivpn.utils;
|
package dev.brighten.antivpn.utils;
|
||||||
|
|
||||||
public record Tuple<F, S>(F first, S second) {
|
public record Tuple<F, S>(F first, S second) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,39 +18,35 @@ package dev.brighten.antivpn.utils.config;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public final class Configuration
|
public final class Configuration {
|
||||||
{
|
|
||||||
|
|
||||||
private static final char SEPARATOR = '.';
|
private static final char SEPARATOR = '.';
|
||||||
final Map<String, Object> self;
|
final Map<String, Object> self;
|
||||||
final Map<String, List<String>> comments;
|
final Map<String, List<String>> comments;
|
||||||
private final Configuration defaults;
|
private final Configuration defaults;
|
||||||
|
|
||||||
public Configuration()
|
public Configuration() {
|
||||||
{
|
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Configuration(Configuration defaults)
|
public Configuration(Configuration defaults) {
|
||||||
{
|
|
||||||
this(new LinkedHashMap<String, Object>(), defaults);
|
this(new LinkedHashMap<String, Object>(), defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
Configuration(Map<?, ?> map, Configuration defaults)
|
Configuration(Map<?, ?> map, Configuration defaults) {
|
||||||
{
|
|
||||||
this.self = new LinkedHashMap<>();
|
this.self = new LinkedHashMap<>();
|
||||||
this.defaults = defaults;
|
this.defaults = defaults;
|
||||||
comments = new HashMap<>();
|
comments = new HashMap<>();
|
||||||
|
|
||||||
for ( Map.Entry<?, ?> entry : map.entrySet() )
|
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||||
{
|
|
||||||
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
|
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
|
||||||
|
|
||||||
if ( entry.getValue() instanceof Map )
|
if (entry.getValue() instanceof Map) {
|
||||||
{
|
this.self.put(
|
||||||
this.self.put( key, new Configuration( (Map) entry.getValue(), ( defaults == null ) ? null : defaults.getSection( key ) ) );
|
key,
|
||||||
} else
|
new Configuration(
|
||||||
{
|
(Map) entry.getValue(), (defaults == null) ? null : defaults.getSection(key)));
|
||||||
|
} else {
|
||||||
this.self.put(key, entry.getValue());
|
this.self.put(key, entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,8 +83,7 @@ public final class Configuration
|
|||||||
|
|
||||||
if (currentLayer == 0) {
|
if (currentLayer == 0) {
|
||||||
currentPath = key;
|
currentPath = key;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
currentPath += "." + key;
|
currentPath += "." + key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,28 +138,23 @@ public final class Configuration
|
|||||||
for (int i = 0; i < line.length(); i++) {
|
for (int i = 0; i < line.length(); i++) {
|
||||||
if (line.charAt(i) == ' ') {
|
if (line.charAt(i) == ' ') {
|
||||||
d += 0.5;
|
d += 0.5;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int) d;
|
return (int) d;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Configuration getSectionFor(String path)
|
private Configuration getSectionFor(String path) {
|
||||||
{
|
|
||||||
int index = path.indexOf(SEPARATOR);
|
int index = path.indexOf(SEPARATOR);
|
||||||
if ( index == -1 )
|
if (index == -1) {
|
||||||
{
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String root = path.substring(0, index);
|
String root = path.substring(0, index);
|
||||||
Object section = self.get(root);
|
Object section = self.get(root);
|
||||||
if ( section == null )
|
if (section == null) {
|
||||||
{
|
|
||||||
section = new Configuration((defaults == null) ? null : defaults.getSection(root));
|
section = new Configuration((defaults == null) ? null : defaults.getSection(root));
|
||||||
self.put(root, section);
|
self.put(root, section);
|
||||||
}
|
}
|
||||||
@@ -172,77 +162,67 @@ public final class Configuration
|
|||||||
return (Configuration) section;
|
return (Configuration) section;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getChild(String path)
|
private String getChild(String path) {
|
||||||
{
|
|
||||||
int index = path.indexOf(SEPARATOR);
|
int index = path.indexOf(SEPARATOR);
|
||||||
return (index == -1) ? path : path.substring(index + 1);
|
return (index == -1) ? path : path.substring(index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
/*------------------------------------------------------------------------*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> T get(String path, T def)
|
public <T> T get(String path, T def) {
|
||||||
{
|
|
||||||
Configuration section = getSectionFor(path);
|
Configuration section = getSectionFor(path);
|
||||||
Object val;
|
Object val;
|
||||||
if ( section == this )
|
if (section == this) {
|
||||||
{
|
|
||||||
val = self.get(path);
|
val = self.get(path);
|
||||||
} else
|
} else {
|
||||||
{
|
|
||||||
val = section.get(getChild(path), def);
|
val = section.get(getChild(path), def);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( val == null && def instanceof Configuration )
|
if (val == null && def instanceof Configuration) {
|
||||||
{
|
|
||||||
self.put(path, def);
|
self.put(path, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (val != null) ? (T) val : def;
|
return (val != null) ? (T) val : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(String path)
|
public boolean contains(String path) {
|
||||||
{
|
|
||||||
return get(path, null) != null;
|
return get(path, null) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get(String path)
|
public Object get(String path) {
|
||||||
{
|
|
||||||
return get(path, getDefault(path));
|
return get(path, getDefault(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getDefault(String path)
|
public Object getDefault(String path) {
|
||||||
{
|
|
||||||
return (defaults == null) ? null : defaults.get(path);
|
return (defaults == null) ? null : defaults.get(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(String path, Object value)
|
public void set(String path, Object value) {
|
||||||
{
|
if (value instanceof Map) {
|
||||||
if ( value instanceof Map )
|
|
||||||
{
|
|
||||||
value = new Configuration((Map) value, (defaults == null) ? null : defaults.getSection(path));
|
value = new Configuration((Map) value, (defaults == null) ? null : defaults.getSection(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
Configuration section = getSectionFor(path);
|
Configuration section = getSectionFor(path);
|
||||||
if ( section == this )
|
if (section == this) {
|
||||||
{
|
if (value == null) {
|
||||||
if ( value == null )
|
|
||||||
{
|
|
||||||
self.remove(path);
|
self.remove(path);
|
||||||
} else
|
} else {
|
||||||
{
|
|
||||||
self.put(path, value);
|
self.put(path, value);
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
{
|
|
||||||
section.set(getChild(path), value);
|
section.set(getChild(path), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
/*------------------------------------------------------------------------*/
|
||||||
public Configuration getSection(String path)
|
public Configuration getSection(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return (Configuration) get( path, ( def instanceof Configuration ) ? def : new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) ) );
|
return (Configuration)
|
||||||
|
get(
|
||||||
|
path,
|
||||||
|
(def instanceof Configuration)
|
||||||
|
? def
|
||||||
|
: new Configuration((defaults == null) ? null : defaults.getSection(path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -250,33 +230,27 @@ public final class Configuration
|
|||||||
*
|
*
|
||||||
* @return top level keys for this section
|
* @return top level keys for this section
|
||||||
*/
|
*/
|
||||||
public Collection<String> getKeys()
|
public Collection<String> getKeys() {
|
||||||
{
|
|
||||||
return new LinkedHashSet<>(self.keySet());
|
return new LinkedHashSet<>(self.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
/*------------------------------------------------------------------------*/
|
||||||
public byte getByte(String path)
|
public byte getByte(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getByte(path, (def instanceof Number) ? ((Number) def).byteValue() : 0);
|
return getByte(path, (def instanceof Number) ? ((Number) def).byteValue() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte getByte(String path, byte def)
|
public byte getByte(String path, byte def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof Number) ? ((Number) val).byteValue() : def;
|
return (val instanceof Number) ? ((Number) val).byteValue() : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Byte> getByteList(String path)
|
public List<Byte> getByteList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<Byte> result = new ArrayList<>();
|
List<Byte> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof Number) {
|
||||||
if ( object instanceof Number )
|
|
||||||
{
|
|
||||||
result.add(((Number) object).byteValue());
|
result.add(((Number) object).byteValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,27 +258,22 @@ public final class Configuration
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getShort(String path)
|
public short getShort(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getShort(path, (def instanceof Number) ? ((Number) def).shortValue() : 0);
|
return getShort(path, (def instanceof Number) ? ((Number) def).shortValue() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getShort(String path, short def)
|
public short getShort(String path, short def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof Number) ? ((Number) val).shortValue() : def;
|
return (val instanceof Number) ? ((Number) val).shortValue() : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Short> getShortList(String path)
|
public List<Short> getShortList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<Short> result = new ArrayList<>();
|
List<Short> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof Number) {
|
||||||
if ( object instanceof Number )
|
|
||||||
{
|
|
||||||
result.add(((Number) object).shortValue());
|
result.add(((Number) object).shortValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,27 +281,22 @@ public final class Configuration
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInt(String path)
|
public int getInt(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getInt(path, (def instanceof Number) ? ((Number) def).intValue() : 0);
|
return getInt(path, (def instanceof Number) ? ((Number) def).intValue() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInt(String path, int def)
|
public int getInt(String path, int def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof Number) ? ((Number) val).intValue() : def;
|
return (val instanceof Number) ? ((Number) val).intValue() : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Integer> getIntList(String path)
|
public List<Integer> getIntList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<Integer> result = new ArrayList<>();
|
List<Integer> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof Number) {
|
||||||
if ( object instanceof Number )
|
|
||||||
{
|
|
||||||
result.add(((Number) object).intValue());
|
result.add(((Number) object).intValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,27 +304,22 @@ public final class Configuration
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLong(String path)
|
public long getLong(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getLong(path, (def instanceof Number) ? ((Number) def).longValue() : 0);
|
return getLong(path, (def instanceof Number) ? ((Number) def).longValue() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLong(String path, long def)
|
public long getLong(String path, long def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof Number) ? ((Number) val).longValue() : def;
|
return (val instanceof Number) ? ((Number) val).longValue() : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Long> getLongList(String path)
|
public List<Long> getLongList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<Long> result = new ArrayList<>();
|
List<Long> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof Number) {
|
||||||
if ( object instanceof Number )
|
|
||||||
{
|
|
||||||
result.add(((Number) object).longValue());
|
result.add(((Number) object).longValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,27 +327,22 @@ public final class Configuration
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getFloat(String path)
|
public float getFloat(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getFloat(path, (def instanceof Number) ? ((Number) def).floatValue() : 0);
|
return getFloat(path, (def instanceof Number) ? ((Number) def).floatValue() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getFloat(String path, float def)
|
public float getFloat(String path, float def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof Number) ? ((Number) val).floatValue() : def;
|
return (val instanceof Number) ? ((Number) val).floatValue() : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Float> getFloatList(String path)
|
public List<Float> getFloatList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<Float> result = new ArrayList<>();
|
List<Float> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof Number) {
|
||||||
if ( object instanceof Number )
|
|
||||||
{
|
|
||||||
result.add(((Number) object).floatValue());
|
result.add(((Number) object).floatValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,27 +350,22 @@ public final class Configuration
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDouble(String path)
|
public double getDouble(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getDouble(path, (def instanceof Number) ? ((Number) def).doubleValue() : 0);
|
return getDouble(path, (def instanceof Number) ? ((Number) def).doubleValue() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDouble(String path, double def)
|
public double getDouble(String path, double def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof Number) ? ((Number) val).doubleValue() : def;
|
return (val instanceof Number) ? ((Number) val).doubleValue() : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Double> getDoubleList(String path)
|
public List<Double> getDoubleList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<Double> result = new ArrayList<>();
|
List<Double> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof Number) {
|
||||||
if ( object instanceof Number )
|
|
||||||
{
|
|
||||||
result.add(((Number) object).doubleValue());
|
result.add(((Number) object).doubleValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,27 +373,22 @@ public final class Configuration
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getBoolean(String path)
|
public boolean getBoolean(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getBoolean(path, (def instanceof Boolean) ? (Boolean) def : false);
|
return getBoolean(path, (def instanceof Boolean) ? (Boolean) def : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getBoolean(String path, boolean def)
|
public boolean getBoolean(String path, boolean def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof Boolean) ? (Boolean) val : def;
|
return (val instanceof Boolean) ? (Boolean) val : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Boolean> getBooleanList(String path)
|
public List<Boolean> getBooleanList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<Boolean> result = new ArrayList<>();
|
List<Boolean> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof Boolean) {
|
||||||
if ( object instanceof Boolean )
|
|
||||||
{
|
|
||||||
result.add((Boolean) object);
|
result.add((Boolean) object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,27 +396,22 @@ public final class Configuration
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getChar(String path)
|
public char getChar(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getChar(path, (def instanceof Character) ? (Character) def : '\u0000');
|
return getChar(path, (def instanceof Character) ? (Character) def : '\u0000');
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getChar(String path, char def)
|
public char getChar(String path, char def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof Character) ? (Character) val : def;
|
return (val instanceof Character) ? (Character) val : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Character> getCharList(String path)
|
public List<Character> getCharList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<Character> result = new ArrayList<>();
|
List<Character> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof Character) {
|
||||||
if ( object instanceof Character )
|
|
||||||
{
|
|
||||||
result.add((Character) object);
|
result.add((Character) object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,27 +419,22 @@ public final class Configuration
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(String path)
|
public String getString(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getString(path, (def instanceof String) ? (String) def : "");
|
return getString(path, (def instanceof String) ? (String) def : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(String path, String def)
|
public String getString(String path, String def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof String) ? (String) val : def;
|
return (val instanceof String) ? (String) val : def;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getStringList(String path)
|
public List<String> getStringList(String path) {
|
||||||
{
|
|
||||||
List<?> list = getList(path);
|
List<?> list = getList(path);
|
||||||
List<String> result = new ArrayList<>();
|
List<String> result = new ArrayList<>();
|
||||||
|
|
||||||
for ( Object object : list )
|
for (Object object : list) {
|
||||||
{
|
if (object instanceof String) {
|
||||||
if ( object instanceof String )
|
|
||||||
{
|
|
||||||
result.add((String) object);
|
result.add((String) object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -509,14 +443,12 @@ public final class Configuration
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
/*------------------------------------------------------------------------*/
|
||||||
public List<?> getList(String path)
|
public List<?> getList(String path) {
|
||||||
{
|
|
||||||
Object def = getDefault(path);
|
Object def = getDefault(path);
|
||||||
return getList(path, (def instanceof List<?>) ? (List<?>) def : Collections.EMPTY_LIST);
|
return getList(path, (def instanceof List<?>) ? (List<?>) def : Collections.EMPTY_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<?> getList(String path, List<?> def)
|
public List<?> getList(String path, List<?> def) {
|
||||||
{
|
|
||||||
Object val = get(path, def);
|
Object val = get(path, def);
|
||||||
return (val instanceof List<?>) ? (List<?>) val : def;
|
return (val instanceof List<?>) ? (List<?>) val : def;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-11
@@ -20,25 +20,21 @@ import java.io.*;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
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
|
static {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
providers.put(YamlConfiguration.class, new YamlConfiguration());
|
providers.put(YamlConfiguration.class, new YamlConfiguration());
|
||||||
} catch ( NoClassDefFoundError ex )
|
} catch (NoClassDefFoundError ex) {
|
||||||
{
|
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
// Ignore, no SnakeYAML
|
// Ignore, no SnakeYAML
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigurationProvider getProvider(Class<? extends ConfigurationProvider> provider)
|
public static ConfigurationProvider getProvider(Class<? extends ConfigurationProvider> provider) {
|
||||||
{
|
|
||||||
return providers.get(provider);
|
return providers.get(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-44
@@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package dev.brighten.antivpn.utils.config;
|
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.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@@ -25,26 +29,20 @@ import org.yaml.snakeyaml.Yaml;
|
|||||||
import org.yaml.snakeyaml.constructor.Constructor;
|
import org.yaml.snakeyaml.constructor.Constructor;
|
||||||
import org.yaml.snakeyaml.representer.Representer;
|
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)
|
@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
|
@Override
|
||||||
protected Yaml initialValue()
|
protected Yaml initialValue() {
|
||||||
{
|
|
||||||
DumperOptions options = new DumperOptions();
|
DumperOptions options = new DumperOptions();
|
||||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||||
Representer representer = new Representer(options)
|
Representer representer =
|
||||||
|
new Representer(options) {
|
||||||
{
|
{
|
||||||
{
|
representers.put(
|
||||||
representers.put( Configuration.class, data -> represent( ( (Configuration) data ).self ));
|
Configuration.class, data -> represent(((Configuration) data).self));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,17 +52,15 @@ public class YamlConfiguration extends ConfigurationProvider
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Configuration config, File file) throws IOException
|
public void save(Configuration config, File file) throws IOException {
|
||||||
{
|
try (Writer writer =
|
||||||
try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), StandardCharsets.UTF_8 ) )
|
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
|
||||||
{
|
|
||||||
save(config, writer);
|
save(config, writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Configuration config, Writer writer)
|
public void save(Configuration config, Writer writer) {
|
||||||
{
|
|
||||||
String contents = this.yaml.get().dump(config.self);
|
String contents = this.yaml.get().dump(config.self);
|
||||||
if (contents.equals("{}\n")) {
|
if (contents.equals("{}\n")) {
|
||||||
contents = "";
|
contents = "";
|
||||||
@@ -90,7 +86,9 @@ public class YamlConfiguration extends ConfigurationProvider
|
|||||||
int layerFromLine = config.getLayerFromLine(line, lineNumber);
|
int layerFromLine = config.getLayerFromLine(line, lineNumber);
|
||||||
|
|
||||||
if (layerFromLine < currentLayer) {
|
if (layerFromLine < currentLayer) {
|
||||||
currentPath = new StringBuilder(config.regressPathBy(currentLayer - layerFromLine, currentPath.toString()));
|
currentPath =
|
||||||
|
new StringBuilder(
|
||||||
|
config.regressPathBy(currentLayer - layerFromLine, currentPath.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String key = config.getKeyFromLine(line);
|
String key = config.getKeyFromLine(line);
|
||||||
@@ -103,7 +101,11 @@ public class YamlConfiguration extends ConfigurationProvider
|
|||||||
|
|
||||||
String path = currentPath.toString();
|
String path = currentPath.toString();
|
||||||
if (config.comments.containsKey(path)) {
|
if (config.comments.containsKey(path)) {
|
||||||
config.comments.get(path).forEach(string -> {
|
config
|
||||||
|
.comments
|
||||||
|
.get(path)
|
||||||
|
.forEach(
|
||||||
|
string -> {
|
||||||
sb.append(string);
|
sb.append(string);
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
});
|
});
|
||||||
@@ -118,32 +120,29 @@ public class YamlConfiguration extends ConfigurationProvider
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Configuration load(File file) throws IOException
|
public Configuration load(File file) throws IOException {
|
||||||
{
|
|
||||||
return load(file, null);
|
return load(file, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Configuration load(File file, Configuration defaults) throws IOException
|
public Configuration load(File file, Configuration defaults) throws IOException {
|
||||||
{
|
try (FileInputStream is = new FileInputStream(file)) {
|
||||||
try ( FileInputStream is = new FileInputStream( file ) )
|
|
||||||
{
|
|
||||||
return load(is, defaults);
|
return load(is, defaults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Configuration load(Reader reader)
|
public Configuration load(Reader reader) {
|
||||||
{
|
|
||||||
return load(reader, null);
|
return load(reader, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
public Configuration load(Reader reader, Configuration defaults)
|
public Configuration load(Reader reader, Configuration defaults) {
|
||||||
{
|
BufferedReader input =
|
||||||
BufferedReader input = reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(reader);
|
reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader);
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
String line;
|
String line;
|
||||||
@@ -156,33 +155,27 @@ public class YamlConfiguration extends ConfigurationProvider
|
|||||||
input.close();
|
input.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return load(builder.toString(), defaults);
|
return load(builder.toString(), defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Configuration load(InputStream is)
|
public Configuration load(InputStream is) {
|
||||||
{
|
|
||||||
return this.load(new InputStreamReader(is, Charset.defaultCharset()));
|
return this.load(new InputStreamReader(is, Charset.defaultCharset()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Configuration load(InputStream is, Configuration defaults)
|
public Configuration load(InputStream is, Configuration defaults) {
|
||||||
{
|
|
||||||
return this.load(new InputStreamReader(is, Charset.defaultCharset()), defaults);
|
return this.load(new InputStreamReader(is, Charset.defaultCharset()), defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Configuration load(String string)
|
public Configuration load(String string) {
|
||||||
{
|
|
||||||
return load(string, null);
|
return load(string, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Configuration load(String contents, Configuration defaults)
|
public Configuration load(String contents, Configuration defaults) {
|
||||||
{
|
|
||||||
Map<String, Object> map;
|
Map<String, Object> map;
|
||||||
LoaderOptions loaderOptions = new LoaderOptions();
|
LoaderOptions loaderOptions = new LoaderOptions();
|
||||||
loaderOptions.setMaxAliasesForCollections(2147483647);
|
loaderOptions.setMaxAliasesForCollections(2147483647);
|
||||||
@@ -193,5 +186,4 @@ public class YamlConfiguration extends ConfigurationProvider
|
|||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -17,21 +17,18 @@
|
|||||||
package dev.brighten.antivpn.utils.json;
|
package dev.brighten.antivpn.utils.json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides static methods to convert comma delimited text into a
|
* This provides static methods to convert comma delimited text into a JSONArray, and to covert a
|
||||||
* JSONArray, and to covert a JSONArray into comma delimited text. Comma
|
* JSONArray into comma delimited text. Comma delimited text is a very popular format for data
|
||||||
* delimited text is a very popular format for data interchange. It is
|
* interchange. It is understood by most database, spreadsheet, and organizer programs.
|
||||||
* understood by most database, spreadsheet, and organizer programs.
|
*
|
||||||
* <p>
|
* <p>Each row of text represents a row in a table or a data record. Each row ends with a NEWLINE
|
||||||
* Each row of text represents a row in a table or a data record. Each row
|
* character. Each row contains one or more values. Values are separated by commas. A value can
|
||||||
* ends with a NEWLINE character. Each row contains one or more values.
|
* contain any character except for comma, unless is is wrapped in single quotes or double quotes.
|
||||||
* 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>
|
*
|
||||||
* 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
|
||||||
* <p>
|
* elements in the JSONObjects can be taken from the names in the first row.
|
||||||
* 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
|
* @author JSON.org
|
||||||
* @version 2010-12-24
|
* @version 2010-12-24
|
||||||
@@ -39,8 +36,7 @@ package dev.brighten.antivpn.utils.json;
|
|||||||
public class CDL {
|
public class CDL {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next value. The value can be wrapped in quotes. The value can
|
* Get the next value. The value can be wrapped in quotes. The value can be empty.
|
||||||
* be empty.
|
|
||||||
*
|
*
|
||||||
* @param x A JSONTokener of the source text.
|
* @param x A JSONTokener of the source text.
|
||||||
* @return The value string, or null if empty.
|
* @return The value string, or null if empty.
|
||||||
@@ -92,8 +88,7 @@ public class CDL {
|
|||||||
for (; ; ) {
|
for (; ; ) {
|
||||||
String value = getValue(x);
|
String value = getValue(x);
|
||||||
char c = x.next();
|
char c = x.next();
|
||||||
if (value == null ||
|
if (value == null || (ja.length() == 0 && value.isEmpty() && c != ',')) {
|
||||||
(ja.length() == 0 && value.length() == 0 && c != ',')) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ja.put(value);
|
ja.put(value);
|
||||||
@@ -105,8 +100,7 @@ public class CDL {
|
|||||||
if (c == '\n' || c == '\r' || c == 0) {
|
if (c == '\n' || c == '\r' || c == 0) {
|
||||||
return ja;
|
return ja;
|
||||||
}
|
}
|
||||||
throw x.syntaxError("Bad character '" + c + "' (" +
|
throw x.syntaxError("Bad character '" + c + "' (" + (int) c + ").");
|
||||||
(int) c + ").");
|
|
||||||
}
|
}
|
||||||
c = x.next();
|
c = x.next();
|
||||||
}
|
}
|
||||||
@@ -114,26 +108,23 @@ public class CDL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a JSONObject from a row of comma delimited text, using a
|
* Produce a JSONObject from a row of comma delimited text, using a parallel JSONArray of strings
|
||||||
* parallel JSONArray of strings to provides the names of the elements.
|
* to provides the names of the elements.
|
||||||
*
|
*
|
||||||
* @param names A JSONArray of names. This is commonly obtained from the
|
* @param names A JSONArray of names. This is commonly obtained from the first row of a comma
|
||||||
* first row of a comma delimited text file using the rowToJSONArray
|
* delimited text file using the rowToJSONArray method.
|
||||||
* method.
|
|
||||||
* @param x A JSONTokener of the source text.
|
* @param x A JSONTokener of the source text.
|
||||||
* @return A JSONObject combining the names and values.
|
* @return A JSONObject combining the names and values.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
|
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) throws JSONException {
|
||||||
throws JSONException {
|
|
||||||
JSONArray ja = rowToJSONArray(x);
|
JSONArray ja = rowToJSONArray(x);
|
||||||
return ja != null ? ja.toJSONObject(names) : null;
|
return ja != null ? ja.toJSONObject(names) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a comma delimited text row from a JSONArray. Values containing
|
* Produce a comma delimited text row from a JSONArray. Values containing the comma character will
|
||||||
* the comma character will be quoted. Troublesome characters may be
|
* be quoted. Troublesome characters may be removed.
|
||||||
* removed.
|
|
||||||
*
|
*
|
||||||
* @param ja A JSONArray of strings.
|
* @param ja A JSONArray of strings.
|
||||||
* @return A string ending in NEWLINE.
|
* @return A string ending in NEWLINE.
|
||||||
@@ -147,9 +138,12 @@ public class CDL {
|
|||||||
Object object = ja.opt(i);
|
Object object = ja.opt(i);
|
||||||
if (object != null) {
|
if (object != null) {
|
||||||
String string = object.toString();
|
String string = object.toString();
|
||||||
if (string.length() > 0 && (string.indexOf(',') >= 0 ||
|
if (string.length() > 0
|
||||||
string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 ||
|
&& (string.indexOf(',') >= 0
|
||||||
string.indexOf(0) >= 0 || string.charAt(0) == '"')) {
|
|| string.indexOf('\n') >= 0
|
||||||
|
|| string.indexOf('\r') >= 0
|
||||||
|
|| string.indexOf(0) >= 0
|
||||||
|
|| string.charAt(0) == '"')) {
|
||||||
sb.append('"');
|
sb.append('"');
|
||||||
int length = string.length();
|
int length = string.length();
|
||||||
for (int j = 0; j < length; j += 1) {
|
for (int j = 0; j < length; j += 1) {
|
||||||
@@ -169,8 +163,8 @@ public class CDL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a JSONArray of JSONObjects from a comma delimited text string,
|
* Produce a JSONArray of JSONObjects from a comma delimited text string, using the first row as a
|
||||||
* using the first row as a source of names.
|
* source of names.
|
||||||
*
|
*
|
||||||
* @param string The comma delimited text.
|
* @param string The comma delimited text.
|
||||||
* @return A JSONArray of JSONObjects.
|
* @return A JSONArray of JSONObjects.
|
||||||
@@ -181,8 +175,8 @@ public class CDL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a JSONArray of JSONObjects from a comma delimited text string,
|
* Produce a JSONArray of JSONObjects from a comma delimited text string, using the first row as a
|
||||||
* using the first row as a source of names.
|
* source of names.
|
||||||
*
|
*
|
||||||
* @param x The JSONTokener containing the comma delimited text.
|
* @param x The JSONTokener containing the comma delimited text.
|
||||||
* @return A JSONArray of JSONObjects.
|
* @return A JSONArray of JSONObjects.
|
||||||
@@ -193,30 +187,28 @@ public class CDL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a JSONArray of JSONObjects from a comma delimited text string
|
* Produce a JSONArray of JSONObjects from a comma delimited text string using a supplied
|
||||||
* using a supplied JSONArray as the source of element names.
|
* JSONArray as the source of element names.
|
||||||
*
|
*
|
||||||
* @param names A JSONArray of strings.
|
* @param names A JSONArray of strings.
|
||||||
* @param string The comma delimited text.
|
* @param string The comma delimited text.
|
||||||
* @return A JSONArray of JSONObjects.
|
* @return A JSONArray of JSONObjects.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static JSONArray toJSONArray(JSONArray names, String string)
|
public static JSONArray toJSONArray(JSONArray names, String string) throws JSONException {
|
||||||
throws JSONException {
|
|
||||||
return toJSONArray(names, new JSONTokener(string));
|
return toJSONArray(names, new JSONTokener(string));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a JSONArray of JSONObjects from a comma delimited text string
|
* Produce a JSONArray of JSONObjects from a comma delimited text string using a supplied
|
||||||
* using a supplied JSONArray as the source of element names.
|
* JSONArray as the source of element names.
|
||||||
*
|
*
|
||||||
* @param names A JSONArray of strings.
|
* @param names A JSONArray of strings.
|
||||||
* @param x A JSONTokener of the source text.
|
* @param x A JSONTokener of the source text.
|
||||||
* @return A JSONArray of JSONObjects.
|
* @return A JSONArray of JSONObjects.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
|
public static JSONArray toJSONArray(JSONArray names, JSONTokener x) throws JSONException {
|
||||||
throws JSONException {
|
|
||||||
if (names == null || names.length() == 0) {
|
if (names == null || names.length() == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -234,11 +226,9 @@ public class CDL {
|
|||||||
return ja;
|
return ja;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a comma delimited text from a JSONArray of JSONObjects. The
|
* Produce a comma delimited text from a JSONArray of JSONObjects. The first row will be a list of
|
||||||
* first row will be a list of names obtained by inspecting the first
|
* names obtained by inspecting the first JSONObject.
|
||||||
* JSONObject.
|
|
||||||
*
|
*
|
||||||
* @param ja A JSONArray of JSONObjects.
|
* @param ja A JSONArray of JSONObjects.
|
||||||
* @return A comma delimited text.
|
* @return A comma delimited text.
|
||||||
@@ -256,17 +246,15 @@ public class CDL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a comma delimited text from a JSONArray of JSONObjects using
|
* Produce a comma delimited text from a JSONArray of JSONObjects using a provided list of names.
|
||||||
* a provided list of names. The list of names is not included in the
|
* The list of names is not included in the output.
|
||||||
* output.
|
|
||||||
*
|
*
|
||||||
* @param names A JSONArray of strings.
|
* @param names A JSONArray of strings.
|
||||||
* @param ja A JSONArray of JSONObjects.
|
* @param ja A JSONArray of JSONObjects.
|
||||||
* @return A comma delimited text.
|
* @return A comma delimited text.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static String toString(JSONArray names, JSONArray ja)
|
public static String toString(JSONArray names, JSONArray ja) throws JSONException {
|
||||||
throws JSONException {
|
|
||||||
if (names == null || names.length() == 0) {
|
if (names == null || names.length() == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
package dev.brighten.antivpn.utils.json;
|
package dev.brighten.antivpn.utils.json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a web browser cookie specification to a JSONObject and back.
|
* Convert a web browser cookie specification to a JSONObject and back. JSON and Cookies are both
|
||||||
* JSON and Cookies are both notations for name/value pairs.
|
* notations for name/value pairs.
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2010-12-24
|
* @version 2010-12-24
|
||||||
@@ -26,14 +26,12 @@ package dev.brighten.antivpn.utils.json;
|
|||||||
public class Cookie {
|
public class Cookie {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a copy of a string in which the characters '+', '%', '=', ';'
|
* Produce a copy of a string in which the characters '+', '%', '=', ';' and control characters
|
||||||
* and control characters are replaced with "%hh". This is a gentle form
|
* are replaced with "%hh". This is a gentle form of URL encoding, attempting to cause as little
|
||||||
* of URL encoding, attempting to cause as little distortion to the
|
* distortion to the string as possible. The characters '=' and ';' are meta characters in
|
||||||
* 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
|
||||||
* cookies. By convention, they are escaped using the URL-encoding. This is
|
* a standard. Often, cookies are expected to have encoded values. We encode '=' and ';' because
|
||||||
* only a convention, not a standard. Often, cookies are expected to have
|
* we must. We encode '%' and '+' because they are meta characters in URL encoding.
|
||||||
* encoded values. We encode '=' and ';' because we must. We encode '%' and
|
|
||||||
* '+' because they are meta characters in URL encoding.
|
|
||||||
*
|
*
|
||||||
* @param string The source string.
|
* @param string The source string.
|
||||||
* @return The escaped result.
|
* @return The escaped result.
|
||||||
@@ -56,21 +54,17 @@ public class Cookie {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a cookie specification string into a JSONObject. The string
|
* Convert a cookie specification string into a JSONObject. The string will contain a name value
|
||||||
* will contain a name value pair separated by '='. The name and the value
|
* pair separated by '='. The name and the value will be unescaped, possibly converting '+' and
|
||||||
* will be unescaped, possibly converting '+' and '%' sequences. The
|
* '%' sequences. The cookie properties may follow, separated by ';', also represented as
|
||||||
* 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
|
||||||
* name=value (except the secure property, which does not have a value).
|
* under the key "name", and the value will be stored under the key "value". This method does not
|
||||||
* The name will be stored under the key "name", and the value will be
|
* do checking or validation of the parameters. It only converts the cookie string into a
|
||||||
* stored under the key "value". This method does not do checking or
|
* JSONObject.
|
||||||
* validation of the parameters. It only converts the cookie string into
|
|
||||||
* a JSONObject.
|
|
||||||
*
|
*
|
||||||
* @param string The cookie specification string.
|
* @param string The cookie specification string.
|
||||||
* @return A JSONObject containing "name", "value", and possibly other
|
* @return A JSONObject containing "name", "value", and possibly other members.
|
||||||
* members.
|
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||||
@@ -99,13 +93,10 @@ public class Cookie {
|
|||||||
return jo;
|
return jo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a JSONObject into a cookie specification string. The JSONObject
|
* Convert a JSONObject into a cookie specification string. The JSONObject must contain "name" and
|
||||||
* must contain "name" and "value" members.
|
* "value" members. If the JSONObject contains "expires", "domain", "path", or "secure" members,
|
||||||
* If the JSONObject contains "expires", "domain", "path", or "secure"
|
* they will be appended to the cookie specification string. All other members are ignored.
|
||||||
* members, they will be appended to the cookie specification string.
|
|
||||||
* All other members are ignored.
|
|
||||||
*
|
*
|
||||||
* @param jo A JSONObject
|
* @param jo A JSONObject
|
||||||
* @return A cookie specification string
|
* @return A cookie specification string
|
||||||
@@ -136,12 +127,10 @@ public class Cookie {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert <code>%</code><i>hh</i> sequences to single characters, and
|
* Convert <code>%</code><i>hh</i> sequences to single characters, and convert plus to space.
|
||||||
* convert plus to space.
|
|
||||||
*
|
*
|
||||||
* @param string A string that may contain
|
* @param string A string that may contain <code>+</code> <small>(plus)</small> and <code>%
|
||||||
* <code>+</code> <small>(plus)</small> and
|
* </code><i>hh</i> sequences.
|
||||||
* <code>%</code><i>hh</i> sequences.
|
|
||||||
* @return The unescaped string.
|
* @return The unescaped string.
|
||||||
*/
|
*/
|
||||||
public static String unescape(String string) {
|
public static String unescape(String string) {
|
||||||
|
|||||||
@@ -27,13 +27,11 @@ import java.util.Iterator;
|
|||||||
public class CookieList {
|
public class CookieList {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a cookie list into a JSONObject. A cookie list is a sequence
|
* Convert a cookie list into a JSONObject. A cookie list is a sequence of name/value pairs. The
|
||||||
* of name/value pairs. The names are separated from the values by '='.
|
* names are separated from the values by '='. The pairs are separated by ';'. The names and the
|
||||||
* The pairs are separated by ';'. The names and the values
|
* values will be unescaped, possibly converting '+' and '%' sequences.
|
||||||
* will be unescaped, possibly converting '+' and '%' sequences.
|
*
|
||||||
* <p>
|
* <p>To add a cookie to a cooklist, cookielistJSONObject.put(cookieJSONObject.getString("name"),
|
||||||
* To add a cookie to a cooklist,
|
|
||||||
* cookielistJSONObject.put(cookieJSONObject.getString("name"),
|
|
||||||
* cookieJSONObject.getString("value"));
|
* cookieJSONObject.getString("value"));
|
||||||
*
|
*
|
||||||
* @param string A cookie list string
|
* @param string A cookie list string
|
||||||
@@ -52,12 +50,10 @@ public class CookieList {
|
|||||||
return jo;
|
return jo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a JSONObject into a cookie list. A cookie list is a sequence
|
* Convert a JSONObject into a cookie list. A cookie list is a sequence of name/value pairs. The
|
||||||
* of name/value pairs. The names are separated from the values by '='.
|
* names are separated from the values by '='. The pairs are separated by ';'. The characters '%',
|
||||||
* The pairs are separated by ';'. The characters '%', '+', '=', and ';'
|
* '+', '=', and ';' in the names and values are replaced by "%hh".
|
||||||
* in the names and values are replaced by "%hh".
|
|
||||||
*
|
*
|
||||||
* @param jo A JSONObject
|
* @param jo A JSONObject
|
||||||
* @return A cookie list string
|
* @return A cookie list string
|
||||||
|
|||||||
@@ -26,42 +26,48 @@ import java.util.Iterator;
|
|||||||
*/
|
*/
|
||||||
public class HTTP {
|
public class HTTP {
|
||||||
|
|
||||||
/**
|
/** Carriage return/line feed. */
|
||||||
* Carriage return/line feed.
|
|
||||||
*/
|
|
||||||
public static final String CRLF = "\r\n";
|
public static final String CRLF = "\r\n";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an HTTP header string into a JSONObject. It can be a request
|
* Convert an HTTP header string into a JSONObject. It can be a request header or a response
|
||||||
* header or a response header. A request header will contain
|
* header. A request header will contain
|
||||||
|
*
|
||||||
* <pre>{
|
* <pre>{
|
||||||
* Method: "POST" (for example),
|
* Method: "POST" (for example),
|
||||||
* "Request-URI": "/" (for example),
|
* "Request-URI": "/" (for example),
|
||||||
* "HTTP-Version": "HTTP/1.1" (for example)
|
* "HTTP-Version": "HTTP/1.1" (for example)
|
||||||
* }</pre>
|
* }</pre>
|
||||||
|
*
|
||||||
* A response header will contain
|
* A response header will contain
|
||||||
|
*
|
||||||
* <pre>{
|
* <pre>{
|
||||||
* "HTTP-Version": "HTTP/1.1" (for example),
|
* "HTTP-Version": "HTTP/1.1" (for example),
|
||||||
* "Fixes-Code": "200" (for example),
|
* "Fixes-Code": "200" (for example),
|
||||||
* "Reason-Phrase": "OK" (for example)
|
* "Reason-Phrase": "OK" (for example)
|
||||||
* }</pre>
|
* }</pre>
|
||||||
* In addition, the other parameters in the header will be captured, using
|
*
|
||||||
* the HTTP field names as JSON names, so that <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
|
* Date: Sun, 26 May 2002 18:06:04 GMT
|
||||||
* Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
|
* Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
|
||||||
* Cache-Control: no-cache</pre>
|
* Cache-Control: no-cache</pre>
|
||||||
|
*
|
||||||
* become
|
* become
|
||||||
|
*
|
||||||
* <pre>{...
|
* <pre>{...
|
||||||
* Date: "Sun, 26 May 2002 18:06:04 GMT",
|
* Date: "Sun, 26 May 2002 18:06:04 GMT",
|
||||||
* Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
|
* Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
|
||||||
* "Cache-Control": "no-cache",
|
* "Cache-Control": "no-cache",
|
||||||
* ...}</pre>
|
* ...}</pre>
|
||||||
* It does no further checking or conversion. It does not parse dates.
|
*
|
||||||
* It does not do '%' transforms on URLs.
|
* 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.
|
* @param string An HTTP header string.
|
||||||
* @return A JSONObject containing the elements and attributes
|
* @return A JSONObject containing the elements and attributes of the XML string.
|
||||||
* of the XML string.
|
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static JSONObject toJSONObject(String string) throws JSONException {
|
public static JSONObject toJSONObject(String string) throws JSONException {
|
||||||
@@ -99,27 +105,29 @@ public class HTTP {
|
|||||||
return jo;
|
return jo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a JSONObject into an HTTP header. A request header must contain
|
* Convert a JSONObject into an HTTP header. A request header must contain
|
||||||
|
*
|
||||||
* <pre>{
|
* <pre>{
|
||||||
* Method: "POST" (for example),
|
* Method: "POST" (for example),
|
||||||
* "Request-URI": "/" (for example),
|
* "Request-URI": "/" (for example),
|
||||||
* "HTTP-Version": "HTTP/1.1" (for example)
|
* "HTTP-Version": "HTTP/1.1" (for example)
|
||||||
* }</pre>
|
* }</pre>
|
||||||
|
*
|
||||||
* A response header must contain
|
* A response header must contain
|
||||||
|
*
|
||||||
* <pre>{
|
* <pre>{
|
||||||
* "HTTP-Version": "HTTP/1.1" (for example),
|
* "HTTP-Version": "HTTP/1.1" (for example),
|
||||||
* "Fixes-Code": "200" (for example),
|
* "Fixes-Code": "200" (for example),
|
||||||
* "Reason-Phrase": "OK" (for example)
|
* "Reason-Phrase": "OK" (for example)
|
||||||
* }</pre>
|
* }</pre>
|
||||||
* Any other members of the JSONObject will be output as HTTP fields.
|
*
|
||||||
* The result will end with two CRLF pairs.
|
* Any other members of the JSONObject will be output as HTTP fields. The result will end with two
|
||||||
|
* CRLF pairs.
|
||||||
*
|
*
|
||||||
* @param jo A JSONObject
|
* @param jo A JSONObject
|
||||||
* @return An HTTP header string.
|
* @return An HTTP header string.
|
||||||
* @throws JSONException if the object does not contain enough
|
* @throws JSONException if the object does not contain enough information.
|
||||||
* information.
|
|
||||||
*/
|
*/
|
||||||
public static String toString(JSONObject jo) throws JSONException {
|
public static String toString(JSONObject jo) throws JSONException {
|
||||||
Iterator keys = jo.keys();
|
Iterator keys = jo.keys();
|
||||||
@@ -145,9 +153,12 @@ public class HTTP {
|
|||||||
sb.append(CRLF);
|
sb.append(CRLF);
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
string = keys.next().toString();
|
string = keys.next().toString();
|
||||||
if (!string.equals("HTTP-Version") && !string.equals("Fixes-Code") &&
|
if (!string.equals("HTTP-Version")
|
||||||
!string.equals("Reason-Phrase") && !string.equals("Method") &&
|
&& !string.equals("Fixes-Code")
|
||||||
!string.equals("Request-URI") && !jo.isNull(string)) {
|
&& !string.equals("Reason-Phrase")
|
||||||
|
&& !string.equals("Method")
|
||||||
|
&& !string.equals("Request-URI")
|
||||||
|
&& !jo.isNull(string)) {
|
||||||
sb.append(string);
|
sb.append(string);
|
||||||
sb.append(": ");
|
sb.append(": ");
|
||||||
sb.append(jo.getString(string));
|
sb.append(jo.getString(string));
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
package dev.brighten.antivpn.utils.json;
|
package dev.brighten.antivpn.utils.json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTTPTokener extends the JSONTokener to provide additional methods
|
* The HTTPTokener extends the JSONTokener to provide additional methods for the parsing of HTTP
|
||||||
* for the parsing of HTTP headers.
|
* headers.
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2010-12-24
|
* @version 2010-12-24
|
||||||
@@ -34,7 +34,6 @@ public class HTTPTokener extends JSONTokener {
|
|||||||
super(string);
|
super(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next token or string. This is used in parsing HTTP headers.
|
* Get the next token or string. This is used in parsing HTTP headers.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -25,48 +25,41 @@ import java.util.Iterator;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A JSONArray is an ordered sequence of values. Its external text form is a
|
* A JSONArray is an ordered sequence of values. Its external text form is a string wrapped in
|
||||||
* string wrapped in square brackets with commas separating the values. The
|
* square brackets with commas separating the values. The internal form is an object having <code>
|
||||||
* internal form is an object having <code>get</code> and <code>opt</code>
|
* get</code> and <code>opt</code> methods for accessing the values by index, and <code>put</code>
|
||||||
* methods for accessing the values by index, and <code>put</code> methods for
|
* methods for adding or replacing values. The values can be any of these types: <code>Boolean
|
||||||
* adding or replacing values. The values can be any of these types:
|
* </code>, <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>, <code>String
|
||||||
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
|
* </code>, or the <code>JSONObject.NULL object</code>.
|
||||||
* <code>Number</code>, <code>String</code>, or the
|
*
|
||||||
* <code>JSONObject.NULL object</code>.
|
* <p>The constructor can convert a JSON text into a Java object. The <code>toString</code> method
|
||||||
* <p>
|
* converts to JSON text.
|
||||||
* The constructor can convert a JSON text into a Java object. The
|
*
|
||||||
* <code>toString</code> method converts to JSON text.
|
* <p>A <code>get</code> method returns a value if one can be found, and throws an exception if one
|
||||||
* <p>
|
* cannot be found. An <code>opt</code> method returns a default value instead of throwing an
|
||||||
* A <code>get</code> method returns a value if one can be found, and throws an
|
* exception, and so is useful for obtaining optional values.
|
||||||
* exception if one cannot be found. An <code>opt</code> method returns a
|
*
|
||||||
* default value instead of throwing an exception, and so is useful for
|
* <p>The generic <code>get()</code> and <code>opt()</code> methods return an object which you can
|
||||||
* obtaining optional values.
|
* cast or query for type. There are also typed <code>get</code> and <code>opt</code> methods that
|
||||||
* <p>
|
* do type checking and type coercion for you.
|
||||||
* The generic <code>get()</code> and <code>opt()</code> methods return an
|
*
|
||||||
* object which you can cast or query for type. There are also typed
|
* <p>The texts produced by the <code>toString</code> methods strictly conform to JSON syntax rules.
|
||||||
* <code>get</code> and <code>opt</code> methods that do type checking and type
|
* The constructors are more forgiving in the texts they will accept:
|
||||||
* coercion for you.
|
*
|
||||||
* <p>
|
|
||||||
* The texts produced by the <code>toString</code> methods strictly conform to
|
|
||||||
* JSON syntax rules. The constructors are more forgiving in the texts they will
|
|
||||||
* accept:
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>An extra <code>,</code> <small>(comma)</small> may appear just
|
* <li>An extra <code>,</code> <small>(comma)</small> may appear just before the closing
|
||||||
* before the closing bracket.</li>
|
* bracket.
|
||||||
* <li>The <code>null</code> value will be inserted when there
|
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
|
||||||
* is <code>,</code> <small>(comma)</small> elision.</li>
|
* <small>(comma)</small> elision.
|
||||||
* <li>Strings may be quoted with <code>'</code> <small>(single
|
* <li>Strings may be quoted with <code>'</code> <small>(single quote)</small>.
|
||||||
* quote)</small>.</li>
|
* <li>Strings do not need to be quoted at all if they do not begin with a quote or single quote,
|
||||||
* <li>Strings do not need to be quoted at all if they do not begin with a quote
|
* and if they do not contain leading or trailing spaces, and if they do not contain any of
|
||||||
* or single quote, and if they do not contain leading or trailing spaces,
|
* these characters: <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
|
||||||
* and if they do not contain any of these characters:
|
* and if they are not the reserved words <code>true</code>, <code>false</code>, or <code>null
|
||||||
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
|
* </code>.
|
||||||
* and if they are not the reserved words <code>true</code>,
|
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as well as by <code>,
|
||||||
* <code>false</code>, or <code>null</code>.</li>
|
* </code> <small>(comma)</small>.
|
||||||
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
|
* <li>Numbers may have the <code>0x-</code> <small>(hex)</small> prefix.
|
||||||
* well as by <code>,</code> <small>(comma)</small>.</li>
|
|
||||||
* <li>Numbers may have the
|
|
||||||
* <code>0x-</code> <small>(hex)</small> prefix.</li>
|
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
@@ -74,16 +67,10 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class JSONArray {
|
public class JSONArray {
|
||||||
|
|
||||||
|
/** The arrayList where the JSONArray's properties are kept. */
|
||||||
/**
|
|
||||||
* The arrayList where the JSONArray's properties are kept.
|
|
||||||
*/
|
|
||||||
private ArrayList myArrayList;
|
private ArrayList myArrayList;
|
||||||
|
|
||||||
|
/** Construct an empty JSONArray. */
|
||||||
/**
|
|
||||||
* Construct an empty JSONArray.
|
|
||||||
*/
|
|
||||||
public JSONArray() {
|
public JSONArray() {
|
||||||
this.myArrayList = new ArrayList();
|
this.myArrayList = new ArrayList();
|
||||||
}
|
}
|
||||||
@@ -126,20 +113,17 @@ public class JSONArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a JSONArray from a source JSON text.
|
* Construct a JSONArray from a source JSON text.
|
||||||
*
|
*
|
||||||
* @param source A string that begins with
|
* @param source A string that begins with <code>[</code> <small>(left bracket)</small> and
|
||||||
* <code>[</code> <small>(left bracket)</small>
|
* ends with <code>]</code> <small>(right bracket)</small>.
|
||||||
* and ends with <code>]</code> <small>(right bracket)</small>.
|
|
||||||
* @throws JSONException If there is a syntax error.
|
* @throws JSONException If there is a syntax error.
|
||||||
*/
|
*/
|
||||||
public JSONArray(String source) throws JSONException {
|
public JSONArray(String source) throws JSONException {
|
||||||
this(new JSONTokener(source));
|
this(new JSONTokener(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a JSONArray from a Collection.
|
* Construct a JSONArray from a Collection.
|
||||||
*
|
*
|
||||||
@@ -155,7 +139,6 @@ public class JSONArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a JSONArray from an array
|
* Construct a JSONArray from an array
|
||||||
*
|
*
|
||||||
@@ -169,12 +152,10 @@ public class JSONArray {
|
|||||||
this.put(JSONObject.wrap(Array.get(array, i)));
|
this.put(JSONObject.wrap(Array.get(array, i)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new JSONException(
|
throw new JSONException("JSONArray initial value should be a string or collection or array.");
|
||||||
"JSONArray initial value should be a string or collection or array.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the object value associated with an index.
|
* Get the object value associated with an index.
|
||||||
*
|
*
|
||||||
@@ -190,52 +171,45 @@ public class JSONArray {
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the boolean value associated with an index.
|
* Get the boolean value associated with an index. The string values "true" and "false" are
|
||||||
* The string values "true" and "false" are converted to boolean.
|
* converted to boolean.
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return The truth.
|
* @return The truth.
|
||||||
* @throws JSONException If there is no value for the index or if the
|
* @throws JSONException If there is no value for the index or if the value is not convertible to
|
||||||
* value is not convertible to boolean.
|
* boolean.
|
||||||
*/
|
*/
|
||||||
public boolean getBoolean(int index) throws JSONException {
|
public boolean getBoolean(int index) throws JSONException {
|
||||||
Object object = get(index);
|
Object object = get(index);
|
||||||
if (object.equals(Boolean.FALSE) ||
|
if (object.equals(Boolean.FALSE)
|
||||||
(object instanceof String &&
|
|| (object instanceof String && ((String) object).equalsIgnoreCase("false"))) {
|
||||||
((String) object).equalsIgnoreCase("false"))) {
|
|
||||||
return false;
|
return false;
|
||||||
} else if (object.equals(Boolean.TRUE) ||
|
} else if (object.equals(Boolean.TRUE)
|
||||||
(object instanceof String &&
|
|| (object instanceof String && ((String) object).equalsIgnoreCase("true"))) {
|
||||||
((String) object).equalsIgnoreCase("true"))) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
|
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the double value associated with an index.
|
* Get the double value associated with an index.
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return The value.
|
* @return The value.
|
||||||
* @throws JSONException If the key is not found or if the value cannot
|
* @throws JSONException If the key is not found or if the value cannot be converted to a number.
|
||||||
* be converted to a number.
|
|
||||||
*/
|
*/
|
||||||
public double getDouble(int index) throws JSONException {
|
public double getDouble(int index) throws JSONException {
|
||||||
Object object = get(index);
|
Object object = get(index);
|
||||||
try {
|
try {
|
||||||
return object instanceof Number ?
|
return object instanceof Number
|
||||||
((Number) object).doubleValue() :
|
? ((Number) object).doubleValue()
|
||||||
Double.parseDouble((String) object);
|
: Double.parseDouble((String) object);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new JSONException("JSONArray[" + index +
|
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||||
"] is not a number.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the int value associated with an index.
|
* Get the int value associated with an index.
|
||||||
*
|
*
|
||||||
@@ -246,73 +220,62 @@ public class JSONArray {
|
|||||||
public int getInt(int index) throws JSONException {
|
public int getInt(int index) throws JSONException {
|
||||||
Object object = get(index);
|
Object object = get(index);
|
||||||
try {
|
try {
|
||||||
return object instanceof Number ?
|
return object instanceof Number
|
||||||
((Number) object).intValue() :
|
? ((Number) object).intValue()
|
||||||
Integer.parseInt((String) object);
|
: Integer.parseInt((String) object);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new JSONException("JSONArray[" + index +
|
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||||
"] is not a number.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the JSONArray associated with an index.
|
* Get the JSONArray associated with an index.
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return A JSONArray value.
|
* @return A JSONArray value.
|
||||||
* @throws JSONException If there is no value for the index. or if the
|
* @throws JSONException If there is no value for the index. or if the value is not a JSONArray
|
||||||
* value is not a JSONArray
|
|
||||||
*/
|
*/
|
||||||
public JSONArray getJSONArray(int index) throws JSONException {
|
public JSONArray getJSONArray(int index) throws JSONException {
|
||||||
Object object = get(index);
|
Object object = get(index);
|
||||||
if (object instanceof JSONArray) {
|
if (object instanceof JSONArray) {
|
||||||
return (JSONArray) object;
|
return (JSONArray) object;
|
||||||
}
|
}
|
||||||
throw new JSONException("JSONArray[" + index +
|
throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
|
||||||
"] is not a JSONArray.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the JSONObject associated with an index.
|
* Get the JSONObject associated with an index.
|
||||||
*
|
*
|
||||||
* @param index subscript
|
* @param index subscript
|
||||||
* @return A JSONObject value.
|
* @return A JSONObject value.
|
||||||
* @throws JSONException If there is no value for the index or if the
|
* @throws JSONException If there is no value for the index or if the value is not a JSONObject
|
||||||
* value is not a JSONObject
|
|
||||||
*/
|
*/
|
||||||
public JSONObject getJSONObject(int index) throws JSONException {
|
public JSONObject getJSONObject(int index) throws JSONException {
|
||||||
Object object = get(index);
|
Object object = get(index);
|
||||||
if (object instanceof JSONObject) {
|
if (object instanceof JSONObject) {
|
||||||
return (JSONObject) object;
|
return (JSONObject) object;
|
||||||
}
|
}
|
||||||
throw new JSONException("JSONArray[" + index +
|
throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
|
||||||
"] is not a JSONObject.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the long value associated with an index.
|
* Get the long value associated with an index.
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return The value.
|
* @return The value.
|
||||||
* @throws JSONException If the key is not found or if the value cannot
|
* @throws JSONException If the key is not found or if the value cannot be converted to a number.
|
||||||
* be converted to a number.
|
|
||||||
*/
|
*/
|
||||||
public long getLong(int index) throws JSONException {
|
public long getLong(int index) throws JSONException {
|
||||||
Object object = get(index);
|
Object object = get(index);
|
||||||
try {
|
try {
|
||||||
return object instanceof Number ?
|
return object instanceof Number
|
||||||
((Number) object).longValue() :
|
? ((Number) object).longValue()
|
||||||
Long.parseLong((String) object);
|
: Long.parseLong((String) object);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new JSONException("JSONArray[" + index +
|
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||||
"] is not a number.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the string associated with an index.
|
* Get the string associated with an index.
|
||||||
*
|
*
|
||||||
@@ -328,7 +291,6 @@ public class JSONArray {
|
|||||||
throw new JSONException("JSONArray[" + index + "] not a string.");
|
throw new JSONException("JSONArray[" + index + "] not a string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the value is null.
|
* Determine if the value is null.
|
||||||
*
|
*
|
||||||
@@ -339,11 +301,10 @@ public class JSONArray {
|
|||||||
return JSONObject.NULL.equals(opt(index));
|
return JSONObject.NULL.equals(opt(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a string from the contents of this JSONArray. The
|
* Make a string from the contents of this JSONArray. The <code>separator</code> string is
|
||||||
* <code>separator</code> string is inserted between each element.
|
* inserted between each element. Warning: This method assumes that the data structure is
|
||||||
* Warning: This method assumes that the data structure is acyclical.
|
* acyclical.
|
||||||
*
|
*
|
||||||
* @param separator A string that will be inserted between the elements.
|
* @param separator A string that will be inserted between the elements.
|
||||||
* @return a string.
|
* @return a string.
|
||||||
@@ -362,7 +323,6 @@ public class JSONArray {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of elements in the JSONArray, included nulls.
|
* Get the number of elements in the JSONArray, included nulls.
|
||||||
*
|
*
|
||||||
@@ -372,24 +332,19 @@ public class JSONArray {
|
|||||||
return this.myArrayList.size();
|
return this.myArrayList.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional object value associated with an index.
|
* Get the optional object value associated with an index.
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return An object value, or null if there is no
|
* @return An object value, or null if there is no object at that index.
|
||||||
* object at that index.
|
|
||||||
*/
|
*/
|
||||||
public Object opt(int index) {
|
public Object opt(int index) {
|
||||||
return (index < 0 || index >= length()) ?
|
return (index < 0 || index >= length()) ? null : this.myArrayList.get(index);
|
||||||
null : this.myArrayList.get(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional boolean value associated with an index.
|
* Get the optional boolean value associated with an index. It returns false if there is no value
|
||||||
* It returns false if there is no value at that index,
|
* at that index, or if the value is not Boolean.TRUE or the String "true".
|
||||||
* or if the value is not Boolean.TRUE or the String "true".
|
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return The truth.
|
* @return The truth.
|
||||||
@@ -398,11 +353,10 @@ public class JSONArray {
|
|||||||
return optBoolean(index, false);
|
return optBoolean(index, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional boolean value associated with an index.
|
* Get the optional boolean value associated with an index. It returns the defaultValue if there
|
||||||
* It returns the defaultValue if there is no value at that index or if
|
* is no value at that index or if it is not a Boolean or the String "true" or "false" (case
|
||||||
* it is not a Boolean or the String "true" or "false" (case insensitive).
|
* insensitive).
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @param defaultValue A boolean default.
|
* @param defaultValue A boolean default.
|
||||||
@@ -416,11 +370,9 @@ public class JSONArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional double value associated with an index.
|
* Get the optional double value associated with an index. NaN is returned if there is no value
|
||||||
* NaN is returned if there is no value for the index,
|
* for the index, or if the value is not a number and cannot be converted to a number.
|
||||||
* or if the value is not a number and cannot be converted to a number.
|
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return The value.
|
* @return The value.
|
||||||
@@ -429,11 +381,9 @@ public class JSONArray {
|
|||||||
return optDouble(index, Double.NaN);
|
return optDouble(index, Double.NaN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional double value associated with an index.
|
* Get the optional double value associated with an index. The defaultValue is returned if there
|
||||||
* The defaultValue is returned if there is no value for the index,
|
* is no value for the index, or if the value is not a number and cannot be converted to a number.
|
||||||
* or if the value is not a number and cannot be converted to a number.
|
|
||||||
*
|
*
|
||||||
* @param index subscript
|
* @param index subscript
|
||||||
* @param defaultValue The default value.
|
* @param defaultValue The default value.
|
||||||
@@ -447,11 +397,9 @@ public class JSONArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional int value associated with an index.
|
* Get the optional int value associated with an index. Zero is returned if there is no value for
|
||||||
* Zero is returned if there is no value for the index,
|
* the index, or if the value is not a number and cannot be converted to a number.
|
||||||
* or if the value is not a number and cannot be converted to a number.
|
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return The value.
|
* @return The value.
|
||||||
@@ -460,11 +408,9 @@ public class JSONArray {
|
|||||||
return optInt(index, 0);
|
return optInt(index, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional int value associated with an index.
|
* Get the optional int value associated with an index. The defaultValue is returned if there is
|
||||||
* The defaultValue is returned if there is no value for the index,
|
* no value for the index, or if the value is not a number and cannot be converted to a number.
|
||||||
* or if the value is not a number and cannot be converted to a number.
|
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @param defaultValue The default value.
|
* @param defaultValue The default value.
|
||||||
@@ -478,24 +424,21 @@ public class JSONArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional JSONArray associated with an index.
|
* Get the optional JSONArray associated with an index.
|
||||||
*
|
*
|
||||||
* @param index subscript
|
* @param index subscript
|
||||||
* @return A JSONArray value, or null if the index has no value,
|
* @return A JSONArray value, or null if the index has no value, or if the value is not a
|
||||||
* or if the value is not a JSONArray.
|
* JSONArray.
|
||||||
*/
|
*/
|
||||||
public JSONArray optJSONArray(int index) {
|
public JSONArray optJSONArray(int index) {
|
||||||
Object o = opt(index);
|
Object o = opt(index);
|
||||||
return o instanceof JSONArray ? (JSONArray) o : null;
|
return o instanceof JSONArray ? (JSONArray) o : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional JSONObject associated with an index.
|
* Get the optional JSONObject associated with an index. Null is returned if the key is not found,
|
||||||
* Null is returned if the key is not found, or null if the index has
|
* or null if the index has no value, or if the value is not a JSONObject.
|
||||||
* no value, or if the value is not a JSONObject.
|
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return A JSONObject value.
|
* @return A JSONObject value.
|
||||||
@@ -505,11 +448,9 @@ public class JSONArray {
|
|||||||
return o instanceof JSONObject ? (JSONObject) o : null;
|
return o instanceof JSONObject ? (JSONObject) o : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional long value associated with an index.
|
* Get the optional long value associated with an index. Zero is returned if there is no value for
|
||||||
* Zero is returned if there is no value for the index,
|
* the index, or if the value is not a number and cannot be converted to a number.
|
||||||
* or if the value is not a number and cannot be converted to a number.
|
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return The value.
|
* @return The value.
|
||||||
@@ -518,11 +459,9 @@ public class JSONArray {
|
|||||||
return optLong(index, 0);
|
return optLong(index, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional long value associated with an index.
|
* Get the optional long value associated with an index. The defaultValue is returned if there is
|
||||||
* The defaultValue is returned if there is no value for the index,
|
* no value for the index, or if the value is not a number and cannot be converted to a number.
|
||||||
* or if the value is not a number and cannot be converted to a number.
|
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @param defaultValue The default value.
|
* @param defaultValue The default value.
|
||||||
@@ -536,11 +475,10 @@ public class JSONArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional string value associated with an index. It returns an
|
* Get the optional string value associated with an index. It returns an empty string if there is
|
||||||
* empty string if there is no value at that index. If the value
|
* no value at that index. If the value is not a string and is not null, then it is coverted to a
|
||||||
* is not a string and is not null, then it is coverted to a string.
|
* string.
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @return A String value.
|
* @return A String value.
|
||||||
@@ -549,10 +487,9 @@ public class JSONArray {
|
|||||||
return optString(index, "");
|
return optString(index, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optional string associated with an index.
|
* Get the optional string associated with an index. The defaultValue is returned if the key is
|
||||||
* The defaultValue is returned if the key is not found.
|
* not found.
|
||||||
*
|
*
|
||||||
* @param index The index must be between 0 and length() - 1.
|
* @param index The index must be between 0 and length() - 1.
|
||||||
* @param defaultValue The default value.
|
* @param defaultValue The default value.
|
||||||
@@ -563,7 +500,6 @@ public class JSONArray {
|
|||||||
return object != null ? object.toString() : defaultValue;
|
return object != null ? object.toString() : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append a boolean value. This increases the array's length by one.
|
* Append a boolean value. This increases the array's length by one.
|
||||||
*
|
*
|
||||||
@@ -575,10 +511,9 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put a value in the JSONArray, where the value will be a
|
* Put a value in the JSONArray, where the value will be a JSONArray which is produced from a
|
||||||
* JSONArray which is produced from a Collection.
|
* Collection.
|
||||||
*
|
*
|
||||||
* @param value A Collection value.
|
* @param value A Collection value.
|
||||||
* @return this.
|
* @return this.
|
||||||
@@ -588,7 +523,6 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append a double value. This increases the array's length by one.
|
* Append a double value. This increases the array's length by one.
|
||||||
*
|
*
|
||||||
@@ -603,7 +537,6 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append an int value. This increases the array's length by one.
|
* Append an int value. This increases the array's length by one.
|
||||||
*
|
*
|
||||||
@@ -615,7 +548,6 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append an long value. This increases the array's length by one.
|
* Append an long value. This increases the array's length by one.
|
||||||
*
|
*
|
||||||
@@ -627,10 +559,9 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put a value in the JSONArray, where the value will be a
|
* Put a value in the JSONArray, where the value will be a JSONObject which is produced from a
|
||||||
* JSONObject which is produced from a Map.
|
* Map.
|
||||||
*
|
*
|
||||||
* @param value A Map value.
|
* @param value A Map value.
|
||||||
* @return this.
|
* @return this.
|
||||||
@@ -640,13 +571,11 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append an object value. This increases the array's length by one.
|
* Append an object value. This increases the array's length by one.
|
||||||
*
|
*
|
||||||
* @param value An object value. The value should be a
|
* @param value An object value. The value should be a Boolean, Double, Integer, JSONArray,
|
||||||
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
|
* JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||||
* JSONObject.NULL object.
|
|
||||||
* @return this.
|
* @return this.
|
||||||
*/
|
*/
|
||||||
public JSONArray put(Object value) {
|
public JSONArray put(Object value) {
|
||||||
@@ -654,11 +583,9 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put or replace a boolean value in the JSONArray. If the index is greater
|
* Put or replace a boolean value in the JSONArray. If the index is greater than the length of the
|
||||||
* than the length of the JSONArray, then null elements will be added as
|
* JSONArray, then null elements will be added as necessary to pad it out.
|
||||||
* necessary to pad it out.
|
|
||||||
*
|
*
|
||||||
* @param index The subscript.
|
* @param index The subscript.
|
||||||
* @param value A boolean value.
|
* @param value A boolean value.
|
||||||
@@ -670,44 +597,37 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put a value in the JSONArray, where the value will be a
|
* Put a value in the JSONArray, where the value will be a JSONArray which is produced from a
|
||||||
* JSONArray which is produced from a Collection.
|
* Collection.
|
||||||
*
|
*
|
||||||
* @param index The subscript.
|
* @param index The subscript.
|
||||||
* @param value A Collection value.
|
* @param value A Collection value.
|
||||||
* @return this.
|
* @return this.
|
||||||
* @throws JSONException If the index is negative or if the value is
|
* @throws JSONException If the index is negative or if the value is not finite.
|
||||||
* not finite.
|
|
||||||
*/
|
*/
|
||||||
public JSONArray put(int index, Collection value) throws JSONException {
|
public JSONArray put(int index, Collection value) throws JSONException {
|
||||||
put(index, new JSONArray(value));
|
put(index, new JSONArray(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put or replace a double value. If the index is greater than the length of
|
* Put or replace a double value. If the index is greater than the length of the JSONArray, then
|
||||||
* the JSONArray, then null elements will be added as necessary to pad
|
* null elements will be added as necessary to pad it out.
|
||||||
* it out.
|
|
||||||
*
|
*
|
||||||
* @param index The subscript.
|
* @param index The subscript.
|
||||||
* @param value A double value.
|
* @param value A double value.
|
||||||
* @return this.
|
* @return this.
|
||||||
* @throws JSONException If the index is negative or if the value is
|
* @throws JSONException If the index is negative or if the value is not finite.
|
||||||
* not finite.
|
|
||||||
*/
|
*/
|
||||||
public JSONArray put(int index, double value) throws JSONException {
|
public JSONArray put(int index, double value) throws JSONException {
|
||||||
put(index, new Double(value));
|
put(index, new Double(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put or replace an int value. If the index is greater than the length of
|
* Put or replace an int value. If the index is greater than the length of the JSONArray, then
|
||||||
* the JSONArray, then null elements will be added as necessary to pad
|
* null elements will be added as necessary to pad it out.
|
||||||
* it out.
|
|
||||||
*
|
*
|
||||||
* @param index The subscript.
|
* @param index The subscript.
|
||||||
* @param value An int value.
|
* @param value An int value.
|
||||||
@@ -719,11 +639,9 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put or replace a long value. If the index is greater than the length of
|
* Put or replace a long value. If the index is greater than the length of the JSONArray, then
|
||||||
* the JSONArray, then null elements will be added as necessary to pad
|
* null elements will be added as necessary to pad it out.
|
||||||
* it out.
|
|
||||||
*
|
*
|
||||||
* @param index The subscript.
|
* @param index The subscript.
|
||||||
* @param value A long value.
|
* @param value A long value.
|
||||||
@@ -735,35 +653,28 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put a value in the JSONArray, where the value will be a
|
* Put a value in the JSONArray, where the value will be a JSONObject that is produced from a Map.
|
||||||
* JSONObject that is produced from a Map.
|
|
||||||
*
|
*
|
||||||
* @param index The subscript.
|
* @param index The subscript.
|
||||||
* @param value The Map value.
|
* @param value The Map value.
|
||||||
* @return this.
|
* @return this.
|
||||||
* @throws JSONException If the index is negative or if the the value is
|
* @throws JSONException If the index is negative or if the the value is an invalid number.
|
||||||
* an invalid number.
|
|
||||||
*/
|
*/
|
||||||
public JSONArray put(int index, Map value) throws JSONException {
|
public JSONArray put(int index, Map value) throws JSONException {
|
||||||
put(index, new JSONObject(value));
|
put(index, new JSONObject(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put or replace an object value in the JSONArray. If the index is greater
|
* Put or replace an object value in the JSONArray. If the index is greater than the length of the
|
||||||
* than the length of the JSONArray, then null elements will be added as
|
* JSONArray, then null elements will be added as necessary to pad it out.
|
||||||
* necessary to pad it out.
|
|
||||||
*
|
*
|
||||||
* @param index The subscript.
|
* @param index The subscript.
|
||||||
* @param value The value to put into the array. The value should be a
|
* @param value The value to put into the array. The value should be a Boolean, Double, Integer,
|
||||||
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
|
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||||
* JSONObject.NULL object.
|
|
||||||
* @return this.
|
* @return this.
|
||||||
* @throws JSONException If the index is negative or if the the value is
|
* @throws JSONException If the index is negative or if the the value is an invalid number.
|
||||||
* an invalid number.
|
|
||||||
*/
|
*/
|
||||||
public JSONArray put(int index, Object value) throws JSONException {
|
public JSONArray put(int index, Object value) throws JSONException {
|
||||||
JSONObject.testValidity(value);
|
JSONObject.testValidity(value);
|
||||||
@@ -781,13 +692,11 @@ public class JSONArray {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an index and close the hole.
|
* Remove an index and close the hole.
|
||||||
*
|
*
|
||||||
* @param index The index of the element to be removed.
|
* @param index The index of the element to be removed.
|
||||||
* @return The value that was associated with the index,
|
* @return The value that was associated with the index, or null if there was no value.
|
||||||
* or null if there was no value.
|
|
||||||
*/
|
*/
|
||||||
public Object remove(int index) {
|
public Object remove(int index) {
|
||||||
Object o = opt(index);
|
Object o = opt(index);
|
||||||
@@ -795,15 +704,12 @@ public class JSONArray {
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produce a JSONObject by combining a JSONArray of names with the values
|
* Produce a JSONObject by combining a JSONArray of names with the values of this JSONArray.
|
||||||
* of this JSONArray.
|
|
||||||
*
|
*
|
||||||
* @param names A JSONArray containing a list of key strings. These will be
|
* @param names A JSONArray containing a list of key strings. These will be paired with the
|
||||||
* paired with the values.
|
* values.
|
||||||
* @return A JSONObject, or null if there are no names or if this JSONArray
|
* @return A JSONObject, or null if there are no names or if this JSONArray has no values.
|
||||||
* has no values.
|
|
||||||
* @throws JSONException If any of the names are null.
|
* @throws JSONException If any of the names are null.
|
||||||
*/
|
*/
|
||||||
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
||||||
@@ -817,17 +723,14 @@ public class JSONArray {
|
|||||||
return jo;
|
return jo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a JSON text of this JSONArray. For compactness, no
|
* Make a JSON text of this JSONArray. For compactness, no unnecessary whitespace is added. If it
|
||||||
* unnecessary whitespace is added. If it is not possible to produce a
|
* is not possible to produce a syntactically correct JSON text then null will be returned
|
||||||
* syntactically correct JSON text then null will be returned instead. This
|
* instead. This could occur if the array contains an invalid number.
|
||||||
* could occur if the array contains an invalid number.
|
|
||||||
* <p>
|
|
||||||
* Warning: This method assumes that the data structure is acyclical.
|
|
||||||
*
|
*
|
||||||
* @return a printable, displayable, transmittable
|
* <p>Warning: This method assumes that the data structure is acyclical.
|
||||||
* representation of the array.
|
*
|
||||||
|
* @return a printable, displayable, transmittable representation of the array.
|
||||||
*/
|
*/
|
||||||
public String toString() {
|
public String toString() {
|
||||||
try {
|
try {
|
||||||
@@ -837,33 +740,27 @@ public class JSONArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a prettyprinted JSON text of this JSONArray.
|
* Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data
|
||||||
* Warning: This method assumes that the data structure is acyclical.
|
* structure is acyclical.
|
||||||
*
|
*
|
||||||
* @param indentFactor The number of spaces to add to each level of
|
* @param indentFactor The number of spaces to add to each level of indentation.
|
||||||
* indentation.
|
* @return a printable, displayable, transmittable representation of the object, beginning with
|
||||||
* @return a printable, displayable, transmittable
|
* <code>[</code> <small>(left bracket)</small> and ending with <code>]</code>
|
||||||
* representation of the object, beginning
|
* <small>(right bracket)</small>.
|
||||||
* with <code>[</code> <small>(left bracket)</small> and ending
|
|
||||||
* with <code>]</code> <small>(right bracket)</small>.
|
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public String toString(int indentFactor) throws JSONException {
|
public String toString(int indentFactor) throws JSONException {
|
||||||
return toString(indentFactor, 0);
|
return toString(indentFactor, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a prettyprinted JSON text of this JSONArray.
|
* Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data
|
||||||
* Warning: This method assumes that the data structure is acyclical.
|
* structure is acyclical.
|
||||||
*
|
*
|
||||||
* @param indentFactor The number of spaces to add to each level of
|
* @param indentFactor The number of spaces to add to each level of indentation.
|
||||||
* indentation.
|
|
||||||
* @param indent The indention of the top level.
|
* @param indent The indention of the top level.
|
||||||
* @return a printable, displayable, transmittable
|
* @return a printable, displayable, transmittable representation of the array.
|
||||||
* representation of the array.
|
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
String toString(int indentFactor, int indent) throws JSONException {
|
String toString(int indentFactor, int indent) throws JSONException {
|
||||||
@@ -874,8 +771,7 @@ public class JSONArray {
|
|||||||
int i;
|
int i;
|
||||||
StringBuffer sb = new StringBuffer("[");
|
StringBuffer sb = new StringBuffer("[");
|
||||||
if (len == 1) {
|
if (len == 1) {
|
||||||
sb.append(JSONObject.valueToString(this.myArrayList.get(0),
|
sb.append(JSONObject.valueToString(this.myArrayList.get(0), indentFactor, indent));
|
||||||
indentFactor, indent));
|
|
||||||
} else {
|
} else {
|
||||||
int newindent = indent + indentFactor;
|
int newindent = indent + indentFactor;
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
@@ -886,8 +782,7 @@ public class JSONArray {
|
|||||||
for (int j = 0; j < newindent; j += 1) {
|
for (int j = 0; j < newindent; j += 1) {
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
}
|
}
|
||||||
sb.append(JSONObject.valueToString(this.myArrayList.get(i),
|
sb.append(JSONObject.valueToString(this.myArrayList.get(i), indentFactor, newindent));
|
||||||
indentFactor, newindent));
|
|
||||||
}
|
}
|
||||||
sb.append('\n');
|
sb.append('\n');
|
||||||
for (i = 0; i < indent; i += 1) {
|
for (i = 0; i < indent; i += 1) {
|
||||||
@@ -898,12 +793,11 @@ public class JSONArray {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the contents of the JSONArray as JSON text to a writer.
|
* Write the contents of the JSONArray as JSON text to a writer. For compactness, no whitespace is
|
||||||
* For compactness, no whitespace is added.
|
* added.
|
||||||
* <p>
|
*
|
||||||
* Warning: This method assumes that the data structure is acyclical.
|
* <p>Warning: This method assumes that the data structure is acyclical.
|
||||||
*
|
*
|
||||||
* @return The writer.
|
* @return The writer.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
|
|||||||
@@ -18,11 +18,9 @@ package dev.brighten.antivpn.utils.json;
|
|||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides static methods to convert an XML text into a JSONArray or
|
* This provides static methods to convert an XML text into a JSONArray or JSONObject, and to covert
|
||||||
* JSONObject, and to covert a JSONArray or JSONObject into an XML text using
|
* a JSONArray or JSONObject into an XML text using the JsonML transform.
|
||||||
* the JsonML transform.
|
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2010-12-23
|
* @version 2010-12-23
|
||||||
@@ -34,13 +32,12 @@ public class JSONML {
|
|||||||
*
|
*
|
||||||
* @param x The XMLTokener containing the source string.
|
* @param x The XMLTokener containing the source string.
|
||||||
* @param arrayForm true if array form, false if object form.
|
* @param arrayForm true if array form, false if object form.
|
||||||
* @param ja The JSONArray that is containing the current tag or null
|
* @param ja The JSONArray that is containing the current tag or null if we are at the outermost
|
||||||
* if we are at the outermost level.
|
* level.
|
||||||
* @return A JSONArray if the value is the outermost tag, otherwise null.
|
* @return A JSONArray if the value is the outermost tag, otherwise null.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
private static Object parse(XMLTokener x, boolean arrayForm,
|
private static Object parse(XMLTokener x, boolean arrayForm, JSONArray ja) throws JSONException {
|
||||||
JSONArray ja) throws JSONException {
|
|
||||||
String attribute;
|
String attribute;
|
||||||
char c;
|
char c;
|
||||||
String closeTag = null;
|
String closeTag = null;
|
||||||
@@ -67,9 +64,7 @@ public class JSONML {
|
|||||||
|
|
||||||
token = x.nextToken();
|
token = x.nextToken();
|
||||||
if (!(token instanceof String)) {
|
if (!(token instanceof String)) {
|
||||||
throw new JSONException(
|
throw new JSONException("Expected a closing name instead of '" + token + "'.");
|
||||||
"Expected a closing name instead of '" +
|
|
||||||
token + "'.");
|
|
||||||
}
|
}
|
||||||
if (x.nextToken() != XML.GT) {
|
if (x.nextToken() != XML.GT) {
|
||||||
throw x.syntaxError("Misshaped close tag");
|
throw x.syntaxError("Misshaped close tag");
|
||||||
@@ -193,8 +188,7 @@ public class JSONML {
|
|||||||
closeTag = (String) parse(x, arrayForm, newja);
|
closeTag = (String) parse(x, arrayForm, newja);
|
||||||
if (closeTag != null) {
|
if (closeTag != null) {
|
||||||
if (!closeTag.equals(tagName)) {
|
if (!closeTag.equals(tagName)) {
|
||||||
throw x.syntaxError("Mismatched '" + tagName +
|
throw x.syntaxError("Mismatched '" + tagName + "' and '" + closeTag + "'");
|
||||||
"' and '" + closeTag + "'");
|
|
||||||
}
|
}
|
||||||
tagName = null;
|
tagName = null;
|
||||||
if (!arrayForm && newja.length() > 0) {
|
if (!arrayForm && newja.length() > 0) {
|
||||||
@@ -212,22 +206,18 @@ public class JSONML {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ja != null) {
|
if (ja != null) {
|
||||||
ja.put(token instanceof String ?
|
ja.put(token instanceof String ? XML.stringToValue((String) token) : token);
|
||||||
XML.stringToValue((String) token) : token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
* Convert a well-formed (but not necessarily valid) XML string into a JSONArray using the JsonML
|
||||||
* JSONArray using the JsonML transform. Each XML tag is represented as
|
* transform. Each XML tag is represented as a JSONArray in which the first element is the tag
|
||||||
* a JSONArray in which the first element is the tag name. If the tag has
|
* name. If the tag has attributes, then the second element will be JSONObject containing the
|
||||||
* 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
|
||||||
* name/value pairs. If the tag contains children, then strings and
|
* child tags. Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||||
* JSONArrays will represent the child tags.
|
|
||||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
|
||||||
*
|
*
|
||||||
* @param string The source string.
|
* @param string The source string.
|
||||||
* @return A JSONArray containing the structured data from the XML string.
|
* @return A JSONArray containing the structured data from the XML string.
|
||||||
@@ -237,15 +227,12 @@ public class JSONML {
|
|||||||
return toJSONArray(new XMLTokener(string));
|
return toJSONArray(new XMLTokener(string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
* Convert a well-formed (but not necessarily valid) XML string into a JSONArray using the JsonML
|
||||||
* JSONArray using the JsonML transform. Each XML tag is represented as
|
* transform. Each XML tag is represented as a JSONArray in which the first element is the tag
|
||||||
* a JSONArray in which the first element is the tag name. If the tag has
|
* name. If the tag has attributes, then the second element will be JSONObject containing the
|
||||||
* 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
|
||||||
* name/value pairs. If the tag contains children, then strings and
|
* child content and tags. Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||||
* JSONArrays will represent the child content and tags.
|
|
||||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
|
||||||
*
|
*
|
||||||
* @param x An XMLTokener.
|
* @param x An XMLTokener.
|
||||||
* @return A JSONArray containing the structured data from the XML string.
|
* @return A JSONArray containing the structured data from the XML string.
|
||||||
@@ -255,16 +242,14 @@ public class JSONML {
|
|||||||
return (JSONArray) parse(x, true, null);
|
return (JSONArray) parse(x, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
* Convert a well-formed (but not necessarily valid) XML string into a JSONObject using the JsonML
|
||||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
* transform. Each XML tag is represented as a JSONObject with a "tagName" property. If the tag
|
||||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
* has attributes, then the attributes will be in the JSONObject as properties. If the tag
|
||||||
* 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
|
||||||
* contains children, the object will have a "childNodes" property which
|
* strings and JsonML JSONObjects.
|
||||||
* will be an array of strings and JsonML JSONObjects.
|
*
|
||||||
* <p>
|
* <p>Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
|
||||||
*
|
*
|
||||||
* @param x An XMLTokener of the XML source text.
|
* @param x An XMLTokener of the XML source text.
|
||||||
* @return A JSONObject containing the structured data from the XML string.
|
* @return A JSONObject containing the structured data from the XML string.
|
||||||
@@ -274,16 +259,14 @@ public class JSONML {
|
|||||||
return (JSONObject) parse(x, false, null);
|
return (JSONObject) parse(x, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
* Convert a well-formed (but not necessarily valid) XML string into a JSONObject using the JsonML
|
||||||
* JSONObject using the JsonML transform. Each XML tag is represented as
|
* transform. Each XML tag is represented as a JSONObject with a "tagName" property. If the tag
|
||||||
* a JSONObject with a "tagName" property. If the tag has attributes, then
|
* has attributes, then the attributes will be in the JSONObject as properties. If the tag
|
||||||
* 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
|
||||||
* contains children, the object will have a "childNodes" property which
|
* strings and JsonML JSONObjects.
|
||||||
* will be an array of strings and JsonML JSONObjects.
|
*
|
||||||
* <p>
|
* <p>Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
||||||
* Comments, prologs, DTDs, and <code><[ [ ]]></code> are ignored.
|
|
||||||
*
|
*
|
||||||
* @param string The XML source text.
|
* @param string The XML source text.
|
||||||
* @return A JSONObject containing the structured data from the XML string.
|
* @return A JSONObject containing the structured data from the XML string.
|
||||||
@@ -293,7 +276,6 @@ public class JSONML {
|
|||||||
return toJSONObject(new XMLTokener(string));
|
return toJSONObject(new XMLTokener(string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverse the JSONML transformation, making an XML text from a JSONArray.
|
* Reverse the JSONML transformation, making an XML text from a JSONArray.
|
||||||
*
|
*
|
||||||
@@ -375,10 +357,9 @@ public class JSONML {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverse the JSONML transformation, making an XML text from a JSONObject.
|
* Reverse the JSONML transformation, making an XML text from a JSONObject. The JSONObject must
|
||||||
* The JSONObject must contain a "tagName" property. If it has children,
|
* contain a "tagName" property. If it has children, then it must have a "childNodes" property
|
||||||
* then it must have a "childNodes" property containing an array of objects.
|
* containing an array of objects. The other properties are attributes with string values.
|
||||||
* The other properties are attributes with string values.
|
|
||||||
*
|
*
|
||||||
* @param jo A JSONObject.
|
* @param jo A JSONObject.
|
||||||
* @return An XML string.
|
* @return An XML string.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -17,19 +17,17 @@
|
|||||||
package dev.brighten.antivpn.utils.json;
|
package dev.brighten.antivpn.utils.json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
|
* The <code>JSONString</code> interface allows a <code>toJSONString()</code> method so that a class
|
||||||
* method so that a class can change the behavior of
|
* can change the behavior of <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
|
||||||
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
|
* and <code>JSONWriter.value(</code>Object<code>)</code>. The <code>toJSONString</code> method will
|
||||||
* and <code>JSONWriter.value(</code>Object<code>)</code>. The
|
* be used instead of the default behavior of using the Object's <code>toString()</code> method and
|
||||||
* <code>toJSONString</code> method will be used instead of the default behavior
|
* quoting the result.
|
||||||
* of using the Object's <code>toString()</code> method and quoting the result.
|
|
||||||
*/
|
*/
|
||||||
public interface JSONString {
|
public interface JSONString {
|
||||||
/**
|
/**
|
||||||
* The <code>toJSONString</code> method allows a class to produce its own JSON
|
* The <code>toJSONString</code> method allows a class to produce its own JSON serialization.
|
||||||
* serialization.
|
|
||||||
*
|
*
|
||||||
* @return A strictly syntactically correct JSON text.
|
* @return A strictly syntactically correct JSON text.
|
||||||
*/
|
*/
|
||||||
public String toJSONString();
|
String toJSONString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,50 +19,49 @@ package dev.brighten.antivpn.utils.json;
|
|||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSONStringer provides a quick and convenient way of producing JSON text.
|
* JSONStringer provides a quick and convenient way of producing JSON text. The texts produced
|
||||||
* The texts produced strictly conform to JSON syntax rules. No whitespace is
|
* strictly conform to JSON syntax rules. No whitespace is added, so the results are ready for
|
||||||
* added, so the results are ready for transmission or storage. Each instance of
|
* transmission or storage. Each instance of JSONStringer can produce one JSON text.
|
||||||
* JSONStringer can produce one JSON text.
|
*
|
||||||
* <p>
|
* <p>A JSONStringer instance provides a <code>value</code> method for appending values to the text,
|
||||||
* A JSONStringer instance provides a <code>value</code> method for appending
|
* and a <code>key</code> method for adding keys before values in objects. There are <code>array
|
||||||
* values to the
|
* </code> and <code>endArray</code> methods that make and bound array values, and <code>object
|
||||||
* text, and a <code>key</code>
|
* </code> and <code>endObject</code> methods which make and bound object values. All of these
|
||||||
* method for adding keys before values in objects. There are <code>array</code>
|
* methods return the JSONWriter instance, permitting cascade style. For example,
|
||||||
* and <code>endArray</code> methods that make and bound array values, and
|
*
|
||||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
* <pre>
|
||||||
* object values. All of these methods return the JSONWriter instance,
|
|
||||||
* permitting cascade style. For example, <pre>
|
|
||||||
* myString = new JSONStringer()
|
* myString = new JSONStringer()
|
||||||
* .object()
|
* .object()
|
||||||
* .key("JSON")
|
* .key("JSON")
|
||||||
* .value("Hello, World!")
|
* .value("Hello, World!")
|
||||||
* .endObject()
|
* .endObject()
|
||||||
* .toString();</pre> which produces the string <pre>
|
* .toString();</pre>
|
||||||
|
*
|
||||||
|
* which produces the string
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
* {"JSON":"Hello, World!"}</pre>
|
* {"JSON":"Hello, World!"}</pre>
|
||||||
* <p>
|
*
|
||||||
* The first method called must be <code>array</code> or <code>object</code>.
|
* <p>The first method called must be <code>array</code> or <code>object</code>. There are no
|
||||||
* There are no methods for adding commas or colons. JSONStringer adds them for
|
* methods for adding commas or colons. JSONStringer adds them for you. Objects and arrays can be
|
||||||
* you. Objects and arrays can be nested up to 20 levels deep.
|
* nested up to 20 levels deep.
|
||||||
* <p>
|
*
|
||||||
* This can sometimes be easier than using a JSONObject to build a string.
|
* <p>This can sometimes be easier than using a JSONObject to build a string.
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2008-09-18
|
* @version 2008-09-18
|
||||||
*/
|
*/
|
||||||
public class JSONStringer extends JSONWriter {
|
public class JSONStringer extends JSONWriter {
|
||||||
/**
|
/** Make a fresh JSONStringer. It can be used to build one JSON text. */
|
||||||
* Make a fresh JSONStringer. It can be used to build one JSON text.
|
|
||||||
*/
|
|
||||||
public JSONStringer() {
|
public JSONStringer() {
|
||||||
super(new StringWriter());
|
super(new StringWriter());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the JSON text. This method is used to obtain the product of the
|
* Return the JSON text. This method is used to obtain the product of the JSONStringer instance.
|
||||||
* JSONStringer instance. It will return <code>null</code> if there was a
|
* It will return <code>null</code> if there was a problem in the construction of the JSON text
|
||||||
* problem in the construction of the JSON text (such as the calls to
|
* (such as the calls to <code>array</code> were not properly balanced with calls to <code>
|
||||||
* <code>array</code> were not properly balanced with calls to
|
* endArray</code>).
|
||||||
* <code>endArray</code>).
|
|
||||||
*
|
*
|
||||||
* @return The JSON text.
|
* @return The JSON text.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ package dev.brighten.antivpn.utils.json;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A JSONTokener takes a source string and extracts characters and tokens from
|
* A JSONTokener takes a source string and extracts characters and tokens from it. It is used by the
|
||||||
* it. It is used by the JSONObject and JSONArray constructors to parse
|
* JSONObject and JSONArray constructors to parse JSON source strings.
|
||||||
* JSON source strings.
|
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2010-12-24
|
* @version 2010-12-24
|
||||||
@@ -36,15 +35,13 @@ public class JSONTokener {
|
|||||||
private Reader reader;
|
private Reader reader;
|
||||||
private boolean usePrevious;
|
private boolean usePrevious;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a JSONTokener from a Reader.
|
* Construct a JSONTokener from a Reader.
|
||||||
*
|
*
|
||||||
* @param reader A reader.
|
* @param reader A reader.
|
||||||
*/
|
*/
|
||||||
public JSONTokener(Reader reader) {
|
public JSONTokener(Reader reader) {
|
||||||
this.reader = reader.markSupported() ?
|
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
|
||||||
reader : new BufferedReader(reader);
|
|
||||||
this.eof = false;
|
this.eof = false;
|
||||||
this.usePrevious = false;
|
this.usePrevious = false;
|
||||||
this.previous = 0;
|
this.previous = 0;
|
||||||
@@ -53,15 +50,11 @@ public class JSONTokener {
|
|||||||
this.line = 1;
|
this.line = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Construct a JSONTokener from an InputStream. */
|
||||||
/**
|
|
||||||
* Construct a JSONTokener from an InputStream.
|
|
||||||
*/
|
|
||||||
public JSONTokener(InputStream inputStream) throws JSONException {
|
public JSONTokener(InputStream inputStream) throws JSONException {
|
||||||
this(new InputStreamReader(inputStream));
|
this(new InputStreamReader(inputStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a JSONTokener from a string.
|
* Construct a JSONTokener from a string.
|
||||||
*
|
*
|
||||||
@@ -74,8 +67,7 @@ public class JSONTokener {
|
|||||||
/**
|
/**
|
||||||
* Get the hex value of a character (base16).
|
* Get the hex value of a character (base16).
|
||||||
*
|
*
|
||||||
* @param c A character between '0' and '9' or between 'A' and 'F' or
|
* @param c A character between '0' and '9' or between 'A' and 'F' or between 'a' and 'f'.
|
||||||
* between 'a' and 'f'.
|
|
||||||
* @return An int between 0 and 15, or -1 if c was not a hex digit.
|
* @return An int between 0 and 15, or -1 if c was not a hex digit.
|
||||||
*/
|
*/
|
||||||
public static int dehexchar(char c) {
|
public static int dehexchar(char c) {
|
||||||
@@ -92,9 +84,8 @@ public class JSONTokener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Back up one character. This provides a sort of lookahead capability,
|
* Back up one character. This provides a sort of lookahead capability, so that you can test for a
|
||||||
* so that you can test for a digit or letter before attempting to parse
|
* digit or letter before attempting to parse the next number or identifier.
|
||||||
* the next number or identifier.
|
|
||||||
*/
|
*/
|
||||||
public void back() throws JSONException {
|
public void back() throws JSONException {
|
||||||
if (usePrevious || index <= 0) {
|
if (usePrevious || index <= 0) {
|
||||||
@@ -110,10 +101,8 @@ public class JSONTokener {
|
|||||||
return eof && !usePrevious;
|
return eof && !usePrevious;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the source string still contains characters that next()
|
* Determine if the source string still contains characters that next() can consume.
|
||||||
* can consume.
|
|
||||||
*
|
*
|
||||||
* @return true if not yet at the end of the source.
|
* @return true if not yet at the end of the source.
|
||||||
*/
|
*/
|
||||||
@@ -126,7 +115,6 @@ public class JSONTokener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next character in the source string.
|
* Get the next character in the source string.
|
||||||
*
|
*
|
||||||
@@ -163,10 +151,8 @@ public class JSONTokener {
|
|||||||
return this.previous;
|
return this.previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consume the next character, and check that it matches a specified
|
* Consume the next character, and check that it matches a specified character.
|
||||||
* character.
|
|
||||||
*
|
*
|
||||||
* @param c The character to match.
|
* @param c The character to match.
|
||||||
* @return The character.
|
* @return The character.
|
||||||
@@ -175,20 +161,18 @@ public class JSONTokener {
|
|||||||
public char next(char c) throws JSONException {
|
public char next(char c) throws JSONException {
|
||||||
char n = next();
|
char n = next();
|
||||||
if (n != c) {
|
if (n != c) {
|
||||||
throw syntaxError("Expected '" + c + "' and instead saw '" +
|
throw syntaxError("Expected '" + c + "' and instead saw '" + n + "'");
|
||||||
n + "'");
|
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next n characters.
|
* Get the next n characters.
|
||||||
*
|
*
|
||||||
* @param n The number of characters to take.
|
* @param n The number of characters to take.
|
||||||
* @return A string of n characters.
|
* @return A string of n characters.
|
||||||
* @throws JSONException Substring bounds error if there are not
|
* @throws JSONException Substring bounds error if there are not n characters remaining in the
|
||||||
* n characters remaining in the source string.
|
* source string.
|
||||||
*/
|
*/
|
||||||
public String next(int n) throws JSONException {
|
public String next(int n) throws JSONException {
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
@@ -208,7 +192,6 @@ public class JSONTokener {
|
|||||||
return new String(chars);
|
return new String(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next char in the string, skipping whitespace.
|
* Get the next char in the string, skipping whitespace.
|
||||||
*
|
*
|
||||||
@@ -224,15 +207,12 @@ public class JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the characters up to the next close quote character.
|
* Return the characters up to the next close quote character. Backslash processing is done. The
|
||||||
* Backslash processing is done. The formal JSON format does not
|
* formal JSON format does not allow strings in single quotes, but an implementation is allowed to
|
||||||
* allow strings in single quotes, but an implementation is allowed to
|
|
||||||
* accept them.
|
* accept them.
|
||||||
*
|
*
|
||||||
* @param quote The quoting character, either
|
* @param quote The quoting character, either <code>"</code> <small>(double quote)</small> or
|
||||||
* <code>"</code> <small>(double quote)</small> or
|
|
||||||
* <code>'</code> <small>(single quote)</small>.
|
* <code>'</code> <small>(single quote)</small>.
|
||||||
* @return A String.
|
* @return A String.
|
||||||
* @throws JSONException Unterminated string.
|
* @throws JSONException Unterminated string.
|
||||||
@@ -287,10 +267,9 @@ public class JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text up but not including the specified character or the
|
* Get the text up but not including the specified character or the end of line, whichever comes
|
||||||
* end of line, whichever comes first.
|
* first.
|
||||||
*
|
*
|
||||||
* @param delimiter A delimiter character.
|
* @param delimiter A delimiter character.
|
||||||
* @return A string.
|
* @return A string.
|
||||||
@@ -309,10 +288,9 @@ public class JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text up but not including one of the specified delimiter
|
* Get the text up but not including one of the specified delimiter characters or the end of line,
|
||||||
* characters or the end of line, whichever comes first.
|
* whichever comes first.
|
||||||
*
|
*
|
||||||
* @param delimiters A set of delimiter characters.
|
* @param delimiters A set of delimiter characters.
|
||||||
* @return A string, trimmed.
|
* @return A string, trimmed.
|
||||||
@@ -322,8 +300,7 @@ public class JSONTokener {
|
|||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
for (; ; ) {
|
for (; ; ) {
|
||||||
c = next();
|
c = next();
|
||||||
if (delimiters.indexOf(c) >= 0 || c == 0 ||
|
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
|
||||||
c == '\n' || c == '\r') {
|
|
||||||
if (c != 0) {
|
if (c != 0) {
|
||||||
back();
|
back();
|
||||||
}
|
}
|
||||||
@@ -333,10 +310,9 @@ public class JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next value. The value can be a Boolean, Double, Integer,
|
* Get the next value. The value can be a Boolean, Double, Integer, JSONArray, JSONObject, Long,
|
||||||
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
|
* or String, or the JSONObject.NULL object.
|
||||||
*
|
*
|
||||||
* @return An object.
|
* @return An object.
|
||||||
* @throws JSONException If syntax error.
|
* @throws JSONException If syntax error.
|
||||||
@@ -374,20 +350,18 @@ public class JSONTokener {
|
|||||||
back();
|
back();
|
||||||
|
|
||||||
string = sb.toString().trim();
|
string = sb.toString().trim();
|
||||||
if (string.equals("")) {
|
if (string.isEmpty()) {
|
||||||
throw syntaxError("Missing value");
|
throw syntaxError("Missing value");
|
||||||
}
|
}
|
||||||
return JSONObject.stringToValue(string);
|
return JSONObject.stringToValue(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skip characters until the next character is the requested character.
|
* Skip characters until the next character is the requested character. If the requested character
|
||||||
* If the requested character is not found, no characters are skipped.
|
* is not found, no characters are skipped.
|
||||||
*
|
*
|
||||||
* @param to A character to skip to.
|
* @param to A character to skip to.
|
||||||
* @return The requested character, or zero if the requested character
|
* @return The requested character, or zero if the requested character is not found.
|
||||||
* is not found.
|
|
||||||
*/
|
*/
|
||||||
public char skipTo(char to) throws JSONException {
|
public char skipTo(char to) throws JSONException {
|
||||||
char c;
|
char c;
|
||||||
@@ -414,7 +388,6 @@ public class JSONTokener {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a JSONException to signal a syntax error.
|
* Make a JSONException to signal a syntax error.
|
||||||
*
|
*
|
||||||
@@ -425,14 +398,12 @@ public class JSONTokener {
|
|||||||
return new JSONException(message + toString());
|
return new JSONException(message + toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a printable string of this JSONTokener.
|
* Make a printable string of this JSONTokener.
|
||||||
*
|
*
|
||||||
* @return " at {index} [character {character} line {line}]"
|
* @return " at {index} [character {character} line {line}]"
|
||||||
*/
|
*/
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return " at " + index + " [character " + this.character + " line " +
|
return " at " + index + " [character " + this.character + " line " + this.line + "]";
|
||||||
this.line + "]";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,67 +20,56 @@ import java.io.IOException;
|
|||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSONWriter provides a quick and convenient way of producing JSON text.
|
* JSONWriter provides a quick and convenient way of producing JSON text. The texts produced
|
||||||
* The texts produced strictly conform to JSON syntax rules. No whitespace is
|
* strictly conform to JSON syntax rules. No whitespace is added, so the results are ready for
|
||||||
* added, so the results are ready for transmission or storage. Each instance of
|
* transmission or storage. Each instance of JSONWriter can produce one JSON text.
|
||||||
* JSONWriter can produce one JSON text.
|
*
|
||||||
* <p>
|
* <p>A JSONWriter instance provides a <code>value</code> method for appending values to the text,
|
||||||
* A JSONWriter instance provides a <code>value</code> method for appending
|
* and a <code>key</code> method for adding keys before values in objects. There are <code>array
|
||||||
* values to the
|
* </code> and <code>endArray</code> methods that make and bound array values, and <code>object
|
||||||
* text, and a <code>key</code>
|
* </code> and <code>endObject</code> methods which make and bound object values. All of these
|
||||||
* method for adding keys before values in objects. There are <code>array</code>
|
* methods return the JSONWriter instance, permitting a cascade style. For example,
|
||||||
* and <code>endArray</code> methods that make and bound array values, and
|
*
|
||||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
* <pre>
|
||||||
* object values. All of these methods return the JSONWriter instance,
|
|
||||||
* permitting a cascade style. For example, <pre>
|
|
||||||
* new JSONWriter(myWriter)
|
* new JSONWriter(myWriter)
|
||||||
* .object()
|
* .object()
|
||||||
* .key("JSON")
|
* .key("JSON")
|
||||||
* .value("Hello, World!")
|
* .value("Hello, World!")
|
||||||
* .endObject();</pre> which writes <pre>
|
* .endObject();</pre>
|
||||||
|
*
|
||||||
|
* which writes
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
* {"JSON":"Hello, World!"}</pre>
|
* {"JSON":"Hello, World!"}</pre>
|
||||||
* <p>
|
*
|
||||||
* The first method called must be <code>array</code> or <code>object</code>.
|
* <p>The first method called must be <code>array</code> or <code>object</code>. There are no
|
||||||
* There are no methods for adding commas or colons. JSONWriter adds them for
|
* methods for adding commas or colons. JSONWriter adds them for you. Objects and arrays can be
|
||||||
* you. Objects and arrays can be nested up to 20 levels deep.
|
* nested up to 20 levels deep.
|
||||||
* <p>
|
*
|
||||||
* This can sometimes be easier than using a JSONObject to build a string.
|
* <p>This can sometimes be easier than using a JSONObject to build a string.
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2010-12-24
|
* @version 2010-12-24
|
||||||
*/
|
*/
|
||||||
public class JSONWriter {
|
public class JSONWriter {
|
||||||
private static final int maxdepth = 20;
|
private static final int maxdepth = 20;
|
||||||
/**
|
|
||||||
* The current mode. Values:
|
/** The current mode. Values: 'a' (array), 'd' (done), 'i' (initial), 'k' (key), 'o' (object). */
|
||||||
* 'a' (array),
|
|
||||||
* 'd' (done),
|
|
||||||
* 'i' (initial),
|
|
||||||
* 'k' (key),
|
|
||||||
* 'o' (object).
|
|
||||||
*/
|
|
||||||
protected char mode;
|
protected char mode;
|
||||||
/**
|
|
||||||
* The writer that will receive the output.
|
/** The writer that will receive the output. */
|
||||||
*/
|
|
||||||
protected Writer writer;
|
protected Writer writer;
|
||||||
/**
|
|
||||||
* The comma flag determines if a comma should be output before the next
|
/** The comma flag determines if a comma should be output before the next value. */
|
||||||
* value.
|
|
||||||
*/
|
|
||||||
private boolean comma;
|
private boolean comma;
|
||||||
/**
|
|
||||||
* The object/array stack.
|
/** The object/array stack. */
|
||||||
*/
|
|
||||||
private JSONObject stack[];
|
private JSONObject stack[];
|
||||||
/**
|
|
||||||
* The stack top index. A value of 0 indicates that the stack is empty.
|
/** The stack top index. A value of 0 indicates that the stack is empty. */
|
||||||
*/
|
|
||||||
private int top;
|
private int top;
|
||||||
|
|
||||||
/**
|
/** Make a fresh JSONWriter. It can be used to build one JSON text. */
|
||||||
* Make a fresh JSONWriter. It can be used to build one JSON text.
|
|
||||||
*/
|
|
||||||
public JSONWriter(Writer w) {
|
public JSONWriter(Writer w) {
|
||||||
this.comma = false;
|
this.comma = false;
|
||||||
this.mode = 'i';
|
this.mode = 'i';
|
||||||
@@ -119,14 +108,13 @@ public class JSONWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Begin appending a new array. All values until the balancing
|
* Begin appending a new array. All values until the balancing <code>endArray</code> will be
|
||||||
* <code>endArray</code> will be appended to this array. The
|
* appended to this array. The <code>endArray</code> method must be called to mark the array's
|
||||||
* <code>endArray</code> method must be called to mark the array's end.
|
* end.
|
||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
* @throws JSONException If the nesting is too deep, or if the object is
|
* @throws JSONException If the nesting is too deep, or if the object is started in the wrong
|
||||||
* started in the wrong place (for example as a key or after the end of the
|
* place (for example as a key or after the end of the outermost array or object).
|
||||||
* outermost array or object).
|
|
||||||
*/
|
*/
|
||||||
public JSONWriter array() throws JSONException {
|
public JSONWriter array() throws JSONException {
|
||||||
if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
|
if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
|
||||||
@@ -148,8 +136,7 @@ public class JSONWriter {
|
|||||||
*/
|
*/
|
||||||
private JSONWriter end(char mode, char c) throws JSONException {
|
private JSONWriter end(char mode, char c) throws JSONException {
|
||||||
if (this.mode != mode) {
|
if (this.mode != mode) {
|
||||||
throw new JSONException(mode == 'a' ? "Misplaced endArray." :
|
throw new JSONException(mode == 'a' ? "Misplaced endArray." : "Misplaced endObject.");
|
||||||
"Misplaced endObject.");
|
|
||||||
}
|
}
|
||||||
this.pop(mode);
|
this.pop(mode);
|
||||||
try {
|
try {
|
||||||
@@ -162,8 +149,7 @@ public class JSONWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End an array. This method most be called to balance calls to
|
* End an array. This method most be called to balance calls to <code>array</code>.
|
||||||
* <code>array</code>.
|
|
||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
* @throws JSONException If incorrectly nested.
|
* @throws JSONException If incorrectly nested.
|
||||||
@@ -173,8 +159,7 @@ public class JSONWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End an object. This method most be called to balance calls to
|
* End an object. This method most be called to balance calls to <code>object</code>.
|
||||||
* <code>object</code>.
|
|
||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
* @throws JSONException If incorrectly nested.
|
* @throws JSONException If incorrectly nested.
|
||||||
@@ -184,13 +169,13 @@ public class JSONWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append a key. The key will be associated with the next value. In an
|
* Append a key. The key will be associated with the next value. In an object, every value must be
|
||||||
* object, every value must be preceded by a key.
|
* preceded by a key.
|
||||||
*
|
*
|
||||||
* @param string A key string.
|
* @param string A key string.
|
||||||
* @return this
|
* @return this
|
||||||
* @throws JSONException If the key is out of place. For example, keys
|
* @throws JSONException If the key is out of place. For example, keys do not belong in arrays or
|
||||||
* do not belong in arrays or if the key is null.
|
* if the key is null.
|
||||||
*/
|
*/
|
||||||
public JSONWriter key(String string) throws JSONException {
|
public JSONWriter key(String string) throws JSONException {
|
||||||
if (string == null) {
|
if (string == null) {
|
||||||
@@ -214,16 +199,14 @@ public class JSONWriter {
|
|||||||
throw new JSONException("Misplaced key.");
|
throw new JSONException("Misplaced key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Begin appending a new object. All keys and values until the balancing
|
* Begin appending a new object. All keys and values until the balancing <code>endObject</code>
|
||||||
* <code>endObject</code> will be appended to this object. The
|
* will be appended to this object. The <code>endObject</code> method must be called to mark the
|
||||||
* <code>endObject</code> method must be called to mark the object's end.
|
* object's end.
|
||||||
*
|
*
|
||||||
* @return this
|
* @return this
|
||||||
* @throws JSONException If the nesting is too deep, or if the object is
|
* @throws JSONException If the nesting is too deep, or if the object is started in the wrong
|
||||||
* started in the wrong place (for example as a key or after the end of the
|
* place (for example as a key or after the end of the outermost array or object).
|
||||||
* outermost array or object).
|
|
||||||
*/
|
*/
|
||||||
public JSONWriter object() throws JSONException {
|
public JSONWriter object() throws JSONException {
|
||||||
if (this.mode == 'i') {
|
if (this.mode == 'i') {
|
||||||
@@ -236,10 +219,8 @@ public class JSONWriter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
throw new JSONException("Misplaced object.");
|
throw new JSONException("Misplaced object.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop an array or object scope.
|
* Pop an array or object scope.
|
||||||
*
|
*
|
||||||
@@ -255,8 +236,7 @@ public class JSONWriter {
|
|||||||
throw new JSONException("Nesting error.");
|
throw new JSONException("Nesting error.");
|
||||||
}
|
}
|
||||||
this.top -= 1;
|
this.top -= 1;
|
||||||
this.mode = this.top == 0 ?
|
this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1] == null ? 'a' : 'k';
|
||||||
'd' : this.stack[this.top - 1] == null ? 'a' : 'k';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -274,10 +254,8 @@ public class JSONWriter {
|
|||||||
this.top += 1;
|
this.top += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append either the value <code>true</code> or the value
|
* Append either the value <code>true</code> or the value <code>false</code>.
|
||||||
* <code>false</code>.
|
|
||||||
*
|
*
|
||||||
* @param b A boolean.
|
* @param b A boolean.
|
||||||
* @return this
|
* @return this
|
||||||
@@ -309,12 +287,11 @@ public class JSONWriter {
|
|||||||
return this.append(Long.toString(l));
|
return this.append(Long.toString(l));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append an object value.
|
* Append an object value.
|
||||||
*
|
*
|
||||||
* @param object The object to append. It can be null, or a Boolean, Number,
|
* @param object The object to append. It can be null, or a Boolean, Number, String, JSONObject,
|
||||||
* String, JSONObject, or JSONArray, or an object that implements JSONString.
|
* or JSONArray, or an object that implements JSONString.
|
||||||
* @return this
|
* @return this
|
||||||
* @throws JSONException If the value is out of sequence.
|
* @throws JSONException If the value is out of sequence.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.brighten.antivpn.utils.json;
|
package dev.brighten.antivpn.utils.json;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|||||||
@@ -18,63 +18,45 @@ package dev.brighten.antivpn.utils.json;
|
|||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides static methods to convert an XML text into a JSONObject,
|
* This provides static methods to convert an XML text into a JSONObject, and to covert a JSONObject
|
||||||
* and to covert a JSONObject into an XML text.
|
* into an XML text.
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2011-02-11
|
* @version 2011-02-11
|
||||||
*/
|
*/
|
||||||
public class XML {
|
public class XML {
|
||||||
|
|
||||||
/**
|
/** The Character '&'. */
|
||||||
* The Character '&'.
|
|
||||||
*/
|
|
||||||
public static final Character AMP = new Character('&');
|
public static final Character AMP = new Character('&');
|
||||||
|
|
||||||
/**
|
/** The Character '''. */
|
||||||
* The Character '''.
|
|
||||||
*/
|
|
||||||
public static final Character APOS = new Character('\'');
|
public static final Character APOS = new Character('\'');
|
||||||
|
|
||||||
/**
|
/** The Character '!'. */
|
||||||
* The Character '!'.
|
|
||||||
*/
|
|
||||||
public static final Character BANG = new Character('!');
|
public static final Character BANG = new Character('!');
|
||||||
|
|
||||||
/**
|
/** The Character '='. */
|
||||||
* The Character '='.
|
|
||||||
*/
|
|
||||||
public static final Character EQ = new Character('=');
|
public static final Character EQ = new Character('=');
|
||||||
|
|
||||||
/**
|
/** The Character '>'. */
|
||||||
* The Character '>'.
|
|
||||||
*/
|
|
||||||
public static final Character GT = new Character('>');
|
public static final Character GT = new Character('>');
|
||||||
|
|
||||||
/**
|
/** The Character '<'. */
|
||||||
* The Character '<'.
|
|
||||||
*/
|
|
||||||
public static final Character LT = new Character('<');
|
public static final Character LT = new Character('<');
|
||||||
|
|
||||||
/**
|
/** The Character '?'. */
|
||||||
* The Character '?'.
|
|
||||||
*/
|
|
||||||
public static final Character QUEST = new Character('?');
|
public static final Character QUEST = new Character('?');
|
||||||
|
|
||||||
/**
|
/** The Character '"'. */
|
||||||
* The Character '"'.
|
|
||||||
*/
|
|
||||||
public static final Character QUOT = new Character('"');
|
public static final Character QUOT = new Character('"');
|
||||||
|
|
||||||
/**
|
/** The Character '/'. */
|
||||||
* The Character '/'.
|
|
||||||
*/
|
|
||||||
public static final Character SLASH = new Character('/');
|
public static final Character SLASH = new Character('/');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace special characters with XML escapes:
|
* Replace special characters with XML escapes:
|
||||||
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* & <small>(ampersand)</small> is replaced by &amp;
|
* & <small>(ampersand)</small> is replaced by &amp;
|
||||||
* < <small>(less than)</small> is replaced by &lt;
|
* < <small>(less than)</small> is replaced by &lt;
|
||||||
@@ -113,8 +95,8 @@ public class XML {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throw an exception if the string contains whitespace.
|
* Throw an exception if the string contains whitespace. Whitespace is not allowed in tagNames and
|
||||||
* Whitespace is not allowed in tagNames and attributes.
|
* attributes.
|
||||||
*
|
*
|
||||||
* @param string
|
* @param string
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
@@ -126,8 +108,7 @@ public class XML {
|
|||||||
}
|
}
|
||||||
for (i = 0; i < length; i += 1) {
|
for (i = 0; i < length; i += 1) {
|
||||||
if (Character.isWhitespace(string.charAt(i))) {
|
if (Character.isWhitespace(string.charAt(i))) {
|
||||||
throw new JSONException("'" + string +
|
throw new JSONException("'" + string + "' contains a space character.");
|
||||||
"' contains a space character.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,8 +122,7 @@ public class XML {
|
|||||||
* @return true if the close tag is processed.
|
* @return true if the close tag is processed.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
private static boolean parse(XMLTokener x, JSONObject context,
|
private static boolean parse(XMLTokener x, JSONObject context, String name) throws JSONException {
|
||||||
String name) throws JSONException {
|
|
||||||
char c;
|
char c;
|
||||||
int i;
|
int i;
|
||||||
JSONObject jsonobject = null;
|
JSONObject jsonobject = null;
|
||||||
@@ -243,8 +223,7 @@ public class XML {
|
|||||||
if (!(token instanceof String)) {
|
if (!(token instanceof String)) {
|
||||||
throw x.syntaxError("Missing value");
|
throw x.syntaxError("Missing value");
|
||||||
}
|
}
|
||||||
jsonobject.accumulate(string,
|
jsonobject.accumulate(string, XML.stringToValue((String) token));
|
||||||
XML.stringToValue((String) token));
|
|
||||||
token = null;
|
token = null;
|
||||||
} else {
|
} else {
|
||||||
jsonobject.accumulate(string, "");
|
jsonobject.accumulate(string, "");
|
||||||
@@ -276,8 +255,7 @@ public class XML {
|
|||||||
} else if (token instanceof String) {
|
} else if (token instanceof String) {
|
||||||
string = (String) token;
|
string = (String) token;
|
||||||
if (string.length() > 0) {
|
if (string.length() > 0) {
|
||||||
jsonobject.accumulate("content",
|
jsonobject.accumulate("content", XML.stringToValue(string));
|
||||||
XML.stringToValue(string));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nested element
|
// Nested element
|
||||||
@@ -286,10 +264,8 @@ public class XML {
|
|||||||
if (parse(x, jsonobject, tagName)) {
|
if (parse(x, jsonobject, tagName)) {
|
||||||
if (jsonobject.length() == 0) {
|
if (jsonobject.length() == 0) {
|
||||||
context.accumulate(tagName, "");
|
context.accumulate(tagName, "");
|
||||||
} else if (jsonobject.length() == 1 &&
|
} else if (jsonobject.length() == 1 && jsonobject.opt("content") != null) {
|
||||||
jsonobject.opt("content") != null) {
|
context.accumulate(tagName, jsonobject.opt("content"));
|
||||||
context.accumulate(tagName,
|
|
||||||
jsonobject.opt("content"));
|
|
||||||
} else {
|
} else {
|
||||||
context.accumulate(tagName, jsonobject);
|
context.accumulate(tagName, jsonobject);
|
||||||
}
|
}
|
||||||
@@ -304,19 +280,17 @@ public class XML {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to convert a string into a number, boolean, or null. If the string
|
* Try to convert a string into a number, boolean, or null. If the string can't be converted,
|
||||||
* can't be converted, return the string. This is much less ambitious than
|
* return the string. This is much less ambitious than JSONObject.stringToValue, especially
|
||||||
* JSONObject.stringToValue, especially because it does not attempt to
|
* because it does not attempt to convert plus forms, octal forms, hex forms, or E forms lacking
|
||||||
* convert plus forms, octal forms, hex forms, or E forms lacking decimal
|
* decimal points.
|
||||||
* points.
|
|
||||||
*
|
*
|
||||||
* @param string A String.
|
* @param string A String.
|
||||||
* @return A simple JSON value.
|
* @return A simple JSON value.
|
||||||
*/
|
*/
|
||||||
public static Object stringToValue(String string) {
|
public static Object stringToValue(String string) {
|
||||||
if (string.equals("")) {
|
if (string.isEmpty()) {
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
if (string.equalsIgnoreCase("true")) {
|
if (string.equalsIgnoreCase("true")) {
|
||||||
@@ -362,16 +336,13 @@ public class XML {
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a well-formed (but not necessarily valid) XML string into a
|
* Convert a well-formed (but not necessarily valid) XML string into a JSONObject. Some
|
||||||
* JSONObject. Some information may be lost in this transformation
|
* information may be lost in this transformation because JSON is a data format and XML is a
|
||||||
* because JSON is a data format and XML is a document format. XML uses
|
* document format. XML uses elements, attributes, and content text, while JSON uses unordered
|
||||||
* elements, attributes, and content text, while JSON uses unordered
|
* collections of name/value pairs and arrays of values. JSON does not does not like to
|
||||||
* collections of name/value pairs and arrays of values. JSON does not
|
* distinguish between elements and attributes. Sequences of similar elements are represented as
|
||||||
* does not like to distinguish between elements and attributes.
|
* JSONArrays. Content text may be placed in a "content" member. Comments, prologs, DTDs, and
|
||||||
* Sequences of similar elements are represented as JSONArrays. Content
|
|
||||||
* text may be placed in a "content" member. Comments, prologs, DTDs, and
|
|
||||||
* <code><[ [ ]]></code> are ignored.
|
* <code><[ [ ]]></code> are ignored.
|
||||||
*
|
*
|
||||||
* @param string The source string.
|
* @param string The source string.
|
||||||
@@ -387,7 +358,6 @@ public class XML {
|
|||||||
return jo;
|
return jo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a JSONObject into a well-formed, element-normal XML string.
|
* Convert a JSONObject into a well-formed, element-normal XML string.
|
||||||
*
|
*
|
||||||
@@ -399,7 +369,6 @@ public class XML {
|
|||||||
return toString(object, null);
|
return toString(object, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a JSONObject into a well-formed, element-normal XML string.
|
* Convert a JSONObject into a well-formed, element-normal XML string.
|
||||||
*
|
*
|
||||||
@@ -408,8 +377,7 @@ public class XML {
|
|||||||
* @return A string.
|
* @return A string.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public static String toString(Object object, String tagName)
|
public static String toString(Object object, String tagName) throws JSONException {
|
||||||
throws JSONException {
|
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
int i;
|
int i;
|
||||||
JSONArray ja;
|
JSONArray ja;
|
||||||
@@ -517,9 +485,11 @@ public class XML {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
} else {
|
} else {
|
||||||
string = (object == null) ? "null" : escape(object.toString());
|
string = (object == null) ? "null" : escape(object.toString());
|
||||||
return (tagName == null) ? "\"" + string + "\"" :
|
return (tagName == null)
|
||||||
(string.length() == 0) ? "<" + tagName + "/>" :
|
? "\"" + string + "\""
|
||||||
"<" + tagName + ">" + string + "</" + tagName + ">";
|
: (string.isEmpty())
|
||||||
|
? "<" + tagName + "/>"
|
||||||
|
: "<" + tagName + ">" + string + "</" + tagName + ">";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,18 +17,16 @@
|
|||||||
package dev.brighten.antivpn.utils.json;
|
package dev.brighten.antivpn.utils.json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The XMLTokener extends the JSONTokener to provide additional methods
|
* The XMLTokener extends the JSONTokener to provide additional methods for the parsing of XML
|
||||||
* for the parsing of XML texts.
|
* texts.
|
||||||
*
|
*
|
||||||
* @author JSON.org
|
* @author JSON.org
|
||||||
* @version 2010-12-24
|
* @version 2010-12-24
|
||||||
*/
|
*/
|
||||||
public class XMLTokener extends JSONTokener {
|
public class XMLTokener extends JSONTokener {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The table of entity values. It initially contains Character values for
|
* The table of entity values. It initially contains Character values for amp, apos, gt, lt, quot.
|
||||||
* amp, apos, gt, lt, quot.
|
|
||||||
*/
|
*/
|
||||||
public static final java.util.HashMap entity;
|
public static final java.util.HashMap entity;
|
||||||
|
|
||||||
@@ -67,22 +65,18 @@ public class XMLTokener extends JSONTokener {
|
|||||||
}
|
}
|
||||||
sb.append(c);
|
sb.append(c);
|
||||||
i = sb.length() - 3;
|
i = sb.length() - 3;
|
||||||
if (i >= 0 && sb.charAt(i) == ']' &&
|
if (i >= 0 && sb.charAt(i) == ']' && sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') {
|
||||||
sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') {
|
|
||||||
sb.setLength(i);
|
sb.setLength(i);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next XML outer token, trimming whitespace. There are two kinds
|
* Get the next XML outer token, trimming whitespace. There are two kinds of tokens: the '<'
|
||||||
* of tokens: the '<' character which begins a markup tag, and the content
|
* character which begins a markup tag, and the content text between markup tags.
|
||||||
* text between markup tags.
|
|
||||||
*
|
*
|
||||||
* @return A string, or a '<' Character, or null if there is no more
|
* @return A string, or a '<' Character, or null if there is no more source text.
|
||||||
* source text.
|
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public Object nextContent() throws JSONException {
|
public Object nextContent() throws JSONException {
|
||||||
@@ -112,10 +106,9 @@ public class XMLTokener extends JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the next entity. These entities are translated to Characters:
|
* Return the next entity. These entities are translated to Characters: <code>
|
||||||
* <code>& ' > < "</code>.
|
* & ' > < "</code>.
|
||||||
*
|
*
|
||||||
* @param ampersand An ampersand character.
|
* @param ampersand An ampersand character.
|
||||||
* @return A Character or an entity String if the entity is not recognized.
|
* @return A Character or an entity String if the entity is not recognized.
|
||||||
@@ -138,16 +131,12 @@ public class XMLTokener extends JSONTokener {
|
|||||||
return object != null ? object : ampersand + string + ";";
|
return object != null ? object : ampersand + string + ";";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next XML meta token. This is used for skipping over <!...>
|
* Returns the next XML meta token. This is used for skipping over <!...> and <?...?> structures.
|
||||||
* and <?...?> structures.
|
|
||||||
*
|
*
|
||||||
* @return Syntax characters (<code>< > / = ! ?</code>) are returned as
|
* @return Syntax characters (<code>< > / = ! ?</code>) are returned as Character, and strings and
|
||||||
* Character, and strings and names are returned as Boolean. We don't care
|
* names are returned as Boolean. We don't care what the values actually are.
|
||||||
* what the values actually are.
|
* @throws JSONException If a string is not properly closed or if the XML is badly structured.
|
||||||
* @throws JSONException If a string is not properly closed or if the XML
|
|
||||||
* is badly structured.
|
|
||||||
*/
|
*/
|
||||||
public Object nextMeta() throws JSONException {
|
public Object nextMeta() throws JSONException {
|
||||||
char c;
|
char c;
|
||||||
@@ -205,12 +194,10 @@ public class XMLTokener extends JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next XML Token. These tokens are found inside of angle
|
* Get the next XML Token. These tokens are found inside of angle brackets. It may be one of these
|
||||||
* brackets. It may be one of these characters: <code>/ > = ! ?</code> or it
|
* characters: <code>/ > = ! ?</code> or it may be a string wrapped in single quotes or double
|
||||||
* may be a string wrapped in single quotes or double quotes, or it may be a
|
* quotes, or it may be a name.
|
||||||
* name.
|
|
||||||
*
|
*
|
||||||
* @return a String or a Character.
|
* @return a String or a Character.
|
||||||
* @throws JSONException If the XML is not well formed.
|
* @throws JSONException If the XML is not well formed.
|
||||||
@@ -290,10 +277,9 @@ public class XMLTokener extends JSONTokener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skip characters until past the requested string.
|
* Skip characters until past the requested string. If it is not found, we are left at the end of
|
||||||
* If it is not found, we are left at the end of the source with a result of false.
|
* the source with a result of false.
|
||||||
*
|
*
|
||||||
* @param to A string to skip past.
|
* @param to A string to skip past.
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
|
|||||||
@@ -21,13 +21,11 @@ import dev.brighten.antivpn.utils.json.JSONObject;
|
|||||||
import dev.brighten.antivpn.utils.json.JsonReader;
|
import dev.brighten.antivpn.utils.json.JsonReader;
|
||||||
import dev.brighten.antivpn.web.objects.QueryResponse;
|
import dev.brighten.antivpn.web.objects.QueryResponse;
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class FunkemunkyAPI {
|
public class FunkemunkyAPI {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Queries <a href="https://funkemunky.cc/vpn">...</a> API and returns information on the IP
|
* Queries <a href="https://funkemunky.cc/vpn">...</a> API and returns information on the IP
|
||||||
*
|
*
|
||||||
* @param ip String
|
* @param ip String
|
||||||
@@ -35,40 +33,47 @@ public class FunkemunkyAPI {
|
|||||||
* @param cachedResults boolean
|
* @param cachedResults boolean
|
||||||
* @return VPNResponse
|
* @return VPNResponse
|
||||||
* @throws JSONException Throws when JSON response is not formatted properly.
|
* @throws JSONException Throws when JSON response is not formatted properly.
|
||||||
* @throws IOException Throws when there is an error connecting to and processing information from API.
|
* @throws IOException Throws when there is an error connecting to and processing information from
|
||||||
|
* API.
|
||||||
*/
|
*/
|
||||||
public static VPNResponse getVPNResponse(String ip, String license, boolean cachedResults /* faster if set to true*/)
|
public static VPNResponse getVPNResponse(
|
||||||
|
String ip, String license, boolean cachedResults /* faster if set to true*/)
|
||||||
throws JSONException, IOException {
|
throws JSONException, IOException {
|
||||||
JSONObject result = JsonReader.readJsonFromUrl(String
|
JSONObject result =
|
||||||
.format("https://funkemunky.cc/vpn?ip=%s&license=%s&cache=%s",
|
JsonReader.readJsonFromUrl(
|
||||||
|
String.format(
|
||||||
|
"https://funkemunky.cc/vpn?ip=%s&license=%s&cache=%s",
|
||||||
ip, license.isEmpty() ? "none" : license, cachedResults));
|
ip, license.isEmpty() ? "none" : license, cachedResults));
|
||||||
|
|
||||||
return VPNResponse.fromJson(result);
|
return VPNResponse.fromJson(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feeds into {@link FunkemunkyAPI#getQueryResponse(String)} using "none" as argument
|
* Feeds into {@link FunkemunkyAPI#getQueryResponse(String)} using "none" as argument to grab
|
||||||
* to grab query information based on the connecting IP address.
|
* query information based on the connecting IP address.
|
||||||
*
|
*
|
||||||
* @return QueryResponse
|
* @return QueryResponse
|
||||||
* @throws JSONException Throws when JSON response is not formatted properly.
|
* @throws JSONException Throws when JSON response is not formatted properly.
|
||||||
* @throws IOException Throws when there is an error connecting to and processing information from API.
|
* @throws IOException Throws when there is an error connecting to and processing information from
|
||||||
|
* API.
|
||||||
*/
|
*/
|
||||||
public static QueryResponse getQueryResponse() throws JSONException, IOException {
|
public static QueryResponse getQueryResponse() throws JSONException, IOException {
|
||||||
return getQueryResponse("none");
|
return getQueryResponse("none");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries <a href="https://funkemunky.cc/vpn/queryCheck">...</a> and returns information based on the
|
* Queries <a href="https://funkemunky.cc/vpn/queryCheck">...</a> and returns information based on
|
||||||
* provided licence input.
|
* the provided licence input.
|
||||||
*
|
*
|
||||||
* @param license String
|
* @param license String
|
||||||
* @return QueryResponse
|
* @return QueryResponse
|
||||||
* @throws JSONException Throws when JSON response is not formatted properly.
|
* @throws JSONException Throws when JSON response is not formatted properly.
|
||||||
* @throws IOException Throws when there is an error connecting to and processing information from API.
|
* @throws IOException Throws when there is an error connecting to and processing information from
|
||||||
|
* API.
|
||||||
*/
|
*/
|
||||||
public static QueryResponse getQueryResponse(String license) throws JSONException, IOException {
|
public static QueryResponse getQueryResponse(String license) throws JSONException, IOException {
|
||||||
JSONObject result = JsonReader.readJsonFromUrl("https://funkemunky.cc/vpn/queryCheck?license=" + license);
|
JSONObject result =
|
||||||
|
JsonReader.readJsonFromUrl("https://funkemunky.cc/vpn/queryCheck?license=" + license);
|
||||||
|
|
||||||
return QueryResponse.fromJson(result);
|
return QueryResponse.fromJson(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ import dev.brighten.antivpn.utils.json.JSONObject;
|
|||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to format the JSON response from <a href="https://funkemunky.cc/vpn/queryCheck">...</a> into an object for project use.
|
* Used to format the JSON response from <a href="https://funkemunky.cc/vpn/queryCheck">...</a> into
|
||||||
|
* an object for project use.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Builder(toBuilder = true)
|
@Builder(toBuilder = true)
|
||||||
@@ -35,7 +35,8 @@ public class QueryResponse {
|
|||||||
private long queriesMax;
|
private long queriesMax;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats response from <a href="https://funkemunky.cc/vpn/queryCheck">...</a> into {@link QueryResponse} for project use.
|
* Formats response from <a href="https://funkemunky.cc/vpn/queryCheck">...</a> into {@link
|
||||||
|
* QueryResponse} for project use.
|
||||||
*
|
*
|
||||||
* @param object JSONObject
|
* @param object JSONObject
|
||||||
* @return QueryResponse
|
* @return QueryResponse
|
||||||
@@ -48,9 +49,11 @@ public class QueryResponse {
|
|||||||
return QueryResponse.builder().validPlan(false).build();
|
return QueryResponse.builder().validPlan(false).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return QueryResponse.builder().validPlan(object.getBoolean("validPlan"))
|
return QueryResponse.builder()
|
||||||
|
.validPlan(object.getBoolean("validPlan"))
|
||||||
.planType(object.getString("planType"))
|
.planType(object.getString("planType"))
|
||||||
.queries(object.getLong("queries"))
|
.queries(object.getLong("queries"))
|
||||||
.queriesMax(object.getLong("queryLimit")).build();
|
.queriesMax(object.getLong("queryLimit"))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,15 @@ import lombok.Data;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class VPNResponse {
|
public class VPNResponse {
|
||||||
private String asn, ip, countryName, countryCode, city, timeZone, method, isp, failureReason = "N/A";
|
private String asn,
|
||||||
|
ip,
|
||||||
|
countryName,
|
||||||
|
countryCode,
|
||||||
|
city,
|
||||||
|
timeZone,
|
||||||
|
method,
|
||||||
|
isp,
|
||||||
|
failureReason = "N/A";
|
||||||
private boolean proxy, cached;
|
private boolean proxy, cached;
|
||||||
private final boolean success;
|
private final boolean success;
|
||||||
private double latitude, longitude;
|
private double latitude, longitude;
|
||||||
@@ -52,8 +60,8 @@ public class VPNResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feeds into {@link VPNResponse#fromJson(JSONObject)} formatting the JSON {@link String} into
|
* Feeds into {@link VPNResponse#fromJson(JSONObject)} formatting the JSON {@link String} into a
|
||||||
* a {@link JSONObject}
|
* {@link JSONObject}
|
||||||
*
|
*
|
||||||
* @param json String
|
* @param json String
|
||||||
* @return VPNResponse
|
* @return VPNResponse
|
||||||
@@ -62,13 +70,12 @@ public class VPNResponse {
|
|||||||
return fromJson(new JSONObject(json));
|
return fromJson(new JSONObject(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final VPNResponse FAILED_RESPONSE = VPNResponse.builder()
|
public static final VPNResponse FAILED_RESPONSE =
|
||||||
.success(false)
|
VPNResponse.builder().success(false).failureReason("Internal plugin API error.").build();
|
||||||
.failureReason("Internal plugin API error.")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats response from <a href="https://funkemunky.cc/vpn">...</a> into {@link VPNResponse} for project use.
|
* Formats response from <a href="https://funkemunky.cc/vpn">...</a> into {@link VPNResponse} for
|
||||||
|
* project use.
|
||||||
*
|
*
|
||||||
* @param jsonObject JSONObject
|
* @param jsonObject JSONObject
|
||||||
* @return VPNResponse
|
* @return VPNResponse
|
||||||
@@ -76,17 +83,28 @@ public class VPNResponse {
|
|||||||
*/
|
*/
|
||||||
public static VPNResponse fromJson(JSONObject jsonObject) throws JSONException {
|
public static VPNResponse fromJson(JSONObject jsonObject) throws JSONException {
|
||||||
if (jsonObject.getBoolean("success")) {
|
if (jsonObject.getBoolean("success")) {
|
||||||
return new VPNResponse(jsonObject.getString("asn"), jsonObject.getString("ip"),
|
return new VPNResponse(
|
||||||
jsonObject.getString("countryName"), jsonObject.getString("countryCode"),
|
jsonObject.getString("asn"),
|
||||||
jsonObject.getString("city"), jsonObject.getString("timeZone"),
|
jsonObject.getString("ip"),
|
||||||
|
jsonObject.getString("countryName"),
|
||||||
|
jsonObject.getString("countryCode"),
|
||||||
|
jsonObject.getString("city"),
|
||||||
|
jsonObject.getString("timeZone"),
|
||||||
jsonObject.has("method") ? jsonObject.getString("method") : "N/A",
|
jsonObject.has("method") ? jsonObject.getString("method") : "N/A",
|
||||||
jsonObject.getString("isp"), "N/A", jsonObject.getBoolean("proxy"),
|
jsonObject.getString("isp"),
|
||||||
jsonObject.getBoolean("cached"), jsonObject.getBoolean("success"),
|
"N/A",
|
||||||
jsonObject.getDouble("latitude"), jsonObject.getDouble("longitude"),
|
jsonObject.getBoolean("proxy"),
|
||||||
jsonObject.getLong("lastAccess"), jsonObject.getInt("queriesLeft"));
|
jsonObject.getBoolean("cached"),
|
||||||
|
jsonObject.getBoolean("success"),
|
||||||
|
jsonObject.getDouble("latitude"),
|
||||||
|
jsonObject.getDouble("longitude"),
|
||||||
|
jsonObject.getLong("lastAccess"),
|
||||||
|
jsonObject.getInt("queriesLeft"));
|
||||||
} else {
|
} else {
|
||||||
return VPNResponse.builder().success(false)
|
return VPNResponse.builder()
|
||||||
.failureReason(jsonObject.getString("failureReason")).build();
|
.success(false)
|
||||||
|
.failureReason(jsonObject.getString("failureReason"))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-28
@@ -1,18 +1,15 @@
|
|||||||
package dev.brighten.antivpn.database;
|
package dev.brighten.antivpn.database;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.lenient;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
import dev.brighten.antivpn.api.VPNConfig;
|
import dev.brighten.antivpn.api.VPNConfig;
|
||||||
import dev.brighten.antivpn.api.VPNExecutor;
|
import dev.brighten.antivpn.api.VPNExecutor;
|
||||||
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
||||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -22,21 +19,19 @@ import java.util.concurrent.CountDownLatch;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import static org.mockito.Mockito.lenient;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
import static org.mockito.Mockito.when;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
abstract class DatabaseIntegrationTestSupport {
|
abstract class DatabaseIntegrationTestSupport {
|
||||||
|
|
||||||
@TempDir
|
@TempDir Path pluginFolder;
|
||||||
Path pluginFolder;
|
|
||||||
|
|
||||||
@Mock
|
@Mock protected AntiVPN antiVPN;
|
||||||
protected AntiVPN antiVPN;
|
|
||||||
|
|
||||||
@Mock
|
@Mock protected VPNConfig vpnConfig;
|
||||||
protected VPNConfig vpnConfig;
|
|
||||||
|
|
||||||
protected TestVPNExecutor vpnExecutor;
|
protected TestVPNExecutor vpnExecutor;
|
||||||
private AutoCloseable mocks;
|
private AutoCloseable mocks;
|
||||||
@@ -93,7 +88,8 @@ abstract class DatabaseIntegrationTestSupport {
|
|||||||
registerDatabase(database);
|
registerDatabase(database);
|
||||||
database.init();
|
database.init();
|
||||||
|
|
||||||
VPNResponse response = VPNResponse.builder()
|
VPNResponse response =
|
||||||
|
VPNResponse.builder()
|
||||||
.ip("1.2.3.4")
|
.ip("1.2.3.4")
|
||||||
.asn("AS123")
|
.asn("AS123")
|
||||||
.countryName("United States")
|
.countryName("United States")
|
||||||
@@ -112,11 +108,13 @@ abstract class DatabaseIntegrationTestSupport {
|
|||||||
assertTrue(storedResponse.get().isProxy());
|
assertTrue(storedResponse.get().isProxy());
|
||||||
|
|
||||||
database.deleteResponse(response.getIp());
|
database.deleteResponse(response.getIp());
|
||||||
awaitCondition(() -> database.getStoredResponse(response.getIp()).isEmpty(),
|
awaitCondition(
|
||||||
|
() -> database.getStoredResponse(response.getIp()).isEmpty(),
|
||||||
"Expected cached response to be deleted");
|
"Expected cached response to be deleted");
|
||||||
|
|
||||||
database.cacheResponse(response);
|
database.cacheResponse(response);
|
||||||
awaitCondition(() -> database.getStoredResponse(response.getIp()).isPresent(),
|
awaitCondition(
|
||||||
|
() -> database.getStoredResponse(response.getIp()).isPresent(),
|
||||||
"Expected cached response to be restored");
|
"Expected cached response to be restored");
|
||||||
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
@@ -126,7 +124,8 @@ abstract class DatabaseIntegrationTestSupport {
|
|||||||
List<UUID> whitelisted = database.getAllWhitelisted();
|
List<UUID> whitelisted = database.getAllWhitelisted();
|
||||||
assertTrue(whitelisted.contains(uuid));
|
assertTrue(whitelisted.contains(uuid));
|
||||||
database.removeWhitelist(uuid);
|
database.removeWhitelist(uuid);
|
||||||
awaitCondition(() -> !database.isWhitelisted(uuid), "Expected UUID whitelist entry to be removed");
|
awaitCondition(
|
||||||
|
() -> !database.isWhitelisted(uuid), "Expected UUID whitelist entry to be removed");
|
||||||
|
|
||||||
CIDRUtils cidr = new CIDRUtils("192.168.1.0/24");
|
CIDRUtils cidr = new CIDRUtils("192.168.1.0/24");
|
||||||
assertFalse(database.isWhitelisted(cidr));
|
assertFalse(database.isWhitelisted(cidr));
|
||||||
@@ -135,7 +134,8 @@ abstract class DatabaseIntegrationTestSupport {
|
|||||||
List<CIDRUtils> whitelistedIps = database.getAllWhitelistedIps();
|
List<CIDRUtils> whitelistedIps = database.getAllWhitelistedIps();
|
||||||
assertTrue(whitelistedIps.stream().anyMatch(entry -> entry.getCidr().equals(cidr.getCidr())));
|
assertTrue(whitelistedIps.stream().anyMatch(entry -> entry.getCidr().equals(cidr.getCidr())));
|
||||||
database.removeWhitelist(cidr);
|
database.removeWhitelist(cidr);
|
||||||
awaitCondition(() -> !database.isWhitelisted(cidr), "Expected CIDR whitelist entry to be removed");
|
awaitCondition(
|
||||||
|
() -> !database.isWhitelisted(cidr), "Expected CIDR whitelist entry to be removed");
|
||||||
|
|
||||||
database.updateAlertsState(uuid, true);
|
database.updateAlertsState(uuid, true);
|
||||||
awaitCondition(() -> awaitAlertsState(database, uuid), "Expected alerts to be enabled");
|
awaitCondition(() -> awaitAlertsState(database, uuid), "Expected alerts to be enabled");
|
||||||
@@ -143,24 +143,30 @@ abstract class DatabaseIntegrationTestSupport {
|
|||||||
awaitCondition(() -> !awaitAlertsState(database, uuid), "Expected alerts to be disabled");
|
awaitCondition(() -> !awaitAlertsState(database, uuid), "Expected alerts to be disabled");
|
||||||
|
|
||||||
database.clearResponses();
|
database.clearResponses();
|
||||||
awaitCondition(() -> database.getStoredResponse(response.getIp()).isEmpty(),
|
awaitCondition(
|
||||||
|
() -> database.getStoredResponse(response.getIp()).isEmpty(),
|
||||||
"Expected cached responses to be cleared");
|
"Expected cached responses to be cleared");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<VPNResponse> awaitStoredResponse(VPNDatabase database, String ip) throws InterruptedException {
|
private Optional<VPNResponse> awaitStoredResponse(VPNDatabase database, String ip)
|
||||||
|
throws InterruptedException {
|
||||||
AtomicReference<Optional<VPNResponse>> result = new AtomicReference<>(Optional.empty());
|
AtomicReference<Optional<VPNResponse>> result = new AtomicReference<>(Optional.empty());
|
||||||
awaitCondition(() -> {
|
awaitCondition(
|
||||||
|
() -> {
|
||||||
Optional<VPNResponse> response = database.getStoredResponse(ip);
|
Optional<VPNResponse> response = database.getStoredResponse(ip);
|
||||||
result.set(response);
|
result.set(response);
|
||||||
return response.isPresent();
|
return response.isPresent();
|
||||||
}, "Timed out waiting for cached response");
|
},
|
||||||
|
"Timed out waiting for cached response");
|
||||||
return result.get();
|
return result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean awaitAlertsState(VPNDatabase database, UUID uuid) throws InterruptedException {
|
private boolean awaitAlertsState(VPNDatabase database, UUID uuid) throws InterruptedException {
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
AtomicReference<Boolean> result = new AtomicReference<>(false);
|
AtomicReference<Boolean> result = new AtomicReference<>(false);
|
||||||
database.alertsState(uuid, enabled -> {
|
database.alertsState(
|
||||||
|
uuid,
|
||||||
|
enabled -> {
|
||||||
result.set(enabled);
|
result.set(enabled);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
});
|
});
|
||||||
@@ -168,7 +174,8 @@ abstract class DatabaseIntegrationTestSupport {
|
|||||||
return result.get();
|
return result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void awaitCondition(CheckedBooleanSupplier condition, String failureMessage) throws InterruptedException {
|
protected void awaitCondition(CheckedBooleanSupplier condition, String failureMessage)
|
||||||
|
throws InterruptedException {
|
||||||
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(10);
|
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(10);
|
||||||
while (System.nanoTime() < deadline) {
|
while (System.nanoTime() < deadline) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
+12
-13
@@ -1,24 +1,22 @@
|
|||||||
package dev.brighten.antivpn.database;
|
package dev.brighten.antivpn.database;
|
||||||
|
|
||||||
import com.mongodb.client.MongoClients;
|
|
||||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.bson.Document;
|
|
||||||
import org.testcontainers.containers.MongoDBContainer;
|
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.mongodb.client.MongoClients;
|
||||||
|
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.testcontainers.containers.MongoDBContainer;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
@Testcontainers
|
@Testcontainers
|
||||||
class MongoDatabaseIntegrationTest extends DatabaseIntegrationTestSupport {
|
class MongoDatabaseIntegrationTest extends DatabaseIntegrationTestSupport {
|
||||||
|
|
||||||
@Container
|
@Container private static final MongoDBContainer MONGO = new MongoDBContainer("mongo:6.0.14");
|
||||||
private static final MongoDBContainer MONGO = new MongoDBContainer("mongo:6.0.14");
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void mongoDatabaseImplementsTheVpnDatabaseContract() throws Exception {
|
void mongoDatabaseImplementsTheVpnDatabaseContract() throws Exception {
|
||||||
@@ -31,7 +29,8 @@ class MongoDatabaseIntegrationTest extends DatabaseIntegrationTestSupport {
|
|||||||
|
|
||||||
when(vpnConfig.getIp()).thenReturn(MONGO.getHost());
|
when(vpnConfig.getIp()).thenReturn(MONGO.getHost());
|
||||||
when(vpnConfig.getPort()).thenReturn(MONGO.getMappedPort(27017));
|
when(vpnConfig.getPort()).thenReturn(MONGO.getMappedPort(27017));
|
||||||
when(vpnConfig.getDatabaseName()).thenReturn("antivpn_" + UUID.randomUUID().toString().replace("-", ""));
|
when(vpnConfig.getDatabaseName())
|
||||||
|
.thenReturn("antivpn_" + UUID.randomUUID().toString().replace("-", ""));
|
||||||
when(vpnConfig.mongoDatabaseURL()).thenReturn("");
|
when(vpnConfig.mongoDatabaseURL()).thenReturn("");
|
||||||
when(vpnConfig.useDatabaseCreds()).thenReturn(false);
|
when(vpnConfig.useDatabaseCreds()).thenReturn(false);
|
||||||
|
|
||||||
|
|||||||
+12
-8
@@ -1,21 +1,22 @@
|
|||||||
package dev.brighten.antivpn.database;
|
package dev.brighten.antivpn.database;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||||
|
import java.sql.DriverManager;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.testcontainers.containers.MySQLContainer;
|
import org.testcontainers.containers.MySQLContainer;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
import java.sql.DriverManager;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@Testcontainers
|
@Testcontainers
|
||||||
class MySqlDatabaseIntegrationTest extends DatabaseIntegrationTestSupport {
|
class MySqlDatabaseIntegrationTest extends DatabaseIntegrationTestSupport {
|
||||||
|
|
||||||
@Container
|
@Container
|
||||||
private static final MySQLContainer<?> MYSQL = new MySQLContainer<>("mysql:8.0.36")
|
private static final MySQLContainer<?> MYSQL =
|
||||||
|
new MySQLContainer<>("mysql:8.0.36")
|
||||||
.withDatabaseName("antivpn")
|
.withDatabaseName("antivpn")
|
||||||
.withUsername("testuser")
|
.withUsername("testuser")
|
||||||
.withPassword("testpass");
|
.withPassword("testpass");
|
||||||
@@ -24,7 +25,9 @@ class MySqlDatabaseIntegrationTest extends DatabaseIntegrationTestSupport {
|
|||||||
void mysqlDatabaseImplementsTheVpnDatabaseContract() throws Exception {
|
void mysqlDatabaseImplementsTheVpnDatabaseContract() throws Exception {
|
||||||
assertTrue(MYSQL.isRunning(), "MySQL Testcontainer should be running");
|
assertTrue(MYSQL.isRunning(), "MySQL Testcontainer should be running");
|
||||||
|
|
||||||
try (var connection = DriverManager.getConnection(MYSQL.getJdbcUrl(), MYSQL.getUsername(), MYSQL.getPassword());
|
try (var connection =
|
||||||
|
DriverManager.getConnection(
|
||||||
|
MYSQL.getJdbcUrl(), MYSQL.getUsername(), MYSQL.getPassword());
|
||||||
var statement = connection.createStatement();
|
var statement = connection.createStatement();
|
||||||
var resultSet = statement.executeQuery("SELECT 1")) {
|
var resultSet = statement.executeQuery("SELECT 1")) {
|
||||||
assertTrue(resultSet.next(), "Expected a row from the MySQL container");
|
assertTrue(resultSet.next(), "Expected a row from the MySQL container");
|
||||||
@@ -32,7 +35,8 @@ class MySqlDatabaseIntegrationTest extends DatabaseIntegrationTestSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pingResult = MYSQL.execInContainer("mysqladmin", "ping", "-h", "127.0.0.1", "-ptestpass");
|
var pingResult = MYSQL.execInContainer("mysqladmin", "ping", "-h", "127.0.0.1", "-ptestpass");
|
||||||
assertEquals(0, pingResult.getExitCode(), "Expected mysqladmin ping to succeed inside the container");
|
assertEquals(
|
||||||
|
0, pingResult.getExitCode(), "Expected mysqladmin ping to succeed inside the container");
|
||||||
|
|
||||||
when(vpnConfig.getIp()).thenReturn(MYSQL.getHost());
|
when(vpnConfig.getIp()).thenReturn(MYSQL.getHost());
|
||||||
when(vpnConfig.getPort()).thenReturn(MYSQL.getMappedPort(3306));
|
when(vpnConfig.getPort()).thenReturn(MYSQL.getMappedPort(3306));
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
package dev.brighten.antivpn.utils;
|
package dev.brighten.antivpn.utils;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import com.sun.net.httpserver.HttpServer;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import dev.brighten.antivpn.AntiVPN;
|
import dev.brighten.antivpn.AntiVPN;
|
||||||
@@ -9,12 +16,6 @@ import dev.brighten.antivpn.api.VPNExecutor;
|
|||||||
import dev.brighten.antivpn.command.CommandExecutor;
|
import dev.brighten.antivpn.command.CommandExecutor;
|
||||||
import dev.brighten.antivpn.command.impl.AllowlistCommand;
|
import dev.brighten.antivpn.command.impl.AllowlistCommand;
|
||||||
import dev.brighten.antivpn.database.VPNDatabase;
|
import dev.brighten.antivpn.database.VPNDatabase;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@@ -25,32 +26,26 @@ import java.util.Optional;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.mockito.Mockito.mock;
|
import org.mockito.Mock;
|
||||||
import static org.mockito.Mockito.never;
|
import org.mockito.MockitoAnnotations;
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
class AllowlistCommandTest {
|
class AllowlistCommandTest {
|
||||||
|
|
||||||
private static final UUID FUNKEMUNKY_UUID = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
|
private static final UUID FUNKEMUNKY_UUID =
|
||||||
|
UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
|
||||||
|
|
||||||
@Mock
|
@Mock private AntiVPN antiVPN;
|
||||||
private AntiVPN antiVPN;
|
|
||||||
|
|
||||||
@Mock
|
@Mock private VPNConfig vpnConfig;
|
||||||
private VPNConfig vpnConfig;
|
|
||||||
|
|
||||||
@Mock
|
@Mock private VPNDatabase database;
|
||||||
private VPNDatabase database;
|
|
||||||
|
|
||||||
@Mock
|
@Mock private dev.brighten.antivpn.api.PlayerExecutor playerExecutor;
|
||||||
private dev.brighten.antivpn.api.PlayerExecutor playerExecutor;
|
|
||||||
|
|
||||||
@Mock
|
@Mock private CommandExecutor commandExecutor;
|
||||||
private CommandExecutor commandExecutor;
|
|
||||||
|
|
||||||
private TestVPNExecutor vpnExecutor;
|
private TestVPNExecutor vpnExecutor;
|
||||||
private AutoCloseable mocks;
|
private AutoCloseable mocks;
|
||||||
@@ -111,7 +106,9 @@ class AllowlistCommandTest {
|
|||||||
|
|
||||||
AtomicInteger mojangHits = new AtomicInteger();
|
AtomicInteger mojangHits = new AtomicInteger();
|
||||||
HttpServer mojangServer = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
|
HttpServer mojangServer = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
|
||||||
mojangServer.createContext("/users/profiles/minecraft/funkemunky", exchange -> {
|
mojangServer.createContext(
|
||||||
|
"/users/profiles/minecraft/funkemunky",
|
||||||
|
exchange -> {
|
||||||
mojangHits.incrementAndGet();
|
mojangHits.incrementAndGet();
|
||||||
respond(exchange);
|
respond(exchange);
|
||||||
});
|
});
|
||||||
@@ -120,8 +117,7 @@ class AllowlistCommandTest {
|
|||||||
try {
|
try {
|
||||||
MiscUtils.setLookupEndpointsForTesting(
|
MiscUtils.setLookupEndpointsForTesting(
|
||||||
"http://127.0.0.1:" + closedPort + "/mojang/uuid?name=",
|
"http://127.0.0.1:" + closedPort + "/mojang/uuid?name=",
|
||||||
"http://127.0.0.1:" + mojangServer.getAddress().getPort() + "/users/profiles/minecraft/"
|
"http://127.0.0.1:" + mojangServer.getAddress().getPort() + "/users/profiles/minecraft/");
|
||||||
);
|
|
||||||
|
|
||||||
String result = command.execute(commandExecutor, new String[] {"add", "funkemunky"});
|
String result = command.execute(commandExecutor, new String[] {"add", "funkemunky"});
|
||||||
|
|
||||||
@@ -138,7 +134,8 @@ class AllowlistCommandTest {
|
|||||||
byte[] bytes = "{\"id\":\"123e4567e89b12d3a456426614174000\"}".getBytes(StandardCharsets.UTF_8);
|
byte[] bytes = "{\"id\":\"123e4567e89b12d3a456426614174000\"}".getBytes(StandardCharsets.UTF_8);
|
||||||
exchange.getResponseHeaders().add("Content-Type", "application/json");
|
exchange.getResponseHeaders().add("Content-Type", "application/json");
|
||||||
exchange.sendResponseHeaders(200, bytes.length);
|
exchange.sendResponseHeaders(200, bytes.length);
|
||||||
try (exchange; OutputStream outputStream = exchange.getResponseBody()) {
|
try (exchange;
|
||||||
|
OutputStream outputStream = exchange.getResponseBody()) {
|
||||||
outputStream.write(bytes);
|
outputStream.write(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.brighten.antivpn.utils;
|
||||||
|
|
||||||
|
public class ReflectionUtils {
|
||||||
|
|
||||||
|
public static <T> T getDeclaredField(Class<?> clazz, String name)
|
||||||
|
throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
var declaredField = clazz.getDeclaredField(name);
|
||||||
|
|
||||||
|
declaredField.setAccessible(true);
|
||||||
|
|
||||||
|
return (T) declaredField.get(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user