mirror of
https://github.com/funkemunky/AntiVPN.git
synced 2026-05-31 17:31:55 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce6e040d3d | |||
|
45d2a7eaa0
|
|||
|
9e68524bd7
|
|||
| 756b3b04c7 | |||
|
8bbc964f0d
|
|||
| 1ebaa64d6d | |||
| 7983172423 | |||
| 3f46db4ad9 | |||
|
0ea8be6ea3
|
|||
| 3ead4a0093 | |||
| 8b5bc65159 | |||
|
11ef7e8d50
|
|||
|
ea4fe063b2
|
|||
|
38dcfcb1fe
|
|||
|
0894ef8e3d
|
|||
| 3892cf43b3 | |||
|
95d8747bd5
|
|||
| ee7a059b01 | |||
| 51340754e6 | |||
| 0e2468d3fb | |||
| faa8bdbb19 | |||
| 0f14e68c36 | |||
| 772e924303 | |||
| a195106708 | |||
|
de6dc8aa7b
|
|||
| 6246bce70d | |||
| 9fac54fe0b | |||
| ba9ab6fb31 | |||
| 39961a3683 | |||
|
5792b81cb1
|
|||
|
e044d27e27
|
|||
|
17cbed8bda
|
|||
|
50c357f508
|
|||
|
dddd860c15
|
|||
|
e09217877c
|
|||
|
7ffba38992
|
|||
|
8a4b86c9ef
|
|||
|
ac57a540c2
|
|||
|
18d9bcea39
|
|||
|
9f7f4b40f0
|
|||
| 7913676323 | |||
| 9ea3141ae7 | |||
|
cd7008ee09
|
|||
|
c743404069
|
|||
| 58395bb705 | |||
| bd8bd943bd | |||
|
c95c9bb8f3
|
|||
| c7734b2294 | |||
| 8faa59f161 | |||
|
c09269cc20
|
|||
| 011d18ad46 | |||
| f4d1c7a62c | |||
| 6dc0f13c2c | |||
| 606144d404 | |||
|
fe2f9271d8
|
|||
|
43aefc1aed
|
|||
| 1c3e653dda | |||
| 1930a86a0d | |||
| edd255e29d | |||
| c191fbccfa | |||
| 9697150465 | |||
| a5ea502f17 | |||
| bb3a31e100 | |||
| 83f06b29c8 | |||
| 247b329280 | |||
| 069142a06b |
@@ -2,10 +2,9 @@ name: create-release.yml
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
build-and-release:
|
||||
name: Build and Release
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v4
|
||||
@@ -28,30 +27,6 @@ jobs:
|
||||
run: mvn -B package --file pom.xml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload AntiVPN
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AntiVPN-Universal
|
||||
path: Universal/target/AntiVPN-*.jar
|
||||
- name: Upload Sponge plugin
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AntiVPN-Sponge
|
||||
path: Sponge/target/Sponge-*.jar
|
||||
release:
|
||||
name: Create Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Download AntiVPN
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: AntiVPN-Universal
|
||||
- name: Download Sponge plugin
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: AntiVPN-Sponge
|
||||
- name: Get Version Number from Pom
|
||||
id: get_version
|
||||
run: echo "VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
|
||||
@@ -69,17 +44,17 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: v${{ github.run_number }}
|
||||
release_name: Release v${{ github.run_number }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: ${{ steps.changelog.outputs.content }}
|
||||
tag_name: v${{ env.VERSION }}
|
||||
release_name: Release v${{ env.VERSION }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: ${{ steps.changelog.outputs.content }}
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: AntiVPN-*-universal.jar
|
||||
asset_path: ./Universal/target/AntiVPN-${{ env.VERSION }}-universal.jar
|
||||
asset_name: AntiVPN-Universal-v${{ env.VERSION }}.jar
|
||||
asset_content_type: application/java-archive
|
||||
- uses: actions/upload-release-asset@v1
|
||||
@@ -87,7 +62,6 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: Sponge-*.jar
|
||||
asset_path: ./Sponge/target/Sponge-${{ env.VERSION }}.jar
|
||||
asset_name: AntiVPN-Sponge-v${{ env.VERSION }}.jar
|
||||
asset_content_type: application/java-archive
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21.0
|
||||
distribution: 'zulu'
|
||||
cache: 'maven'
|
||||
- name: Compile
|
||||
run: mvn -B package --file pom.xml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026 Dawson Hessler
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Bukkit</artifactId>
|
||||
<version>1.10.0</version>
|
||||
</parent>
|
||||
<groupId>dev.brighten.antivpn.bukkit</groupId>
|
||||
<artifactId>Loader</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
|
||||
<resource>antivpn-bukkit.jarinjar</resource>
|
||||
<file>${project.parent.basedir}/Plugin/target/Plugin-${project.version}.jar</file>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.20.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>loader-utils</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bukkit.loader;
|
||||
|
||||
import dev.brighten.antivpn.loader.JarInJarClassLoader;
|
||||
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class BukkitLoaderPlugin extends JavaPlugin {
|
||||
|
||||
private static final String JAR_NAME = "antivpn-bukkit.jarinjar";
|
||||
private static final String SOURCE_NAME = "antivpn-source.jarinjar";
|
||||
private static final String BOOTSTRAP_CLASS = "dev.brighten.antivpn.bukkit.BukkitPlugin";
|
||||
|
||||
private final LoaderBootstrap plugin;
|
||||
|
||||
public BukkitLoaderPlugin() {
|
||||
JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
||||
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, JavaPlugin.class, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.plugin.onLoad(getDataFolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.plugin.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.plugin.onDisable();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
name: KauriVPN
|
||||
main: dev.brighten.antivpn.bukkit.loader.BukkitLoaderPlugin
|
||||
version: ${project.version}
|
||||
author: funkemunky
|
||||
api-version: 1.13
|
||||
folia-supported: true
|
||||
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026 Dawson Hessler
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Bukkit</artifactId>
|
||||
<version>1.10.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>Plugin</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<!-- Replace this with your package! -->
|
||||
<shadedPattern>dev.brighten.antivpn.bukkit.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.20.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Source</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>loader-utils</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.OfflinePlayer;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BukkitListener extends VPNExecutor implements Listener {
|
||||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
Bukkit.getPluginManager()
|
||||
.registerEvents(this, BukkitPlugin.pluginInstance.getPlugin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
Bukkit.getLogger().log(level, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
|
||||
ChatColor.translateAlternateColorCodes('&', command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
HandlerList.unregisterAll(this);
|
||||
Bukkit.getPluginManager().disablePlugin(BukkitPlugin.pluginInstance.getPlugin());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onLogin(final PlayerLoginEvent event) {
|
||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.orElse(new OfflinePlayer(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getName(),
|
||||
event.getAddress()
|
||||
));
|
||||
|
||||
player.checkPlayer(result -> {
|
||||
if(!result.resultType().isShouldBlock()) return;
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_BANNED);
|
||||
event.setKickMessage(switch (result.resultType()) {
|
||||
case DENIED_COUNTRY -> StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getCountryVanillaKickReason(),
|
||||
player,
|
||||
result.response()
|
||||
);
|
||||
case DENIED_PROXY ->
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickMessage(),
|
||||
player,
|
||||
result.response()
|
||||
);
|
||||
default -> "You were kicked by KauriVPN for an unknown reason!";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onJoin(final PlayerJoinEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.ifPresent(APIPlayer::checkAlertsState);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
+17
-1
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
@@ -27,7 +43,7 @@ public class BukkitPlayer extends APIPlayer {
|
||||
public void run() {
|
||||
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance);
|
||||
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||
} else player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
|
||||
}
|
||||
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
+42
-11
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
@@ -7,6 +23,7 @@ import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
||||
import lombok.Getter;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
@@ -17,20 +34,34 @@ import org.bukkit.plugin.SimplePluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BukkitPlugin extends JavaPlugin {
|
||||
public class BukkitPlugin implements LoaderBootstrap {
|
||||
|
||||
public static BukkitPlugin pluginInstance;
|
||||
private SimpleCommandMap commandMap;
|
||||
@Getter
|
||||
private File dataFolder;
|
||||
private final List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
|
||||
@Getter
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public BukkitPlugin(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Getter
|
||||
private PlayerCommandRunner playerCommandRunner;
|
||||
|
||||
@Override
|
||||
public void onLoad(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
|
||||
@@ -43,18 +74,18 @@ public class BukkitPlugin extends JavaPlugin {
|
||||
// Loading our bStats metrics to be pushed to https://bstats.org
|
||||
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
Bukkit.getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(this, 12615);
|
||||
Metrics metrics = new Metrics(plugin, 12615);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0;
|
||||
}
|
||||
}.runTaskTimerAsynchronously(this, 12000, 12000);
|
||||
}.runTaskTimerAsynchronously(plugin, 12000, 12000);
|
||||
}
|
||||
|
||||
Bukkit.getLogger().info("Setting up and registering commands...");
|
||||
// We need access to the commandMap to register our commands without using the "proper" method
|
||||
if (pluginInstance.getServer().getPluginManager() instanceof SimplePluginManager manager) {
|
||||
if (Bukkit.getServer().getPluginManager() instanceof SimplePluginManager manager) {
|
||||
try {
|
||||
Field field = SimplePluginManager.class.getDeclaredField("commandMap");
|
||||
field.setAccessible(true);
|
||||
@@ -73,7 +104,7 @@ public class BukkitPlugin extends JavaPlugin {
|
||||
registeredCommands.add(newCommand);
|
||||
|
||||
// This tells Bukkit to register our command for use.
|
||||
commandMap.register(pluginInstance.getName(), newCommand);
|
||||
commandMap.register(plugin.getName(), newCommand);
|
||||
}
|
||||
|
||||
//TODO Finish system before implementing on startup
|
||||
@@ -83,7 +114,7 @@ public class BukkitPlugin extends JavaPlugin {
|
||||
.get());
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();*/
|
||||
|
||||
reloadConfig();
|
||||
plugin.reloadConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,19 +140,19 @@ public class BukkitPlugin extends JavaPlugin {
|
||||
}
|
||||
|
||||
Bukkit.getLogger().info("Unregistering listeners...");
|
||||
HandlerList.unregisterAll(this);
|
||||
HandlerList.unregisterAll(plugin);
|
||||
|
||||
Bukkit.getLogger().info("Cancelling any running tasks...");
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
Bukkit.getScheduler().cancelTasks(plugin);
|
||||
}
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
|
||||
if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MySqlVPN) {
|
||||
if(database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
+17
-1
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
@@ -35,7 +51,7 @@ public class PlayerCommandRunner {
|
||||
public void run() {
|
||||
action.getAction().run();
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance);
|
||||
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||
|
||||
playerActions.poll();
|
||||
}
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bukkit.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
+23
-77
@@ -1,95 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026 Dawson Hessler
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.9.4</version>
|
||||
<version>1.10.0</version>
|
||||
</parent>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>Plugin</module>
|
||||
<module>Loader</module>
|
||||
</modules>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>Bukkit</artifactId>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<!-- Replace this with your package! -->
|
||||
<shadedPattern>dev.brighten.antivpn.bukkit.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.20.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>1.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,133 +0,0 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.CheckResult;
|
||||
import dev.brighten.antivpn.api.OfflinePlayer;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.message.VpnString;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.Tuple;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class BukkitListener extends VPNExecutor implements Listener {
|
||||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
BukkitPlugin.pluginInstance.getServer().getPluginManager()
|
||||
.registerEvents(this, BukkitPlugin.pluginInstance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
Bukkit.getLogger().log(level, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
|
||||
ChatColor.translateAlternateColorCodes('&', command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
HandlerList.unregisterAll(this);
|
||||
BukkitPlugin.pluginInstance.getServer().getPluginManager().disablePlugin(BukkitPlugin.pluginInstance);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onLogin(final PlayerLoginEvent event) {
|
||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.orElse(new OfflinePlayer(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getName(),
|
||||
event.getAddress()
|
||||
));
|
||||
|
||||
CheckResult instantResult = player.checkPlayer(result -> {
|
||||
if(!result.resultType().isShouldBlock()) return;
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "Adding %s to kick", event.getPlayer().getName());
|
||||
AntiVPN.getInstance().getExecutor().getToKick().add(new Tuple<>(result, event.getPlayer().getUniqueId()));
|
||||
});
|
||||
|
||||
if(!instantResult.resultType().isShouldBlock()) return;
|
||||
|
||||
AntiVPN.getInstance().getExecutor().getToKick()
|
||||
.add(new Tuple<>(instantResult, event.getPlayer().getUniqueId()));
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "%s was kicked from pre-login cache with IP %s", event.getPlayer().getName(), instantResult.response().getIp());
|
||||
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_BANNED);
|
||||
switch (instantResult.resultType()) {
|
||||
case DENIED_COUNTRY -> event.setKickMessage(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().countryVanillaKickReason(),
|
||||
player,
|
||||
instantResult.response()
|
||||
)));
|
||||
case DENIED_PROXY -> {
|
||||
if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.filter(APIPlayer::isAlertsEnabled)
|
||||
.forEach(pl ->
|
||||
pl.sendMessage(StringUtil.varReplace(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&',
|
||||
AntiVPN.getInstance().getVpnConfig().alertMessage()),
|
||||
player,
|
||||
instantResult.response())));
|
||||
}
|
||||
event.setKickMessage(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickString(),
|
||||
player,
|
||||
instantResult.response()
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onJoin(final PlayerJoinEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.ifPresent(player -> AntiVPN.getInstance().getDatabase().alertsState(player.getUuid(), enabled -> {
|
||||
if(enabled) {
|
||||
player.setAlertsEnabled(true);
|
||||
player.sendMessage(AntiVPN.getInstance().getMessageHandler()
|
||||
.getString("command-alerts-toggled")
|
||||
.getFormattedMessage(new VpnString.Var<>("state", true)));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
name: KauriVPN
|
||||
main: dev.brighten.antivpn.bukkit.BukkitPlugin
|
||||
version: ${project.version}
|
||||
author: funkemunky
|
||||
api-version: 1.13
|
||||
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026 Dawson Hessler
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Bungee</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>dev.brighten.antivpn.bungee</groupId>
|
||||
<artifactId>BungeeLoader</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
|
||||
<resource>antivpn-bungee.jarinjar</resource>
|
||||
<file>${project.parent.basedir}/BungeePlugin/target/BungeePlugin-${project.version}.jar</file>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.2</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>loader-utils</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.loader.JarInJarClassLoader;
|
||||
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
public class BungeeLoaderPlugin extends Plugin {
|
||||
|
||||
private static final String JAR_NAME = "antivpn-bungee.jarinjar";
|
||||
private static final String SOURCE_NAME = "antivpn-source.jarinjar";
|
||||
private static final String BOOTSTRAP_CLASS = "dev.brighten.antivpn.bungee.BungeePlugin";
|
||||
|
||||
private final LoaderBootstrap plugin;
|
||||
|
||||
public BungeeLoaderPlugin() {
|
||||
JarInJarClassLoader loader = new JarInJarClassLoader(getClass().getClassLoader(), JAR_NAME, SOURCE_NAME);
|
||||
this.plugin = loader.instantiatePlugin(BOOTSTRAP_CLASS, Plugin.class, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.plugin.onLoad(getDataFolder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.plugin.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.plugin.onDisable();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name: KauriVPN
|
||||
main: dev.brighten.antivpn.bungee.BungeePlugin
|
||||
main: dev.brighten.antivpn.bungee.BungeeLoaderPlugin
|
||||
description: A simple and fast antivpn plugin.
|
||||
version: ${project.version}
|
||||
author: funkemunky
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026 Dawson Hessler
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Bungee</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>BungeePlugin</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<!-- Replace this with your package! -->
|
||||
<shadedPattern>dev.brighten.antivpn.bungee.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Source</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.2</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>loader-utils</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+42
-39
@@ -1,12 +1,27 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.*;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.Tuple;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
@@ -25,7 +40,7 @@ public class BungeeListener extends VPNExecutor implements Listener {
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager()
|
||||
.registerListener(BungeePlugin.pluginInstance, this);
|
||||
.registerListener(BungeePlugin.pluginInstance.getPlugin(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,7 +50,7 @@ public class BungeeListener extends VPNExecutor implements Listener {
|
||||
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
log(Level.INFO, log, objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,12 +66,12 @@ public class BungeeListener extends VPNExecutor implements Listener {
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance);
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance.getPlugin());
|
||||
if (cacheResetTask != null) {
|
||||
cacheResetTask.cancel();
|
||||
cacheResetTask = null;
|
||||
}
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance);
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance.getPlugin());
|
||||
BungeePlugin.pluginInstance.onDisable();
|
||||
}
|
||||
|
||||
@@ -73,43 +88,31 @@ public class BungeeListener extends VPNExecutor implements Listener {
|
||||
((InetSocketAddress) event.getConnection().getSocketAddress()).getAddress());
|
||||
});
|
||||
|
||||
CheckResult instantResult = player.checkPlayer(result -> {
|
||||
player.checkPlayer(result -> {
|
||||
if (!result.resultType().isShouldBlock()) return;
|
||||
AntiVPN.getInstance().getExecutor().getToKick()
|
||||
.add(new Tuple<>(result, player.getUuid()));
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
event.setReason(TextComponent.fromLegacy(StringUtil.varReplace(switch (result.resultType()) {
|
||||
case DENIED_PROXY -> StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickMessage(), player, result.response());
|
||||
case DENIED_COUNTRY -> StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getCountryVanillaKickReason(), player, result.response());
|
||||
default -> "You were kicked by KauriVPN for an unknown reason!";
|
||||
}, player, result.response())));
|
||||
});
|
||||
}
|
||||
|
||||
if (!instantResult.resultType().isShouldBlock()) {
|
||||
return;
|
||||
}
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onJoin(LoginEvent event) {
|
||||
if(event.isCancelled()) return;
|
||||
|
||||
AntiVPN.getInstance().getExecutor().getToKick()
|
||||
.add(new Tuple<>(instantResult, player.getUuid()));
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO,
|
||||
"%s was kicked from pre-login proxy cache.",
|
||||
event.getConnection().getName());
|
||||
|
||||
|
||||
switch (instantResult.resultType()) {
|
||||
case DENIED_PROXY -> event.setReason(TextComponent.fromLegacy(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickString(),
|
||||
player,
|
||||
instantResult.response()))));
|
||||
case DENIED_COUNTRY -> event.setReason(TextComponent.fromLegacy(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().countryVanillaKickReason(),
|
||||
player,
|
||||
instantResult.response()))));
|
||||
}
|
||||
// Handling player alerts on join
|
||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getConnection().getUniqueId())
|
||||
.ifPresent(APIPlayer::checkAlertsState);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.bungee.command.BungeeCommand;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.loader.LoaderBootstrap;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BungeePlugin implements LoaderBootstrap {
|
||||
|
||||
public static BungeePlugin pluginInstance;
|
||||
|
||||
@Getter
|
||||
private File dataFolder;
|
||||
|
||||
@Getter
|
||||
private final Plugin plugin;
|
||||
|
||||
public BungeePlugin(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
|
||||
//Setting up config
|
||||
ProxyServer.getInstance().getLogger().info("Loading config...");
|
||||
|
||||
|
||||
//Loading plugin
|
||||
ProxyServer.getInstance().getLogger().info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
ProxyServer.getInstance().getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(getPlugin(), 12616);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
ProxyServer.getInstance().getScheduler().schedule(getPlugin(),
|
||||
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
|
||||
10, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
ProxyServer.getInstance().getPluginManager().registerCommand(getPlugin(), new BungeeCommand(command));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
AntiVPN.getInstance().stop();
|
||||
}
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
if(database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
}
|
||||
}
|
||||
|
||||
public ProxyServer getProxy() {
|
||||
return ProxyServer.getInstance();
|
||||
}
|
||||
}
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bungee.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.bungee.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
+18
-75
@@ -1,88 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026 Dawson Hessler
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.9.4</version>
|
||||
<version>1.10.0</version>
|
||||
</parent>
|
||||
<packaging>pom</packaging>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>Bungee</artifactId>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<!-- Replace this with your package! -->
|
||||
<shadedPattern>dev.brighten.antivpn.bungee.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>1.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.2</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,64 +0,0 @@
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.bungee.command.BungeeCommand;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BungeePlugin extends Plugin {
|
||||
|
||||
public static BungeePlugin pluginInstance;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
|
||||
//Setting up config
|
||||
getProxy().getLogger().info("Loading config...");
|
||||
|
||||
|
||||
//Loading plugin
|
||||
getProxy().getLogger().info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
getProxy().getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(this, 12616);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
getProxy().getScheduler().schedule(this,
|
||||
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
|
||||
10, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
getProxy().getPluginManager().registerCommand(pluginInstance, new BungeeCommand(command));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
AntiVPN.getInstance().stop();
|
||||
}
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
|
||||
if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if(database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2026 Dawson Hessler
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>1.10.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>Source</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.44</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.github.benmanes.caffeine</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.github.benmanes.caffeine</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.h2</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.h2</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.bson</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.bson</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mongodb</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mongodb</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mysql.cj</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mysql.cj</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mysql.jdbc</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mysql.jdbc</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>funkemunky-releases</id>
|
||||
<url>https://nexus.funkemunky.cc/content/repositories/releases/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>9.3.0</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.2.220</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>9.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
<version>9.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>2.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>3.1.8</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<version>3.12.14</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>26.0.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+20
-4
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn;
|
||||
|
||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
@@ -41,10 +57,10 @@ import java.util.List;
|
||||
@MavenLibrary(
|
||||
groupId = "com.mysql",
|
||||
artifactId = "mysql-connector-j",
|
||||
version = "9.1.0",
|
||||
version = "9.3.0",
|
||||
relocations = {
|
||||
@Relocate(from = "com.my\\" + "sql.cj", to = "dev.brighten.antivpn.shaded.com.mysql.cj"),
|
||||
@Relocate(from = "com.my\\" + "sql.jdbc", to = "dev.brighten.antivpn.shaded.com.mysql.jdbc")
|
||||
@Relocate(from = "com.my\\" + "sql.jdbc", to = "dev.brighten.antivpn.shaded.com.mysql.jdbc"),
|
||||
}
|
||||
)
|
||||
@MavenLibrary(groupId = "com.\\github\\.ben-manes\\.caffeine", artifactId = "caffeine", version = "3.1.8",
|
||||
@@ -109,7 +125,7 @@ public class AntiVPN {
|
||||
break;
|
||||
}
|
||||
case "mysql":
|
||||
case "sql":{
|
||||
case "sql": {
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||
INSTANCE.database = new MySqlVPN();
|
||||
INSTANCE.database.init();
|
||||
@@ -190,7 +206,7 @@ public class AntiVPN {
|
||||
executor.log("Failed to deregister H2 driver: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
VPNExecutor.threadExecutor.shutdown();
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().shutdown();
|
||||
if(database != null) database.shutdown();
|
||||
}
|
||||
|
||||
+58
-14
@@ -1,8 +1,25 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.message.VpnString;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -40,23 +57,45 @@ public abstract class APIPlayer {
|
||||
public void updateAlertsState() {
|
||||
//Updating into database so its synced across servers and saved on logout.
|
||||
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
|
||||
|
||||
sendMessage(AntiVPN.getInstance().getMessageHandler()
|
||||
.getString("command-alerts-toggled")
|
||||
.getFormattedMessage(new VpnString.Var<>("state", alertsEnabled)));
|
||||
}
|
||||
|
||||
public CheckResult checkPlayer(Consumer<CheckResult> onKick) {
|
||||
public void checkAlertsState() {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() ->
|
||||
AntiVPN.getInstance().getDatabase().alertsState(uuid, state -> {
|
||||
if(state) {
|
||||
alertsEnabled = true;
|
||||
updateAlertsState();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public void checkPlayer(Consumer<CheckResult> onResult) {
|
||||
if (hasPermission("antivpn.bypass") //Has bypass permission
|
||||
//Is exempt
|
||||
|| (uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
|
||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress())
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress() + "/32")
|
||||
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
|
||||
.anyMatch(name::startsWith)) return new CheckResult(null, ResultType.WHITELISTED);
|
||||
.anyMatch(name::startsWith)) {
|
||||
onResult.accept(new CheckResult(null, ResultType.WHITELISTED, false));
|
||||
return;
|
||||
}
|
||||
|
||||
CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress());
|
||||
|
||||
if(cachedResult != null) {
|
||||
if(cachedResult.response().getIp().equals(ip.getHostAddress())) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Cached result for " + ip.getHostAddress() + " is " + cachedResult.resultType());
|
||||
return cachedResult;
|
||||
if(cachedResult.resultType().isShouldBlock()) {
|
||||
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(cachedResult, this);
|
||||
}
|
||||
onResult.accept(cachedResult);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,36 +105,41 @@ public abstract class APIPlayer {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "The API query was not a success! " +
|
||||
"You may need to upgrade your license on " +
|
||||
"https://funkemunky.cc/shop");
|
||||
onResult.accept(new CheckResult(null, ResultType.API_FAILURE, false));
|
||||
return;
|
||||
}
|
||||
// If the countryList() size is zero, no need to check.
|
||||
// Running country check first
|
||||
CheckResult checkResult;
|
||||
if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty()
|
||||
if (!AntiVPN.getInstance().getVpnConfig().getCountryList().isEmpty()
|
||||
&& !((uuid != null && AntiVPN.getInstance().getExecutor()
|
||||
.isWhitelisted(uuid))
|
||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress()))
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress() + "/32"))
|
||||
// This bit of code will decide whether or not to kick the player
|
||||
// If it contains the code and it is set to whitelist, it will not kick
|
||||
// as they are equal and vise versa. However, if the contains does not match
|
||||
// the state, it will kick.
|
||||
&& AntiVPN.getInstance().getVpnConfig().countryList()
|
||||
&& AntiVPN.getInstance().getVpnConfig().getCountryList()
|
||||
.contains(result.getCountryCode())
|
||||
!= AntiVPN.getInstance().getVpnConfig().whitelistCountries()) {
|
||||
!= AntiVPN.getInstance().getVpnConfig().getWhitelistCountries()) {
|
||||
//Using our built in kicking system if no commands are configured
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_COUNTRY);
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_COUNTRY, false);
|
||||
} else if (result.isProxy()) {
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_PROXY);
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_PROXY, false);
|
||||
} else {
|
||||
checkResult = new CheckResult(result, ResultType.ALLOWED);
|
||||
checkResult = new CheckResult(result, ResultType.ALLOWED, false);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Result for " + ip.getHostAddress() + " is " + checkResult.resultType());
|
||||
|
||||
checkResultCache.put(ip.getHostAddress(), checkResult);
|
||||
onKick.accept(checkResult);
|
||||
checkResultCache.put(ip.getHostAddress(), new CheckResult(checkResult.response(), checkResult.resultType(), true));
|
||||
if(checkResult.resultType().isShouldBlock()) {
|
||||
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(checkResult, this);
|
||||
}
|
||||
onResult.accept(checkResult);
|
||||
AntiVPN.getInstance().checked++;
|
||||
});
|
||||
return new CheckResult(null, ResultType.UNKNOWN);
|
||||
onResult.accept(new CheckResult(null, ResultType.UNKNOWN, false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
public record CheckResult(VPNResponse response, ResultType resultType, boolean isFromCache) {
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OfflinePlayer extends APIPlayer {
|
||||
|
||||
public OfflinePlayer(UUID uuid, String name, InetAddress ip) {
|
||||
super(uuid, name, ip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PlayerExecutor {
|
||||
|
||||
Optional<APIPlayer> getPlayer(String name);
|
||||
|
||||
Optional<APIPlayer> getPlayer(UUID uuid);
|
||||
|
||||
void unloadPlayer(UUID uuid);
|
||||
|
||||
List<APIPlayer> getOnlinePlayers();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public enum ResultType {
|
||||
ALLOWED(false),
|
||||
WHITELISTED(false),
|
||||
DENIED_COUNTRY(true),
|
||||
DENIED_PROXY(true),
|
||||
API_FAILURE(false),
|
||||
UNKNOWN(false);
|
||||
|
||||
@Getter
|
||||
private final boolean shouldBlock;
|
||||
|
||||
ResultType(boolean shouldBlock) {
|
||||
this.shouldBlock = shouldBlock;
|
||||
}
|
||||
}
|
||||
+58
-123
@@ -1,7 +1,24 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.ConfigDefault;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -52,61 +69,60 @@ public class VPNConfig {
|
||||
defCountrylist = new ConfigDefault<>(new ArrayList<>(), "countries.list",
|
||||
AntiVPN.getInstance());
|
||||
|
||||
private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg,
|
||||
countryVanillaKickReason;
|
||||
private List<String> prefixWhitelists, commands, countryList, countryKickCommands;
|
||||
@Getter
|
||||
private String license;
|
||||
@Getter
|
||||
private String kickMessage;
|
||||
@Getter
|
||||
private String databaseType;
|
||||
@Getter
|
||||
private String databaseName;
|
||||
private String mongoURL;
|
||||
@Getter
|
||||
private String username;
|
||||
@Getter
|
||||
private String password;
|
||||
@Getter
|
||||
private String ip;
|
||||
@Getter
|
||||
private String alertMsg;
|
||||
@Getter
|
||||
private String countryVanillaKickReason;
|
||||
@Getter
|
||||
private List<String> prefixWhitelists;
|
||||
private List<String> commands;
|
||||
@Getter
|
||||
private List<String> countryList;
|
||||
private List<String> countryKickCommands;
|
||||
private int port;
|
||||
private boolean cacheResults, databaseEnabled, useCredentials, commandsEnabled, kickPlayers, alertToStaff,
|
||||
metrics, whitelistCountries;
|
||||
private boolean cacheResults;
|
||||
@Getter
|
||||
private boolean databaseEnabled;
|
||||
private boolean useCredentials;
|
||||
@Getter
|
||||
private boolean commandsEnabled;
|
||||
@Getter
|
||||
private boolean kickPlayers;
|
||||
private boolean alertToStaff;
|
||||
private boolean metrics;
|
||||
private boolean whitelistCountries;
|
||||
|
||||
/**
|
||||
* License from https://funkemunky.cc/shop to be used for more queries.
|
||||
* @return String
|
||||
*/
|
||||
public String getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, results will be cached to reduce queries to https://funkemunky.cc
|
||||
* If true, results will be cached to reduce queries to <a href="https://funkemunky.cc">...</a>
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean cachedResults() {
|
||||
return cacheResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be used for vanilla kick message when {@link VPNConfig#runCommands()} is true.
|
||||
* @return String
|
||||
*/
|
||||
public String getKickString() {
|
||||
return kickMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message to send staff on proxy detection.
|
||||
* @return String
|
||||
*/
|
||||
public String alertMessage() {
|
||||
return alertMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, staff will be alerted on proxy detection.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean alertToStaff() {
|
||||
public boolean isAlertToSTaff() {
|
||||
return alertToStaff;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, will run {@link VPNConfig#commands()} on detect. If not, it will use vanilla kicking methods.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean runCommands() {
|
||||
return commandsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commands to run on proxy detection.
|
||||
* @return List
|
||||
@@ -115,31 +131,6 @@ public class VPNConfig {
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* If false, no commands nor kick will be run on proxy detection.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean kickPlayersOnDetect() {
|
||||
return kickPlayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Strings of which are checked against the beginning of player names. Used to
|
||||
* allow Geyser-connected players to join.
|
||||
* @return List
|
||||
*/
|
||||
public List<String> getPrefixWhitelists() {
|
||||
return prefixWhitelists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we want to use a database
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isDatabaseEnabled() {
|
||||
return databaseEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the database we want to connect to requires credentials.
|
||||
* @return boolean
|
||||
@@ -156,59 +147,11 @@ public class VPNConfig {
|
||||
return mongoURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database type. Either MySQL and Mongo.
|
||||
* @return String
|
||||
*/
|
||||
public String getDatabaseType() {
|
||||
return databaseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database name
|
||||
* @return String
|
||||
*/
|
||||
public String getDatabaseName() {
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database username
|
||||
* @return String
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database Password
|
||||
* @return String
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database IP
|
||||
* @return String
|
||||
*/
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of ISO country codes we need to check.
|
||||
* @return List
|
||||
*/
|
||||
public List<String> countryList() {
|
||||
return countryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist them.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean whitelistCountries() {
|
||||
public boolean getWhitelistCountries() {
|
||||
return whitelistCountries;
|
||||
}
|
||||
|
||||
@@ -220,14 +163,6 @@ public class VPNConfig {
|
||||
return countryKickCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vanilla kick reason for bad country locations
|
||||
* @return String
|
||||
*/
|
||||
public String countryVanillaKickReason() {
|
||||
return countryVanillaKickReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port
|
||||
* based on {@link VPNConfig#getDatabaseType()} lowerCase().
|
||||
@@ -251,7 +186,7 @@ public class VPNConfig {
|
||||
|
||||
|
||||
/**
|
||||
* If true, https://bstats.org metrics will be collected to improve KauriVPN.
|
||||
* If true, <a href="https://bstats.org">...</a> metrics will be collected to improve KauriVPN.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean metrics() {
|
||||
@@ -287,4 +222,4 @@ public class VPNConfig {
|
||||
countryVanillaKickReason = defaultCountryKickReason.get();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+70
-40
@@ -1,6 +1,23 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.Tuple;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
@@ -9,23 +26,20 @@ import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@Getter
|
||||
public abstract class VPNExecutor {
|
||||
public static ScheduledExecutorService threadExecutor = Executors.newScheduledThreadPool(2);
|
||||
|
||||
@Getter
|
||||
private final ScheduledExecutorService threadExecutor = Executors.newScheduledThreadPool(2);
|
||||
private final Set<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
|
||||
@Getter
|
||||
private final Set<String> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Set<CIDRUtils> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Queue<Tuple<CheckResult, UUID>> toKick = new LinkedBlockingQueue<>();
|
||||
private final Queue<APIPlayer> playersToRecheck = new LinkedBlockingQueue<>();
|
||||
private ScheduledFuture<?> kickTask = null;
|
||||
|
||||
@Getter
|
||||
private final List<Tuple<CheckResult, UUID>> toKick = Collections.synchronizedList(new LinkedList<>());
|
||||
|
||||
public abstract void registerListeners();
|
||||
|
||||
@@ -42,15 +56,13 @@ public abstract class VPNExecutor {
|
||||
}
|
||||
|
||||
public void startKickChecks() {
|
||||
threadExecutor.scheduleAtFixedRate(() -> {
|
||||
kickTask = threadExecutor.scheduleAtFixedRate(() -> {
|
||||
synchronized (toKick) {
|
||||
if(toKick.isEmpty()) return;
|
||||
|
||||
Iterator<Tuple<CheckResult, UUID>> i = toKick.iterator();
|
||||
|
||||
while(i.hasNext()) {
|
||||
var toCheck = i.next();
|
||||
Tuple<CheckResult, UUID> toCheck;
|
||||
|
||||
while((toCheck = toKick.poll()) != null) {
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second());
|
||||
|
||||
if(player.isEmpty()) {
|
||||
@@ -58,48 +70,62 @@ public abstract class VPNExecutor {
|
||||
}
|
||||
|
||||
handleKickingOfPlayer(toCheck.first(), player.get());
|
||||
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}, 8, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void handleKickingOfPlayer(CheckResult result, APIPlayer player) {
|
||||
if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) AntiVPN.getInstance().getPlayerExecutor()
|
||||
|
||||
//Ensuring kick task is always running
|
||||
if(kickTask == null || kickTask.isDone() || kickTask.isCancelled()) {
|
||||
startKickChecks();
|
||||
}
|
||||
|
||||
if (AntiVPN.getInstance().getVpnConfig().isAlertToSTaff()) AntiVPN.getInstance().getPlayerExecutor()
|
||||
.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(APIPlayer::isAlertsEnabled)
|
||||
.forEach(pl ->
|
||||
pl.sendMessage(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig()
|
||||
.alertMessage(), player, result.response()))));
|
||||
.getAlertMsg(), player, result.response()))));
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
if(AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickString(), player, result.response()));
|
||||
.getKickMessage(), player, result.response()));
|
||||
case DENIED_COUNTRY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.countryVanillaKickReason(), player, result.response()));
|
||||
.getCountryVanillaKickReason(), player, result.response()));
|
||||
}
|
||||
} else {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isCommandsEnabled()) return;
|
||||
}
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().runCommands()) return;
|
||||
Runnable runCommands = () -> {
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
||||
runCommand(StringUtil.varReplace(command, player, result.response()));
|
||||
}
|
||||
}
|
||||
case DENIED_COUNTRY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
|
||||
runCommand(StringUtil.varReplace(command, player, result.response()));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
||||
runCommand(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(command, player, result.response())));
|
||||
}
|
||||
}
|
||||
case DENIED_COUNTRY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
|
||||
runCommand(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(command, player, result.response())));
|
||||
}
|
||||
}
|
||||
// Fixes the commands running too fast and causing messaging errors by any downstream plugins like LiteBans
|
||||
var scheduleResult = threadExecutor.schedule(runCommands, 1, TimeUnit.SECONDS);
|
||||
|
||||
if(scheduleResult.isCancelled()) {
|
||||
runCommands.run();
|
||||
}
|
||||
|
||||
//Ensuring players are actually kicked as they are supposed to be.
|
||||
toKick.add(new Tuple<>(result, player.getUuid()));
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
@@ -109,11 +135,15 @@ public abstract class VPNExecutor {
|
||||
return whitelisted.contains(uuid);
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(String ip) {
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(ip);
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(cidr);
|
||||
}
|
||||
try {
|
||||
return whitelistedIps.contains(new CIDRUtils(cidr));
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return whitelistedIps.contains(ip);
|
||||
}
|
||||
|
||||
public CompletableFuture<VPNResponse> checkIp(String ip) {
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Command {
|
||||
|
||||
public abstract String permission();
|
||||
|
||||
public abstract String name();
|
||||
|
||||
public abstract String[] aliases();
|
||||
|
||||
public abstract String description();
|
||||
|
||||
public abstract String usage();
|
||||
|
||||
public abstract String parent();
|
||||
|
||||
public abstract Command[] children();
|
||||
|
||||
public abstract String execute(CommandExecutor executor, String[] args);
|
||||
|
||||
public abstract List<String> tabComplete(CommandExecutor executor, String alias, String[] args);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CommandExecutor {
|
||||
|
||||
void sendMessage(String message, Object... objects);
|
||||
boolean hasPermission(String permission);
|
||||
Optional<APIPlayer> getPlayer();
|
||||
boolean isPlayer();
|
||||
|
||||
}
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AllowlistCommand extends Command {
|
||||
|
||||
private static final String[] secondArgs = new String[] {"add", "remove", "show", "search"};
|
||||
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.allowlist";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "allowlist";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"whitelist"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Add/remove players to/from exemption list.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "<add <player/uuid/ip> | remove <player/uuid/ip> | show [page] | search <query> [page]>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
if(args.length == 0 || Arrays.stream(secondArgs).noneMatch(arg -> arg.equalsIgnoreCase(args[0]))) {
|
||||
return "&cUsage: /antivpn allowlist " + usage();
|
||||
}
|
||||
|
||||
if(args[0].equalsIgnoreCase("show")) {
|
||||
// args[1] = optional page number (defaults to 1)
|
||||
int page = 1;
|
||||
if (args.length > 1) {
|
||||
try {
|
||||
page = Integer.parseInt(args[1]);
|
||||
if (page < 1) page = 1;
|
||||
} catch (NumberFormatException e) {
|
||||
page = 1;
|
||||
}
|
||||
}
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
List<UUID> uuids = databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
||||
List<CIDRUtils> ips = databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
||||
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (UUID uuid : uuids) {
|
||||
entries.add("&7- &fUUID: &e" + uuid);
|
||||
}
|
||||
for (CIDRUtils cidr : ips) {
|
||||
entries.add("&7- &fIP: &e" + cidr.getCidr());
|
||||
}
|
||||
|
||||
return buildPage(entries, page, null, "show");
|
||||
}
|
||||
|
||||
if(args[0].equalsIgnoreCase("search")) {
|
||||
// args[1..n-1] = query terms; args[n] = optional page number if last arg is an integer
|
||||
if (args.length < 2) {
|
||||
return "&cUsage: /antivpn allowlist search <query> [page]";
|
||||
}
|
||||
|
||||
// Detect optional trailing page number
|
||||
int page = 1;
|
||||
int queryEnd = args.length;
|
||||
try {
|
||||
int candidate = Integer.parseInt(args[args.length - 1]);
|
||||
if (candidate >= 1 && args.length > 2) {
|
||||
page = candidate;
|
||||
queryEnd = args.length - 1;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {}
|
||||
|
||||
String search = String.join(" ", Arrays.copyOfRange(args, 1, queryEnd)).toLowerCase();
|
||||
// Strip color code characters to prevent formatting injection in output
|
||||
String safeSearch = search.replace("&", "");
|
||||
|
||||
if (safeSearch.isEmpty()) {
|
||||
return "&cUsage: /antivpn allowlist search <query> [page]";
|
||||
}
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
List<UUID> uuids = databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelisted()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelisted());
|
||||
List<CIDRUtils> ips = databaseEnabled
|
||||
? AntiVPN.getInstance().getDatabase().getAllWhitelistedIps()
|
||||
: new ArrayList<>(AntiVPN.getInstance().getExecutor().getWhitelistedIps());
|
||||
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (UUID uuid : uuids) {
|
||||
String entry = uuid.toString();
|
||||
if (entry.toLowerCase().contains(search)) {
|
||||
entries.add("&7- &fUUID: &e" + entry);
|
||||
}
|
||||
}
|
||||
for (CIDRUtils cidr : ips) {
|
||||
String entry = cidr.getCidr();
|
||||
if (entry.toLowerCase().contains(search)) {
|
||||
entries.add("&7- &fIP: &e" + entry);
|
||||
}
|
||||
}
|
||||
|
||||
return buildPage(entries, page, safeSearch, "search " + safeSearch);
|
||||
}
|
||||
|
||||
if(args.length == 1)
|
||||
return "&cYou have to provide a player to allow or deny exemption.";
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
if(!databaseEnabled) executor.sendMessage("&cThe database is currently not setup, " +
|
||||
"so any changes here will disappear after a restart.");
|
||||
|
||||
CIDRUtils cidrUtils;
|
||||
|
||||
try {
|
||||
cidrUtils = new CIDRUtils(args[1]);
|
||||
} catch(IllegalArgumentException | UnknownHostException e) {
|
||||
cidrUtils = null;
|
||||
}
|
||||
|
||||
if(cidrUtils != null) {
|
||||
if(!databaseEnabled) {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(cidrUtils);
|
||||
yield String.format("&aAdded &6%s &ato exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
||||
yield String.format("&cRemoved &%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} else return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(cidrUtils);
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(cidrUtils);
|
||||
yield String.format("&aAdded &6%s &ato exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(cidrUtils);
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(cidrUtils);
|
||||
yield String.format("&cRemoved &6%s &cfrom the exemption allowlist.", cidrUtils.getCidr());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
}
|
||||
if(MiscUtils.isIpv4(args[1])) {
|
||||
if(!databaseEnabled) {
|
||||
try {
|
||||
return switch(args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(new CIDRUtils(args[1] + "/32"));
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&aAdded &6%s &ato the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(new CIDRUtils(args[1] + "/32"));
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&cRemoved &6%s &cfrom the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Invalid IP format for allowlist command", e);
|
||||
return "&cInvalid IP format for allowlist command";
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add", "insert" -> {
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&aAdded &6%s &a to the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(new CIDRUtils(args[1] + "/32"));
|
||||
yield String.format("&cRemoved &6%s &c from the exemption allowlist.", args[1] + "/32");
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Invalid IP format for allowlist command", e);
|
||||
return "&cInvalid IP format for allowlist command";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UUID uuid;
|
||||
try {
|
||||
uuid = UUID.fromString(args[1]);
|
||||
} catch(IllegalArgumentException e) {
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[1]);
|
||||
|
||||
if(player.isEmpty()) {
|
||||
return "&cThe player \"" + args[1] + "\" is not online, so please provide a UUID.";
|
||||
}
|
||||
|
||||
uuid = player.get().getUuid();
|
||||
}
|
||||
|
||||
if(!databaseEnabled) {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
yield String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
yield String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
} else {
|
||||
return switch (args[0].toLowerCase()) {
|
||||
case "add" -> {
|
||||
AntiVPN.getInstance().getDatabase().addWhitelist(uuid);
|
||||
yield String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
case "remove", "delete" -> {
|
||||
AntiVPN.getInstance().getDatabase().removeWhitelist(uuid);
|
||||
yield String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
default -> "&c\"" + args[0] + "\" is not a valid argument";
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return switch (args.length) {
|
||||
case 1 -> Arrays.stream(secondArgs)
|
||||
.filter(narg -> narg.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
case 2 -> {
|
||||
if (args[0].equalsIgnoreCase("show") || args[0].equalsIgnoreCase("search")) {
|
||||
yield Collections.emptyList();
|
||||
}
|
||||
yield AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.map(APIPlayer::getName)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[1].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
default -> Collections.emptyList();
|
||||
};
|
||||
}
|
||||
|
||||
private String buildPage(List<String> entries, int page, String safeSearch, String subcommandPrefix) {
|
||||
int pageSize = 10;
|
||||
int totalPages = Math.max(1, (entries.size() + pageSize - 1) / pageSize);
|
||||
if (page > totalPages) page = totalPages;
|
||||
|
||||
List<String> messages = new ArrayList<>();
|
||||
messages.add("&8&m-----------------------------------------------------");
|
||||
messages.add("&6&lAllowlist Entries &8(&7Page &f" + page + "&7/&f" + totalPages + "&8)"
|
||||
+ (safeSearch != null ? " &7(search: &f" + safeSearch + "&7)" : ""));
|
||||
messages.add("");
|
||||
|
||||
if (entries.isEmpty()) {
|
||||
messages.add(safeSearch != null
|
||||
? "&cNo allowlist entries matching &f\"" + safeSearch + "&c\" were found."
|
||||
: "&cThe allowlist is empty.");
|
||||
} else {
|
||||
int start = (page - 1) * pageSize;
|
||||
int end = Math.min(start + pageSize, entries.size());
|
||||
for (int i = start; i < end; i++) {
|
||||
messages.add(entries.get(i));
|
||||
}
|
||||
if (totalPages > 1) {
|
||||
messages.add("");
|
||||
if (page > 1) {
|
||||
messages.add("&7Previous page: &f/antivpn allowlist " + subcommandPrefix + " " + (page - 1));
|
||||
}
|
||||
if (page < totalPages) {
|
||||
messages.add("&7Next page: &f/antivpn allowlist " + subcommandPrefix + " " + (page + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
messages.add("&8&m-----------------------------------------------------");
|
||||
return String.join("\n", messages);
|
||||
}
|
||||
}
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
+18
-1
@@ -1,6 +1,23 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
|
||||
@@ -45,7 +62,7 @@ public class ClearCacheCommand extends Command {
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
AntiVPN.getInstance().getDatabase().clearResponses();
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> AntiVPN.getInstance().getDatabase().clearResponses());
|
||||
return "&aCleared all cached API response information!";
|
||||
}
|
||||
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
+17
-1
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
@@ -51,7 +67,7 @@ public class PlanCommand extends Command {
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
||||
QueryResponse result;
|
||||
try {
|
||||
if(AntiVPN.getInstance().getVpnConfig().getLicense().isEmpty()) {
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database;
|
||||
|
||||
public class DatabaseException extends RuntimeException {
|
||||
public DatabaseException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database;
|
||||
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface VPNDatabase {
|
||||
Optional<VPNResponse> getStoredResponse(String ip);
|
||||
|
||||
void cacheResponse(VPNResponse toCache);
|
||||
|
||||
void deleteResponse(String ip);
|
||||
|
||||
boolean isWhitelisted(UUID uuid);
|
||||
|
||||
boolean isWhitelisted(String cidr);
|
||||
|
||||
boolean isWhitelisted(CIDRUtils cidr);
|
||||
|
||||
void addWhitelist(UUID uuid);
|
||||
|
||||
void removeWhitelist(UUID uuid);
|
||||
|
||||
void addWhitelist(CIDRUtils cidr);
|
||||
|
||||
void removeWhitelist(CIDRUtils cidr);
|
||||
|
||||
List<UUID> getAllWhitelisted();
|
||||
|
||||
List<CIDRUtils> getAllWhitelistedIps();
|
||||
|
||||
void alertsState(UUID uuid, Consumer<Boolean> result);
|
||||
|
||||
void updateAlertsState(UUID uuid, boolean state);
|
||||
|
||||
void clearResponses();
|
||||
|
||||
void init();
|
||||
|
||||
void shutdown();
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.local;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.sql.utils.ExecutableStatement;
|
||||
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigInteger;
|
||||
import java.net.UnknownHostException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class H2VPN implements VPNDatabase {
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
|
||||
public H2VPN() {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
//Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
|
||||
//Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
}, 2, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed())
|
||||
return Optional.empty();
|
||||
|
||||
VPNResponse response = cachedResponses.get(ip, ip2 -> {
|
||||
try(ExecutableStatement statement = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip)) {
|
||||
try(ResultSet rs = statement.executeQuery()) {
|
||||
if (rs != null && rs.next()) {
|
||||
return new VPNResponse(rs.getString("asn"), rs.getString("ip"),
|
||||
rs.getString("countryName"), rs.getString("countryCode"),
|
||||
rs.getString("city"), rs.getString("timeZone"),
|
||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(), -1);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting a response for "
|
||||
+ ip, e);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return Optional.ofNullable(response);
|
||||
}
|
||||
|
||||
/*
|
||||
* Query.
|
||||
* prepare("create table if not exists `responses` (`ip` varchar(45) not null, "
|
||||
* +
|
||||
* "`countryName` varchar(64), `countryCode` varchar(10), `city` varchar(64), `timeZone` varchar(64), "
|
||||
* +
|
||||
* "`method` varchar(32), `isp` varchar(32), `proxy` boolean, `cached` boolean "
|
||||
* + "`latitude` double, `longitude` double)");
|
||||
*/
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().cachedResults()) {
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
|
||||
try(var statement = Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
||||
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName())
|
||||
.append(toCache.getCountryCode()).append(toCache.getCity()).append(toCache.getTimeZone())
|
||||
.append(toCache.getMethod()).append(toCache.getIsp()).append(toCache.isProxy())
|
||||
.append(toCache.isCached()).append(new Timestamp(System.currentTimeMillis()))
|
||||
.append(toCache.getLatitude()).append(toCache.getLongitude())) {
|
||||
statement.execute();
|
||||
} catch(SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not cache response for IP: " + toCache.getIp(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteResponse(String ip) {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
try(var statement = Query.prepare("delete from `responses` where `ip` = ?").append(ip)) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not delete response from IP: " + ip, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return false;
|
||||
try(var statement = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString())) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
return set != null && set.next() && set.getString("uuid") != null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not check whitelist for uuid '" + uuid + "' due to SQL error.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
return isWhitelisted(new CIDRUtils(cidr));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return false;
|
||||
|
||||
BigInteger start = cidr.getStartIpInt();
|
||||
BigInteger end = cidr.getEndIpInt();
|
||||
|
||||
try(var statement = Query.prepare("SELECT * FROM `whitelisted-ranges` WHERE ip_start <= ? AND ip_end >= ?")
|
||||
.append(start).append(end)) {
|
||||
|
||||
try(var result = statement.executeQuery()) {
|
||||
return result.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not check whitelist for cidr '" + cidr + "' due to SQL error.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
try(var statement = Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not add uuid '" + uuid + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
try(var statement = Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not remove uuid '" + uuid + "' from whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
try(var statement = Query.prepare("insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
||||
.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) {
|
||||
statement.execute();
|
||||
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(CIDRUtils cidr) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
try(var statement = Query.prepare("delete from `whitelisted-ranges` where `cidr_string` = ?").append(cidr.getCidr())) {
|
||||
statement.execute();
|
||||
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not remove cidr '" + cidr + "' from whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAllWhitelisted() {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return uuids;
|
||||
|
||||
try(var statement = Query.prepare("select uuid from `whitelisted`")) {
|
||||
statement.execute(set -> uuids.add(UUID.fromString(set.getString("uuid"))));
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted players due to SQL error.", e);
|
||||
}
|
||||
|
||||
return uuids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CIDRUtils> getAllWhitelistedIps() {
|
||||
List<CIDRUtils> ips = new ArrayList<>();
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return ips;
|
||||
try(var statement = Query.prepare("select `cidr_string`, `ip_start`, `ip_end` from `whitelisted-ranges`")) {
|
||||
statement.execute(set -> {
|
||||
try {
|
||||
String cidrString = set.getString("cidr_string");
|
||||
|
||||
ips.add(new CIDRUtils(cidrString));
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.logException("Could not format ip "
|
||||
+ set.getString("cidr_string") + " into a CIDR!", e);
|
||||
}
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted ips due to SQL error.", e);
|
||||
}
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
||||
|
||||
try(var statement = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString())) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting alerts state for " + uuid, e);
|
||||
result.accept(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean enabled) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
if(enabled) {
|
||||
//We want to make sure there isn't already a uuid inserted to prevent double insertions
|
||||
alertsState(uuid, alreadyEnabled -> { //No need to make another thread execute, already async
|
||||
if(!alreadyEnabled) {
|
||||
try(var statement = Query.prepare("insert into `alerts` (`uuid`) values (?)")
|
||||
.append(uuid.toString())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.logException("There was a problem updating alerts state for " + uuid, e);
|
||||
}
|
||||
} //No need to insert again of already enabled
|
||||
});
|
||||
//Removing any uuid from the alerts table will disable alerts globally.
|
||||
} else {
|
||||
try(var statement = Query.prepare("delete from `alerts` where `uuid` = ?").append(uuid.toString())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem updating alerts state for "
|
||||
+ uuid, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearResponses() {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
try(var statement = Query.prepare("delete from `responses`")) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem clearing responses.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
AntiVPN.getInstance().getExecutor().log("Initializing H2...");
|
||||
MySQL.initH2();
|
||||
try {
|
||||
for (Version<H2VPN> version : Version.h2Versions) {
|
||||
if(version.needsUpdate(this)) {
|
||||
version.update(this);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not complete version setup due to SQL error", e);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating tables...");
|
||||
|
||||
//Running check for old table types to update
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
|
||||
MySQL.shutdown();
|
||||
}
|
||||
|
||||
public void backupDatabase() {
|
||||
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases");
|
||||
|
||||
if(!dataFolder.exists() || MySQL.isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var connection = Query.getConn();
|
||||
if (connection == null || connection.getMetaData() == null
|
||||
|| !connection.getMetaData().getDatabaseProductName().equalsIgnoreCase("H2")) {
|
||||
return;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not verify database type before H2 backup.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
File backupDir = new File(dataFolder, "backups");
|
||||
if (!backupDir.exists() && !backupDir.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not create backup directory");
|
||||
return;
|
||||
}
|
||||
|
||||
File backupFile = new File(backupDir, "database.h2_backup_" + System.currentTimeMillis() + ".zip");
|
||||
String backupPath = backupFile.getAbsolutePath()
|
||||
.replace("\\", "/")
|
||||
.replace("'", "''");
|
||||
|
||||
try (var statement = Query.prepare("BACKUP TO '" + backupPath + "'")) {
|
||||
statement.execute();
|
||||
AntiVPN.getInstance().getExecutor().log("Created H2 backup at " + backupFile.getName());
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not create H2 backup before migration.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.local.version;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.sql.utils.ExecutableStatement;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class First implements Version<VPNDatabase> {
|
||||
|
||||
private final List<AutoCloseable> toClose = new ArrayList<>();
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
try {
|
||||
closeOnEnd(Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(Query
|
||||
.prepare("create table if not exists `responses` (`ip` varchar(45) not null, `asn` varchar(12),"
|
||||
+ "`countryName` text, `countryCode` varchar(10), `city` text, `timeZone` varchar(64), "
|
||||
+ "`method` varchar(32), `isp` text, `proxy` boolean, `cached` boolean, `inserted` timestamp,"
|
||||
+ "`latitude` double, `longitude` double)")).execute();
|
||||
closeOnEnd(Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)"))
|
||||
.execute();
|
||||
closeOnEnd(Query.prepare("create table if not exists `database_version` (`version` int)")).execute();
|
||||
closeOnEnd(Query.prepare("insert into `database_version` (`version`) values (?)")
|
||||
.append(versionNumber())).execute();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating indexes...");
|
||||
createIndexIfAbsent("whitelisted", "uuid_1", "`uuid`");
|
||||
createIndexIfAbsent("responses", "ip_1", "`ip`");
|
||||
createIndexIfAbsent("responses", "proxy_1", "`proxy`");
|
||||
createIndexIfAbsent("responses", "inserted_1", "`inserted`");
|
||||
createIndexIfAbsent("whitelisted-ips", "ip_1", "`ip`");
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Failed to update database", e);
|
||||
} finally {
|
||||
MiscUtils.close(toClose.toArray(AutoCloseable[]::new));
|
||||
toClose.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutableStatement closeOnEnd(ExecutableStatement statement) {
|
||||
toClose.add(statement);
|
||||
return statement;
|
||||
}
|
||||
|
||||
protected void createIndexIfAbsent(String tableName, String indexName, String columnList) throws SQLException {
|
||||
if (hasIndex(tableName, indexName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeOnEnd(Query.prepare(String.format(
|
||||
"create index `%s` on `%s` (%s)",
|
||||
indexName,
|
||||
tableName,
|
||||
columnList
|
||||
))).execute();
|
||||
}
|
||||
|
||||
protected void dropIndexIfPresent(String tableName, String indexName) throws SQLException {
|
||||
if (!hasIndex(tableName, indexName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeOnEnd(Query.prepare(String.format(
|
||||
"drop index `%s` on `%s`",
|
||||
indexName,
|
||||
tableName
|
||||
))).execute();
|
||||
}
|
||||
|
||||
protected boolean hasIndex(String tableName, String indexName) throws SQLException {
|
||||
DatabaseMetaData metaData = Query.getConn().getMetaData();
|
||||
|
||||
try (ResultSet indexes = metaData.getIndexInfo(null, null, tableName, false, false)) {
|
||||
while (indexes.next()) {
|
||||
String existingIndexName = indexes.getString("INDEX_NAME");
|
||||
|
||||
if (existingIndexName != null && existingIndexName.equalsIgnoreCase(indexName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try(var statement = Query.prepare("select * from `database_version` where version = 0")) {
|
||||
try(ResultSet set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.local.version;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.database.sql.utils.ExecutableStatement;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Second extends First implements Version<VPNDatabase> {
|
||||
private final List<AutoCloseable> toClose = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
if(database instanceof H2VPN h2VPN && !(database instanceof MySqlVPN)) {
|
||||
h2VPN.backupDatabase();
|
||||
}
|
||||
List<String> whitelistedIps = new ArrayList<>();
|
||||
|
||||
try (var statement = Query.prepare("SELECT * FROM `whitelisted-ips`")) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
whitelistedIps.add(set.getString("ip"));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Could not get whitelisted ips from database!", e);
|
||||
}
|
||||
|
||||
try {
|
||||
closeOnEnd(Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ranges` " +
|
||||
"(id INT AUTO_INCREMENT PRIMARY KEY, " +
|
||||
"cidr_string VARCHAR(45), " +
|
||||
"ip_start BIGINT NOT NULL, " +
|
||||
"ip_end BIGINT NOT NULL)"))
|
||||
.execute();
|
||||
createIndexIfAbsent("whitelisted-ranges", "idx_ip_range", "ip_start, ip_end");
|
||||
|
||||
var cidrs = whitelistedIps.stream().map(ip -> {
|
||||
try {
|
||||
return new CIDRUtils(ip + "/32");
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException("Could not format ip " + ip + " into a CIDR!", e);
|
||||
}
|
||||
}).toList();
|
||||
var insertStatement = Query.prepare("INSERT INTO `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) VALUES (?, ?, ?)");
|
||||
for (CIDRUtils cidr : cidrs) {
|
||||
insertStatement = insertStatement
|
||||
.append(cidr.toString())
|
||||
.append(cidr.getStartIpInt())
|
||||
.append(cidr.getEndIpInt())
|
||||
.addBatch();
|
||||
}
|
||||
|
||||
int[] updateCounts = insertStatement.executeBatch();
|
||||
|
||||
for (int updateCount : updateCounts) {
|
||||
if(updateCount == 0) {
|
||||
throw new RuntimeException("Could not insert a CIDR from previous whitelisted lists, attempted to restore previous database!");
|
||||
}
|
||||
}
|
||||
|
||||
dropIndexIfPresent("whitelisted-ips", "ip_1");
|
||||
closeOnEnd(Query.prepare("DROP TABLE `whitelisted-ips`")).execute();
|
||||
closeOnEnd(Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)").append(versionNumber())).execute();
|
||||
} catch (Throwable e) {
|
||||
AntiVPN.getInstance().getExecutor().log("Failed to update database to version 1: " + e.getMessage());
|
||||
try {
|
||||
rollback(whitelistedIps);
|
||||
} catch (SQLException ex) {
|
||||
throw new DatabaseException("Failed to rollback database!", e);
|
||||
}
|
||||
throw new DatabaseException("Failed to update to version one, rolling back database!", e);
|
||||
} finally {
|
||||
MiscUtils.close(toClose.toArray(AutoCloseable[]::new));
|
||||
toClose.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ExecutableStatement closeOnEnd(ExecutableStatement statement) {
|
||||
toClose.add(statement);
|
||||
return statement;
|
||||
}
|
||||
|
||||
private void rollback(List<String> ipAddresses) throws SQLException {
|
||||
AntiVPN.getInstance().getExecutor().log("Rolling back to version 0...");
|
||||
dropIndexIfPresent("whitelisted-ranges", "idx_ip_range");
|
||||
try(var statement = Query.prepare("DROP TABLE `whitelisted-ranges`")) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
try(var statement = Query.prepare("DELETE FROM `database_version` WHERE version = ?").append(versionNumber())) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
try(var statement = Query.prepare("CREATE TABLE IF NOT EXISTS `whitelisted-ips` (`ip` VARCHAR(45) NOT NULL)")) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
createIndexIfAbsent("whitelisted-ips", "ip_1", "`ip`");
|
||||
|
||||
try(var statement = Query.prepare("DELETE FROM `whitelisted-ips`")) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
try(var statement = Query.prepare("INSERT INTO `whitelisted-ips` (`ip`) VALUES (?)")) {
|
||||
for (String ip : ipAddresses) {
|
||||
statement.append(ip);
|
||||
statement.addBatch();
|
||||
}
|
||||
|
||||
statement.executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try (var statement = Query.prepare("select * from `database_version` where version = 1")) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.local.version;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.UnknownHostException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Third implements Version<VPNDatabase> {
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
List<CIDRUtils> ipRanges = new ArrayList<>();
|
||||
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
||||
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
||||
try (var preparedQuery = Query.prepare("select ip_start, ip_end from `whitelisted-ranges`")) {
|
||||
preparedQuery.execute(set -> {
|
||||
BigInteger start = set.getBigDecimal("ip_start").toBigInteger();
|
||||
BigInteger end = set.getBigDecimal("ip_end").toBigInteger();
|
||||
|
||||
try {
|
||||
var range = MiscUtils.rangeToCidrs(start, end);
|
||||
|
||||
if(range.size() > 1) {
|
||||
rangesToRemove.add(new BigInteger[]{start, end});
|
||||
rangesToInsert.addAll(range);
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "Found multiple CIDR ranges for whitelist range for %s, %s!", start, end);
|
||||
} else ipRanges.addAll(range);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(
|
||||
String.format("Could not convert ip range to CIDR! %s, %s", start, end), e);
|
||||
}
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not get all whitelisted ranges due to SQL error.", e);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Inserting %s new ranges into database...", rangesToInsert.size());
|
||||
|
||||
for (CIDRUtils cidr : rangesToInsert) {
|
||||
try(var statement = Query.prepare("insert into `whitelisted-ranges` (`cidr_string`, `ip_start`, `ip_end`) values (?, ?, ?)")
|
||||
.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt())) {
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not add cidr '" + cidr + "' to whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Removing %s old ranges from database...", rangesToRemove.size());
|
||||
|
||||
for (BigInteger[] range : rangesToRemove) {
|
||||
try(var statement = Query.prepare("delete from `whitelisted-ranges` where `ip_start` = ? and `ip_end` = ?")) {
|
||||
statement.append(range[0]).append(range[1]).execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not remove cidr range '" + range[0] + ", " + range[1] + "' from whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Updating %s ranges to proper CIDR notation with the database", ipRanges.size());
|
||||
|
||||
for (CIDRUtils cidr : ipRanges) {
|
||||
try(var statement = Query.prepare("update `whitelisted-ranges` set `cidr_string` = ? where `ip_start` = ? and `ip_end` = ?")) {
|
||||
statement.append(cidr.getCidr()).append(cidr.getStartIpInt()).append(cidr.getEndIpInt()).execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not update cidr '" + cidr + "' to proper CIDR notation in whitelist due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
try (var preparedStatement = Query.prepare("INSERT INTO `database_version` (`version`) VALUES (?)").append(versionNumber())) {
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not update database version to 2 due to SQL error.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(VPNDatabase database) {
|
||||
try (var statement = Query.prepare("select * from `database_version` where version = 2")) {
|
||||
try(var set = statement.executeQuery()) {
|
||||
return !set.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+114
-82
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.mongo;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
@@ -8,23 +24,28 @@ import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.Indexes;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MongoVPN implements VPNDatabase {
|
||||
|
||||
private MongoCollection<Document> settingsDocument, cacheDocument;
|
||||
public MongoCollection<Document> settingsDocument;
|
||||
MongoCollection<Document> cacheDocument;
|
||||
private MongoClient client;
|
||||
public MongoDatabase antivpnDatabase;
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
@@ -32,7 +53,7 @@ public class MongoVPN implements VPNDatabase {
|
||||
.build();
|
||||
|
||||
public MongoVPN() {
|
||||
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||
|
||||
//Refreshing whitelisted players
|
||||
@@ -55,7 +76,7 @@ public class MongoVPN implements VPNDatabase {
|
||||
long lastUpdate = rdoc.get("lastAccess", 0L);
|
||||
|
||||
if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) {
|
||||
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> deleteResponse(ip));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -83,29 +104,31 @@ public class MongoVPN implements VPNDatabase {
|
||||
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
Document rdoc = new Document("ip", toCache.getIp());
|
||||
if(AntiVPN.getInstance().getVpnConfig().cachedResults()) {
|
||||
Document rdoc = new Document("ip", toCache.getIp());
|
||||
|
||||
rdoc.put("asn", toCache.getAsn());
|
||||
rdoc.put("countryName", toCache.getCountryName());
|
||||
rdoc.put("countryCode", toCache.getCountryCode());
|
||||
rdoc.put("city", toCache.getCity());
|
||||
rdoc.put("isp", toCache.getIsp());
|
||||
rdoc.put("method", toCache.getMethod());
|
||||
rdoc.put("timeZone", toCache.getTimeZone());
|
||||
rdoc.put("proxy", toCache.isProxy());
|
||||
rdoc.put("cached", toCache.isCached());
|
||||
rdoc.put("success", toCache.isSuccess());
|
||||
rdoc.put("latitude", toCache.getLatitude());
|
||||
rdoc.put("longitude", toCache.getLongitude());
|
||||
rdoc.put("lastAccess", System.currentTimeMillis());
|
||||
rdoc.put("asn", toCache.getAsn());
|
||||
rdoc.put("countryName", toCache.getCountryName());
|
||||
rdoc.put("countryCode", toCache.getCountryCode());
|
||||
rdoc.put("city", toCache.getCity());
|
||||
rdoc.put("isp", toCache.getIsp());
|
||||
rdoc.put("method", toCache.getMethod());
|
||||
rdoc.put("timeZone", toCache.getTimeZone());
|
||||
rdoc.put("proxy", toCache.isProxy());
|
||||
rdoc.put("cached", toCache.isCached());
|
||||
rdoc.put("success", toCache.isSuccess());
|
||||
rdoc.put("latitude", toCache.getLatitude());
|
||||
rdoc.put("longitude", toCache.getLongitude());
|
||||
rdoc.put("lastAccess", System.currentTimeMillis());
|
||||
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
Bson update = new Document("$set", rdoc);
|
||||
cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update,
|
||||
new UpdateOptions().upsert(true));
|
||||
});
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
||||
Bson update = new Document("$set", rdoc);
|
||||
cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update,
|
||||
new UpdateOptions().upsert(true));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,42 +144,57 @@ public class MongoVPN implements VPNDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(String ip) {
|
||||
return settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip", ip))).first() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(UUID uuid, boolean whitelisted) {
|
||||
if(whitelisted) {
|
||||
Document wdoc = new Document("setting", "whitelist");
|
||||
wdoc.put("uuid", uuid.toString());
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
|
||||
.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("uuid", uuid.toString()))));
|
||||
public boolean isWhitelisted(String cidr) {
|
||||
try {
|
||||
return isWhitelisted(new CIDRUtils(cidr));
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().log("Failed to check whitelist for IP: " + cidr, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(String ip, boolean whitelisted) {
|
||||
if(whitelisted) {
|
||||
Document wdoc = new Document("setting", "whitelist").append("ip", ip);
|
||||
public boolean isWhitelisted(CIDRUtils cidr) {
|
||||
var start = new Decimal128(new BigDecimal(cidr.getStartIpInt()));
|
||||
var end = new Decimal128(new BigDecimal(cidr.getEndIpInt()));
|
||||
return settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.lte("ip_start", start), Filters.gte("ip_end", end))).first() != null;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip);
|
||||
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip);
|
||||
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
|
||||
.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip", ip))));
|
||||
}
|
||||
@Override
|
||||
public void addWhitelist(UUID uuid) {
|
||||
Document wdoc = new Document("setting", "whitelist");
|
||||
wdoc.put("uuid", uuid.toString());
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
settingsDocument.insertOne(wdoc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(UUID uuid) {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
settingsDocument.deleteMany(Filters
|
||||
.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("uuid", uuid.toString())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhitelist(CIDRUtils cidr) {
|
||||
Document doc = new Document("setting", "whitelist");
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.getCidr());
|
||||
|
||||
settingsDocument.insertOne(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWhitelist(CIDRUtils cidr) {
|
||||
settingsDocument.deleteMany(Filters
|
||||
.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
||||
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,39 +207,30 @@ public class MongoVPN implements VPNDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllWhitelistedIps() {
|
||||
List<String> ips = new ArrayList<>();
|
||||
public List<CIDRUtils> getAllWhitelistedIps() {
|
||||
List<CIDRUtils> ips = new ArrayList<>();
|
||||
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.exists("ip")))
|
||||
.forEach((Consumer<? super Document>) doc -> ips.add(doc.getString("ip")));
|
||||
Filters.exists("cidr_string"))).forEach((Consumer<? super Document>) doc -> {
|
||||
try {
|
||||
var cidr = new CIDRUtils(doc.getString("cidr_string"));
|
||||
ips.add(cidr);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not format ip " + doc.getString("cidr_string") + " into a CIDR!", e);
|
||||
}
|
||||
});
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getStoredResponseAsync(String ip, Consumer<Optional<VPNResponse>> result) {
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isWhitelistedAsync(UUID uuid, Consumer<Boolean> result) {
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isWhitelistedAsync(String ip, Consumer<Boolean> result) {
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(settingsDocument
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> result.accept(settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "alerts"),
|
||||
Filters.eq("uuid", uuid.toString()))).first() != null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean state) {
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().execute(() -> {
|
||||
settingsDocument.deleteMany(Filters.and(Filters.eq("setting", "alerts"),
|
||||
Filters.eq("uuid", uuid.toString())));
|
||||
if(state) {
|
||||
@@ -241,14 +270,17 @@ public class MongoVPN implements VPNDatabase {
|
||||
|
||||
client = MongoClients.create(settingsBld.build());
|
||||
}
|
||||
MongoDatabase antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getVpnConfig().getDatabaseName());
|
||||
antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getVpnConfig().getDatabaseName());
|
||||
|
||||
settingsDocument = antivpnDatabase.getCollection("settings");
|
||||
if(settingsDocument.listIndexes().first() == null) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created index for settings collection!");
|
||||
settingsDocument.createIndex(Indexes.ascending("ip"));
|
||||
}
|
||||
|
||||
cacheDocument = antivpnDatabase.getCollection("cache");
|
||||
|
||||
for (Version<MongoVPN> mongoDbVersion : Version.mongoDbVersions) {
|
||||
if(mongoDbVersion.needsUpdate(this)) {
|
||||
mongoDbVersion.update(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.mongo.version;
|
||||
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.Indexes;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import org.bson.Document;
|
||||
|
||||
public class MongoFirst implements Version<MongoVPN> {
|
||||
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
if(database.settingsDocument.listIndexes().first() == null) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created index for settings collection!");
|
||||
database.settingsDocument.createIndex(Indexes.ascending("ip"));
|
||||
database.settingsDocument.createIndex(Indexes.ascending("setting"));
|
||||
}
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.mongo.version;
|
||||
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.Indexes;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MongoSecond implements Version<MongoVPN> {
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
List<Document> backup = new ArrayList<>();
|
||||
database.settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.exists("ip")))
|
||||
.forEach((Consumer<? super Document>) doc -> {
|
||||
backup.add(new Document(doc));
|
||||
|
||||
String ip = doc.getString("ip");
|
||||
|
||||
try {
|
||||
var cidr = new CIDRUtils(ip + "/32");
|
||||
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.toString());
|
||||
doc.remove("ip");
|
||||
|
||||
database.settingsDocument.replaceOne(Filters.eq("_id", doc.getObjectId("_id")), doc);
|
||||
} catch (UnknownHostException e) {
|
||||
rollback(backup, database);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
database.settingsDocument.createIndex(Indexes.compoundIndex(Indexes.ascending("ip_start"), Indexes.ascending("ip_end")));
|
||||
database.settingsDocument.createIndex(Indexes.ascending("cidr_string"));
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
|
||||
private void rollback(List<Document> toRollback, MongoVPN database) {
|
||||
AntiVPN.getInstance().getExecutor().log("Rolling back to version 0...");
|
||||
toRollback.forEach(doc -> database.settingsDocument.replaceOne(Filters.eq("_id", doc.getObjectId("_id")), doc));
|
||||
toRollback.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.mongo.version;
|
||||
|
||||
import com.mongodb.client.model.Filters;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
import dev.brighten.antivpn.utils.CIDRUtils;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import org.bson.Document;
|
||||
import org.bson.types.Decimal128;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class MongoThird implements Version<MongoVPN> {
|
||||
@Override
|
||||
public void update(MongoVPN database) throws DatabaseException {
|
||||
List<CIDRUtils> ipRanges = new ArrayList<>();
|
||||
List<CIDRUtils> rangesToInsert = new ArrayList<>();
|
||||
List<BigInteger[]> rangesToRemove = new ArrayList<>();
|
||||
database.settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"), Filters.exists("cidr_string")))
|
||||
.forEach((Consumer<? super Document>) doc -> {
|
||||
BigInteger start = doc.get("ip_start", Decimal128.class).bigDecimalValue().toBigInteger();
|
||||
BigInteger end = doc.get("ip_end", Decimal128.class).bigDecimalValue().toBigInteger();
|
||||
|
||||
try {
|
||||
var range = MiscUtils.rangeToCidrs(start, end);
|
||||
|
||||
if(range.size() > 1) {
|
||||
rangesToRemove.add(new BigInteger[]{start, end});
|
||||
rangesToInsert.addAll(range);
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "Found multiple CIDR ranges for whitelist range for %s, %s!", start, end);
|
||||
} else ipRanges.addAll(range);
|
||||
} catch (UnknownHostException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(
|
||||
String.format("Could not convert ip range to CIDR! %s, %s", start, end), e);
|
||||
}
|
||||
});
|
||||
|
||||
if(!rangesToInsert.isEmpty()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Inserting %s new ranges into database...", rangesToInsert.size());
|
||||
var documentsToInsert = rangesToInsert.stream().map(cidr -> {
|
||||
Document doc = new Document("setting", "whitelist");
|
||||
doc.append("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt())));
|
||||
doc.append("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())));
|
||||
doc.append("cidr_string", cidr.getCidr());
|
||||
|
||||
return doc;
|
||||
}).toList();
|
||||
|
||||
database.settingsDocument.insertMany(documentsToInsert);
|
||||
}
|
||||
if(!rangesToRemove.isEmpty()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Removing %s old ranges from database...", rangesToRemove.size());
|
||||
rangesToRemove.forEach(range -> database.settingsDocument
|
||||
.deleteMany(Filters.and(
|
||||
Filters.gte("ip_start", new Decimal128(new BigDecimal(range[0]))),
|
||||
Filters.lte("ip_end", new Decimal128(new BigDecimal(range[1]))))));
|
||||
}
|
||||
|
||||
if(!ipRanges.isEmpty()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Updating %s CIDRs in database with proper notation...", ipRanges.size());
|
||||
|
||||
ipRanges.forEach(cidr -> database.settingsDocument
|
||||
.updateMany(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip_start", new Decimal128(new BigDecimal(cidr.getStartIpInt()))),
|
||||
Filters.eq("ip_end", new Decimal128(new BigDecimal(cidr.getEndIpInt())))),
|
||||
new Document("$set", new Document("cidr_string", cidr.getCidr()))));
|
||||
}
|
||||
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
versionCollect.insertOne(new Document("version", versionNumber()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int versionNumber() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsUpdate(MongoVPN database) {
|
||||
var versionCollect = database.antivpnDatabase.getCollection("version");
|
||||
|
||||
return versionCollect.find(Filters.eq("version", versionNumber())).first() == null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.sql;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
||||
import dev.brighten.antivpn.database.version.Version;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MySqlVPN extends H2VPN {
|
||||
|
||||
public MySqlVPN() {
|
||||
AntiVPN.getInstance().getExecutor().getThreadExecutor().scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
//Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
|
||||
//Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
}, 2, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
AntiVPN.getInstance().getExecutor().log("Initializing MySQL...");
|
||||
MySQL.init();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Checking for updates...");
|
||||
|
||||
//Running check for old table types to update
|
||||
try {
|
||||
for (Version<MySqlVPN> version : Version.mysqlVersions) {
|
||||
if(version.needsUpdate(this)) {
|
||||
version.update(this);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not complete version setup due to SQL error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-29
@@ -1,53 +1,51 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ExecutableStatement {
|
||||
private PreparedStatement statement;
|
||||
public class ExecutableStatement implements AutoCloseable {
|
||||
@Getter
|
||||
private final PreparedStatement statement;
|
||||
private int pos = 1;
|
||||
|
||||
public ExecutableStatement(PreparedStatement statement) {
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public Integer execute() {
|
||||
try {
|
||||
return statement.executeUpdate();
|
||||
} finally {
|
||||
MiscUtils.close(statement);
|
||||
}
|
||||
public int execute() throws SQLException {
|
||||
return statement.executeUpdate();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void execute(ResultSetIterator iterator) {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
rs = statement.executeQuery();
|
||||
public void execute(ResultSetIterator iterator) throws SQLException {
|
||||
try(var rs = statement.executeQuery()) {
|
||||
while (rs.next()) iterator.next(rs);
|
||||
} finally {
|
||||
MiscUtils.close(statement, rs);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void executeSingle(ResultSetIterator iterator) {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
rs = statement.executeQuery();
|
||||
if (rs.next()) iterator.next(rs);
|
||||
else iterator.next(null);
|
||||
} finally {
|
||||
MiscUtils.close(statement, rs);
|
||||
}
|
||||
public int[] executeBatch() throws SQLException {
|
||||
return statement.executeBatch();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ResultSet executeQuery() {
|
||||
public ResultSet executeQuery() throws SQLException {
|
||||
return statement.executeQuery();
|
||||
}
|
||||
|
||||
@@ -135,4 +133,15 @@ public class ExecutableStatement {
|
||||
statement.setBytes(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement addBatch() {
|
||||
statement.addBatch();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
+74
-41
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import com.mysql.cj.jdbc.Driver;
|
||||
@@ -9,7 +25,6 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
@@ -20,12 +35,17 @@ public class MySQL {
|
||||
public static void init() {
|
||||
try {
|
||||
if (conn == null || conn.isClosed()) {
|
||||
DriverManager.registerDriver(new Driver());
|
||||
conn = DriverManager.getConnection("jdbc:mysql://" + AntiVPN.getInstance().getVpnConfig().getIp()
|
||||
+ ":" + AntiVPN.getInstance().getVpnConfig().getPort()
|
||||
+ "/?useSSL=true&autoReconnect=true",
|
||||
AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPassword());
|
||||
String url = "jdbc:mysql://" + AntiVPN.getInstance().getVpnConfig().getIp()
|
||||
+ ":" + AntiVPN.getInstance().getVpnConfig().getPort()
|
||||
+ "/?useSSL=true&autoReconnect=true";
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("user", AntiVPN.getInstance().getVpnConfig().getUsername());
|
||||
properties.setProperty("password", AntiVPN.getInstance().getVpnConfig().getPassword());
|
||||
|
||||
conn = new Driver().connect(url, properties);
|
||||
if (conn == null) {
|
||||
throw new SQLException("MySQL driver did not accept URL: " + url);
|
||||
}
|
||||
conn.setAutoCommit(true);
|
||||
Query.use(conn);
|
||||
Query.prepare("CREATE DATABASE IF NOT EXISTS `"
|
||||
@@ -35,17 +55,15 @@ public class MySQL {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to load mysql: " + e.getMessage(), e);
|
||||
throw new RuntimeException("Could not initialize MySQL connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean didRetry = false;
|
||||
|
||||
public static void initH2() {
|
||||
if(didRetry) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING,
|
||||
"Already attempted to retry H2 connection, skipping.");
|
||||
return;
|
||||
}
|
||||
initH2(true);
|
||||
}
|
||||
|
||||
private static void initH2(boolean allowRetry) {
|
||||
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases");
|
||||
if (!dataFolder.exists() && dataFolder.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created database directory");
|
||||
@@ -69,9 +87,13 @@ public class MySQL {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.log("H2 database file is incompatible with this version of AntiVPN. " +
|
||||
"Backing up old database file...");
|
||||
backupOldDB(dbFile, dataFolder);
|
||||
initH2();
|
||||
didRetry = true;
|
||||
shutdown();
|
||||
if (allowRetry && backupOldDB(dbFile, dataFolder)) {
|
||||
initH2(false);
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log(
|
||||
"Could not back up and remove the incompatible H2 database file automatically.");
|
||||
}
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + ex.getCause().toString(), ex);
|
||||
}
|
||||
@@ -81,31 +103,42 @@ public class MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
private static void backupOldDB(File dbFile, File dataFolder) {
|
||||
if (dbFile.exists()) {
|
||||
try {
|
||||
// Optional: Make backup first
|
||||
File backupDir = new File(dataFolder, "backups");
|
||||
if(backupDir.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created backup directory");
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log("Backup directory already exists");
|
||||
}
|
||||
File backupFile = new File(backupDir, "database.mv.db.backup_" + System.currentTimeMillis());
|
||||
Files.copy(dbFile.toPath(), backupFile.toPath());
|
||||
|
||||
// Actually delete the file
|
||||
if (!dbFile.delete()) {
|
||||
// If normal delete fails, try force delete on JVM exit
|
||||
dbFile.deleteOnExit();
|
||||
AntiVPN.getInstance().getExecutor().log("Could not delete database file - will try again on shutdown");
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully deleted incompatible database file");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to handle database file", ex);
|
||||
}
|
||||
public static boolean backupOldDB(File dbFile, File dataFolder) {
|
||||
if (!dbFile.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!dbFile.isFile()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Skipping backup for non-file path: " + dbFile.getAbsolutePath());
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
File backupDir = new File(dataFolder, "backups");
|
||||
if(backupDir.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created backup directory");
|
||||
} else if (backupDir.exists()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Backup directory already exists");
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not create backup directory");
|
||||
return false;
|
||||
}
|
||||
File backupFile = new File(backupDir, dbFile.getName() + ".backup_" + System.currentTimeMillis());
|
||||
Files.copy(dbFile.toPath(), backupFile.toPath());
|
||||
|
||||
if (!dbFile.delete()) {
|
||||
dbFile.deleteOnExit();
|
||||
AntiVPN.getInstance().getExecutor().log("Could not delete database file - will try again on shutdown");
|
||||
return false;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully deleted incompatible database file");
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to handle database file", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void use() {
|
||||
+10
-19
@@ -1,26 +1,17 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class Query {
|
||||
@Getter
|
||||
private static Connection conn;
|
||||
|
||||
public static void use(Connection conn) {
|
||||
Query.conn = conn;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SqlSourceToSinkFlow")
|
||||
public static ExecutableStatement prepare(@Language("SQL") String sql) throws SQLException {
|
||||
return new ExecutableStatement(conn.prepareStatement(sql));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public interface ResultSetIterator {
|
||||
void next(ResultSet rs) throws SQLException;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.sql.version;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.version.First;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class MySQLFirst extends First {
|
||||
|
||||
@Override
|
||||
public void update(VPNDatabase database) throws DatabaseException {
|
||||
try(var statement = Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " +
|
||||
"WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';")) {
|
||||
statement.execute(set -> {
|
||||
if(set.getObject("DATA_TYPE").toString().contains("varchar")) {
|
||||
AntiVPN.getInstance().getExecutor().log("Using old database format for storing responses! " +
|
||||
"Dropping table and creating a new one...");
|
||||
try(var state = Query.prepare("drop table `responses`")) {
|
||||
if(state.execute() > 0) {
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
} catch (SQLException e) {
|
||||
throw new DatabaseException("Could not update MySQL database", e);
|
||||
}
|
||||
super.update(database);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.version;
|
||||
|
||||
import dev.brighten.antivpn.database.DatabaseException;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.local.version.First;
|
||||
import dev.brighten.antivpn.database.local.version.Second;
|
||||
import dev.brighten.antivpn.database.local.version.Third;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.mongo.version.MongoFirst;
|
||||
import dev.brighten.antivpn.database.mongo.version.MongoSecond;
|
||||
import dev.brighten.antivpn.database.mongo.version.MongoThird;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.database.sql.version.MySQLFirst;
|
||||
|
||||
|
||||
public interface Version<DB> {
|
||||
void update(DB database) throws DatabaseException;
|
||||
int versionNumber();
|
||||
boolean needsUpdate(DB database);
|
||||
|
||||
Version<MongoVPN>[] mongoDbVersions = new Version[] {new MongoFirst(), new MongoSecond(), new MongoThird()};
|
||||
Version<MySqlVPN>[] mysqlVersions = new Version[] {new MySQLFirst(), new Second(), new Third()};
|
||||
Version<H2VPN>[] h2Versions = new Version[] {new First(), new Second(), new Third()};
|
||||
}
|
||||
@@ -0,0 +1,864 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.NonnullByDefault;
|
||||
import dev.brighten.antivpn.utils.Supplier;
|
||||
import dev.brighten.antivpn.utils.Suppliers;
|
||||
import lombok.Getter;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.RecordComponentVisitor;
|
||||
import org.objectweb.asm.commons.ClassRemapper;
|
||||
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
|
||||
* into the classloader.
|
||||
*/
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
@NonnullByDefault
|
||||
public final class LibraryLoader {
|
||||
private static final int RELOCATION_FORMAT_VERSION = 5;
|
||||
private static final String RELOCATION_METADATA_PATH = "META-INF/antivpn-relocation.properties";
|
||||
|
||||
@SuppressWarnings("Guava")
|
||||
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = AntiVPN.getInstance().getClass().getClassLoader() instanceof URLClassLoader ?
|
||||
Suppliers.memoize(() ->
|
||||
URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader()))
|
||||
: null;
|
||||
|
||||
public static void loadAll(Object object) {
|
||||
if(URL_INJECTOR == null)
|
||||
return;
|
||||
loadAll(object.getClass());
|
||||
}
|
||||
|
||||
public static void loadAll(Class<?> clazz) {
|
||||
if(URL_INJECTOR == null)
|
||||
return;
|
||||
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
|
||||
|
||||
for (MavenLibrary lib : libs) {
|
||||
// Create relocations map if any are defined
|
||||
Map<String, String> relocations = new HashMap<>();
|
||||
for (Relocate relocate : lib.relocations()) {
|
||||
relocations.put(relocate.from().replace("\\", ""), relocate.to());
|
||||
}
|
||||
|
||||
load(lib.groupId().replace("\\", ""), lib.artifactId(), lib.version(), lib.repo().url(), relocations);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(String groupId, String artifactId, String version, String repoUrl,
|
||||
Map<String, String> relocations) {
|
||||
load(new Dependency(groupId, artifactId, version, repoUrl), relocations);
|
||||
}
|
||||
|
||||
public static void load(Dependency d, Map<String, String> relocations) {
|
||||
System.out.printf("Loading dependency %s:%s:%s from %s%n",
|
||||
d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl());
|
||||
String name = d.getArtifactId() + "-" + d.getVersion();
|
||||
|
||||
// If we have relocations, add a suffix to identify the relocated version
|
||||
String fileName = name + ".jar";
|
||||
if (!relocations.isEmpty()) {
|
||||
fileName = name + "-relocated.jar";
|
||||
}
|
||||
|
||||
File saveLocation = new File(getLibFolder(), fileName);
|
||||
File originalJar = new File(getLibFolder(), name + ".jar");
|
||||
|
||||
// Download the original jar if it doesn't exist
|
||||
if (!originalJar.exists()) {
|
||||
try {
|
||||
System.out.println("Dependency '" + name +
|
||||
"' is not already in the libraries folder. Attempting to download...");
|
||||
URL url = d.getUrl();
|
||||
|
||||
try (InputStream is = url.openStream()) {
|
||||
Files.copy(is, originalJar.toPath());
|
||||
}
|
||||
System.out.println("Dependency '" + name + "' successfully downloaded.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Unable to download dependency: " + d, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild relocated jars when the relocation format changes or the cached jar is stale.
|
||||
if (!relocations.isEmpty() && shouldRebuildRelocatedJar(saveLocation, relocations)) {
|
||||
try {
|
||||
System.out.println("Relocating packages for " + name + "...");
|
||||
relocateJar(originalJar, saveLocation, relocations);
|
||||
System.out.println("Successfully relocated packages for " + name);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Failed to relocate packages for dependency: " + d, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the appropriate jar (original or relocated)
|
||||
File jarToLoad = relocations.isEmpty() ? originalJar : saveLocation;
|
||||
|
||||
if (!jarToLoad.exists()) {
|
||||
throw new RuntimeException("Unable to find dependency jar: " + jarToLoad.getAbsolutePath());
|
||||
}
|
||||
|
||||
try {
|
||||
URL_INJECTOR.get().addURL(jarToLoad.toURI().toURL());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to load dependency: " + jarToLoad, e);
|
||||
}
|
||||
|
||||
System.out.println("Loaded dependency '" + name + "' successfully.");
|
||||
}
|
||||
|
||||
private static void relocateJar(File sourceJar, File targetJar, Map<String, String> relocations)
|
||||
throws IOException {
|
||||
// Track service files to avoid duplicates
|
||||
Map<String, StringBuilder> serviceFiles = new HashMap<>();
|
||||
|
||||
Files.deleteIfExists(targetJar.toPath());
|
||||
|
||||
try (JarFile jar = new JarFile(sourceJar);
|
||||
JarOutputStream jos = new JarOutputStream(Files.newOutputStream(targetJar.toPath()))) {
|
||||
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
|
||||
// Skip directories
|
||||
if (entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
if (name.startsWith("META-INF/services/")) {
|
||||
// Process service files but don't write yet
|
||||
processServiceFile(name, is, serviceFiles, relocations);
|
||||
} else if (name.endsWith(".class")) {
|
||||
// Relocate class file path as well as content
|
||||
String relocatedPath = relocateClassPath(name, relocations);
|
||||
|
||||
JarEntry newEntry = new JarEntry(relocatedPath);
|
||||
jos.putNextEntry(newEntry);
|
||||
|
||||
byte[] classBytes = readAllBytes(is);
|
||||
byte[] relocatedBytes = relocateClass(name, classBytes, relocations);
|
||||
jos.write(relocatedBytes);
|
||||
jos.closeEntry();
|
||||
} else {
|
||||
// Relocate package-scoped resources so ResourceBundle lookups follow relocated packages.
|
||||
String relocatedPath = relocateResourcePath(name, relocations);
|
||||
|
||||
JarEntry newEntry = new JarEntry(relocatedPath);
|
||||
jos.putNextEntry(newEntry);
|
||||
copyStream(is, jos);
|
||||
jos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now write all service files after processing
|
||||
for (Map.Entry<String, StringBuilder> entry : serviceFiles.entrySet()) {
|
||||
try {
|
||||
JarEntry serviceEntry = new JarEntry(entry.getKey());
|
||||
jos.putNextEntry(serviceEntry);
|
||||
jos.write(entry.getValue().toString().getBytes(StandardCharsets.UTF_8));
|
||||
jos.closeEntry();
|
||||
} catch (Exception e) {
|
||||
// Log but continue with other service files
|
||||
System.out.println("Warning: Could not write service file " +
|
||||
entry.getKey() + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
writeRelocationMetadata(jos, relocations);
|
||||
}
|
||||
|
||||
validateRelocatedJar(targetJar, relocations);
|
||||
}
|
||||
|
||||
private static boolean shouldRebuildRelocatedJar(File relocatedJar, Map<String, String> relocations) {
|
||||
if (!relocatedJar.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try (JarFile jar = new JarFile(relocatedJar)) {
|
||||
JarEntry metadataEntry = jar.getJarEntry(RELOCATION_METADATA_PATH);
|
||||
if (metadataEntry == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Properties metadata = new Properties();
|
||||
try (InputStream is = jar.getInputStream(metadataEntry)) {
|
||||
metadata.load(is);
|
||||
}
|
||||
|
||||
if (!String.valueOf(RELOCATION_FORMAT_VERSION).equals(metadata.getProperty("formatVersion"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
String key = "relocation." + relocation.getKey();
|
||||
if (!relocation.getValue().equals(metadata.getProperty(key))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Integer.toString(relocations.size()).equals(metadata.getProperty("relocationCount"));
|
||||
} catch (IOException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeRelocationMetadata(JarOutputStream jos, Map<String, String> relocations)
|
||||
throws IOException {
|
||||
Properties metadata = new Properties();
|
||||
metadata.setProperty("formatVersion", Integer.toString(RELOCATION_FORMAT_VERSION));
|
||||
metadata.setProperty("relocationCount", Integer.toString(relocations.size()));
|
||||
|
||||
Map<String, String> sortedRelocations = new TreeMap<>(relocations);
|
||||
for (Map.Entry<String, String> relocation : sortedRelocations.entrySet()) {
|
||||
metadata.setProperty("relocation." + relocation.getKey(), relocation.getValue());
|
||||
}
|
||||
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
metadata.store(buffer, "AntiVPN relocation metadata");
|
||||
|
||||
JarEntry metadataEntry = new JarEntry(RELOCATION_METADATA_PATH);
|
||||
jos.putNextEntry(metadataEntry);
|
||||
jos.write(buffer.toByteArray());
|
||||
jos.closeEntry();
|
||||
}
|
||||
|
||||
private static void processServiceFile(String name, InputStream is,
|
||||
Map<String, StringBuilder> serviceFiles,
|
||||
Map<String, String> relocations) throws IOException {
|
||||
// Read service file content
|
||||
String content = new String(readAllBytes(is));
|
||||
StringBuilder contentBuilder = serviceFiles.computeIfAbsent(name, k -> new StringBuilder());
|
||||
|
||||
// Process and relocate service implementations
|
||||
for (String line : content.split("\n")) {
|
||||
String trimmed = line.trim();
|
||||
if (!trimmed.isEmpty() && !trimmed.startsWith("#")) {
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
if (trimmed.startsWith(relocation.getKey())) {
|
||||
trimmed = relocation.getValue() +
|
||||
trimmed.substring(relocation.getKey().length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
contentBuilder.append(trimmed).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] relocateClass(String entryName, byte[] classBytes, Map<String, String> relocations) {
|
||||
try {
|
||||
// Convert to slash notation for ASM
|
||||
Remapper prefixRemapper = getPrefixRemapper(relocations);
|
||||
|
||||
// Create custom ClassWriter to handle missing classes
|
||||
ClassReader reader = new ClassReader(classBytes);
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
||||
@Override
|
||||
protected String getCommonSuperClass(String type1, String type2) {
|
||||
try {
|
||||
return super.getCommonSuperClass(type1, type2);
|
||||
} catch (RuntimeException e) {
|
||||
// Fall back to Object when classes can't be loaded
|
||||
return "java/lang/Object";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ClassVisitor visitor = createStringRelocationVisitor(new ClassRemapper(writer, prefixRemapper), relocations);
|
||||
visitor = createMySqlUtilFallbackVisitor(entryName, visitor);
|
||||
|
||||
// Process class with remapper
|
||||
reader.accept(visitor, 0);
|
||||
|
||||
return relocateUtf8Constants(writer.toByteArray(), relocations);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to relocate class entry " + entryName, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String relocateReflectiveClassName(String className) {
|
||||
if (className == null || className.startsWith("dev.brighten.antivpn.shaded.")) {
|
||||
return className;
|
||||
}
|
||||
|
||||
if (className.startsWith("com.mysql.cj") || className.startsWith("com.mysql.jdbc")) {
|
||||
return "dev.brighten.antivpn.shaded." + className;
|
||||
}
|
||||
|
||||
return className;
|
||||
}
|
||||
|
||||
private static byte[] relocateUtf8Constants(byte[] classBytes, Map<String, String> relocations) throws IOException {
|
||||
Map<String, String> dotMappings = new HashMap<>();
|
||||
Map<String, String> slashMappings = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
||||
dotMappings.put(entry.getKey(), entry.getValue());
|
||||
slashMappings.put(entry.getKey().replace('.', '/'), entry.getValue().replace('.', '/'));
|
||||
}
|
||||
|
||||
DataInputStream in = new DataInputStream(new ByteArrayInputStream(classBytes));
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(classBytes.length + 256);
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
|
||||
out.writeInt(in.readInt());
|
||||
out.writeShort(in.readUnsignedShort());
|
||||
out.writeShort(in.readUnsignedShort());
|
||||
|
||||
int constantPoolCount = in.readUnsignedShort();
|
||||
out.writeShort(constantPoolCount);
|
||||
|
||||
for (int i = 1; i < constantPoolCount; i++) {
|
||||
int tag = in.readUnsignedByte();
|
||||
out.writeByte(tag);
|
||||
|
||||
switch (tag) {
|
||||
case 1 -> {
|
||||
String value = in.readUTF();
|
||||
String relocated = relocateStringValue(value, dotMappings, slashMappings);
|
||||
out.writeUTF(relocated);
|
||||
}
|
||||
case 3, 4 -> out.writeInt(in.readInt());
|
||||
case 5, 6 -> {
|
||||
out.writeLong(in.readLong());
|
||||
i++;
|
||||
}
|
||||
case 7, 8, 16, 19, 20 -> out.writeShort(in.readUnsignedShort());
|
||||
case 9, 10, 11, 12, 17, 18 -> {
|
||||
out.writeShort(in.readUnsignedShort());
|
||||
out.writeShort(in.readUnsignedShort());
|
||||
}
|
||||
case 15 -> {
|
||||
out.writeByte(in.readUnsignedByte());
|
||||
out.writeShort(in.readUnsignedShort());
|
||||
}
|
||||
default -> throw new IOException("Unknown constant pool tag " + tag);
|
||||
}
|
||||
}
|
||||
|
||||
copyStream(in, out);
|
||||
out.flush();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
private static Remapper getPrefixRemapper(Map<String, String> relocations) {
|
||||
Map<String, String> slashMappings = new HashMap<>();
|
||||
Map<String, String> dotMappings = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
||||
dotMappings.put(entry.getKey(), entry.getValue());
|
||||
String fromSlash = entry.getKey().replace('.', '/');
|
||||
String toSlash = entry.getValue().replace('.', '/');
|
||||
slashMappings.put(fromSlash, toSlash);
|
||||
}
|
||||
|
||||
// Create customized remapper for package prefixes
|
||||
return new Remapper() {
|
||||
@Override
|
||||
public String map(String typeName) {
|
||||
if (typeName == null) return null;
|
||||
|
||||
for (Map.Entry<String, String> entry : slashMappings.entrySet()) {
|
||||
String from = entry.getKey();
|
||||
String to = entry.getValue();
|
||||
|
||||
if (typeName.startsWith(from)) {
|
||||
return to + typeName.substring(from.length());
|
||||
}
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object mapValue(Object value) {
|
||||
if (value instanceof String stringValue) {
|
||||
return relocateStringValue(stringValue, dotMappings, slashMappings);
|
||||
}
|
||||
return super.mapValue(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ClassVisitor createMySqlUtilFallbackVisitor(String entryName, ClassVisitor delegate) {
|
||||
if (!"com/mysql/cj/util/Util.class".equals(entryName)) {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
return new ClassVisitor(Opcodes.ASM9, delegate) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (visitor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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)) {
|
||||
return visitor;
|
||||
}
|
||||
|
||||
return new MethodVisitor(Opcodes.ASM9, visitor) {
|
||||
@Override
|
||||
public void visitCode() {
|
||||
super.visitCode();
|
||||
super.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
super.visitMethodInsn(Opcodes.INVOKESTATIC,
|
||||
"dev/brighten/antivpn/depends/LibraryLoader",
|
||||
"relocateReflectiveClassName",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;",
|
||||
false);
|
||||
super.visitVarInsn(Opcodes.ASTORE, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ClassVisitor createStringRelocationVisitor(ClassVisitor delegate,
|
||||
Map<String, String> relocations) {
|
||||
Map<String, String> dotMappings = new HashMap<>();
|
||||
Map<String, String> slashMappings = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
||||
dotMappings.put(entry.getKey(), entry.getValue());
|
||||
slashMappings.put(entry.getKey().replace('.', '/'), entry.getValue().replace('.', '/'));
|
||||
}
|
||||
|
||||
return new ClassVisitor(Opcodes.ASM9, delegate) {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible), dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
||||
String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
|
||||
RecordComponentVisitor visitor = super.visitRecordComponent(name, descriptor, signature);
|
||||
if (visitor == null) {
|
||||
return null;
|
||||
}
|
||||
return new RecordComponentVisitor(Opcodes.ASM9, visitor) {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
||||
String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
||||
FieldVisitor visitor = super.visitField(access, name, descriptor, signature,
|
||||
relocateAsmValue(value, dotMappings, slashMappings));
|
||||
if (visitor == null) {
|
||||
return null;
|
||||
}
|
||||
return new FieldVisitor(Opcodes.ASM9, visitor) {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
||||
String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
if (visitor == null) {
|
||||
return null;
|
||||
}
|
||||
return new MethodVisitor(Opcodes.ASM9, visitor) {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
return wrapAnnotationVisitor(super.visitAnnotationDefault(), dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitAnnotation(descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
||||
String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor,
|
||||
boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitParameterAnnotation(parameter, descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitInsnAnnotation(int typeRef, org.objectweb.asm.TypePath typePath,
|
||||
String descriptor, boolean visible) {
|
||||
return wrapAnnotationVisitor(super.visitInsnAnnotation(typeRef, typePath, descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
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.Label[] start,
|
||||
org.objectweb.asm.Label[] end,
|
||||
int[] index, String descriptor,
|
||||
boolean visible) {
|
||||
return wrapAnnotationVisitor(
|
||||
super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible),
|
||||
dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
super.visitLdcInsn(relocateAsmValue(value, dotMappings, slashMappings));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String descriptor, org.objectweb.asm.Handle bootstrapMethodHandle,
|
||||
Object... bootstrapMethodArguments) {
|
||||
Object[] relocatedArgs = new Object[bootstrapMethodArguments.length];
|
||||
for (int i = 0; i < bootstrapMethodArguments.length; i++) {
|
||||
relocatedArgs[i] = relocateAsmValue(bootstrapMethodArguments[i], dotMappings, slashMappings);
|
||||
}
|
||||
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, relocatedArgs);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static AnnotationVisitor wrapAnnotationVisitor(AnnotationVisitor delegate,
|
||||
Map<String, String> dotMappings,
|
||||
Map<String, String> slashMappings) {
|
||||
if (delegate == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AnnotationVisitor(Opcodes.ASM9, delegate) {
|
||||
@Override
|
||||
public void visit(String name, Object value) {
|
||||
super.visit(name, relocateAsmValue(value, dotMappings, slashMappings));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
|
||||
return wrapAnnotationVisitor(super.visitAnnotation(name, descriptor), dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
return wrapAnnotationVisitor(super.visitArray(name), dotMappings, slashMappings);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Object relocateAsmValue(Object value, Map<String, String> dotMappings,
|
||||
Map<String, String> slashMappings) {
|
||||
if (value instanceof String stringValue) {
|
||||
return relocateStringValue(stringValue, dotMappings, slashMappings);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String relocateStringValue(String value, Map<String, String> dotMappings,
|
||||
Map<String, String> slashMappings) {
|
||||
for (Map.Entry<String, String> entry : dotMappings.entrySet()) {
|
||||
String from = entry.getKey();
|
||||
String relocated = relocateByPrefixes(value, from, entry.getValue(), '.', '$');
|
||||
if (!relocated.equals(value)) {
|
||||
return relocated;
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : slashMappings.entrySet()) {
|
||||
String from = entry.getKey();
|
||||
String to = entry.getValue();
|
||||
|
||||
String relocated = relocateByPrefixes(value, from, to, '/', '$');
|
||||
if (!relocated.equals(value)) {
|
||||
return relocated;
|
||||
}
|
||||
|
||||
relocated = relocateByPrefixes(value, "/" + from, "/" + to, '/', '$');
|
||||
if (!relocated.equals(value)) {
|
||||
return relocated;
|
||||
}
|
||||
|
||||
relocated = relocateByPrefixes(value, "L" + from, "L" + to, '/', '$', ';');
|
||||
if (!relocated.equals(value)) {
|
||||
return relocated;
|
||||
}
|
||||
|
||||
relocated = relocateByPrefixes(value, "[L" + from, "[L" + to, '/', '$', ';');
|
||||
if (!relocated.equals(value)) {
|
||||
return relocated;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String relocateByPrefixes(String value, String from, String to, char... delimiters) {
|
||||
if (value.equals(from)) {
|
||||
return to;
|
||||
}
|
||||
|
||||
for (char delimiter : delimiters) {
|
||||
if (value.startsWith(from + delimiter)) {
|
||||
return to + value.substring(from.length());
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void validateRelocatedJar(File targetJar, Map<String, String> relocations) throws IOException {
|
||||
Set<String> relocatedPrefixes = new HashSet<>();
|
||||
Map<String, String> dotMappings = new HashMap<>();
|
||||
Map<String, String> slashMappings = new HashMap<>();
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
relocatedPrefixes.add(relocation.getValue().replace('.', '/') + "/");
|
||||
dotMappings.put(relocation.getKey(), relocation.getValue());
|
||||
slashMappings.put(relocation.getKey().replace('.', '/'), relocation.getValue().replace('.', '/'));
|
||||
}
|
||||
|
||||
try (JarFile jar = new JarFile(targetJar)) {
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean shouldValidate = false;
|
||||
for (String relocatedPrefix : relocatedPrefixes) {
|
||||
if (entry.getName().startsWith(relocatedPrefix)) {
|
||||
shouldValidate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldValidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
findUnrelocatedConstant(entry.getName(), readAllBytes(is), dotMappings, slashMappings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void findUnrelocatedConstant(String entryName, byte[] classBytes, Map<String, String> dotMappings,
|
||||
Map<String, String> slashMappings) throws IOException {
|
||||
DataInputStream in = new DataInputStream(new ByteArrayInputStream(classBytes));
|
||||
in.readInt();
|
||||
in.readUnsignedShort();
|
||||
in.readUnsignedShort();
|
||||
int constantPoolCount = in.readUnsignedShort();
|
||||
|
||||
for (int i = 1; i < constantPoolCount; i++) {
|
||||
int tag = in.readUnsignedByte();
|
||||
switch (tag) {
|
||||
case 1 -> {
|
||||
String value = in.readUTF();
|
||||
String relocated = relocateStringValue(value, dotMappings, slashMappings);
|
||||
if (!value.equals(relocated)) {
|
||||
throw new IOException("Relocated jar still contains original reference '" + value
|
||||
+ "' in class entry " + entryName);
|
||||
}
|
||||
}
|
||||
case 3, 4 -> in.readInt();
|
||||
case 5, 6 -> {
|
||||
in.readLong();
|
||||
i++;
|
||||
}
|
||||
case 7, 8, 16, 19, 20 -> in.readUnsignedShort();
|
||||
case 9, 10, 11, 12, 17, 18 -> {
|
||||
in.readUnsignedShort();
|
||||
in.readUnsignedShort();
|
||||
}
|
||||
case 15 -> {
|
||||
in.readUnsignedByte();
|
||||
in.readUnsignedShort();
|
||||
}
|
||||
default -> throw new IOException("Unknown constant pool tag " + tag + " while validating " + entryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String relocateClassPath(String path, Map<String, String> relocations) {
|
||||
// Convert path to package format (replacing / with .)
|
||||
String packagePath = path.substring(0, path.length() - 6).replace('/', '.');
|
||||
|
||||
// Apply relocations
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
if (packagePath.startsWith(relocation.getKey())) {
|
||||
packagePath = relocation.getValue() + packagePath.substring(relocation.getKey().length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to path format
|
||||
return packagePath.replace('.', '/') + ".class";
|
||||
}
|
||||
|
||||
private static String relocateResourcePath(String path, Map<String, String> relocations) {
|
||||
if (path.startsWith("META-INF/")) {
|
||||
return path;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
String fromPath = relocation.getKey().replace('.', '/');
|
||||
String toPath = relocation.getValue().replace('.', '/');
|
||||
|
||||
if (path.startsWith(fromPath + "/")) {
|
||||
return toPath + path.substring(fromPath.length());
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private static byte[] readAllBytes(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int bytesRead;
|
||||
byte[] data = new byte[1024];
|
||||
while ((bytesRead = is.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, bytesRead);
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
private static void copyStream(InputStream is, OutputStream os) throws IOException {
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
private static File getLibFolder() {
|
||||
File pluginDataFolder = AntiVPN.getInstance().getPluginFolder();
|
||||
File libs = new File(pluginDataFolder, "libraries");
|
||||
if(libs.mkdirs()) {
|
||||
System.out.println("Created libraries folder!");
|
||||
}
|
||||
return libs;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@NonnullByDefault
|
||||
// Fix the Dependency class to preserve original groupId for downloading
|
||||
public static final class Dependency {
|
||||
private final String groupId;
|
||||
private final String artifactId;
|
||||
private final String version;
|
||||
private final String repoUrl;
|
||||
// Keep the original groupId/artifactId for Maven downloads
|
||||
private final String originalGroupId;
|
||||
private final String originalArtifactId;
|
||||
|
||||
public Dependency(String groupId, String artifactId, String version, String repoUrl) {
|
||||
this.originalGroupId = Objects.requireNonNull(groupId, "groupId");
|
||||
this.originalArtifactId = Objects.requireNonNull(artifactId, "artifactId");
|
||||
this.groupId = this.originalGroupId;
|
||||
this.artifactId = this.originalArtifactId;
|
||||
this.version = Objects.requireNonNull(version, "version");
|
||||
this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl");
|
||||
}
|
||||
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
String repo = this.repoUrl;
|
||||
if (!repo.endsWith("/")) {
|
||||
repo += "/";
|
||||
}
|
||||
repo += "%s/%s/%s/%s-%s.jar";
|
||||
|
||||
// Always use original groupId for Maven repository URL
|
||||
String url = String.format(repo, this.originalGroupId.replace(".", "/"),
|
||||
this.originalArtifactId, this.version, this.originalArtifactId, this.version);
|
||||
return new URL(url);
|
||||
}
|
||||
|
||||
// Rest of the class unchanged
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to indicate the required libraries for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibraries {
|
||||
|
||||
MavenLibrary[] value() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to indicate a required library for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Repeatable(MavenLibraries.class)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibrary {
|
||||
|
||||
/**
|
||||
* The group id of the library
|
||||
*
|
||||
* @return the group id of the library
|
||||
*/
|
||||
String groupId();
|
||||
|
||||
/**
|
||||
* The artifact id of the library
|
||||
*
|
||||
* @return the artifact id of the library
|
||||
*/
|
||||
String artifactId();
|
||||
|
||||
/**
|
||||
* The version of the library
|
||||
*
|
||||
* @return the version of the library
|
||||
*/
|
||||
String version();
|
||||
|
||||
/**
|
||||
* The repo where the library can be obtained from
|
||||
*
|
||||
* @return the repo where the library can be obtained from
|
||||
*/
|
||||
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
|
||||
|
||||
Relocate[] relocations() default {}; // Add this line
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({})
|
||||
public @interface Relocate {
|
||||
String from();
|
||||
String to();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Represents a maven repository.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.LOCAL_VARIABLE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Repository {
|
||||
|
||||
/**
|
||||
* Gets the base url of the repository.
|
||||
*
|
||||
* @return the base url of the repository
|
||||
*/
|
||||
String url();
|
||||
|
||||
}
|
||||
+10
-19
@@ -1,26 +1,17 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.message;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.message;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class that enables to get an IP range from CIDR specification. It supports
|
||||
* both IPv4 and IPv6.
|
||||
*/
|
||||
@Getter
|
||||
public class CIDRUtils {
|
||||
private final String cidr;
|
||||
|
||||
private final InetAddress inetAddress;
|
||||
|
||||
private InetAddress startAddress;
|
||||
private BigInteger startIpInt, endIpInt;
|
||||
private InetAddress endAddress;
|
||||
private final int prefixLength;
|
||||
|
||||
|
||||
public CIDRUtils(String cidr) throws UnknownHostException {
|
||||
|
||||
this.cidr = cidr;
|
||||
|
||||
/* split CIDR to address and prefix part */
|
||||
if (this.cidr.contains("/")) {
|
||||
int index = this.cidr.indexOf("/");
|
||||
String addressPart = this.cidr.substring(0, index);
|
||||
String networkPart = this.cidr.substring(index + 1);
|
||||
|
||||
inetAddress = InetAddress.getByName(addressPart);
|
||||
prefixLength = Integer.parseInt(networkPart);
|
||||
|
||||
calculate();
|
||||
} else {
|
||||
throw new IllegalArgumentException("not an valid CIDR format!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void calculate() throws UnknownHostException {
|
||||
|
||||
ByteBuffer maskBuffer;
|
||||
int targetSize;
|
||||
if (inetAddress.getAddress().length == 4) {
|
||||
maskBuffer =
|
||||
ByteBuffer
|
||||
.allocate(4)
|
||||
.putInt(-1);
|
||||
targetSize = 4;
|
||||
} else {
|
||||
maskBuffer = ByteBuffer.allocate(16)
|
||||
.putLong(-1L)
|
||||
.putLong(-1L);
|
||||
targetSize = 16;
|
||||
}
|
||||
|
||||
BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(prefixLength);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(inetAddress.getAddress());
|
||||
BigInteger ipVal = new BigInteger(1, buffer.array());
|
||||
|
||||
BigInteger startIp = ipVal.and(mask);
|
||||
this.startIpInt = startIp;
|
||||
BigInteger endIp = startIp.add(mask.not());
|
||||
this.endIpInt = endIp;
|
||||
|
||||
byte[] startIpArr = toBytes(startIp.toByteArray(), targetSize);
|
||||
byte[] endIpArr = toBytes(endIp.toByteArray(), targetSize);
|
||||
|
||||
this.startAddress = InetAddress.getByAddress(startIpArr);
|
||||
this.endAddress = InetAddress.getByAddress(endIpArr);
|
||||
|
||||
}
|
||||
|
||||
private byte[] toBytes(byte[] array, int targetSize) {
|
||||
int counter = 0;
|
||||
List<Byte> newArr = new ArrayList<Byte>();
|
||||
while (counter < targetSize && (array.length - 1 - counter >= 0)) {
|
||||
newArr.add(0, array[array.length - 1 - counter]);
|
||||
counter++;
|
||||
}
|
||||
|
||||
int size = newArr.size();
|
||||
for (int i = 0; i < (targetSize - size); i++) {
|
||||
|
||||
newArr.add(0, (byte) 0);
|
||||
}
|
||||
|
||||
byte[] ret = new byte[newArr.size()];
|
||||
for (int i = 0; i < newArr.size(); i++) {
|
||||
ret[i] = newArr.get(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public boolean isInRange(String ipAddress) throws UnknownHostException {
|
||||
InetAddress address = InetAddress.getByName(ipAddress);
|
||||
BigInteger start = new BigInteger(1, this.startAddress.getAddress());
|
||||
BigInteger end = new BigInteger(1, this.endAddress.getAddress());
|
||||
BigInteger target = new BigInteger(1, address.getAddress());
|
||||
|
||||
int st = start.compareTo(target);
|
||||
int te = target.compareTo(end);
|
||||
|
||||
return (st < 0 || st == 0) && (te < 0 || te == 0);
|
||||
}
|
||||
}
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EvictingMap<K, V> extends LinkedHashMap<K, V> {
|
||||
|
||||
@Getter
|
||||
private final int size;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||
return size() >= size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
/**
|
||||
* Holder for extra methods of {@code Objects} only in web. Intended to be empty for regular
|
||||
* version.
|
||||
*/
|
||||
abstract class ExtraObjectsMethodsForWeb {}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Optional;
|
||||
|
||||
public class IpUtils {
|
||||
public static Optional<BigDecimal> getIpDecimal(String address) {
|
||||
try {
|
||||
InetAddress inet = InetAddress.getByName(address);
|
||||
|
||||
if(inet instanceof Inet4Address) {
|
||||
return Optional.of(BigDecimal.valueOf(ipv4ToLong(address)));
|
||||
} return Optional.of(new BigDecimal(ipv6ToDecimalFormat(address)));
|
||||
} catch(Exception e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static long ipv4ToLong(String address) {
|
||||
String[] addrArray = address.split("\\.");
|
||||
|
||||
long ipDecimal = 0;
|
||||
|
||||
for (int i = 0; i < addrArray.length; i++) {
|
||||
|
||||
int power = 3 - i;
|
||||
ipDecimal += ((Integer.parseInt(addrArray[i]) % 256 * Math.pow(256, power)));
|
||||
}
|
||||
|
||||
return ipDecimal;
|
||||
}
|
||||
|
||||
public static String getIpv4(long ip) {
|
||||
StringBuilder sb = new StringBuilder(15);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sb.insert(0, ip & 0xff);
|
||||
|
||||
if (i < 3) {
|
||||
sb.insert(0, '.');
|
||||
}
|
||||
|
||||
ip >>= 8;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static boolean isIpv4(BigDecimal ip) {
|
||||
return ip.compareTo(BigDecimal.valueOf(4294967295L)) <= 0;
|
||||
}
|
||||
|
||||
public static boolean isIpv6(BigDecimal ip) {
|
||||
return ip.compareTo(BigDecimal.valueOf(4294967295L)) > 0;
|
||||
}
|
||||
public static boolean isIpv4(String ip) {
|
||||
return ip.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$");
|
||||
}
|
||||
|
||||
public static boolean isNotIp(String ip) {
|
||||
return !isIpv4(ip) && !isIpv6(ip);
|
||||
}
|
||||
|
||||
public static boolean isIpv6(String ip) {
|
||||
return ip.matches("^([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)$|^(([0-9a-fA-F]{1,4}:){0,6}([0-9a-fA-F]{1,4}|:))?(::([0-9a-fA-F]{1,4}:){0,5}([0-9a-fA-F]{1,4}|:))?$");
|
||||
}
|
||||
|
||||
public static String getIpv4(BigDecimal ip) {
|
||||
try {
|
||||
return Inet4Address.getByAddress(ip.toBigInteger().toByteArray()).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getIpv6(BigDecimal ip) {
|
||||
try {
|
||||
return Inet6Address.getByAddress(ip.toBigInteger().toByteArray()).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
public static BigInteger ipv6ToDecimalFormat(String ipAddress) throws UnknownHostException {
|
||||
return new BigInteger(1, Inet6Address.getByName(ipAddress).getAddress());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.utils.json.JSONObject;
|
||||
import dev.brighten.antivpn.utils.json.JsonReader;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MiscUtils {
|
||||
|
||||
private static final Pattern ipv4 = Pattern.compile("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
|
||||
|
||||
public static void close(Closeable... closeables) {
|
||||
try {
|
||||
for (Closeable closeable : closeables) if (closeable != null) closeable.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void close(AutoCloseable... closeables) {
|
||||
try {
|
||||
for (AutoCloseable closeable : closeables) if (closeable != null) closeable.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copy(InputStream in, File file) {
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
int lenght;
|
||||
byte[] buf = new byte[1024];
|
||||
|
||||
while ((lenght = in.read(buf)) > 0)
|
||||
{
|
||||
out.write(buf, 0, lenght);
|
||||
}
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ThreadFactory createThreadFactory(String threadName) {
|
||||
return r -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName(threadName);
|
||||
return thread;
|
||||
};
|
||||
}
|
||||
|
||||
public static List<CIDRUtils> rangeToCidrs(BigInteger start, BigInteger end) throws UnknownHostException {
|
||||
List<CIDRUtils> cidrs = new ArrayList<>();
|
||||
|
||||
while (start.compareTo(end) <= 0) {
|
||||
// Find the number of trailing zero bits — this determines max block size alignment
|
||||
int trailingZeros = start.equals(BigInteger.ZERO)
|
||||
? 128 // handle the edge case
|
||||
: start.getLowestSetBit();
|
||||
|
||||
// Find the largest block that fits
|
||||
BigInteger remaining = end.subtract(start).add(BigInteger.ONE);
|
||||
int maxBits = remaining.bitLength() - 1;
|
||||
|
||||
int blockBits = Math.min(trailingZeros, maxBits);
|
||||
int prefixLen = 32 - blockBits; // use 128 for IPv6
|
||||
|
||||
// Build the CIDR string
|
||||
byte[] addrBytes = toFixedLengthBytes(start, 4); // use 16 for IPv6
|
||||
String cidr = InetAddress.getByAddress(addrBytes).getHostAddress() + "/" + prefixLen;
|
||||
cidrs.add(new CIDRUtils(cidr));
|
||||
|
||||
// Advance past this block
|
||||
start = start.add(BigInteger.ONE.shiftLeft(blockBits));
|
||||
}
|
||||
|
||||
return cidrs;
|
||||
}
|
||||
|
||||
private static byte[] toFixedLengthBytes(BigInteger value, int length) {
|
||||
byte[] raw = value.toByteArray();
|
||||
byte[] result = new byte[length];
|
||||
int srcPos = Math.max(0, raw.length - length);
|
||||
int destPos = Math.max(0, length - raw.length);
|
||||
System.arraycopy(raw, srcPos, result, destPos, Math.min(raw.length, length));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static UUID lookupUUID(String playername) {
|
||||
try {
|
||||
JSONObject object = JsonReader
|
||||
.readJsonFromUrl("https://funkemunky.cc/mojang/uuid?name=" + playername);
|
||||
|
||||
if(object.has("uuid")) {
|
||||
return UUID.fromString(object.getString("uuid"));
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playername + "! Falling back to Mojang API", e);
|
||||
return lookupMojangUuid(playername);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static UUID lookupMojangUuid(String playerName) {
|
||||
try {
|
||||
JSONObject object = JsonReader.readJsonFromUrl("https://api.mojang.com/users/profiles/minecraft/" + playerName);
|
||||
|
||||
if(object.has("id")) {
|
||||
return UUID.fromString(object.getString("id"));
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playerName + " from Mojang!:", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static boolean isIpv4(String ip)
|
||||
{
|
||||
return ipv4.matcher(ip).matches();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface NonnullByDefault {
|
||||
|
||||
}
|
||||
+10
-8
@@ -1,15 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Guava Authors
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
+18
-6
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
@@ -12,15 +28,11 @@ public class StringUtil {
|
||||
return "&m-----------------------------------------------------";
|
||||
}
|
||||
|
||||
public static String lineNoStrike(String color) {
|
||||
return color + "-----------------------------------------------------";
|
||||
}
|
||||
|
||||
public static String varReplace(String input, APIPlayer player, VPNResponse result) {
|
||||
return input.replace("%player%", player.getName())
|
||||
return translateAlternateColorCodes('&', input.replace("%player%", player.getName())
|
||||
.replace("%reason%", result.getMethod())
|
||||
.replace("%country%", result.getCountryName())
|
||||
.replace("%city%", result.getCity());
|
||||
.replace("%city%", result.getCity()));
|
||||
}
|
||||
|
||||
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Supplier<T> extends java.util.function.Supplier<T> {
|
||||
T get();
|
||||
}
|
||||
+10
-8
@@ -1,15 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Guava Authors
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
public record Tuple<F, S>(F first, S second) {
|
||||
|
||||
}
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils.config;
|
||||
|
||||
import java.util.*;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils.config;
|
||||
|
||||
import java.io.*;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils.config;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
* This provides static methods to convert comma delimited text into a
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
* Convert a web browser cookie specification to a JSONObject and back.
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
* The HTTPTokener extends the JSONTokener to provide additional methods
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2008 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/**
|
||||
+15
-23
@@ -1,28 +1,20 @@
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 JSON.org
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
+16
-24
@@ -1,31 +1,23 @@
|
||||
/*
|
||||
* Copyright 2026 Dawson Hessler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils.json;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A JSONTokener takes a source string and extracts characters and tokens from
|
||||
* it. It is used by the JSONObject and JSONArray constructors to parse
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user