Compare commits

..

89 Commits

Author SHA1 Message Date
Dawson Hessler e03deb6ba6 1.6.1 2022-02-08 14:56:06 -05:00
Dawson Hessler 6142ef603d Merge branch 'master' of https://github.com/funkemunky/AntiVPN 2022-02-08 14:55:39 -05:00
Dawson Hessler 2d82e0c433 Fixing potential memory leak 2022-02-08 14:55:37 -05:00
Dawson 3b629f4796 Merge pull request #15 from unbeproducoes/patch-1
Try to use the new MySQL driver first.
2022-02-06 11:29:28 -05:00
Unbê Produções 23481bd786 Check the new driver before the old one 2022-02-05 02:27:02 -03:00
Unbê Produções 46156c4286 update MySQL Driver
=3
2022-02-05 02:19:22 -03:00
Dawson Hessler cd502b6f34 Adding mongo support and /antivpn clearcache command 2022-01-12 15:37:02 -05:00
Dawson Hessler 7ee04b74ea Fixing config 2021-12-22 09:51:26 -05:00
Dawson Hessler 206d375bbd Fixing version and sql on load 2021-12-19 13:48:05 -05:00
Dawson Hessler cba3c2f2d9 Fixing KauriVPN on Velocity 1.5.2.1 2021-12-19 13:45:53 -05:00
Dawson Hessler 5ec4cb98e3 Starting to implement h2, fixing Java 17 bug, v1.5.2 2021-12-14 10:34:23 -05:00
Dawson Hessler e44bd5843d Working on mysql missing libraries 2021-11-06 13:01:56 -04:00
Dawson Hessler a9d356a04a 1.5.1
- Added ip exemptions in addition to the existing player exemptions./
- Fixing System.out usage warnings that some users were experiencing.
- Fixing MySQL drivers not loading on some servers.
- Fixing bug that would make whitelisted players not load for awhile after server starts
2021-11-04 10:36:31 -04:00
Dawson Hessler 5ba19b42f9 1.5.1 KauriVPN plugin.yml rename and System print fix 2021-11-04 08:27:32 -04:00
Dawson Hessler 2082ad6d8e Adding bstats functionality and fixing version 2021-09-14 19:05:07 -04:00
Dawson Hessler 256f500dff Fixing conflicts 2021-09-14 18:43:04 -04:00
Dawson Hessler 9f05467553 Adding Velocity support to AntiVPN (1.5) 2021-09-14 18:41:24 -04:00
funkemunky b23fed5392 Fixing no messages error 2021-09-09 15:55:41 -04:00
funkemunky 28094f7d46 Commenting out init for now until system is finished 2021-09-09 15:36:35 -04:00
funkemunky 723aa6a127 Fixing messages from config not loading or setting 2021-09-09 15:35:25 -04:00
funkemunky 7a229b23ff Turning alerts on for players on reloads if they are already online 2021-09-09 15:25:55 -04:00
funkemunky 795f0333c7 Fixing whitelist bug and alertsState bug
- Whitelisted players would not actually return as whitelisted from the database because of use the getFetchSize parameter, which was not used as it was intended.
- The same reasoning as above is why the alertsState would not function.
2021-09-09 15:25:42 -04:00
funkemunky b573fca58b Fixing sql errors and fixing NPE of missing VpnStrings 2021-09-09 15:09:02 -04:00
funkemunky c1ef2eef56 1.4.1 Fixing table truncation to update 2021-09-09 14:53:29 -04:00
funkemunky a3cb7e8e8a Adding check for closed connections to prevent errors 2021-09-09 14:50:18 -04:00
funkemunky 2bf16ad6b7 Added alerts state saving 2021-09-09 14:49:38 -04:00
Dawson Hessler e37b4c3e6f Starting to add custom messages to AntiVPN 2021-09-06 15:17:27 -04:00
Dawson Hessler 334c894fe2 Fixing bug that prevented players from getting kicked and fixes alerts not being populated 2021-08-27 14:44:40 -04:00
Dawson Hessler 3b15a4c919 Adding bstats 2021-08-27 13:12:13 -04:00
Dawson Hessler b1cf629945 Update version to 1.4.0 2021-08-27 12:51:16 -04:00
Dawson Hessler 5907a9496b Fixing command running when disabled on Bukkit instance of kaurivpn 2021-08-27 12:50:41 -04:00
Dawson Hessler 7cb4bae972 Adding the actual alert sending 2021-08-27 12:49:58 -04:00
Dawson Hessler f32beb4dc9 Replacing out print usage with bungee/bukkit logger where I can 2021-08-27 12:47:06 -04:00
Dawson Hessler feb2049d99 Fixing bug that sent the same description for every command 2021-08-27 12:46:18 -04:00
Dawson Hessler c9655782ee Renaming to sender to keep naming consistency 2021-08-27 12:45:42 -04:00
Dawson Hessler 729381a4e5 Adding antivpn alerts command and more to the command API 2021-08-27 12:45:16 -04:00
Dawson Hessler 9786a93ca8 Fixing allowlist command tab complete for args 2021-08-21 16:21:01 -04:00
Dawson Hessler fbba41fd71 Potentially fixing Java 16 issues v1.3.1 2021-08-21 13:33:17 -04:00
Dawson Hessler 2cd7951bca v1.3 2021-08-20 15:09:07 -04:00
Dawson Hessler 1ce3f28398 removing colon 2021-08-20 15:08:06 -04:00
Dawson Hessler e5107bd2c3 Fixing sql 2021-08-20 15:03:31 -04:00
Dawson Hessler ccdc260b68 Merging 2021-08-20 14:12:23 -04:00
Dawson Hessler 7533b32039 Fixing Bukkit vpn processing and adding failure reasons to output 2021-08-20 14:11:37 -04:00
Dawson 73bddca5c3 Merge pull request #1 from brysondev/master 2021-07-26 20:55:20 -04:00
Dawson Hessler 68b6335ad5 Adding kick toggling to the config 2021-07-18 19:58:05 -04:00
Dawson Hessler 665b313828 Adding commands on vpn detect on login 2021-07-18 19:51:23 -04:00
Bryson 88dfcc3349 removed redundant check that causes exception 2021-07-18 12:15:23 -04:00
Bryson d04c33c676 removed debug prints 2021-07-18 11:50:55 -04:00
Bryson 1f6043c20d Corrected some mistakes to get it working 2021-07-18 11:50:03 -04:00
Bryson a60c1b2360 updated to non-deprecated class 2021-07-18 11:49:44 -04:00
Bryson 5fa7387927 corrected invalid sql queries 2021-07-18 09:38:31 -04:00
Dawson Hessler a2dc04dc51 1.2.1 update fixing sql issue 2021-06-19 13:05:13 -04:00
Dawson Hessler c21098a511 1.2 update 2021-06-18 16:42:54 -04:00
Dawson Hessler c4336b2760 Fixing permission name (too used to making anticheats) 2021-06-18 16:38:47 -04:00
Dawson Hessler 0a7c2c0207 Implementing database and allowlist system 2021-06-18 16:37:09 -04:00
Dawson Hessler 09482b970b setting it to snapshot 2021-06-18 11:27:48 -04:00
Dawson Hessler 11c0c177fe Updating to 1.2 2021-06-18 11:27:30 -04:00
Dawson Hessler 1c636c3b6f Implementing prefix exemptions 2021-06-18 11:26:57 -04:00
Dawson 15a5d3ba4f Adding license 2021-06-18 11:20:25 -04:00
Dawson 123221cd58 Create README.md 2021-06-18 11:17:51 -04:00
Dawson Hessler 64be97b22c Implemented bungee commands and tab complete 1.1.1 2021-06-18 11:09:07 -04:00
funkemunky 83bb904c7d Completing 1.1 update 2021-06-17 13:05:09 -04:00
funkemunky d867a3ecd6 Implementing Bukkit shutdown cleanup so reloads work 2021-06-17 12:35:51 -04:00
funkemunky 1b569531a0 removing files that should now be ignored by gitignore 2021-06-17 12:29:55 -04:00
funkemunky 2b1763d0f7 Update .gitignore 2021-06-17 12:29:14 -04:00
funkemunky 5d790ce56d Finished commands system on Bukkit - tested and working 2021-06-17 12:27:22 -04:00
funkemunky 86f886e2e7 Update pom.xml 2021-06-17 12:00:08 -04:00
funkemunky 3fcb3fe157 Implementing commands system into antivpn [v1.1] 2021-06-17 12:00:06 -04:00
Dawson Hessler 20e6cbde9f Adding print to console msg 2021-06-16 15:33:35 -04:00
Dawson Hessler f5fd7001e7 Adding bypass permission and updating version to 1.0.1 2021-06-15 15:40:19 -04:00
Dawson Hessler 68cd7ef810 Implementing warning message 2021-06-15 15:07:40 -04:00
Dawson Hessler d28f6a8ac0 Fixing load for bungee plugin 2021-06-15 14:57:38 -04:00
Dawson Hessler e1abdb7bf6 Completed new antivpn plugin 2021-06-15 13:48:39 -04:00
Dawson Hessler 9039a97894 Implementing JSON APi and porting VPNResponse class 2021-06-15 10:42:07 -04:00
Dawson Hessler a4a6c87fa2 Starting project 2021-06-15 10:38:14 -04:00
Dawson Hessler c569bad355 Removing all of the old files 2021-06-15 10:30:01 -04:00
Dawson Hessler c4c2d20732 Completed legacy plugin 2021-06-15 10:27:30 -04:00
Dawson Hessler 83d1c23088 Fixing CPU issue 2020-10-02 12:16:37 -04:00
Dawson Hessler 8205c4b8e7 Added bypass permission
antivpn.bypass
2020-09-09 13:09:35 -04:00
Dawson Hessler ea6c5dd281 Updating to latest API and version of Atlas 2020-09-08 14:17:39 -04:00
funkemunky 98abc4ab94 Updated for latest Atlas and improved performance a lot 2020-01-22 12:40:30 -05:00
Dawson Hessler 4cb921bfb0 Revert "Updated antivpn for later version"
This reverts commit 6f128bc2b7.
2020-01-22 09:22:42 -05:00
Dawson Hessler 09467575da Merge branch 'master' of https://github.com/funkemunky/AntiVPN 2020-01-22 09:22:33 -05:00
Dawson Hessler 6f128bc2b7 Updated antivpn for later version 2020-01-22 09:21:18 -05:00
funkemunky 9afb143a3d Improved performance of AntiVPN plugin 2020-01-21 09:25:19 -05:00
funkemunky 72f8598624 Update AntiVPN.java 2020-01-15 09:02:41 -05:00
funkemunky 3afcdc371d Update JoinListener.java 2020-01-13 09:26:03 -05:00
funkemunky c789aa58af Added data, info, and reload command 2020-01-13 09:18:46 -05:00
funkemunky 5ef4492e84 Added backwards and forwards compatibility for Atlas 1.6+ 2020-01-13 08:33:56 -05:00
100 changed files with 11448 additions and 1277 deletions
+268 -5
View File
@@ -1,3 +1,176 @@
# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,maven,java,intellij,eclipse,netbeans
# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,maven,java,intellij,eclipse,netbeans
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project
### Eclipse Patch ###
# Spring Boot Tooling
.sts4-cache/
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/
# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/
# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml
### Java ###
# Compiled class file
*.class
@@ -22,8 +195,98 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# project stuff
*.iml
target/**
out/**
.idea/**
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
.flattened-pom.xml
### NetBeans ###
**/nbproject/private/
**/nbproject/Makefile-*.mk
**/nbproject/Package-*.bash
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,maven,java,intellij,eclipse,netbeans
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.6.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Assembly</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>cc.funkemunky.utils</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
</properties>
</project>
+67
View File
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.6.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Assembly</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Bungee</artifactId>
<version>${version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Velocity</artifactId>
<version>${version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>${version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Bukkit</artifactId>
<version>${version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.6.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Bukkit</artifactId>
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>dev.brighten.antivpn.bukkit.org.bstats</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.13.2</artifactId>
<version>1.13.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.6.1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>toml4j</artifactId>
<groupId>com.moandjiezana.toml</groupId>
</exclusion>
<exclusion>
<artifactId>mysql-connector-java</artifactId>
<groupId>mysql</groupId>
</exclusion>
<exclusion>
<artifactId>h2</artifactId>
<groupId>com.h2database</groupId>
</exclusion>
<exclusion>
<artifactId>mongo-java-driver</artifactId>
<groupId>org.mongodb</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cc.funkemunky.utils</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
</properties>
</project>
+83
View File
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.6.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Bukkit</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<!-- Replace this with your package! -->
<shadedPattern>dev.brighten.antivpn.bukkit.org.bstats</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>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.13.2</artifactId>
<version>1.13.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.6.1</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>
@@ -0,0 +1,40 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.PlayerExecutor;
import dev.brighten.antivpn.command.CommandExecutor;
import lombok.RequiredArgsConstructor;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Optional;
@RequiredArgsConstructor
public class BukkitCommandExecutor implements CommandExecutor {
private final CommandSender sender;
@Override
public void sendMessage(String message) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message));
}
@Override
public boolean hasPermission(String permission) {
return sender.hasPermission(permission);
}
@Override
public Optional<APIPlayer> getPlayer() {
if(!isPlayer()) return Optional.empty();
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((Player)sender).getUniqueId());
}
@Override
public boolean isPlayer() {
return sender instanceof Player;
}
}
@@ -0,0 +1,181 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.api.VPNConfig;
import dev.brighten.antivpn.bukkit.util.ConfigDefault;
import org.bukkit.Bukkit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class BukkitConfig implements VPNConfig {
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
"license", BukkitPlugin.pluginInstance), kickStringDefault =
new ConfigDefault<>("Proxies are not allowed on our server",
"kickMessage", BukkitPlugin.pluginInstance),
defaultDatabaseType = new ConfigDefault<>("MySQL",
"database.type", BukkitPlugin.pluginInstance),
defaultDatabaseName = new ConfigDefault<>("kaurivpn",
"database.database", BukkitPlugin.pluginInstance),
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", BukkitPlugin.pluginInstance),
defaultUsername = new ConfigDefault<>("root",
"database.username", BukkitPlugin.pluginInstance),
defaultPassword = new ConfigDefault<>("password",
"database.password", BukkitPlugin.pluginInstance),
defaultIp = new ConfigDefault<>("localhost", "database.ip", BukkitPlugin.pluginInstance),
defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" +
" &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message",
BukkitPlugin.pluginInstance);
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
"cachedResults", BukkitPlugin.pluginInstance),
defaultUseCredentials = new ConfigDefault<>(true,
"database.useCredentials", BukkitPlugin.pluginInstance),
defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled",
BukkitPlugin.pluginInstance), defaultCommandsEnable = new ConfigDefault<>(false,
"commands.enabled", BukkitPlugin.pluginInstance), defaultKickPlayers
= new ConfigDefault<>(true, "kickPlayers", BukkitPlugin.pluginInstance),
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled",
BukkitPlugin.pluginInstance),
defaultMetrics = new ConfigDefault<>(true, "bstats", BukkitPlugin.pluginInstance);
private final ConfigDefault<Integer>
defaultPort = new ConfigDefault<>(-1, "database.port", BukkitPlugin.pluginInstance);
private final ConfigDefault<List<String>> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(),
"prefixWhitelists", BukkitPlugin.pluginInstance), defaultCommands = new ConfigDefault<>(
Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute",
BukkitPlugin.pluginInstance);
private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg;
private List<String> prefixWhitelists, commands;
private int port;
private boolean cacheResults, databaseEnabled, useCredentials, commandsEnabled, kickPlayers, alertToStaff, metrics;
@Override
public String getLicense() {
return license;
}
@Override
public boolean cachedResults() {
return cacheResults;
}
@Override
public String getKickString() {
return kickMessage;
}
@Override
public String alertMessage() {
return alertMsg;
}
@Override
public boolean alertToStaff() {
return alertToStaff;
}
@Override
public boolean runCommands() {
return commandsEnabled;
}
@Override
public List<String> commands() {
return commands;
}
@Override
public boolean kickPlayersOnDetect() {
return kickPlayers;
}
@Override
public List<String> getPrefixWhitelists() {
return prefixWhitelists;
}
@Override
public boolean isDatabaseEnabled() {
return databaseEnabled;
}
@Override
public boolean useDatabaseCreds() {
return useCredentials;
}
@Override
public String mongoDatabaseURL() {
return mongoURL;
}
@Override
public String getDatabaseType() {
return databaseType;
}
@Override
public String getDatabaseName() {
return databaseName;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getIp() {
return ip;
}
@Override
public int getPort() {
if(port == -1) {
switch (getDatabaseType().toLowerCase()) {
case "mongodb":
case "mongo":
case "mongod":
return 27017;
case "sql":
case "mysql":
return 3306;
}
}
return port;
}
@Override
public boolean metrics() {
return metrics;
}
public void update() {
license = licenseDefault.get();
kickMessage = kickStringDefault.get();
cacheResults = cacheResultsDefault.get();
prefixWhitelists = prefixWhitelistsDefault.get();
databaseEnabled = defaultDatabaseEnabled.get();
useCredentials = defaultUseCredentials.get();
databaseType = defaultDatabaseType.get();
databaseName = defaultDatabaseName.get();
mongoURL = defaultMongoURL.get();
username = defaultUsername.get();
password = defaultPassword.get();
ip = defaultIp.get();
port = defaultPort.get();
commandsEnabled = defaultCommandsEnable.get();
commands = defaultCommands.get();
kickPlayers = defaultKickPlayers.get();
alertToStaff = defaultAlertToStaff.get();
alertMsg = defaultAlertMsg.get();
metrics = defaultMetrics.get();
}
}
@@ -0,0 +1,119 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.message.VpnString;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import java.util.Optional;
import java.util.logging.Level;
public class BukkitListener extends VPNExecutor implements Listener {
private BukkitTask cacheResetTask;
@Override
public void registerListeners() {
BukkitPlugin.pluginInstance.getServer().getPluginManager()
.registerEvents(this, BukkitPlugin.pluginInstance);
}
@Override
public void runCacheReset() {
cacheResetTask = new BukkitRunnable() {
public void run() {
resetCache();
}
}.runTaskTimerAsynchronously(BukkitPlugin.pluginInstance, 24000, 24000); //Reset cache every 20 minutes
HandlerList.unregisterAll(this);
threadExecutor.shutdown();
}
@Override
public void shutdown() {
if(cacheResetTask != null && !cacheResetTask.isCancelled()) cacheResetTask.cancel();
}
@Override
public void log(String log, Object... objects) {
Bukkit.getLogger().log(Level.INFO, String.format(log, objects));
}
@EventHandler
public void onJoin(final PlayerJoinEvent event) {
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
.ifPresent(player -> {
AntiVPN.getInstance().getDatabase().alertsState(player.getUuid(), enabled -> {
if(enabled) {
AntiVPN.getInstance().getExecutor().log("Enabled");
player.setAlertsEnabled(true);
player.sendMessage(AntiVPN.getInstance().getMessageHandler()
.getString("command-alerts-toggled")
.getFormattedMessage(new VpnString.Var<>("state", true)));
} else AntiVPN.getInstance().getExecutor().log("Not enabled");
});
});
}
@EventHandler
public void onListener(final PlayerLoginEvent event) {
//If they're exempt, don't check.
if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getAddress().getHostAddress())) return;
checkIp(event.getAddress().getHostAddress(), AntiVPN.getInstance().getConfig().cachedResults(), result -> {
if(result.isSuccess() && result.isProxy()) {
new BukkitRunnable() {
public void run() {
Player player = event.getPlayer();
if(!player.hasPermission("antivpn.bypass") //Has bypass permission
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getConfig().getPrefixWhitelists().stream()
.anyMatch(prefix -> player.getName().startsWith(prefix))) {
if (AntiVPN.getInstance().getConfig().kickPlayersOnDetect())
player.kickPlayer(ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getConfig().getKickString()));
//Ensuring the user wishes to alert to staff
if(AntiVPN.getInstance().getConfig().alertToStaff())
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.filter(APIPlayer::isAlertsEnabled)
.forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getConfig()
.alertMessage().replace("%player%", event.getPlayer().getName())
.replace("%reason%", result.getMethod())
.replace("%country%", result.getCountryName())
.replace("%city%", result.getCity())));
//In case the user wants to run their own commands instead of using the built in kicking
if(AntiVPN.getInstance().getConfig().runCommands())
for (String command : AntiVPN.getInstance().getConfig().commands()) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
ChatColor.translateAlternateColorCodes('&',
command.replace("%player%", event.getPlayer().getName())));
}
}
Bukkit.getLogger().info(player.getPlayer().getName()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
}
}.runTask(BukkitPlugin.pluginInstance);
} else if(!result.isSuccess()) {
Bukkit.getLogger().log(Level.WARNING,
"The API query was not a success! " +
"You may need to upgrade your license on https://funkemunky.cc/shop");
}
});
}
}
@@ -0,0 +1,38 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.api.APIPlayer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
public class BukkitPlayer extends APIPlayer {
private final Player player;
public BukkitPlayer(Player player) {
super(player.getUniqueId(), player.getName(), player.getAddress().getAddress());
this.player = player;
}
@Override
public void sendMessage(String message) {
player.sendMessage(ChatColor.translateAlternateColorCodes('&', message));
}
@Override
public void kickPlayer(String reason) {
if(!Bukkit.isPrimaryThread()) {
new BukkitRunnable() {
public void run() {
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
}
}.runTask(BukkitPlugin.pluginInstance);
} else player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
}
@Override
public boolean hasPermission(String permission) {
return player.hasPermission(permission);
}
}
@@ -0,0 +1,46 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.PlayerExecutor;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.stream.Collectors;
public class BukkitPlayerExecutor implements PlayerExecutor {
private final Map<Player, BukkitPlayer> cachedPlayers = new WeakHashMap<>();
@Override
public Optional<APIPlayer> getPlayer(String name) {
final Player player = Bukkit.getPlayer(name);
if(player == null) {
return Optional.empty();
}
return Optional.of(cachedPlayers.computeIfAbsent(player, BukkitPlayer::new));
}
@Override
public Optional<APIPlayer> getPlayer(UUID uuid) {
final Player player = Bukkit.getPlayer(uuid);
if(player == null) {
return Optional.empty();
}
return Optional.of(cachedPlayers.computeIfAbsent(player, BukkitPlayer::new));
}
@Override
public List<APIPlayer> getOnlinePlayers() {
return Bukkit.getOnlinePlayers().stream()
.map(pl -> cachedPlayers.computeIfAbsent(pl, BukkitPlayer::new))
.collect(Collectors.toList());
}
}
@@ -0,0 +1,165 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.bukkit.util.ConfigDefault;
import dev.brighten.antivpn.command.Command;
import lombok.val;
import net.md_5.bungee.api.ChatColor;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.MultiLineChart;
import org.bstats.charts.SingleLineChart;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.SimplePluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
public class BukkitPlugin extends JavaPlugin {
public static BukkitPlugin pluginInstance;
private SimpleCommandMap commandMap;
private List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
private SingleLineChart vpnDetections, ipsChecked;
public void onEnable() {
pluginInstance = this;
//Loading config
Bukkit.getLogger().info("Loading config...");
saveDefaultConfig();
Bukkit.getLogger().info("Starting AntiVPN services...");
AntiVPN.start(new BukkitConfig(), new BukkitListener(), new BukkitPlayerExecutor(), getDataFolder());
if(AntiVPN.getInstance().getConfig().metrics()) {
Bukkit.getLogger().info("Starting bStats metrics...");
Metrics metrics = new Metrics(this, 12615);
metrics.addCustomChart(vpnDetections = new SingleLineChart("vpn_detections",
() -> AntiVPN.getInstance().detections));
metrics.addCustomChart(ipsChecked = new SingleLineChart("ips_checked",
() -> AntiVPN.getInstance().checked));
new BukkitRunnable() {
public void run() {
AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0;
}
}.runTaskTimerAsynchronously(this, 12000, 12000);
}
Bukkit.getLogger().info("Setting up and registering commands...");
if (pluginInstance.getServer().getPluginManager() instanceof SimplePluginManager) {
SimplePluginManager manager = (SimplePluginManager) pluginInstance.getServer().getPluginManager();
try {
Field field = SimplePluginManager.class.getDeclaredField("commandMap");
field.setAccessible(true);
commandMap = (SimpleCommandMap) field.get(manager);
} catch (IllegalArgumentException | SecurityException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
for (Command command : AntiVPN.getInstance().getCommands()) {
val newCommand = new org.bukkit.command.Command(command.name(), command.description(), command.usage(),
Arrays.asList(command.aliases())) {
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args)
throws IllegalArgumentException {
val children = command.children();
if(children.length > 0 && args.length > 0) {
for (Command child : children) {
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
return child.tabComplete(new BukkitCommandExecutor(sender), alias, IntStream
.range(0, args.length - 1)
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
}
}
}
return command.tabComplete(new BukkitCommandExecutor(sender), alias, args);
}
@Override
public boolean execute(CommandSender sender, String s, String[] args) {
if(!sender.hasPermission("antivpn.command.*")
&& !sender.hasPermission(command.permission())) {
sender.sendMessage(ChatColor.RED + "No permission.");
return true;
}
val children = command.children();
if(children.length > 0 && args.length > 0) {
for (Command child : children) {
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
if(!sender.hasPermission("antivpn.command.*")
&& !sender.hasPermission(child.permission())) {
sender.sendMessage(ChatColor.RED + "No permission.");
return true;
}
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
child.execute(new BukkitCommandExecutor(sender), IntStream
.range(0, args.length - 1)
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
return true;
}
}
}
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
command.execute(new BukkitCommandExecutor(sender), args)));
return true;
}
};
registeredCommands.add(newCommand);
commandMap.register(pluginInstance.getName(), newCommand);
}
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), BukkitPlugin.pluginInstance)
.get());
//TODO Finish system before implementing on startup
/*Bukkit.getLogger().info("Getting strings...");
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), BukkitPlugin.pluginInstance)
.get());
AntiVPN.getInstance().getMessageHandler().reloadStrings();*/
}
@Override
public void onDisable() {
Bukkit.getLogger().info("Stopping plugin services...");
AntiVPN.getInstance().stop();
Bukkit.getLogger().info("Unregistering commands...");
try {
Field field = SimpleCommandMap.class.getDeclaredField("knownCommands");
field.setAccessible(true);
Map<String, org.bukkit.command.Command> knownCommands =
(Map<String, org.bukkit.command.Command>) field.get(commandMap);
knownCommands.values().removeAll(registeredCommands);
registeredCommands.clear();
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
Bukkit.getLogger().info("Unregistering listeners...");
HandlerList.unregisterAll(this);
Bukkit.getLogger().info("Cancelling any running tasks...");
Bukkit.getScheduler().cancelTasks(this);
}
}
@@ -0,0 +1,28 @@
package dev.brighten.antivpn.bukkit.util;
import lombok.AllArgsConstructor;
import org.bukkit.plugin.Plugin;
@AllArgsConstructor
public class ConfigDefault<A> {
private final A defaultValue;
private final String path;
private final Plugin plugin;
public A get() {
if(plugin.getConfig().get(path) != null)
return (A) plugin.getConfig().get(path);
else {
plugin.getConfig().set(path, defaultValue);
plugin.saveConfig();
return defaultValue;
}
}
public A set(A value) {
plugin.getConfig().set(path, value);
return value;
}
}
+5
View File
@@ -0,0 +1,5 @@
name: KauriVPN
main: dev.brighten.antivpn.bukkit.BukkitPlugin
version: ${project.version}
author: funkemunky
api-version: 1.13
+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.6.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Bungee</artifactId>
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>dev.brighten.antivpn.bungee.org.bstats</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.6.1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>toml4j</artifactId>
<groupId>com.moandjiezana.toml</groupId>
</exclusion>
<exclusion>
<artifactId>mysql-connector-java</artifactId>
<groupId>mysql</groupId>
</exclusion>
<exclusion>
<artifactId>h2</artifactId>
<groupId>com.h2database</groupId>
</exclusion>
<exclusion>
<artifactId>mongo-java-driver</artifactId>
<groupId>org.mongodb</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.github.bungee</groupId>
<artifactId>BungeeCord-1.8</artifactId>
<version>1.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cc.funkemunky.utils</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
</properties>
</project>
+83
View File
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.6.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Bungee</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<!-- Replace this with your package! -->
<shadedPattern>dev.brighten.antivpn.bungee.org.bstats</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>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.6.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.github.bungee</groupId>
<artifactId>BungeeCord-1.8</artifactId>
<version>1.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bungeecord</artifactId>
<version>2.2.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,41 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.command.CommandExecutor;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.Optional;
@RequiredArgsConstructor
public class BungeeCommandExecutor implements CommandExecutor {
private final CommandSender sender;
@Override
public void sendMessage(String message) {
sender.sendMessage(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&', message)));
}
@Override
public boolean hasPermission(String permission) {
return sender.hasPermission(permission);
}
@Override
public Optional<APIPlayer> getPlayer() {
if(!isPlayer()) return Optional.empty();
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((ProxiedPlayer) sender).getUniqueId());
}
@Override
public boolean isPlayer() {
return sender instanceof ProxiedPlayer;
}
}
@@ -0,0 +1,179 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.api.VPNConfig;
import dev.brighten.antivpn.bungee.util.ConfigDefault;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BungeeConfig implements VPNConfig {
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
"license", BungeePlugin.pluginInstance), kickStringDefault =
new ConfigDefault<>("Proxies are not allowed on our server",
"kickMessage", BungeePlugin.pluginInstance),
defaultDatabaseType = new ConfigDefault<>("MySQL",
"database.type", BungeePlugin.pluginInstance),
defaultDatabaseName = new ConfigDefault<>("kaurivpn",
"database.database", BungeePlugin.pluginInstance),
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", BungeePlugin.pluginInstance),
defaultUsername = new ConfigDefault<>("root",
"database.username", BungeePlugin.pluginInstance),
defaultPassword = new ConfigDefault<>("password",
"database.password", BungeePlugin.pluginInstance),
defaultIp = new ConfigDefault<>("localhost", "database.ip", BungeePlugin.pluginInstance),
defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" +
" &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message",
BungeePlugin.pluginInstance);
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
"cachedResults", BungeePlugin.pluginInstance),
defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled",
BungeePlugin.pluginInstance), defaultCommandsEnable = new ConfigDefault<>(false,
"commands.enabled", BungeePlugin.pluginInstance), defaultKickPlayers
= new ConfigDefault<>(true, "kickPlayers", BungeePlugin.pluginInstance),
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled",
BungeePlugin.pluginInstance),
defaultUseCredentials = new ConfigDefault<>(true,
"database.useCredentials", BungeePlugin.pluginInstance),
defaultMetrics = new ConfigDefault<>(true, "bstats", BungeePlugin.pluginInstance);
private final ConfigDefault<Integer>
defaultPort = new ConfigDefault<>(-1, "database.port", BungeePlugin.pluginInstance);
private final ConfigDefault<List<String>> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(),
"prefixWhitelists", BungeePlugin.pluginInstance), defaultCommands = new ConfigDefault<>(
Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute",
BungeePlugin.pluginInstance);
private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg;
private List<String> prefixWhitelists, commands;
private int port;
private boolean cacheResults, useCredentials, databaseEnabled, commandsEnabled, kickPlayers, alertToStaff, metrics;
@Override
public String getLicense() {
return license;
}
@Override
public boolean cachedResults() {
return cacheResults;
}
@Override
public String getKickString() {
return kickMessage;
}
@Override
public String alertMessage() {
return alertMsg;
}
@Override
public boolean alertToStaff() {
return alertToStaff;
}
@Override
public boolean runCommands() {
return commandsEnabled;
}
@Override
public List<String> commands() {
return commands;
}
@Override
public boolean kickPlayersOnDetect() {
return kickPlayers;
}
@Override
public List<String> getPrefixWhitelists() {
return prefixWhitelists;
}
@Override
public boolean isDatabaseEnabled() {
return databaseEnabled;
}
@Override
public boolean useDatabaseCreds() {
return useCredentials;
}
@Override
public String mongoDatabaseURL() {
return mongoURL;
}
@Override
public String getDatabaseType() {
return databaseType;
}
@Override
public String getDatabaseName() {
return databaseName;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getIp() {
return ip;
}
@Override
public int getPort() {
if(port == -1) {
switch (getDatabaseType().toLowerCase()) {
case "mongodb":
case "mongo":
case "mongod":
return 27017;
case "sql":
case "mysql":
return 3306;
}
}
return port;
}
@Override
public boolean metrics() {
return metrics;
}
public void update() {
license = licenseDefault.get();
kickMessage = kickStringDefault.get();
cacheResults = cacheResultsDefault.get();
prefixWhitelists = prefixWhitelistsDefault.get();
databaseEnabled = defaultDatabaseEnabled.get();
useCredentials = defaultUseCredentials.get();
databaseType = defaultDatabaseType.get();
databaseName = defaultDatabaseName.get();
mongoURL = defaultMongoURL.get();
username = defaultUsername.get();
password = defaultPassword.get();
ip = defaultIp.get();
port = defaultPort.get();
commandsEnabled = defaultCommandsEnable.get();
commands = defaultCommands.get();
kickPlayers = defaultKickPlayers.get();
alertToStaff = defaultAlertToStaff.get();
alertMsg = defaultAlertMsg.get();
metrics = defaultMetrics.get();
}
}
@@ -0,0 +1,96 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.VPNExecutor;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import net.md_5.bungee.event.EventHandler;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class BungeeListener extends VPNExecutor implements Listener {
private ScheduledTask cacheResetTask;
@Override
public void registerListeners() {
BungeePlugin.pluginInstance.getProxy().getPluginManager()
.registerListener(BungeePlugin.pluginInstance, this);
}
@Override
public void runCacheReset() {
cacheResetTask = BungeePlugin.pluginInstance.getProxy().getScheduler().schedule(BungeePlugin.pluginInstance,
this::resetCache, 20, 20, TimeUnit.MINUTES);
}
@Override
public void shutdown() {
if(cacheResetTask != null) {
cacheResetTask.cancel();
cacheResetTask = null;
}
threadExecutor.shutdown();
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListener(this);
}
@Override
public void log(String log, Object... objects) {
BungeeCord.getInstance().getLogger().log(Level.INFO, String.format(log, objects));
}
@EventHandler
public void onListener(final PostLoginEvent event) {
if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getAddress().getAddress()
.getHostAddress())
|| AntiVPN.getInstance().getConfig().getPrefixWhitelists().stream()
.anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return;
checkIp(event.getPlayer().getAddress().getAddress().getHostAddress(),
AntiVPN.getInstance().getConfig().cachedResults(), result -> {
if(result.isSuccess() && result.isProxy()) {
if(AntiVPN.getInstance().getConfig().kickPlayersOnDetect())
event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
AntiVPN.getInstance().getConfig().getKickString())));
BungeeCord.getInstance().getLogger().info(event.getPlayer().getName()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
if(AntiVPN.getInstance().getConfig().alertToStaff()) //Ensuring the user wishes to alert to staff
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.filter(APIPlayer::isAlertsEnabled)
.forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getConfig().alertMessage()
.replace("%player%", event.getPlayer().getName())
.replace("%reason%", result.getMethod())
.replace("%country%", result.getCountryName())
.replace("%city%", result.getCity())));
//In case the user wants to run their own commands instead of using the built in kicking
if(AntiVPN.getInstance().getConfig().runCommands()) {
for (String command : AntiVPN.getInstance().getConfig().commands()) {
BungeeCord.getInstance().getPluginManager()
.dispatchCommand(BungeeCord.getInstance().getConsole(),
ChatColor.translateAlternateColorCodes('&',
command.replace("%player%", event.getPlayer().getName())));
}
}
AntiVPN.getInstance().detections++;
} else if(!result.isSuccess()) {
BungeeCord.getInstance().getLogger()
.log(Level.WARNING,
"The API query was not a success! " +
"You may need to upgrade your license on https://funkemunky.cc/shop");
}
AntiVPN.getInstance().checked++;
});
}
}
@@ -0,0 +1,34 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.api.APIPlayer;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public class BungeePlayer extends APIPlayer {
private final ProxiedPlayer player;
public BungeePlayer(ProxiedPlayer player) {
super(player.getUniqueId(), player.getName(), player.getAddress().getAddress());
this.player = player;
}
@Override
public void sendMessage(String message) {
player.sendMessage(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&', message)));
}
@Override
public void kickPlayer(String reason) {
player.disconnect(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&', reason)));
}
@Override
public boolean hasPermission(String permission) {
return player.hasPermission(permission);
}
}
@@ -0,0 +1,39 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.PlayerExecutor;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.*;
import java.util.stream.Collectors;
public class BungeePlayerExecutor implements PlayerExecutor {
private final Map<ProxiedPlayer, BungeePlayer> cachedPlayers = new WeakHashMap<>();
@Override
public Optional<APIPlayer> getPlayer(String name) {
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(name);
if(player == null) return Optional.empty();
return Optional.of(cachedPlayers.computeIfAbsent(player, BungeePlayer::new));
}
@Override
public Optional<APIPlayer> getPlayer(UUID uuid) {
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(uuid);
if(player == null) return Optional.empty();
return Optional.of(cachedPlayers.computeIfAbsent(player, BungeePlayer::new));
}
@Override
public List<APIPlayer> getOnlinePlayers() {
return BungeeCord.getInstance().getPlayers().stream()
.map(pl -> cachedPlayers.computeIfAbsent(pl, BungeePlayer::new))
.collect(Collectors.toList());
}
}
@@ -0,0 +1,120 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.bungee.util.Config;
import dev.brighten.antivpn.bungee.util.ConfigDefault;
import dev.brighten.antivpn.command.Command;
import lombok.Getter;
import lombok.val;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Plugin;
import org.bstats.bungeecord.Metrics;
import org.bstats.charts.SingleLineChart;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class BungeePlugin extends Plugin {
public static BungeePlugin pluginInstance;
@Getter
private Config config;
private SingleLineChart vpnDetections, ipsChecked;
private static final BaseComponent[] noPermission = new ComponentBuilder("No permission").color(ChatColor.RED)
.create();
@Override
public void onEnable() {
pluginInstance = this;
//Setting up config
BungeeCord.getInstance().getLogger().info("Loading config...");
config = new Config();
//Loading plugin
BungeeCord.getInstance().getLogger().info("Starting AntiVPN services...");
AntiVPN.start(new BungeeConfig(), new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
if(AntiVPN.getInstance().getConfig().metrics()) {
BungeeCord.getInstance().getLogger().info("Starting bStats metrics...");
Metrics metrics = new Metrics(this, 12616);
metrics.addCustomChart(vpnDetections = new SingleLineChart("vpn_detections",
() -> AntiVPN.getInstance().detections));
metrics.addCustomChart(ipsChecked = new SingleLineChart("ips_checked",
() -> AntiVPN.getInstance().checked));
BungeeCord.getInstance().getScheduler().schedule(this,
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
10, 10, TimeUnit.MINUTES);
}
//TODO Add command functionality for BungeeCord
for (Command command : AntiVPN.getInstance().getCommands()) {
BungeeCord.getInstance().getPluginManager().registerCommand(pluginInstance, new net.md_5.bungee.api.plugin
.Command(command.name(), command.permission(), command.aliases()) {
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("antivpn.command.*")
&& !sender.hasPermission(command.permission())) {
sender.sendMessage(noPermission);
return;
}
val children = command.children();
if(children.length > 0 && args.length > 0) {
for (Command child : children) {
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
if(!sender.hasPermission("antivpn.command.*")
&& !sender.hasPermission(child.permission())) {
sender.sendMessage(noPermission);
return;
}
sender.sendMessage(TextComponent
.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
child.execute(new BungeeCommandExecutor(sender), IntStream
.range(0, args.length - 1)
.mapToObj(i -> args[i + 1]).toArray(String[]::new)))));
return;
}
}
}
sender.sendMessage(TextComponent
.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
command.execute(new BungeeCommandExecutor(sender), args))));
}
});
}
BungeeCord.getInstance().getLogger().info("Getting strings...");
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), BungeePlugin.pluginInstance)
.get());
//TODO Finish system before implementing on startup
/*BungeeCord.getInstance().getLogger().info("Getting strings...");
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), BungeePlugin.pluginInstance)
.get());
AntiVPN.getInstance().getMessageHandler().reloadStrings();*/
}
@Override
public void onDisable() {
AntiVPN.getInstance().stop();
}
}
@@ -0,0 +1,114 @@
package dev.brighten.antivpn.bungee.util;
import com.google.common.io.ByteStreams;
import dev.brighten.antivpn.bungee.BungeePlugin;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Author: nitramleo (Martin)
* Date created: 10-Aug-18
*/
public class Config {
private File file;
private Configuration configuration;
public Config() {
this.file = new File(BungeePlugin.pluginInstance.getDataFolder(), "config.yml");
try {
if (!this.file.exists()) {
if (!BungeePlugin.pluginInstance.getDataFolder().exists()) {
BungeePlugin.pluginInstance.getDataFolder().mkdir();
}
this.file.createNewFile();
try (final InputStream is = BungeePlugin.pluginInstance.getResourceAsStream("config.yml");
final OutputStream os = new FileOutputStream(this.file)) {
ByteStreams.copy(is, os);
}
}
this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(this.file);
}
catch (IOException e) {
e.printStackTrace();
}
}
public void load() {
this.file = new File(BungeePlugin.pluginInstance.getDataFolder(), "config.yml");
try {
this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(this.file);
}
catch (IOException e) {
e.printStackTrace();
}
}
public void save() {
try {
ConfigurationProvider.getProvider( YamlConfiguration.class).save(this.configuration, this.file);
}
catch (IOException e) {
e.printStackTrace();
}
}
public Configuration getConfiguration() {
return this.configuration;
}
public File getFile() {
return this.file;
}
public double getDouble(final String path) {
if (this.configuration.get(path) != null) {
return this.configuration.getDouble(path);
}
return 0.0;
}
public int getInt(final String path) {
if (this.configuration.get(path) != null) {
return this.configuration.getInt(path);
}
return 0;
}
public Object get(final String path) {
return this.configuration.get(path);
}
public void set(final String path, final Object object) {
configuration.set(path, object);
}
public boolean getBoolean(final String path) {
return this.configuration.get(path) != null && this.configuration.getBoolean(path);
}
public String getString(final String path) {
if (this.configuration.get(path) != null) {
return ChatColor.translateAlternateColorCodes('&', this.configuration.getString(path));
}
return "String at path: " + path + " not found!";
}
public List<String> getStringList(final String path) {
if (this.configuration.get(path) != null) {
final ArrayList<String> strings = new ArrayList<String>();
for (final String string : this.configuration.getStringList(path)) {
strings.add(ChatColor.translateAlternateColorCodes('&', string));
}
return strings;
}
return Arrays.asList("String List at path: " + path + " not found!");
}
}
@@ -0,0 +1,28 @@
package dev.brighten.antivpn.bungee.util;
import dev.brighten.antivpn.bungee.BungeePlugin;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class ConfigDefault<A> {
private final A defaultValue;
private final String path;
private final BungeePlugin plugin;
public A get() {
if(plugin.getConfig().get(path) != null)
return (A) plugin.getConfig().get(path);
else {
plugin.getConfig().set(path, defaultValue);
plugin.getConfig().save();
return defaultValue;
}
}
public A set(A value) {
plugin.getConfig().set(path, value);
return value;
}
}
+5
View File
@@ -0,0 +1,5 @@
name: KauriVPN
main: dev.brighten.antivpn.bungee.BungeePlugin
description: A simple and fast antivpn plugin.
version: ${project.version}
author: funkemunky
+75
View File
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.6.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</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.moandjiezana.toml</groupId>
<artifactId>toml4j</artifactId>
<version>0.7.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.12.10</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,113 @@
package dev.brighten.antivpn;
import dev.brighten.antivpn.api.PlayerExecutor;
import dev.brighten.antivpn.api.VPNConfig;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.impl.AntiVPNCommand;
import dev.brighten.antivpn.database.VPNDatabase;
import dev.brighten.antivpn.database.mongo.MongoVPN;
import dev.brighten.antivpn.database.sql.MySqlVPN;
import dev.brighten.antivpn.message.MessageHandler;
import dev.brighten.antivpn.utils.VPNResponse;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.utils.json.JSONObject;
import dev.brighten.antivpn.utils.json.JsonReader;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter(AccessLevel.PRIVATE)
public class AntiVPN {
private static AntiVPN INSTANCE;
private VPNConfig config;
private VPNExecutor executor;
private PlayerExecutor playerExecutor;
private VPNDatabase database;
private MessageHandler messageHandler;
private List<Command> commands = new ArrayList<>();
public int detections, checked;
private File pluginFolder;
public static void start(VPNConfig config, VPNExecutor executor, PlayerExecutor playerExecutor, File pluginFolder) {
//Initializing
INSTANCE = new AntiVPN();
INSTANCE.pluginFolder = pluginFolder;
INSTANCE.config = config;
INSTANCE.executor = executor;
INSTANCE.playerExecutor = playerExecutor;
INSTANCE.executor.registerListeners();
INSTANCE.config.update();
INSTANCE.messageHandler = new MessageHandler();
switch(INSTANCE.config.getDatabaseType().toLowerCase()) {
case "mysql":
case "sql":{
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
INSTANCE.database = new MySqlVPN();
INSTANCE.database.init();
break;
}
case "mongo":
case "mongodb":
case "mongod": {
INSTANCE.database = new MongoVPN();
INSTANCE.database.init();
break;
}
default: {
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.config.getDatabaseType() + "\". " +
"Options: [MySQL]");
break;
}
}
//Registering commands
INSTANCE.registerCommands();
//Turning on alerts of players who are already online.
playerExecutor.getOnlinePlayers().forEach(player -> {
//We want to make sure they even have permission to see alerts before we make a bunch
//of unnecessary database queries.
if(player.hasPermission("antivpn.command.alerts")) {
//Running database check for enabled alerts.
INSTANCE.database.alertsState(player.getUuid(), player::setAlertsEnabled);
}
});
}
public void stop() {
executor.shutdown();
if(database != null) database.shutdown();
}
public static AntiVPN getInstance() {
assert INSTANCE != null: "AntiVPN has not been initialized!";
return INSTANCE;
}
public static VPNResponse getVPNResponse(String ip, String license, boolean cachedResults /* faster if set to true*/)
throws JSONException, IOException {
JSONObject result = JsonReader.readJsonFromUrl(String
.format("https://funkemunky.cc/vpn?ip=%s&license=%s&cache=%s",
ip, license.length() == 0 ? "none" : license, cachedResults));
return VPNResponse.fromJson(result);
}
private void registerCommands() {
commands.add(new AntiVPNCommand());
}
}
@@ -0,0 +1,39 @@
package dev.brighten.antivpn.api;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.message.VpnString;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.net.InetAddress;
import java.util.UUID;
@Getter
public abstract class APIPlayer {
private final UUID uuid;
private final String name;
private final InetAddress ip;
private boolean alertsEnabled;
public APIPlayer(UUID uuid, String name, InetAddress ip) {
this.uuid = uuid;
this.name = name;
this.ip = ip;
}
public abstract void sendMessage(String message);
public abstract void kickPlayer(String reason);
public abstract boolean hasPermission(String permission);
public void setAlertsEnabled(boolean alertsEnabled) {
this.alertsEnabled = alertsEnabled;
}
public void updateAlertsState() {
//Updating into database so its synced across servers and saved on logout.
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
}
}
@@ -0,0 +1,14 @@
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);
List<APIPlayer> getOnlinePlayers();
}
@@ -0,0 +1,47 @@
package dev.brighten.antivpn.api;
import java.util.List;
public interface VPNConfig {
String getLicense();
boolean cachedResults();
String getKickString();
String alertMessage();
boolean alertToStaff();
boolean runCommands();
List<String> commands();
boolean kickPlayersOnDetect();
List<String> getPrefixWhitelists();
boolean isDatabaseEnabled();
boolean useDatabaseCreds();
String mongoDatabaseURL();
String getDatabaseType();
String getDatabaseName();
String getUsername();
String getPassword();
String getIp();
int getPort();
boolean metrics();
void update();
}
@@ -0,0 +1,102 @@
package dev.brighten.antivpn.api;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.utils.EvictingMap;
import dev.brighten.antivpn.utils.VPNResponse;
import dev.brighten.antivpn.utils.json.JSONException;
import lombok.Getter;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public abstract class VPNExecutor {
public static ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
public static final Map<String, VPNResponse> responseCache = new EvictingMap<>(5000);
@Getter
private final Set<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
@Getter
private final Set<String> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
public abstract void registerListeners();
public abstract void runCacheReset();
public void resetCache() {
responseCache.clear();
}
public abstract void shutdown();
public abstract void log(String log, Object... objects);
public boolean isWhitelisted(UUID uuid) {
return whitelisted.contains(uuid);
}
public boolean isWhitelisted(String ip) {
return whitelistedIps.contains(ip);
}
public void checkIp(String ip, boolean cachedResults, Consumer<VPNResponse> result) {
threadExecutor.execute(() -> result.accept(responseCache.compute(ip, (key, val) -> {
if(val == null) {
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
if(cachedRes.isPresent()) return cachedRes.get();
else {
try {
VPNResponse response = AntiVPN
.getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults);
if(response.isSuccess()) {
AntiVPN.getInstance().getDatabase().cacheResponse(response);
} else {
log("Query to VPN API failed! Reason: " + response.getFailureReason());
}
return response;
} catch (JSONException | IOException e) {
log("Query to VPN API failed! Reason: Java Exception");
e.printStackTrace();
}
}
}
return val;
})));
}
public VPNResponse checkIp(String ip, boolean cachedResults) {
return responseCache.compute(ip, (key, val) -> {
if(val == null) {
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
if(cachedRes.isPresent()) return cachedRes.get();
else {
try {
VPNResponse response = AntiVPN
.getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults);
if(response.isSuccess()) {
threadExecutor.execute(() -> AntiVPN.getInstance().getDatabase().cacheResponse(response));
} else {
log("Query to VPN API failed! Reason: " + response.getFailureReason());
}
return response;
} catch (JSONException | IOException e) {
log("Query to VPN API failed! Reason: Java Exception");
e.printStackTrace();
}
}
}
return val;
});
}
}
@@ -0,0 +1,24 @@
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,15 @@
package dev.brighten.antivpn.command;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.PlayerExecutor;
import java.util.Optional;
public interface CommandExecutor {
void sendMessage(String message);
boolean hasPermission(String permission);
Optional<APIPlayer> getPlayer();
boolean isPlayer();
}
@@ -0,0 +1,68 @@
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.message.VpnString;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class AlertsCommand extends Command {
@Override
public String permission() {
return "antivpn.command.alerts";
}
@Override
public String name() {
return "alerts";
}
@Override
public String[] aliases() {
return new String[] {"valerts", "vpnalerts"};
}
@Override
public String description() {
return "toggle VPN use alerts";
}
@Override
public String usage() {
return "";
}
@Override
public String parent() {
return "antivpn";
}
@Override
public Command[] children() {
return new Command[0];
}
@Override
public String execute(CommandExecutor executor, String[] args) {
Optional<APIPlayer> pgetter = executor.getPlayer();
if(!pgetter.isPresent()) return AntiVPN.getInstance().getMessageHandler()
.getString("command-misc-playerRequired").getMessage();
APIPlayer player = pgetter.get();
player.setAlertsEnabled(!player.isAlertsEnabled());
player.updateAlertsState();
return AntiVPN.getInstance().getMessageHandler().getString("command-alerts-toggled")
.getFormattedMessage(new VpnString.Var<>("state", player.isAlertsEnabled()));
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
return Collections.emptyList();
}
}
@@ -0,0 +1,162 @@
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.MiscUtils;
import java.util.*;
import java.util.stream.Collectors;
public class AllowlistCommand extends Command {
private static final String[] secondArgs = new String[] {"add", "remove"};
@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/remove> <player/uuid/ip>";
}
@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.length == 1)
return "&cYou have to provide a player to allow or deny exemption.";
boolean databaseEnabled = AntiVPN.getInstance().getConfig().isDatabaseEnabled();
if(!databaseEnabled) executor.sendMessage("&cThe database is currently not setup, " +
"so any changes here will disappear after a restart.");
if(MiscUtils.isIpv4(args[1])) {
if(!databaseEnabled) {
switch(args[0].toLowerCase()) {
case "add": {
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(args[1]);
return String.format("&aAdded &6%s &ato the exemption allowlist.", args[1]);
}
case "remove":
case "delete": {
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(args[1]);
return String.format("&cRemoved &6%s &cfrom the exemption allowlist.", args[1]);
}
default: {
return "&c\"" + args[0] + "\" is not a valid argument";
}
}
} else {
switch(args[0].toLowerCase()) {
case "add": {
AntiVPN.getInstance().getDatabase().setWhitelisted(args[1], true);
return String.format("&aAdded &6%s &a to the exemption allowlist.", args[1]);
}
case "remove":
case "delete": {
AntiVPN.getInstance().getDatabase().setWhitelisted(args[1], false);
return String.format("&cRemoved &6%s &c from the exemption allowlist.", args[1]);
}
default: {
return "&c\"" + args[0] + "\" is not a valid argument";
}
}
}
} else {
UUID uuid = null;
try {
uuid = UUID.fromString(args[1]);
} catch(IllegalArgumentException e) {
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[1]);
if(!player.isPresent()) {
return "&cThe player \"" + args[1] + "\" is not online, so please provide a UUID.";
}
uuid = player.get().getUuid();
}
if(!databaseEnabled) {
switch(args[0].toLowerCase()) {
case "add": {
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
return String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
}
case "remove":
case "delete": {
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
return String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
}
default: {
return "&c\"" + args[0] + "\" is not a valid argument";
}
}
} else {
switch(args[0].toLowerCase()) {
case "add": {
AntiVPN.getInstance().getDatabase().setWhitelisted(uuid, true);
return String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
}
case "remove":
case "delete": {
AntiVPN.getInstance().getDatabase().setWhitelisted(uuid, false);
return String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
}
default: {
return "&c\"" + args[0] + "\" is not a valid argument";
}
}
}
}
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
switch(args.length) {
case 1: {
return Arrays.stream(secondArgs)
.filter(narg -> narg.toLowerCase().startsWith(args[0].toLowerCase()))
.collect(Collectors.toList());
}
case 2: {
return AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.map(APIPlayer::getName)
.filter(name -> name.toLowerCase().startsWith(args[1].toLowerCase()))
.collect(Collectors.toList());
}
}
return Collections.emptyList();
}
}
@@ -0,0 +1,81 @@
package dev.brighten.antivpn.command.impl;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.CommandExecutor;
import dev.brighten.antivpn.utils.StringUtil;
import java.util.*;
import java.util.stream.Collectors;
public class AntiVPNCommand extends Command {
@Override
public String permission() {
return "antivpn.command";
}
@Override
public String name() {
return "antivpn";
}
@Override
public String[] aliases() {
return new String[] {"kaurivpn", "kvpn", "vpn", "avpn"};
}
@Override
public String description() {
return "The main help command";
}
@Override
public String usage() {
return "";
}
@Override
public String parent() {
return "";
}
@Override
public Command[] children() {
return new Command[] {new LookupCommand(), new AllowlistCommand(), new AlertsCommand(),
new ClearCacheCommand()};
}
@Override
public String execute(CommandExecutor uuid, String[] args) {
List<String> messages = new ArrayList<>();
messages.add(StringUtil.line("&8"));
messages.add("&6&lAntiVPN Help Page");
messages.add("");
for (Command cmd : AntiVPN.getInstance().getCommands()) {
messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + cmd.parent()
+ (cmd.parent().length() > 0 ? " " : "") + "&f" + cmd.name() + " &7"
+ cmd.usage(), cmd.description()));
}
for (Command child : children()) {
messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + child.parent()
+ (child.parent().length() > 0 ? " " : "") + "&f" + child.name() + " &7"
+ child.usage(), child.description()));
}
messages.add(StringUtil.line("&8"));
return String.join("\n", messages);
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
if(args.length == 1)
return Arrays.stream(children())
.map(Command::name)
.filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase()))
.collect(Collectors.toList());
return Collections.emptyList();
}
}
@@ -0,0 +1,58 @@
package dev.brighten.antivpn.command.impl;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.CommandExecutor;
import java.util.Collections;
import java.util.List;
public class ClearCacheCommand extends Command {
@Override
public String permission() {
return "antivpn.command.clearcache";
}
@Override
public String name() {
return "clearcache";
}
@Override
public String[] aliases() {
return new String[] {"clear", "cc"};
}
@Override
public String description() {
return "Clear the API response cache if you're having problems.";
}
@Override
public String usage() {
return "";
}
@Override
public String parent() {
return "antivpn";
}
@Override
public Command[] children() {
return new Command[0];
}
@Override
public String execute(CommandExecutor executor, String[] args) {
AntiVPN.getInstance().getDatabase().clearResponses();
VPNExecutor.responseCache.clear();
return "&aCleared all cached API response information!";
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
return Collections.emptyList();
}
}
@@ -0,0 +1,96 @@
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.StringUtil;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors;
public class LookupCommand extends Command {
@Override
public String permission() {
return "antivpn.command.lookup";
}
@Override
public String name() {
return "lookup";
}
@Override
public String[] aliases() {
return new String[] {"check"};
}
@Override
public String description() {
return "Lookup a player's ip info";
}
@Override
public String usage() {
return "<player>";
}
@Override
public String parent() {
return "antivpn";
}
@Override
public Command[] children() {
return new Command[0];
}
@Override
public String execute(CommandExecutor executor, String[] args) {
if(args.length == 0) {
return "&cPlease supply a player to check.";
}
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[0]);
if(!player.isPresent()) {
return String.format("&cNo player found with the name \"%s\"", args[0]);
}
AntiVPN.getInstance().getExecutor().checkIp(player.get().getIp().getHostAddress(),
false, result -> {
if(!result.isSuccess()) {
executor.sendMessage("&cThere was an error trying to find the information of this player.");
} else {
executor.sendMessage(StringUtil.line("&8"));
executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information");
executor.sendMessage("");
executor.sendMessage(String.format("&e%s&8: &f%s", "Proxy", result.isProxy()
? "&a" + result.getMethod() : "&cNo"));
executor.sendMessage(String.format("&e%s&8: &f%s", "ISP", result.getIsp()));
executor.sendMessage(String.format("&e%s&8: &f%s", "Country", result.getCountryName()));
executor.sendMessage(String.format("&e%s&8: &f%s", "City", result.getCity()));
executor.sendMessage(String.format("&e%s&8: &f%s", "Coordinates", result.getLatitude()
+ "&7/&f" + result.getLongitude()));
executor.sendMessage(StringUtil.line("&8"));
}
});
return "&7Looking up the IP information for player " + player.get().getName() + "...";
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
if(args.length == 1) return AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.map(APIPlayer::getName)
.filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase()))
.collect(Collectors.toList());
return Collections.emptyList();
}
}
@@ -0,0 +1,42 @@
package dev.brighten.antivpn.database;
import dev.brighten.antivpn.utils.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);
boolean isWhitelisted(UUID uuid);
boolean isWhitelisted(String ip);
void setWhitelisted(UUID uuid, boolean whitelisted);
void setWhitelisted(String ip, boolean whitelisted);
List<UUID> getAllWhitelisted();
List<String> getAllWhitelistedIps();
void getStoredResponseAsync(String ip, Consumer<Optional<VPNResponse>> result);
void isWhitelistedAsync(UUID uuid, Consumer<Boolean> result);
void isWhitelistedAsync(String ip, Consumer<Boolean> result);
void alertsState(UUID uuid, Consumer<Boolean> result);
void updateAlertsState(UUID uuid, boolean state);
void clearResponses();
void init();
void shutdown();
}
@@ -0,0 +1,208 @@
package dev.brighten.antivpn.database.mongo;
import com.mongodb.*;
import com.mongodb.client.MongoClient;
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 dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.database.VPNDatabase;
import dev.brighten.antivpn.utils.VPNResponse;
import org.bson.Document;
import java.util.*;
import java.util.function.Consumer;
public class MongoVPN implements VPNDatabase {
private MongoCollection<Document> settingsDocument, cacheDocument;
private MongoClient client;
private MongoDatabase antivpnDatabase;
@Override
public Optional<VPNResponse> getStoredResponse(String ip) {
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
if(rdoc != null) {
return Optional.of(VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip)
.countryName(rdoc.getString("countryName"))
.countryCode(rdoc.getString("countryCode"))
.city(rdoc.getString("city"))
.isp(rdoc.getString("isp"))
.method(rdoc.getString("method"))
.timeZone(rdoc.getString("timeZone"))
.proxy(rdoc.getBoolean("proxy"))
.cached(rdoc.getBoolean("cached"))
.success(true)
.latitude(rdoc.getDouble("latitude"))
.longitude(rdoc.getDouble("longitude"))
.build());
}
return Optional.empty();
}
@Override
public void cacheResponse(VPNResponse toCache) {
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());
VPNExecutor.threadExecutor.execute(() -> {
cacheDocument.deleteMany(Filters.eq("ip", toCache.getIp()));
cacheDocument.insertOne(rdoc);
});
}
@Override
public boolean isWhitelisted(UUID uuid) {
return settingsDocument
.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.eq("uuid", uuid.toString()))).first() != null;
}
@Override
public boolean isWhitelisted(String 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());
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
} else {
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
.and(
Filters.eq("setting", "whitelist"),
Filters.eq("uuid", uuid.toString()))));
}
}
@Override
public void setWhitelisted(String ip, boolean whitelisted) {
if(whitelisted) {
Document wdoc = new Document("setting", "whitelist");
wdoc.put("ip", ip);
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
} else {
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
.and(
Filters.eq("setting", "whitelist"),
Filters.eq("ip", ip))));
}
}
@Override
public List<UUID> getAllWhitelisted() {
List<UUID> uuids = new ArrayList<>();
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.exists("uuid")))
.forEach((Consumer<? super Document>) doc -> uuids.add(UUID.fromString(doc.getString("uuid"))));
return uuids;
}
@Override
public List<String> getAllWhitelistedIps() {
List<String> ips = new ArrayList<>();
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.exists("ip")))
.forEach((Consumer<? super Document>) doc -> ips.add(doc.getString("ip")));
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
.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(() -> {
settingsDocument.deleteMany(Filters.and(Filters.eq("setting", "alerts"),
Filters.eq("uuid", uuid.toString())));
if(state) {
Document adoc = new Document("setting", "alerts");
adoc.put("uuid", uuid.toString());
settingsDocument.insertOne(adoc);
}
});
}
@Override
public void clearResponses() {
cacheDocument.deleteMany(Filters.exists("ip"));
}
@Override
public void init() {
if(AntiVPN.getInstance().getConfig().mongoDatabaseURL().length() > 0) { //URL
ConnectionString cs = new ConnectionString(AntiVPN.getInstance().getConfig().mongoDatabaseURL());
MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(cs).build();
client = MongoClients.create(settings);
} else {
MongoClientSettings.Builder settingsBld = MongoClientSettings.builder().readPreference(ReadPreference.nearest())
.applyToClusterSettings(builder -> {
builder.hosts(Collections.singletonList(new ServerAddress(AntiVPN.getInstance().getConfig().getIp(),
AntiVPN.getInstance().getConfig().getPort())));
});
if(AntiVPN.getInstance().getConfig().useDatabaseCreds()) {
settingsBld = settingsBld.credential(MongoCredential
.createCredential(AntiVPN.getInstance().getConfig().getUsername(),
AntiVPN.getInstance().getConfig().getDatabaseName(),
AntiVPN.getInstance().getConfig().getPassword().toCharArray()));
}
client = MongoClients.create(settingsBld.build());
}
antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getConfig().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");
}
@Override
public void shutdown() {
settingsDocument = null;
cacheDocument = null;
client.close();
}
}
@@ -0,0 +1,338 @@
package dev.brighten.antivpn.database.sql;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.database.VPNDatabase;
import dev.brighten.antivpn.database.sql.utils.MySQL;
import dev.brighten.antivpn.database.sql.utils.Query;
import dev.brighten.antivpn.utils.VPNResponse;
import lombok.SneakyThrows;
import java.sql.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class MySqlVPN implements VPNDatabase {
private Thread whitelistedThread;
public MySqlVPN() {
whitelistedThread = new Thread(() -> {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(2));
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
// Updating from database
if (AntiVPN.getInstance().getConfig().isDatabaseEnabled()) {
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
AntiVPN.getInstance().getExecutor().getWhitelisted()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
}
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(4));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
whitelistedThread.start();
}
@Override
public Optional<VPNResponse> getStoredResponse(String ip) {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled()|| MySQL.isClosed())
return Optional.empty();
ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).executeQuery();
try {
if (rs != null && rs.next()) {
VPNResponse response = 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"),
System.currentTimeMillis(), -1);
return Optional.of(response);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return Optional.empty();
}
/*
* Query.
* prepare("create table if not exists `responses` (`ip` varchar(45) not null, "
* +
* "`countryName` varchar(64), `countryCode` varchar(10), `city` varchar(64), `timeZone` varchar(64), "
* +
* "`method` varchar(32), `isp` varchar(32), `proxy` boolean, `cached` boolean "
* + "`latitude` double, `longitude` double)");
*/
@Override
public void cacheResponse(VPNResponse toCache) {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled() || MySQL.isClosed())
return;
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()).execute();
}
@SneakyThrows
@Override
public boolean isWhitelisted(UUID uuid) {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled() || MySQL.isClosed())
return false;
ResultSet set = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
.append(uuid.toString()).executeQuery();
return set != null && set.next() && set.getString("uuid") != null;
}
@SneakyThrows
@Override
public boolean isWhitelisted(String ip) {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled() || MySQL.isClosed())
return false;
ResultSet set = Query.prepare("select `ip` from `whitelisted-ips` where `ip` = ? limit 1")
.append(ip).executeQuery();
return set != null && set.next() && set.getString("ip") != null;
}
@Override
public void setWhitelisted(UUID uuid, boolean whitelisted) {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled() || MySQL.isClosed())
return;
if (whitelisted) {
if (!isWhitelisted(uuid)) {
Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString()).execute();
}
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
} else {
Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString()).execute();
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
}
}
@Override
public void setWhitelisted(String ip, boolean whitelisted) {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled() || MySQL.isClosed())
return;
if(whitelisted) {
if(!isWhitelisted(ip)) {
Query.prepare("insert into `whitelisted-ips` (`ip`) values (?)").append(ip).execute();
}
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip);
} else {
Query.prepare("delete from `whitelisted-ips` where `ip` = ?").append(ip).execute();
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip);
}
}
@Override
public List<UUID> getAllWhitelisted() {
List<UUID> uuids = new ArrayList<>();
if(!MySQL.isClosed()) Query.prepare("select uuid from `whitelisted`")
.execute(set -> uuids.add(UUID.fromString(set.getString("uuid"))));
return uuids;
}
@Override
public List<String> getAllWhitelistedIps() {
List<String> ips = new ArrayList<>();
if(!MySQL.isClosed()) Query.prepare("select `ip` from `whitelisted-ips`")
.execute(set -> ips.add(set.getString("ip")));
return ips;
}
@Override
public void getStoredResponseAsync(String ip, Consumer<Optional<VPNResponse>> result) {
if(MySQL.isClosed()) return;
VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip)));
}
@Override
public void isWhitelistedAsync(UUID uuid, Consumer<Boolean> result) {
if(MySQL.isClosed()) return;
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid)));
}
@Override
public void isWhitelistedAsync(String ip, Consumer<Boolean> result) {
if(MySQL.isClosed()) return;
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip)));
}
@Override
public void alertsState(UUID uuid, Consumer<Boolean> result) {
if(MySQL.isClosed()) return;
VPNExecutor.threadExecutor.execute(() -> {
ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
.append(uuid.toString()).executeQuery();
try {
result.accept(set != null && set.next() && set.getString("uuid") != null);
} catch (SQLException e) {
e.printStackTrace();
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) {
Query.prepare("insert into `alerts` (`uuid`) values (?)").append(uuid.toString())
.execute();
} //No need to insert again of already enabled
});
//Removing any uuid from the alerts table will disable alerts globally.
} else VPNExecutor.threadExecutor.execute(() ->
Query.prepare("delete from `alerts` where `uuid` = ?")
.append(uuid.toString())
.execute());
}
@Override
public void clearResponses() {
if(MySQL.isClosed()) return;
VPNExecutor.threadExecutor.execute(() -> Query.prepare("delete from `responses`").execute());
}
@Override
public void init() {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled())
return;
AntiVPN.getInstance().getExecutor().log("Initializing MySQL...");
MySQL.init();
AntiVPN.getInstance().getExecutor().log("Creating tables...");
//Running check for old table types to update
oldTableCheck: {
Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " +
"WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';").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...");
if(Query.prepare("drop table `responses`").execute() > 0) {
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
}
}
});
}
Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)").execute();
Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)").execute();
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();
Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)").execute();
AntiVPN.getInstance().getExecutor().log("Creating indexes...");
try {
// Ref:
// https://dba.stackexchange.com/questions/24531/mysql-create-index-if-not-exists
String query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" +
" AND table_name='whitelisted' AND index_name='uuid_1';";
ResultSet rs = Query.prepare(query).executeQuery();
int id = 0;
whitelistedIndex: {
while (rs.next()) {
id = rs.getInt("IndexExists");
}
if (id == 0) {
Query.prepare("create index `uuid_1` on `whitelisted` (`uuid`)").execute();
}
id = 0;
}
responsesIndex: {
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " +
"AND table_name='responses' AND index_name='ip_1';";
rs = Query.prepare(query).executeQuery();
while (rs.next()) {
id = rs.getInt("IndexExists");
}
if (id == 0) {
Query.prepare("create index `ip_1` on `responses` (`ip`)").execute();
}
id = 0;
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " +
"AND table_name='responses' AND index_name='proxy_1';";
rs = Query.prepare(query).executeQuery();
while (rs.next()) {
id = rs.getInt("IndexExists");
}
if (id == 0) {
Query.prepare("create index `proxy_1` on `responses` (`proxy`)").execute();
}
id = 0;
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" +
" AND table_name='responses' AND index_name='inserted_1';";
rs = Query.prepare(query).executeQuery();
while (rs.next()) {
id = rs.getInt("IndexExists");
}
if (id == 0) {
Query.prepare("create index `inserted_1` on `responses` (`inserted`)").execute();
}
id = 0;
}
whitelistedIpsIndex: {
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" +
" AND table_name='whitelisted-ips' AND index_name='ip_1';";
rs = Query.prepare(query).executeQuery();
while (rs.next()) {
id = rs.getInt("IndexExists");
}
if (id == 0) {
Query.prepare("create index `ip_1` on `whitelisted-ips` (`ip`)").execute();
}
}
} catch (Exception e) {
System.err.println("MySQL Excepton created" + e.getMessage());
}
}
@Override
public void shutdown() {
if (!AntiVPN.getInstance().getConfig().isDatabaseEnabled())
return;
MySQL.shutdown();
}
}
@@ -0,0 +1,138 @@
package dev.brighten.antivpn.database.sql.utils;
import dev.brighten.antivpn.utils.MiscUtils;
import lombok.SneakyThrows;
import java.sql.*;
import java.util.UUID;
public class ExecutableStatement {
private 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);
}
}
@SneakyThrows
public void execute(ResultSetIterator iterator) {
ResultSet rs = null;
try {
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);
}
}
@SneakyThrows
public ResultSet executeQuery() {
return statement.executeQuery();
}
@SneakyThrows
public ExecutableStatement append(Object obj) {
statement.setObject(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(String obj) {
statement.setString(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(UUID uuid) {
if (uuid != null) statement.setString(pos++, uuid.toString().replace("-", ""));
else statement.setString(pos++, null);
return this;
}
@SneakyThrows
public ExecutableStatement append(Array obj) {
statement.setArray(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Integer obj) {
statement.setInt(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Short obj) {
statement.setShort(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Long obj) {
statement.setLong(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Float obj) {
statement.setFloat(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Double obj) {
statement.setDouble(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Date obj) {
statement.setDate(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Timestamp obj) {
statement.setTimestamp(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Time obj) {
statement.setTime(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(Blob obj) {
statement.setBlob(pos++, obj);
return this;
}
@SneakyThrows
public ExecutableStatement append(byte[] obj) {
statement.setBytes(pos++, obj);
return this;
}
}
@@ -0,0 +1,87 @@
package dev.brighten.antivpn.database.sql.utils;
import dev.brighten.antivpn.AntiVPN;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class MySQL {
private static Connection conn;
public static void init() {
try {
if (conn == null || conn.isClosed()) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
Class.forName("com.mysql.jdbc.Driver");
}
conn = DriverManager.getConnection("jdbc:mysql://" + AntiVPN.getInstance().getConfig().getIp()
+ ":" + AntiVPN.getInstance().getConfig().getPort()
+ "/?useSSL=true&autoReconnect=true",
AntiVPN.getInstance().getConfig().getUsername(),
AntiVPN.getInstance().getConfig().getPassword());
conn.setAutoCommit(true);
Query.use(conn);
Query.prepare("CREATE DATABASE IF NOT EXISTS `"
+ AntiVPN.getInstance().getConfig().getDatabaseName() + "`").execute();
Query.prepare("USE `" + AntiVPN.getInstance().getConfig().getDatabaseName() + "`").execute();
AntiVPN.getInstance().getExecutor().log("Connection to MySQL has been established.");
}
} catch (Exception e) {
AntiVPN.getInstance().getExecutor().log("Failed to load mysql: " + e.getMessage());
e.printStackTrace();
}
}
/*public static void initH2() {
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases" + File.separator + "database");
try {
Class.forName("org.h2.Driver");
conn = new NonClosableConnection(new JdbcConnection("jdbc:h2:file:" +
dataFolder.getAbsolutePath(), new Properties()));
conn.setAutoCommit(true);
Query.use(conn);
AntiVPN.getInstance().getExecutor().log("Connection to SQlLite has been established.");
} catch (SQLException ex) {
AntiVPN.getInstance().getExecutor().log("SQLite exception on initialize");
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
AntiVPN.getInstance().getExecutor().log("No H2 library found!");
}
}*/
public static void use() {
try {
init();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void shutdown() {
try {
if(conn != null && !conn.isClosed()) {
if(conn instanceof NonClosableConnection) {
((NonClosableConnection)conn).shutdown();
} else conn.close();
conn = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean isClosed() {
if(conn == null)
return true;
try {
return conn.isClosed();
} catch (SQLException e) {
e.printStackTrace();
return true;
}
}
}
@@ -0,0 +1,122 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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 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.database.sql.utils;
import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
* A wrapper around a {@link Connection} which blocks usage of the default {@link #close()} method.
*/
public class NonClosableConnection implements Connection {
private final Connection delegate;
public NonClosableConnection(Connection delegate) {
this.delegate = delegate;
}
/**
* Actually {@link #close() closes} the underlying connection.
*/
public final void shutdown() throws SQLException {
this.delegate.close();
}
@Override
public final void close() throws SQLException {
// do nothing
}
@Override
public final boolean isWrapperFor(Class<?> iface) throws SQLException {
return iface.isInstance(this.delegate) || this.delegate.isWrapperFor(iface);
}
@SuppressWarnings("unchecked")
@Override
public final <T> T unwrap(Class<T> iface) throws SQLException {
if (iface.isInstance(this.delegate)) {
return (T) this.delegate;
}
return this.delegate.unwrap(iface);
}
// Forward to the delegate connection
@Override public Statement createStatement() throws SQLException { return this.delegate.createStatement(); }
@Override public PreparedStatement prepareStatement(String sql) throws SQLException { return this.delegate.prepareStatement(sql); }
@Override public CallableStatement prepareCall(String sql) throws SQLException { return this.delegate.prepareCall(sql); }
@Override public String nativeSQL(String sql) throws SQLException { return this.delegate.nativeSQL(sql); }
@Override public void setAutoCommit(boolean autoCommit) throws SQLException { this.delegate.setAutoCommit(autoCommit); }
@Override public boolean getAutoCommit() throws SQLException { return this.delegate.getAutoCommit(); }
@Override public void commit() throws SQLException { this.delegate.commit(); }
@Override public void rollback() throws SQLException { this.delegate.rollback(); }
@Override public boolean isClosed() throws SQLException { return this.delegate.isClosed(); }
@Override public DatabaseMetaData getMetaData() throws SQLException { return this.delegate.getMetaData(); }
@Override public void setReadOnly(boolean readOnly) throws SQLException { this.delegate.setReadOnly(readOnly); }
@Override public boolean isReadOnly() throws SQLException { return this.delegate.isReadOnly(); }
@Override public void setCatalog(String catalog) throws SQLException { this.delegate.setCatalog(catalog); }
@Override public String getCatalog() throws SQLException { return this.delegate.getCatalog(); }
@Override public void setTransactionIsolation(int level) throws SQLException { this.delegate.setTransactionIsolation(level); }
@Override public int getTransactionIsolation() throws SQLException { return this.delegate.getTransactionIsolation(); }
@Override public SQLWarning getWarnings() throws SQLException { return this.delegate.getWarnings(); }
@Override public void clearWarnings() throws SQLException { this.delegate.clearWarnings(); }
@Override public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.createStatement(resultSetType, resultSetConcurrency); }
@Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); }
@Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency); }
@Override public Map<String, Class<?>> getTypeMap() throws SQLException { return this.delegate.getTypeMap(); }
@Override public void setTypeMap(Map<String, Class<?>> map) throws SQLException { this.delegate.setTypeMap(map); }
@Override public void setHoldability(int holdability) throws SQLException { this.delegate.setHoldability(holdability); }
@Override public int getHoldability() throws SQLException { return this.delegate.getHoldability(); }
@Override public Savepoint setSavepoint() throws SQLException { return this.delegate.setSavepoint(); }
@Override public Savepoint setSavepoint(String name) throws SQLException { return this.delegate.setSavepoint(name); }
@Override public void rollback(Savepoint savepoint) throws SQLException { this.delegate.rollback(savepoint); }
@Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { this.delegate.releaseSavepoint(savepoint); }
@Override public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); }
@Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); }
@Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); }
@Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { return this.delegate.prepareStatement(sql, autoGeneratedKeys); }
@Override public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { return this.delegate.prepareStatement(sql, columnIndexes); }
@Override public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { return this.delegate.prepareStatement(sql, columnNames); }
@Override public Clob createClob() throws SQLException { return this.delegate.createClob(); }
@Override public Blob createBlob() throws SQLException { return this.delegate.createBlob(); }
@Override public NClob createNClob() throws SQLException { return this.delegate.createNClob(); }
@Override public SQLXML createSQLXML() throws SQLException { return this.delegate.createSQLXML(); }
@Override public boolean isValid(int timeout) throws SQLException { return this.delegate.isValid(timeout); }
@Override public void setClientInfo(String name, String value) throws SQLClientInfoException { this.delegate.setClientInfo(name, value); }
@Override public void setClientInfo(Properties properties) throws SQLClientInfoException { this.delegate.setClientInfo(properties); }
@Override public String getClientInfo(String name) throws SQLException { return this.delegate.getClientInfo(name); }
@Override public Properties getClientInfo() throws SQLException { return this.delegate.getClientInfo(); }
@Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { return this.delegate.createArrayOf(typeName, elements); }
@Override public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return this.delegate.createStruct(typeName, attributes); }
@Override public void setSchema(String schema) throws SQLException { this.delegate.setSchema(schema); }
@Override public String getSchema() throws SQLException { return this.delegate.getSchema(); }
@Override public void abort(Executor executor) throws SQLException { this.delegate.abort(executor); }
@Override public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { this.delegate.setNetworkTimeout(executor, milliseconds); }
@Override public int getNetworkTimeout() throws SQLException { return this.delegate.getNetworkTimeout(); }
}
@@ -0,0 +1,25 @@
package dev.brighten.antivpn.database.sql.utils;
import lombok.SneakyThrows;
import java.sql.Connection;
public class Query {
private static Connection conn;
public static void use(Connection conn) {
Query.conn = conn;
}
@SneakyThrows
public static ExecutableStatement prepare(String query) {
return new ExecutableStatement(conn.prepareStatement(query));
}
@SneakyThrows
public static ExecutableStatement prepare(String query, Connection con) {
return new ExecutableStatement(con.prepareStatement(query));
}
}
@@ -0,0 +1,7 @@
package dev.brighten.antivpn.database.sql.utils;
import java.sql.ResultSet;
public interface ResultSetIterator {
void next(ResultSet rs) throws Exception;
}
@@ -0,0 +1,39 @@
package dev.brighten.antivpn.message;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class MessageHandler {
private final Map<String, VpnString> messages = new HashMap<>();
public VpnString getString(String key) {
if(!messages.containsKey(key)) {
throw new NullPointerException("There is no VpnString with the key \"" + key + "\"");
}
return messages.get(key);
}
public void reloadStrings() {
for (VpnString value : messages.values()) {
value.updateString();
}
}
public void clearStrings() {
messages.clear();
}
public void addString(VpnString string, Function<VpnString, String> getter) {
string.setConfigStringGetter(getter);
messages.put(string.getKey(), string);
}
public void initStrings(Function<VpnString, String> getter) {
addString(new VpnString("command-misc-playerRequired",
"&cYou must be a player to execute this command!"), getter);
addString(new VpnString("command-alerts-toggled",
"&7Your player proxy notifications have been set to: &e%state%"), getter);
}
}
@@ -0,0 +1,58 @@
package dev.brighten.antivpn.message;
import dev.brighten.antivpn.api.APIPlayer;
import lombok.*;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Function;
@Getter
public class VpnString {
private final String key;
private final String defaultMessage;
private String message;
@Setter
private Function<VpnString, String> configStringGetter;
public VpnString(String key, String defaultMessage) {
this.key = key;
this.defaultMessage = defaultMessage;
this.message = defaultMessage;
}
@SneakyThrows
public void updateString() {
if(configStringGetter == null) throw new Exception("The configStringGetter for string " + key + " is null!");
message = configStringGetter.apply(this);
}
public String getFormattedMessage(Var<String, Object>... replacements) {
String formatted = message;
for (Var<String, Object> replacement : replacements) {
formatted = formatted
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
}
return formatted;
}
public void sendMessage(APIPlayer player, Var<String, Object>... replacements) {
String formatted = message;
for (Var<String, Object> replacement : replacements) {
formatted = formatted
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
}
player.sendMessage(formatted);
}
@Getter
@RequiredArgsConstructor
public static class Var<S, O> {
private final String key;
private final Object replacement;
}
}
@@ -0,0 +1,19 @@
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,44 @@
package dev.brighten.antivpn.utils;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
public class MiscUtils {
public static void close(Closeable... closeables) {
try {
for (Closeable closeable : closeables) if (closeable != null) closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(AutoCloseable... closeables) {
try {
for (AutoCloseable closeable : closeables) if (closeable != null) closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean isIpv4(String ip)
{
try {
InetAddress address = InetAddress.getByName(ip);
return address instanceof Inet4Address;
} catch(Exception e) {
return false;
}
}
}
@@ -0,0 +1,15 @@
package dev.brighten.antivpn.utils;
public class StringUtil {
public static String line(String color) {
return color + "&m-----------------------------------------------------";
}
public static String line() {
return "&m-----------------------------------------------------";
}
public static String lineNoStrike(String color) {
return color + "-----------------------------------------------------";
}
}
@@ -0,0 +1,78 @@
package dev.brighten.antivpn.utils;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.utils.json.JSONObject;
import lombok.*;
@Getter
@Setter
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class VPNResponse {
private String asn, ip, countryName, countryCode, city, timeZone, method, isp, failureReason = "N/A";
private boolean proxy, cached;
private final boolean success;
private double latitude, longitude;
private long lastAccess;
private long queriesLeft;
public JSONObject toJson() throws JSONException {
JSONObject json = new JSONObject();
json.put("ip", ip);
json.put("countryName", countryName);
json.put("countryCode", countryCode);
json.put("city", city);
json.put("method", method);
json.put("isp", isp);
json.put("proxy", proxy);
json.put("success", success);
json.put("timeZone", timeZone);
json.put("success", true);
json.put("queriesLeft", queriesLeft);
json.put("cached", cached);
return json;
}
public static VPNResponse fromJson(String json) throws JSONException {
JSONObject jsonObject = new JSONObject(json);
if(jsonObject.getBoolean("success")) {
return new VPNResponse(jsonObject.getString("asn"), jsonObject.getString("ip"),
jsonObject.getString("countryName"), jsonObject.getString("countryCode"),
jsonObject.getString("city"), jsonObject.getString("timeZone"),
jsonObject.has("method") ? jsonObject.getString("method") : "N/A",
jsonObject.getString("isp"), "N/A", jsonObject.getBoolean("proxy"),
jsonObject.getBoolean("cached"), jsonObject.getBoolean("success"),
jsonObject.getDouble("latitude"), jsonObject.getDouble("longitude"),
jsonObject.getLong("lastAccess"), jsonObject.getInt("queriesLeft"));
} else {
VPNResponse response = new VPNResponse(false);
response.failureReason = jsonObject.getString("failureReason");
return response;
}
}
public static VPNResponse fromJson(JSONObject jsonObject) throws JSONException {
if(jsonObject.getBoolean("success")) {
return new VPNResponse(jsonObject.getString("asn"), jsonObject.getString("ip"),
jsonObject.getString("countryName"), jsonObject.getString("countryCode"),
jsonObject.getString("city"), jsonObject.getString("timeZone"),
jsonObject.has("method") ? jsonObject.getString("method") : "N/A",
jsonObject.getString("isp"), "N/A", jsonObject.getBoolean("proxy"),
jsonObject.getBoolean("cached"), jsonObject.getBoolean("success"),
jsonObject.getDouble("latitude"), jsonObject.getDouble("longitude"),
jsonObject.getLong("lastAccess"), jsonObject.getInt("queriesLeft"));
} else {
VPNResponse response = new VPNResponse(false);
response.failureReason = jsonObject.getString("failureReason");
return response;
}
}
}
@@ -0,0 +1,290 @@
package dev.brighten.antivpn.utils.json;
/*
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.
*/
/**
* This provides static methods to convert comma delimited text into a
* JSONArray, and to covert a JSONArray into comma delimited text. Comma
* delimited text is a very popular format for data interchange. It is
* understood by most database, spreadsheet, and organizer programs.
* <p>
* Each row of text represents a row in a table or a data record. Each row
* ends with a NEWLINE character. Each row contains one or more values.
* Values are separated by commas. A value can contain any character except
* for comma, unless is is wrapped in single quotes or double quotes.
* <p>
* The first row usually contains the names of the columns.
* <p>
* A comma delimited list can be converted into a JSONArray of JSONObjects.
* The names for the elements in the JSONObjects can be taken from the names
* in the first row.
*
* @author JSON.org
* @version 2010-12-24
*/
public class CDL {
/**
* Get the next value. The value can be wrapped in quotes. The value can
* be empty.
*
* @param x A JSONTokener of the source text.
* @return The value string, or null if empty.
* @throws JSONException if the quoted string is badly formed.
*/
private static String getValue(JSONTokener x) throws JSONException {
char c;
char q;
StringBuffer sb;
do {
c = x.next();
} while (c == ' ' || c == '\t');
switch (c) {
case 0:
return null;
case '"':
case '\'':
q = c;
sb = new StringBuffer();
for (; ; ) {
c = x.next();
if (c == q) {
break;
}
if (c == 0 || c == '\n' || c == '\r') {
throw x.syntaxError("Missing close quote '" + q + "'.");
}
sb.append(c);
}
return sb.toString();
case ',':
x.back();
return "";
default:
x.back();
return x.nextTo(',');
}
}
/**
* Produce a JSONArray of strings from a row of comma delimited values.
*
* @param x A JSONTokener of the source text.
* @return A JSONArray of strings.
* @throws JSONException
*/
public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
JSONArray ja = new JSONArray();
for (; ; ) {
String value = getValue(x);
char c = x.next();
if (value == null ||
(ja.length() == 0 && value.length() == 0 && c != ',')) {
return null;
}
ja.put(value);
for (; ; ) {
if (c == ',') {
break;
}
if (c != ' ') {
if (c == '\n' || c == '\r' || c == 0) {
return ja;
}
throw x.syntaxError("Bad character '" + c + "' (" +
(int) c + ").");
}
c = x.next();
}
}
}
/**
* Produce a JSONObject from a row of comma delimited text, using a
* parallel JSONArray of strings to provides the names of the elements.
*
* @param names A JSONArray of names. This is commonly obtained from the
* first row of a comma delimited text file using the rowToJSONArray
* method.
* @param x A JSONTokener of the source text.
* @return A JSONObject combining the names and values.
* @throws JSONException
*/
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
throws JSONException {
JSONArray ja = rowToJSONArray(x);
return ja != null ? ja.toJSONObject(names) : null;
}
/**
* Produce a comma delimited text row from a JSONArray. Values containing
* the comma character will be quoted. Troublesome characters may be
* removed.
*
* @param ja A JSONArray of strings.
* @return A string ending in NEWLINE.
*/
public static String rowToString(JSONArray ja) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < ja.length(); i += 1) {
if (i > 0) {
sb.append(',');
}
Object object = ja.opt(i);
if (object != null) {
String string = object.toString();
if (string.length() > 0 && (string.indexOf(',') >= 0 ||
string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 ||
string.indexOf(0) >= 0 || string.charAt(0) == '"')) {
sb.append('"');
int length = string.length();
for (int j = 0; j < length; j += 1) {
char c = string.charAt(j);
if (c >= ' ' && c != '"') {
sb.append(c);
}
}
sb.append('"');
} else {
sb.append(string);
}
}
}
sb.append('\n');
return sb.toString();
}
/**
* Produce a JSONArray of JSONObjects from a comma delimited text string,
* using the first row as a source of names.
*
* @param string The comma delimited text.
* @return A JSONArray of JSONObjects.
* @throws JSONException
*/
public static JSONArray toJSONArray(String string) throws JSONException {
return toJSONArray(new JSONTokener(string));
}
/**
* Produce a JSONArray of JSONObjects from a comma delimited text string,
* using the first row as a source of names.
*
* @param x The JSONTokener containing the comma delimited text.
* @return A JSONArray of JSONObjects.
* @throws JSONException
*/
public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
return toJSONArray(rowToJSONArray(x), x);
}
/**
* Produce a JSONArray of JSONObjects from a comma delimited text string
* using a supplied JSONArray as the source of element names.
*
* @param names A JSONArray of strings.
* @param string The comma delimited text.
* @return A JSONArray of JSONObjects.
* @throws JSONException
*/
public static JSONArray toJSONArray(JSONArray names, String string)
throws JSONException {
return toJSONArray(names, new JSONTokener(string));
}
/**
* Produce a JSONArray of JSONObjects from a comma delimited text string
* using a supplied JSONArray as the source of element names.
*
* @param names A JSONArray of strings.
* @param x A JSONTokener of the source text.
* @return A JSONArray of JSONObjects.
* @throws JSONException
*/
public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
throws JSONException {
if (names == null || names.length() == 0) {
return null;
}
JSONArray ja = new JSONArray();
for (; ; ) {
JSONObject jo = rowToJSONObject(names, x);
if (jo == null) {
break;
}
ja.put(jo);
}
if (ja.length() == 0) {
return null;
}
return ja;
}
/**
* Produce a comma delimited text from a JSONArray of JSONObjects. The
* first row will be a list of names obtained by inspecting the first
* JSONObject.
*
* @param ja A JSONArray of JSONObjects.
* @return A comma delimited text.
* @throws JSONException
*/
public static String toString(JSONArray ja) throws JSONException {
JSONObject jo = ja.optJSONObject(0);
if (jo != null) {
JSONArray names = jo.names();
if (names != null) {
return rowToString(names) + toString(names, ja);
}
}
return null;
}
/**
* Produce a comma delimited text from a JSONArray of JSONObjects using
* a provided list of names. The list of names is not included in the
* output.
*
* @param names A JSONArray of strings.
* @param ja A JSONArray of JSONObjects.
* @return A comma delimited text.
* @throws JSONException
*/
public static String toString(JSONArray names, JSONArray ja)
throws JSONException {
if (names == null || names.length() == 0) {
return null;
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < ja.length(); i += 1) {
JSONObject jo = ja.optJSONObject(i);
if (jo != null) {
sb.append(rowToString(jo.toJSONArray(names)));
}
}
return sb.toString();
}
}
@@ -0,0 +1,174 @@
package dev.brighten.antivpn.utils.json;
/*
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.
*/
/**
* Convert a web browser cookie specification to a JSONObject and back.
* JSON and Cookies are both notations for name/value pairs.
*
* @author JSON.org
* @version 2010-12-24
*/
public class Cookie {
/**
* Produce a copy of a string in which the characters '+', '%', '=', ';'
* and control characters are replaced with "%hh". This is a gentle form
* of URL encoding, attempting to cause as little distortion to the
* string as possible. The characters '=' and ';' are meta characters in
* cookies. By convention, they are escaped using the URL-encoding. This is
* only a convention, not a standard. Often, cookies are expected to have
* encoded values. We encode '=' and ';' because we must. We encode '%' and
* '+' because they are meta characters in URL encoding.
*
* @param string The source string.
* @return The escaped result.
*/
public static String escape(String string) {
char c;
String s = string.trim();
StringBuffer sb = new StringBuffer();
int length = s.length();
for (int i = 0; i < length; i += 1) {
c = s.charAt(i);
if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') {
sb.append('%');
sb.append(Character.forDigit((char) ((c >>> 4) & 0x0f), 16));
sb.append(Character.forDigit((char) (c & 0x0f), 16));
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Convert a cookie specification string into a JSONObject. The string
* will contain a name value pair separated by '='. The name and the value
* will be unescaped, possibly converting '+' and '%' sequences. The
* cookie properties may follow, separated by ';', also represented as
* name=value (except the secure property, which does not have a value).
* The name will be stored under the key "name", and the value will be
* stored under the key "value". This method does not do checking or
* validation of the parameters. It only converts the cookie string into
* a JSONObject.
*
* @param string The cookie specification string.
* @return A JSONObject containing "name", "value", and possibly other
* members.
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
String name;
JSONObject jo = new JSONObject();
Object value;
JSONTokener x = new JSONTokener(string);
jo.put("name", x.nextTo('='));
x.next('=');
jo.put("value", x.nextTo(';'));
x.next();
while (x.more()) {
name = unescape(x.nextTo("=;"));
if (x.next() != '=') {
if (name.equals("secure")) {
value = Boolean.TRUE;
} else {
throw x.syntaxError("Missing '=' in cookie parameter.");
}
} else {
value = unescape(x.nextTo(';'));
x.next();
}
jo.put(name, value);
}
return jo;
}
/**
* Convert a JSONObject into a cookie specification string. The JSONObject
* must contain "name" and "value" members.
* If the JSONObject contains "expires", "domain", "path", or "secure"
* members, they will be appended to the cookie specification string.
* All other members are ignored.
*
* @param jo A JSONObject
* @return A cookie specification string
* @throws JSONException
*/
public static String toString(JSONObject jo) throws JSONException {
StringBuffer sb = new StringBuffer();
sb.append(escape(jo.getString("name")));
sb.append("=");
sb.append(escape(jo.getString("value")));
if (jo.has("expires")) {
sb.append(";expires=");
sb.append(jo.getString("expires"));
}
if (jo.has("domain")) {
sb.append(";domain=");
sb.append(escape(jo.getString("domain")));
}
if (jo.has("path")) {
sb.append(";path=");
sb.append(escape(jo.getString("path")));
}
if (jo.optBoolean("secure")) {
sb.append(";secure");
}
return sb.toString();
}
/**
* Convert <code>%</code><i>hh</i> sequences to single characters, and
* convert plus to space.
*
* @param string A string that may contain
* <code>+</code>&nbsp;<small>(plus)</small> and
* <code>%</code><i>hh</i> sequences.
* @return The unescaped string.
*/
public static String unescape(String string) {
int length = string.length();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; ++i) {
char c = string.charAt(i);
if (c == '+') {
c = ' ';
} else if (c == '%' && i + 2 < length) {
int d = JSONTokener.dehexchar(string.charAt(i + 1));
int e = JSONTokener.dehexchar(string.charAt(i + 2));
if (d >= 0 && e >= 0) {
c = (char) (d * 16 + e);
i += 2;
}
}
sb.append(c);
}
return sb.toString();
}
}
@@ -0,0 +1,93 @@
package dev.brighten.antivpn.utils.json;
/*
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.
*/
import java.util.Iterator;
/**
* Convert a web browser cookie list string to a JSONObject and back.
*
* @author JSON.org
* @version 2010-12-24
*/
public class CookieList {
/**
* Convert a cookie list into a JSONObject. A cookie list is a sequence
* of name/value pairs. The names are separated from the values by '='.
* The pairs are separated by ';'. The names and the values
* will be unescaped, possibly converting '+' and '%' sequences.
* <p>
* To add a cookie to a cooklist,
* cookielistJSONObject.put(cookieJSONObject.getString("name"),
* cookieJSONObject.getString("value"));
*
* @param string A cookie list string
* @return A JSONObject
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
JSONObject jo = new JSONObject();
JSONTokener x = new JSONTokener(string);
while (x.more()) {
String name = Cookie.unescape(x.nextTo('='));
x.next('=');
jo.put(name, Cookie.unescape(x.nextTo(';')));
x.next();
}
return jo;
}
/**
* Convert a JSONObject into a cookie list. A cookie list is a sequence
* of name/value pairs. The names are separated from the values by '='.
* The pairs are separated by ';'. The characters '%', '+', '=', and ';'
* in the names and values are replaced by "%hh".
*
* @param jo A JSONObject
* @return A cookie list string
* @throws JSONException
*/
public static String toString(JSONObject jo) throws JSONException {
boolean b = false;
Iterator keys = jo.keys();
String string;
StringBuffer sb = new StringBuffer();
while (keys.hasNext()) {
string = keys.next().toString();
if (!jo.isNull(string)) {
if (b) {
sb.append(';');
}
sb.append(Cookie.escape(string));
sb.append("=");
sb.append(Cookie.escape(jo.getString(string)));
b = true;
}
}
return sb.toString();
}
}
@@ -0,0 +1,168 @@
package dev.brighten.antivpn.utils.json;
/*
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.
*/
import java.util.Iterator;
/**
* Convert an HTTP header to a JSONObject and back.
*
* @author JSON.org
* @version 2010-12-24
*/
public class HTTP {
/**
* Carriage return/line feed.
*/
public static final String CRLF = "\r\n";
/**
* Convert an HTTP header string into a JSONObject. It can be a request
* header or a response header. A request header will contain
* <pre>{
* Method: "POST" (for example),
* "Request-URI": "/" (for example),
* "HTTP-Version": "HTTP/1.1" (for example)
* }</pre>
* A response header will contain
* <pre>{
* "HTTP-Version": "HTTP/1.1" (for example),
* "Fixes-Code": "200" (for example),
* "Reason-Phrase": "OK" (for example)
* }</pre>
* In addition, the other parameters in the header will be captured, using
* the HTTP field names as JSON names, so that <pre>
* Date: Sun, 26 May 2002 18:06:04 GMT
* Cookie: Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s
* Cache-Control: no-cache</pre>
* become
* <pre>{...
* Date: "Sun, 26 May 2002 18:06:04 GMT",
* Cookie: "Q=q2=PPEAsg--; B=677gi6ouf29bn&b=2&f=s",
* "Cache-Control": "no-cache",
* ...}</pre>
* It does no further checking or conversion. It does not parse dates.
* It does not do '%' transforms on URLs.
*
* @param string An HTTP header string.
* @return A JSONObject containing the elements and attributes
* of the XML string.
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
JSONObject jo = new JSONObject();
HTTPTokener x = new HTTPTokener(string);
String token;
token = x.nextToken();
if (token.toUpperCase().startsWith("HTTP")) {
// Response
jo.put("HTTP-Version", token);
jo.put("Fixes-Code", x.nextToken());
jo.put("Reason-Phrase", x.nextTo('\0'));
x.next();
} else {
// Request
jo.put("Method", token);
jo.put("Request-URI", x.nextToken());
jo.put("HTTP-Version", x.nextToken());
}
// Fields
while (x.more()) {
String name = x.nextTo(':');
x.next(':');
jo.put(name, x.nextTo('\0'));
x.next();
}
return jo;
}
/**
* Convert a JSONObject into an HTTP header. A request header must contain
* <pre>{
* Method: "POST" (for example),
* "Request-URI": "/" (for example),
* "HTTP-Version": "HTTP/1.1" (for example)
* }</pre>
* A response header must contain
* <pre>{
* "HTTP-Version": "HTTP/1.1" (for example),
* "Fixes-Code": "200" (for example),
* "Reason-Phrase": "OK" (for example)
* }</pre>
* Any other members of the JSONObject will be output as HTTP fields.
* The result will end with two CRLF pairs.
*
* @param jo A JSONObject
* @return An HTTP header string.
* @throws JSONException if the object does not contain enough
* information.
*/
public static String toString(JSONObject jo) throws JSONException {
Iterator keys = jo.keys();
String string;
StringBuffer sb = new StringBuffer();
if (jo.has("Fixes-Code") && jo.has("Reason-Phrase")) {
sb.append(jo.getString("HTTP-Version"));
sb.append(' ');
sb.append(jo.getString("Fixes-Code"));
sb.append(' ');
sb.append(jo.getString("Reason-Phrase"));
} else if (jo.has("Method") && jo.has("Request-URI")) {
sb.append(jo.getString("Method"));
sb.append(' ');
sb.append('"');
sb.append(jo.getString("Request-URI"));
sb.append('"');
sb.append(' ');
sb.append(jo.getString("HTTP-Version"));
} else {
throw new JSONException("Not enough material for an HTTP header.");
}
sb.append(CRLF);
while (keys.hasNext()) {
string = keys.next().toString();
if (!string.equals("HTTP-Version") && !string.equals("Fixes-Code") &&
!string.equals("Reason-Phrase") && !string.equals("Method") &&
!string.equals("Request-URI") && !jo.isNull(string)) {
sb.append(string);
sb.append(": ");
sb.append(jo.getString(string));
sb.append(CRLF);
}
}
sb.append(CRLF);
return sb.toString();
}
}
@@ -0,0 +1,80 @@
package dev.brighten.antivpn.utils.json;
/*
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.
*/
/**
* The HTTPTokener extends the JSONTokener to provide additional methods
* for the parsing of HTTP headers.
*
* @author JSON.org
* @version 2010-12-24
*/
public class HTTPTokener extends JSONTokener {
/**
* Construct an HTTPTokener from a string.
*
* @param string A source string.
*/
public HTTPTokener(String string) {
super(string);
}
/**
* Get the next token or string. This is used in parsing HTTP headers.
*
* @return A String.
* @throws JSONException
*/
public String nextToken() throws JSONException {
char c;
char q;
StringBuffer sb = new StringBuffer();
do {
c = next();
} while (Character.isWhitespace(c));
if (c == '"' || c == '\'') {
q = c;
for (; ; ) {
c = next();
if (c < ' ') {
throw syntaxError("Unterminated string.");
}
if (c == q) {
return sb.toString();
}
sb.append(c);
}
}
for (; ; ) {
if (c == 0 || Character.isWhitespace(c)) {
return sb.toString();
}
sb.append(c);
c = next();
}
}
}
@@ -0,0 +1,946 @@
package dev.brighten.antivpn.utils.json;
/*
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.
*/
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* A JSONArray is an ordered sequence of values. Its external text form is a
* string wrapped in square brackets with commas separating the values. The
* internal form is an object having <code>get</code> and <code>opt</code>
* methods for accessing the values by index, and <code>put</code> methods for
* adding or replacing values. The values can be any of these types:
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
* <code>Number</code>, <code>String</code>, or the
* <code>JSONObject.NULL object</code>.
* <p>
* The constructor can convert a JSON text into a Java object. The
* <code>toString</code> method converts to JSON text.
* <p>
* A <code>get</code> method returns a value if one can be found, and throws an
* exception if one cannot be found. An <code>opt</code> method returns a
* default value instead of throwing an exception, and so is useful for
* obtaining optional values.
* <p>
* The generic <code>get()</code> and <code>opt()</code> methods return an
* object which you can cast or query for type. There are also typed
* <code>get</code> and <code>opt</code> methods that do type checking and type
* coercion for you.
* <p>
* The texts produced by the <code>toString</code> methods strictly conform to
* JSON syntax rules. The constructors are more forgiving in the texts they will
* accept:
* <ul>
* <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
* before the closing bracket.</li>
* <li>The <code>null</code> value will be inserted when there
* is <code>,</code>&nbsp;<small>(comma)</small> elision.</li>
* <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
* quote)</small>.</li>
* <li>Strings do not need to be quoted at all if they do not begin with a quote
* or single quote, and if they do not contain leading or trailing spaces,
* and if they do not contain any of these characters:
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
* and if they are not the reserved words <code>true</code>,
* <code>false</code>, or <code>null</code>.</li>
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
* well as by <code>,</code> <small>(comma)</small>.</li>
* <li>Numbers may have the
* <code>0x-</code> <small>(hex)</small> prefix.</li>
* </ul>
*
* @author JSON.org
* @version 2011-05-04
*/
public class JSONArray {
/**
* The arrayList where the JSONArray's properties are kept.
*/
private ArrayList myArrayList;
/**
* Construct an empty JSONArray.
*/
public JSONArray() {
this.myArrayList = new ArrayList();
}
/**
* Construct a JSONArray from a JSONTokener.
*
* @param x A JSONTokener
* @throws JSONException If there is a syntax error.
*/
public JSONArray(JSONTokener x) throws JSONException {
this();
if (x.nextClean() != '[') {
throw x.syntaxError("A JSONArray text must start with '['");
}
if (x.nextClean() != ']') {
x.back();
for (; ; ) {
if (x.nextClean() == ',') {
x.back();
this.myArrayList.add(JSONObject.NULL);
} else {
x.back();
this.myArrayList.add(x.nextValue());
}
switch (x.nextClean()) {
case ';':
case ',':
if (x.nextClean() == ']') {
return;
}
x.back();
break;
case ']':
return;
default:
throw x.syntaxError("Expected a ',' or ']'");
}
}
}
}
/**
* Construct a JSONArray from a source JSON text.
*
* @param source A string that begins with
* <code>[</code>&nbsp;<small>(left bracket)</small>
* and ends with <code>]</code>&nbsp;<small>(right bracket)</small>.
* @throws JSONException If there is a syntax error.
*/
public JSONArray(String source) throws JSONException {
this(new JSONTokener(source));
}
/**
* Construct a JSONArray from a Collection.
*
* @param collection A Collection.
*/
public JSONArray(Collection collection) {
this.myArrayList = new ArrayList();
if (collection != null) {
Iterator iter = collection.iterator();
while (iter.hasNext()) {
this.myArrayList.add(JSONObject.wrap(iter.next()));
}
}
}
/**
* Construct a JSONArray from an array
*
* @throws JSONException If not an array.
*/
public JSONArray(Object array) throws JSONException {
this();
if (array.getClass().isArray()) {
int length = Array.getLength(array);
for (int i = 0; i < length; i += 1) {
this.put(JSONObject.wrap(Array.get(array, i)));
}
} else {
throw new JSONException(
"JSONArray initial value should be a string or collection or array.");
}
}
/**
* Get the object value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return An object value.
* @throws JSONException If there is no value for the index.
*/
public Object get(int index) throws JSONException {
Object object = opt(index);
if (object == null) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
return object;
}
/**
* Get the boolean value associated with an index.
* The string values "true" and "false" are converted to boolean.
*
* @param index The index must be between 0 and length() - 1.
* @return The truth.
* @throws JSONException If there is no value for the index or if the
* value is not convertible to boolean.
*/
public boolean getBoolean(int index) throws JSONException {
Object object = get(index);
if (object.equals(Boolean.FALSE) ||
(object instanceof String &&
((String) object).equalsIgnoreCase("false"))) {
return false;
} else if (object.equals(Boolean.TRUE) ||
(object instanceof String &&
((String) object).equalsIgnoreCase("true"))) {
return true;
}
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
}
/**
* Get the double value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot
* be converted to a number.
*/
public double getDouble(int index) throws JSONException {
Object object = get(index);
try {
return object instanceof Number ?
((Number) object).doubleValue() :
Double.parseDouble((String) object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index +
"] is not a number.");
}
}
/**
* Get the int value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value is not a number.
*/
public int getInt(int index) throws JSONException {
Object object = get(index);
try {
return object instanceof Number ?
((Number) object).intValue() :
Integer.parseInt((String) object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index +
"] is not a number.");
}
}
/**
* Get the JSONArray associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return A JSONArray value.
* @throws JSONException If there is no value for the index. or if the
* value is not a JSONArray
*/
public JSONArray getJSONArray(int index) throws JSONException {
Object object = get(index);
if (object instanceof JSONArray) {
return (JSONArray) object;
}
throw new JSONException("JSONArray[" + index +
"] is not a JSONArray.");
}
/**
* Get the JSONObject associated with an index.
*
* @param index subscript
* @return A JSONObject value.
* @throws JSONException If there is no value for the index or if the
* value is not a JSONObject
*/
public JSONObject getJSONObject(int index) throws JSONException {
Object object = get(index);
if (object instanceof JSONObject) {
return (JSONObject) object;
}
throw new JSONException("JSONArray[" + index +
"] is not a JSONObject.");
}
/**
* Get the long value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot
* be converted to a number.
*/
public long getLong(int index) throws JSONException {
Object object = get(index);
try {
return object instanceof Number ?
((Number) object).longValue() :
Long.parseLong((String) object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index +
"] is not a number.");
}
}
/**
* Get the string associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return A string value.
* @throws JSONException If there is no string value for the index.
*/
public String getString(int index) throws JSONException {
Object object = get(index);
if (object instanceof String) {
return (String) object;
}
throw new JSONException("JSONArray[" + index + "] not a string.");
}
/**
* Determine if the value is null.
*
* @param index The index must be between 0 and length() - 1.
* @return true if the value at the index is null, or if there is no value.
*/
public boolean isNull(int index) {
return JSONObject.NULL.equals(opt(index));
}
/**
* Make a string from the contents of this JSONArray. The
* <code>separator</code> string is inserted between each element.
* Warning: This method assumes that the data structure is acyclical.
*
* @param separator A string that will be inserted between the elements.
* @return a string.
* @throws JSONException If the array contains an invalid number.
*/
public String join(String separator) throws JSONException {
int len = length();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i += 1) {
if (i > 0) {
sb.append(separator);
}
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
}
return sb.toString();
}
/**
* Get the number of elements in the JSONArray, included nulls.
*
* @return The length (or size).
*/
public int length() {
return this.myArrayList.size();
}
/**
* Get the optional object value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return An object value, or null if there is no
* object at that index.
*/
public Object opt(int index) {
return (index < 0 || index >= length()) ?
null : this.myArrayList.get(index);
}
/**
* Get the optional boolean value associated with an index.
* It returns false if there is no value at that index,
* or if the value is not Boolean.TRUE or the String "true".
*
* @param index The index must be between 0 and length() - 1.
* @return The truth.
*/
public boolean optBoolean(int index) {
return optBoolean(index, false);
}
/**
* Get the optional boolean value associated with an index.
* It returns the defaultValue if there is no value at that index or if
* it is not a Boolean or the String "true" or "false" (case insensitive).
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue A boolean default.
* @return The truth.
*/
public boolean optBoolean(int index, boolean defaultValue) {
try {
return getBoolean(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional double value associated with an index.
* NaN is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public double optDouble(int index) {
return optDouble(index, Double.NaN);
}
/**
* Get the optional double value associated with an index.
* The defaultValue is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index subscript
* @param defaultValue The default value.
* @return The value.
*/
public double optDouble(int index, double defaultValue) {
try {
return getDouble(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional int value associated with an index.
* Zero is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public int optInt(int index) {
return optInt(index, 0);
}
/**
* Get the optional int value associated with an index.
* The defaultValue is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public int optInt(int index, int defaultValue) {
try {
return getInt(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional JSONArray associated with an index.
*
* @param index subscript
* @return A JSONArray value, or null if the index has no value,
* or if the value is not a JSONArray.
*/
public JSONArray optJSONArray(int index) {
Object o = opt(index);
return o instanceof JSONArray ? (JSONArray) o : null;
}
/**
* Get the optional JSONObject associated with an index.
* Null is returned if the key is not found, or null if the index has
* no value, or if the value is not a JSONObject.
*
* @param index The index must be between 0 and length() - 1.
* @return A JSONObject value.
*/
public JSONObject optJSONObject(int index) {
Object o = opt(index);
return o instanceof JSONObject ? (JSONObject) o : null;
}
/**
* Get the optional long value associated with an index.
* Zero is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public long optLong(int index) {
return optLong(index, 0);
}
/**
* Get the optional long value associated with an index.
* The defaultValue is returned if there is no value for the index,
* or if the value is not a number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public long optLong(int index, long defaultValue) {
try {
return getLong(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional string value associated with an index. It returns an
* empty string if there is no value at that index. If the value
* is not a string and is not null, then it is coverted to a string.
*
* @param index The index must be between 0 and length() - 1.
* @return A String value.
*/
public String optString(int index) {
return optString(index, "");
}
/**
* Get the optional string associated with an index.
* The defaultValue is returned if the key is not found.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return A String value.
*/
public String optString(int index, String defaultValue) {
Object object = opt(index);
return object != null ? object.toString() : defaultValue;
}
/**
* Append a boolean value. This increases the array's length by one.
*
* @param value A boolean value.
* @return this.
*/
public JSONArray put(boolean value) {
put(value ? Boolean.TRUE : Boolean.FALSE);
return this;
}
/**
* Put a value in the JSONArray, where the value will be a
* JSONArray which is produced from a Collection.
*
* @param value A Collection value.
* @return this.
*/
public JSONArray put(Collection value) {
put(new JSONArray(value));
return this;
}
/**
* Append a double value. This increases the array's length by one.
*
* @param value A double value.
* @return this.
* @throws JSONException if the value is not finite.
*/
public JSONArray put(double value) throws JSONException {
Double d = new Double(value);
JSONObject.testValidity(d);
put(d);
return this;
}
/**
* Append an int value. This increases the array's length by one.
*
* @param value An int value.
* @return this.
*/
public JSONArray put(int value) {
put(new Integer(value));
return this;
}
/**
* Append an long value. This increases the array's length by one.
*
* @param value A long value.
* @return this.
*/
public JSONArray put(long value) {
put(new Long(value));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a
* JSONObject which is produced from a Map.
*
* @param value A Map value.
* @return this.
*/
public JSONArray put(Map value) {
put(new JSONObject(value));
return this;
}
/**
* Append an object value. This increases the array's length by one.
*
* @param value An object value. The value should be a
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
* JSONObject.NULL object.
* @return this.
*/
public JSONArray put(Object value) {
this.myArrayList.add(value);
return this;
}
/**
* Put or replace a boolean value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
*
* @param index The subscript.
* @param value A boolean value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, boolean value) throws JSONException {
put(index, value ? Boolean.TRUE : Boolean.FALSE);
return this;
}
/**
* Put a value in the JSONArray, where the value will be a
* JSONArray which is produced from a Collection.
*
* @param index The subscript.
* @param value A Collection value.
* @return this.
* @throws JSONException If the index is negative or if the value is
* not finite.
*/
public JSONArray put(int index, Collection value) throws JSONException {
put(index, new JSONArray(value));
return this;
}
/**
* Put or replace a double value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad
* it out.
*
* @param index The subscript.
* @param value A double value.
* @return this.
* @throws JSONException If the index is negative or if the value is
* not finite.
*/
public JSONArray put(int index, double value) throws JSONException {
put(index, new Double(value));
return this;
}
/**
* Put or replace an int value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad
* it out.
*
* @param index The subscript.
* @param value An int value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, int value) throws JSONException {
put(index, new Integer(value));
return this;
}
/**
* Put or replace a long value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad
* it out.
*
* @param index The subscript.
* @param value A long value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, long value) throws JSONException {
put(index, new Long(value));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a
* JSONObject that is produced from a Map.
*
* @param index The subscript.
* @param value The Map value.
* @return this.
* @throws JSONException If the index is negative or if the the value is
* an invalid number.
*/
public JSONArray put(int index, Map value) throws JSONException {
put(index, new JSONObject(value));
return this;
}
/**
* Put or replace an object value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
*
* @param index The subscript.
* @param value The value to put into the array. The value should be a
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
* JSONObject.NULL object.
* @return this.
* @throws JSONException If the index is negative or if the the value is
* an invalid number.
*/
public JSONArray put(int index, Object value) throws JSONException {
JSONObject.testValidity(value);
if (index < 0) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
if (index < length()) {
this.myArrayList.set(index, value);
} else {
while (index != length()) {
put(JSONObject.NULL);
}
put(value);
}
return this;
}
/**
* Remove an index and close the hole.
*
* @param index The index of the element to be removed.
* @return The value that was associated with the index,
* or null if there was no value.
*/
public Object remove(int index) {
Object o = opt(index);
this.myArrayList.remove(index);
return o;
}
/**
* Produce a JSONObject by combining a JSONArray of names with the values
* of this JSONArray.
*
* @param names A JSONArray containing a list of key strings. These will be
* paired with the values.
* @return A JSONObject, or null if there are no names or if this JSONArray
* has no values.
* @throws JSONException If any of the names are null.
*/
public JSONObject toJSONObject(JSONArray names) throws JSONException {
if (names == null || names.length() == 0 || length() == 0) {
return null;
}
JSONObject jo = new JSONObject();
for (int i = 0; i < names.length(); i += 1) {
jo.put(names.getString(i), this.opt(i));
}
return jo;
}
/**
* Make a JSON text of this JSONArray. For compactness, no
* unnecessary whitespace is added. If it is not possible to produce a
* syntactically correct JSON text then null will be returned instead. This
* could occur if the array contains an invalid number.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return a printable, displayable, transmittable
* representation of the array.
*/
public String toString() {
try {
return '[' + join(",") + ']';
} catch (Exception e) {
return null;
}
}
/**
* Make a prettyprinted JSON text of this JSONArray.
* Warning: This method assumes that the data structure is acyclical.
*
* @param indentFactor The number of spaces to add to each level of
* indentation.
* @return a printable, displayable, transmittable
* representation of the object, beginning
* with <code>[</code>&nbsp;<small>(left bracket)</small> and ending
* with <code>]</code>&nbsp;<small>(right bracket)</small>.
* @throws JSONException
*/
public String toString(int indentFactor) throws JSONException {
return toString(indentFactor, 0);
}
/**
* Make a prettyprinted JSON text of this JSONArray.
* Warning: This method assumes that the data structure is acyclical.
*
* @param indentFactor The number of spaces to add to each level of
* indentation.
* @param indent The indention of the top level.
* @return a printable, displayable, transmittable
* representation of the array.
* @throws JSONException
*/
String toString(int indentFactor, int indent) throws JSONException {
int len = length();
if (len == 0) {
return "[]";
}
int i;
StringBuffer sb = new StringBuffer("[");
if (len == 1) {
sb.append(JSONObject.valueToString(this.myArrayList.get(0),
indentFactor, indent));
} else {
int newindent = indent + indentFactor;
sb.append('\n');
for (i = 0; i < len; i += 1) {
if (i > 0) {
sb.append(",\n");
}
for (int j = 0; j < newindent; j += 1) {
sb.append(' ');
}
sb.append(JSONObject.valueToString(this.myArrayList.get(i),
indentFactor, newindent));
}
sb.append('\n');
for (i = 0; i < indent; i += 1) {
sb.append(' ');
}
}
sb.append(']');
return sb.toString();
}
/**
* Write the contents of the JSONArray as JSON text to a writer.
* For compactness, no whitespace is added.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return The writer.
* @throws JSONException
*/
public Writer write(Writer writer) throws JSONException {
try {
boolean b = false;
int len = length();
writer.write('[');
for (int i = 0; i < len; i += 1) {
if (b) {
writer.write(',');
}
Object v = this.myArrayList.get(i);
if (v instanceof JSONObject) {
((JSONObject) v).write(writer);
} else if (v instanceof JSONArray) {
((JSONArray) v).write(writer);
} else {
writer.write(JSONObject.valueToString(v));
}
b = true;
}
writer.write(']');
return writer;
} catch (IOException e) {
throw new JSONException(e);
}
}
}
@@ -0,0 +1,30 @@
package dev.brighten.antivpn.utils.json;
/**
* The JSONException is thrown by the JSON.org classes when things are amiss.
*
* @author JSON.org
* @version 2010-12-24
*/
public class JSONException extends Exception {
private static final long serialVersionUID = 0;
private Throwable cause;
/**
* Constructs a JSONException with an explanatory message.
*
* @param message Detail about the reason for the exception.
*/
public JSONException(String message) {
super(message);
}
public JSONException(Throwable cause) {
super(cause.getMessage());
this.cause = cause;
}
public Throwable getCause() {
return this.cause;
}
}
@@ -0,0 +1,464 @@
package dev.brighten.antivpn.utils.json;
/*
Copyright (c) 2008 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.
*/
import java.util.Iterator;
/**
* This provides static methods to convert an XML text into a JSONArray or
* JSONObject, and to covert a JSONArray or JSONObject into an XML text using
* the JsonML transform.
*
* @author JSON.org
* @version 2010-12-23
*/
public class JSONML {
/**
* Parse XML values and store them in a JSONArray.
*
* @param x The XMLTokener containing the source string.
* @param arrayForm true if array form, false if object form.
* @param ja The JSONArray that is containing the current tag or null
* if we are at the outermost level.
* @return A JSONArray if the value is the outermost tag, otherwise null.
* @throws JSONException
*/
private static Object parse(XMLTokener x, boolean arrayForm,
JSONArray ja) throws JSONException {
String attribute;
char c;
String closeTag = null;
int i;
JSONArray newja = null;
JSONObject newjo = null;
Object token;
String tagName = null;
// Test for and skip past these forms:
// <!-- ... -->
// <![ ... ]]>
// <! ... >
// <? ... ?>
while (true) {
token = x.nextContent();
if (token == XML.LT) {
token = x.nextToken();
if (token instanceof Character) {
if (token == XML.SLASH) {
// Close tag </
token = x.nextToken();
if (!(token instanceof String)) {
throw new JSONException(
"Expected a closing name instead of '" +
token + "'.");
}
if (x.nextToken() != XML.GT) {
throw x.syntaxError("Misshaped close tag");
}
return token;
} else if (token == XML.BANG) {
// <!
c = x.next();
if (c == '-') {
if (x.next() == '-') {
x.skipPast("-->");
}
x.back();
} else if (c == '[') {
token = x.nextToken();
if (token.equals("CDATA") && x.next() == '[') {
if (ja != null) {
ja.put(x.nextCDATA());
}
} else {
throw x.syntaxError("Expected 'CDATA['");
}
} else {
i = 1;
do {
token = x.nextMeta();
if (token == null) {
throw x.syntaxError("Missing '>' after '<!'.");
} else if (token == XML.LT) {
i += 1;
} else if (token == XML.GT) {
i -= 1;
}
} while (i > 0);
}
} else if (token == XML.QUEST) {
// <?
x.skipPast("?>");
} else {
throw x.syntaxError("Misshaped tag");
}
// Open tag <
} else {
if (!(token instanceof String)) {
throw x.syntaxError("Bad tagName '" + token + "'.");
}
tagName = (String) token;
newja = new JSONArray();
newjo = new JSONObject();
if (arrayForm) {
newja.put(tagName);
if (ja != null) {
ja.put(newja);
}
} else {
newjo.put("tagName", tagName);
if (ja != null) {
ja.put(newjo);
}
}
token = null;
for (; ; ) {
if (token == null) {
token = x.nextToken();
}
if (token == null) {
throw x.syntaxError("Misshaped tag");
}
if (!(token instanceof String)) {
break;
}
// attribute = value
attribute = (String) token;
if (!arrayForm && (attribute == "tagName" || attribute == "childNode")) {
throw x.syntaxError("Reserved attribute.");
}
token = x.nextToken();
if (token == XML.EQ) {
token = x.nextToken();
if (!(token instanceof String)) {
throw x.syntaxError("Missing value");
}
newjo.accumulate(attribute, XML.stringToValue((String) token));
token = null;
} else {
newjo.accumulate(attribute, "");
}
}
if (arrayForm && newjo.length() > 0) {
newja.put(newjo);
}
// Empty tag <.../>
if (token == XML.SLASH) {
if (x.nextToken() != XML.GT) {
throw x.syntaxError("Misshaped tag");
}
if (ja == null) {
if (arrayForm) {
return newja;
} else {
return newjo;
}
}
// Content, between <...> and </...>
} else {
if (token != XML.GT) {
throw x.syntaxError("Misshaped tag");
}
closeTag = (String) parse(x, arrayForm, newja);
if (closeTag != null) {
if (!closeTag.equals(tagName)) {
throw x.syntaxError("Mismatched '" + tagName +
"' and '" + closeTag + "'");
}
tagName = null;
if (!arrayForm && newja.length() > 0) {
newjo.put("childNodes", newja);
}
if (ja == null) {
if (arrayForm) {
return newja;
} else {
return newjo;
}
}
}
}
}
} else {
if (ja != null) {
ja.put(token instanceof String ?
XML.stringToValue((String) token) : token);
}
}
}
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONArray using the JsonML transform. Each XML tag is represented as
* a JSONArray in which the first element is the tag name. If the tag has
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child tags.
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
*
* @param string The source string.
* @return A JSONArray containing the structured data from the XML string.
* @throws JSONException
*/
public static JSONArray toJSONArray(String string) throws JSONException {
return toJSONArray(new XMLTokener(string));
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONArray using the JsonML transform. Each XML tag is represented as
* a JSONArray in which the first element is the tag name. If the tag has
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child content and tags.
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
*
* @param x An XMLTokener.
* @return A JSONArray containing the structured data from the XML string.
* @throws JSONException
*/
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
return (JSONArray) parse(x, true, null);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
* a JSONObject with a "tagName" property. If the tag has attributes, then
* the attributes will be in the JSONObject as properties. If the tag
* contains children, the object will have a "childNodes" property which
* will be an array of strings and JsonML JSONObjects.
* <p>
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
*
* @param x An XMLTokener of the XML source text.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException
*/
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
return (JSONObject) parse(x, false, null);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
* a JSONObject with a "tagName" property. If the tag has attributes, then
* the attributes will be in the JSONObject as properties. If the tag
* contains children, the object will have a "childNodes" property which
* will be an array of strings and JsonML JSONObjects.
* <p>
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
*
* @param string The XML source text.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
return toJSONObject(new XMLTokener(string));
}
/**
* Reverse the JSONML transformation, making an XML text from a JSONArray.
*
* @param ja A JSONArray.
* @return An XML string.
* @throws JSONException
*/
public static String toString(JSONArray ja) throws JSONException {
int i;
JSONObject jo;
String key;
Iterator keys;
int length;
Object object;
StringBuffer sb = new StringBuffer();
String tagName;
String value;
// Emit <tagName
tagName = ja.getString(0);
XML.noSpace(tagName);
tagName = XML.escape(tagName);
sb.append('<');
sb.append(tagName);
object = ja.opt(1);
if (object instanceof JSONObject) {
i = 2;
jo = (JSONObject) object;
// Emit the attributes
keys = jo.keys();
while (keys.hasNext()) {
key = keys.next().toString();
XML.noSpace(key);
value = jo.optString(key);
if (value != null) {
sb.append(' ');
sb.append(XML.escape(key));
sb.append('=');
sb.append('"');
sb.append(XML.escape(value));
sb.append('"');
}
}
} else {
i = 1;
}
//Emit content in body
length = ja.length();
if (i >= length) {
sb.append('/');
sb.append('>');
} else {
sb.append('>');
do {
object = ja.get(i);
i += 1;
if (object != null) {
if (object instanceof String) {
sb.append(XML.escape(object.toString()));
} else if (object instanceof JSONObject) {
sb.append(toString((JSONObject) object));
} else if (object instanceof JSONArray) {
sb.append(toString((JSONArray) object));
}
}
} while (i < length);
sb.append('<');
sb.append('/');
sb.append(tagName);
sb.append('>');
}
return sb.toString();
}
/**
* Reverse the JSONML transformation, making an XML text from a JSONObject.
* The JSONObject must contain a "tagName" property. If it has children,
* then it must have a "childNodes" property containing an array of objects.
* The other properties are attributes with string values.
*
* @param jo A JSONObject.
* @return An XML string.
* @throws JSONException
*/
public static String toString(JSONObject jo) throws JSONException {
StringBuffer sb = new StringBuffer();
int i;
JSONArray ja;
String key;
Iterator keys;
int length;
Object object;
String tagName;
String value;
//Emit <tagName
tagName = jo.optString("tagName");
if (tagName == null) {
return XML.escape(jo.toString());
}
XML.noSpace(tagName);
tagName = XML.escape(tagName);
sb.append('<');
sb.append(tagName);
//Emit the attributes
keys = jo.keys();
while (keys.hasNext()) {
key = keys.next().toString();
if (!key.equals("tagName") && !key.equals("childNodes")) {
XML.noSpace(key);
value = jo.optString(key);
if (value != null) {
sb.append(' ');
sb.append(XML.escape(key));
sb.append('=');
sb.append('"');
sb.append(XML.escape(value));
sb.append('"');
}
}
}
//Emit content in body
ja = jo.optJSONArray("childNodes");
if (ja == null) {
sb.append('/');
sb.append('>');
} else {
sb.append('>');
length = ja.length();
for (i = 0; i < length; i += 1) {
object = ja.get(i);
if (object != null) {
if (object instanceof String) {
sb.append(XML.escape(object.toString()));
} else if (object instanceof JSONObject) {
sb.append(toString((JSONObject) object));
} else if (object instanceof JSONArray) {
sb.append(toString((JSONArray) object));
}
}
}
sb.append('<');
sb.append('/');
sb.append(tagName);
sb.append('>');
}
return sb.toString();
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
package dev.brighten.antivpn.utils.json;
/**
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
* method so that a class can change the behavior of
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
* and <code>JSONWriter.value(</code>Object<code>)</code>. The
* <code>toJSONString</code> method will be used instead of the default behavior
* of using the Object's <code>toString()</code> method and quoting the result.
*/
public interface JSONString {
/**
* The <code>toJSONString</code> method allows a class to produce its own JSON
* serialization.
*
* @return A strictly syntactically correct JSON text.
*/
public String toJSONString();
}
@@ -0,0 +1,80 @@
package dev.brighten.antivpn.utils.json;
/*
Copyright (c) 2006 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.
*/
import java.io.StringWriter;
/**
* JSONStringer provides a quick and convenient way of producing JSON text.
* The texts produced strictly conform to JSON syntax rules. No whitespace is
* added, so the results are ready for transmission or storage. Each instance of
* JSONStringer can produce one JSON text.
* <p>
* A JSONStringer instance provides a <code>value</code> method for appending
* values to the
* text, and a <code>key</code>
* method for adding keys before values in objects. There are <code>array</code>
* and <code>endArray</code> methods that make and bound array values, and
* <code>object</code> and <code>endObject</code> methods which make and bound
* object values. All of these methods return the JSONWriter instance,
* permitting cascade style. For example, <pre>
* myString = new JSONStringer()
* .object()
* .key("JSON")
* .value("Hello, World!")
* .endObject()
* .toString();</pre> which produces the string <pre>
* {"JSON":"Hello, World!"}</pre>
* <p>
* The first method called must be <code>array</code> or <code>object</code>.
* There are no methods for adding commas or colons. JSONStringer adds them for
* you. Objects and arrays can be nested up to 20 levels deep.
* <p>
* This can sometimes be easier than using a JSONObject to build a string.
*
* @author JSON.org
* @version 2008-09-18
*/
public class JSONStringer extends JSONWriter {
/**
* Make a fresh JSONStringer. It can be used to build one JSON text.
*/
public JSONStringer() {
super(new StringWriter());
}
/**
* Return the JSON text. This method is used to obtain the product of the
* JSONStringer instance. It will return <code>null</code> if there was a
* problem in the construction of the JSON text (such as the calls to
* <code>array</code> were not properly balanced with calls to
* <code>endArray</code>).
*
* @return The JSON text.
*/
public String toString() {
return this.mode == 'd' ? this.writer.toString() : null;
}
}
@@ -0,0 +1,446 @@
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
* JSON source strings.
*
* @author JSON.org
* @version 2010-12-24
*/
public class JSONTokener {
private int character;
private boolean eof;
private int index;
private int line;
private char previous;
private Reader reader;
private boolean usePrevious;
/**
* Construct a JSONTokener from a Reader.
*
* @param reader A reader.
*/
public JSONTokener(Reader reader) {
this.reader = reader.markSupported() ?
reader : new BufferedReader(reader);
this.eof = false;
this.usePrevious = false;
this.previous = 0;
this.index = 0;
this.character = 1;
this.line = 1;
}
/**
* Construct a JSONTokener from an InputStream.
*/
public JSONTokener(InputStream inputStream) throws JSONException {
this(new InputStreamReader(inputStream));
}
/**
* Construct a JSONTokener from a string.
*
* @param s A source string.
*/
public JSONTokener(String s) {
this(new StringReader(s));
}
/**
* Get the hex value of a character (base16).
*
* @param c A character between '0' and '9' or between 'A' and 'F' or
* between 'a' and 'f'.
* @return An int between 0 and 15, or -1 if c was not a hex digit.
*/
public static int dehexchar(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'A' && c <= 'F') {
return c - ('A' - 10);
}
if (c >= 'a' && c <= 'f') {
return c - ('a' - 10);
}
return -1;
}
/**
* Back up one character. This provides a sort of lookahead capability,
* so that you can test for a digit or letter before attempting to parse
* the next number or identifier.
*/
public void back() throws JSONException {
if (usePrevious || index <= 0) {
throw new JSONException("Stepping back two steps is not supported");
}
this.index -= 1;
this.character -= 1;
this.usePrevious = true;
this.eof = false;
}
public boolean end() {
return eof && !usePrevious;
}
/**
* Determine if the source string still contains characters that next()
* can consume.
*
* @return true if not yet at the end of the source.
*/
public boolean more() throws JSONException {
next();
if (end()) {
return false;
}
back();
return true;
}
/**
* Get the next character in the source string.
*
* @return The next character, or 0 if past the end of the source string.
*/
public char next() throws JSONException {
int c;
if (this.usePrevious) {
this.usePrevious = false;
c = this.previous;
} else {
try {
c = this.reader.read();
} catch (IOException exception) {
throw new JSONException(exception);
}
if (c <= 0) { // End of stream
this.eof = true;
c = 0;
}
}
this.index += 1;
if (this.previous == '\r') {
this.line += 1;
this.character = c == '\n' ? 0 : 1;
} else if (c == '\n') {
this.line += 1;
this.character = 0;
} else {
this.character += 1;
}
this.previous = (char) c;
return this.previous;
}
/**
* Consume the next character, and check that it matches a specified
* character.
*
* @param c The character to match.
* @return The character.
* @throws JSONException if the character does not match.
*/
public char next(char c) throws JSONException {
char n = next();
if (n != c) {
throw syntaxError("Expected '" + c + "' and instead saw '" +
n + "'");
}
return n;
}
/**
* Get the next n characters.
*
* @param n The number of characters to take.
* @return A string of n characters.
* @throws JSONException Substring bounds error if there are not
* n characters remaining in the source string.
*/
public String next(int n) throws JSONException {
if (n == 0) {
return "";
}
char[] chars = new char[n];
int pos = 0;
while (pos < n) {
chars[pos] = next();
if (end()) {
throw syntaxError("Substring bounds error");
}
pos += 1;
}
return new String(chars);
}
/**
* Get the next char in the string, skipping whitespace.
*
* @return A character, or 0 if there are no more characters.
* @throws JSONException
*/
public char nextClean() throws JSONException {
for (; ; ) {
char c = next();
if (c == 0 || c > ' ') {
return c;
}
}
}
/**
* Return the characters up to the next close quote character.
* Backslash processing is done. The formal JSON format does not
* allow strings in single quotes, but an implementation is allowed to
* accept them.
*
* @param quote The quoting character, either
* <code>"</code>&nbsp;<small>(double quote)</small> or
* <code>'</code>&nbsp;<small>(single quote)</small>.
* @return A String.
* @throws JSONException Unterminated string.
*/
public String nextString(char quote) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
for (; ; ) {
c = next();
switch (c) {
case 0:
case '\n':
case '\r':
throw syntaxError("Unterminated string");
case '\\':
c = next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
sb.append((char) Integer.parseInt(next(4), 16));
break;
case '"':
case '\'':
case '\\':
case '/':
sb.append(c);
break;
default:
throw syntaxError("Illegal escape.");
}
break;
default:
if (c == quote) {
return sb.toString();
}
sb.append(c);
}
}
}
/**
* Get the text up but not including the specified character or the
* end of line, whichever comes first.
*
* @param delimiter A delimiter character.
* @return A string.
*/
public String nextTo(char delimiter) throws JSONException {
StringBuffer sb = new StringBuffer();
for (; ; ) {
char c = next();
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
if (c != 0) {
back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the text up but not including one of the specified delimiter
* characters or the end of line, whichever comes first.
*
* @param delimiters A set of delimiter characters.
* @return A string, trimmed.
*/
public String nextTo(String delimiters) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
for (; ; ) {
c = next();
if (delimiters.indexOf(c) >= 0 || c == 0 ||
c == '\n' || c == '\r') {
if (c != 0) {
back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the next value. The value can be a Boolean, Double, Integer,
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
*
* @return An object.
* @throws JSONException If syntax error.
*/
public Object nextValue() throws JSONException {
char c = nextClean();
String string;
switch (c) {
case '"':
case '\'':
return nextString(c);
case '{':
back();
return new JSONObject(this);
case '[':
back();
return new JSONArray(this);
}
/*
* Handle unquoted text. This could be the values true, false, or
* null, or it can be a number. An implementation (such as this one)
* is allowed to also accept non-standard forms.
*
* Accumulate characters until we reach the end of the text or a
* formatting character.
*/
StringBuffer sb = new StringBuffer();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = next();
}
back();
string = sb.toString().trim();
if (string.equals("")) {
throw syntaxError("Missing value");
}
return JSONObject.stringToValue(string);
}
/**
* Skip characters until the next character is the requested character.
* If the requested character is not found, no characters are skipped.
*
* @param to A character to skip to.
* @return The requested character, or zero if the requested character
* is not found.
*/
public char skipTo(char to) throws JSONException {
char c;
try {
int startIndex = this.index;
int startCharacter = this.character;
int startLine = this.line;
reader.mark(Integer.MAX_VALUE);
do {
c = next();
if (c == 0) {
reader.reset();
this.index = startIndex;
this.character = startCharacter;
this.line = startLine;
return c;
}
} while (c != to);
} catch (IOException exc) {
throw new JSONException(exc);
}
back();
return c;
}
/**
* Make a JSONException to signal a syntax error.
*
* @param message The error message.
* @return A JSONException object, suitable for throwing
*/
public JSONException syntaxError(String message) {
return new JSONException(message + toString());
}
/**
* Make a printable string of this JSONTokener.
*
* @return " at {index} [character {character} line {line}]"
*/
public String toString() {
return " at " + index + " [character " + this.character + " line " +
this.line + "]";
}
}
@@ -0,0 +1,332 @@
package dev.brighten.antivpn.utils.json;
import java.io.IOException;
import java.io.Writer;
/*
Copyright (c) 2006 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.
*/
/**
* JSONWriter provides a quick and convenient way of producing JSON text.
* The texts produced strictly conform to JSON syntax rules. No whitespace is
* added, so the results are ready for transmission or storage. Each instance of
* JSONWriter can produce one JSON text.
* <p>
* A JSONWriter instance provides a <code>value</code> method for appending
* values to the
* text, and a <code>key</code>
* method for adding keys before values in objects. There are <code>array</code>
* and <code>endArray</code> methods that make and bound array values, and
* <code>object</code> and <code>endObject</code> methods which make and bound
* object values. All of these methods return the JSONWriter instance,
* permitting a cascade style. For example, <pre>
* new JSONWriter(myWriter)
* .object()
* .key("JSON")
* .value("Hello, World!")
* .endObject();</pre> which writes <pre>
* {"JSON":"Hello, World!"}</pre>
* <p>
* The first method called must be <code>array</code> or <code>object</code>.
* There are no methods for adding commas or colons. JSONWriter adds them for
* you. Objects and arrays can be nested up to 20 levels deep.
* <p>
* This can sometimes be easier than using a JSONObject to build a string.
*
* @author JSON.org
* @version 2010-12-24
*/
public class JSONWriter {
private static final int maxdepth = 20;
/**
* The current mode. Values:
* 'a' (array),
* 'd' (done),
* 'i' (initial),
* 'k' (key),
* 'o' (object).
*/
protected char mode;
/**
* The writer that will receive the output.
*/
protected Writer writer;
/**
* The comma flag determines if a comma should be output before the next
* value.
*/
private boolean comma;
/**
* The object/array stack.
*/
private JSONObject stack[];
/**
* The stack top index. A value of 0 indicates that the stack is empty.
*/
private int top;
/**
* Make a fresh JSONWriter. It can be used to build one JSON text.
*/
public JSONWriter(Writer w) {
this.comma = false;
this.mode = 'i';
this.stack = new JSONObject[maxdepth];
this.top = 0;
this.writer = w;
}
/**
* Append a value.
*
* @param string A string value.
* @return this
* @throws JSONException If the value is out of sequence.
*/
private JSONWriter append(String string) throws JSONException {
if (string == null) {
throw new JSONException("Null pointer");
}
if (this.mode == 'o' || this.mode == 'a') {
try {
if (this.comma && this.mode == 'a') {
this.writer.write(',');
}
this.writer.write(string);
} catch (IOException e) {
throw new JSONException(e);
}
if (this.mode == 'o') {
this.mode = 'k';
}
this.comma = true;
return this;
}
throw new JSONException("Value out of sequence.");
}
/**
* Begin appending a new array. All values until the balancing
* <code>endArray</code> will be appended to this array. The
* <code>endArray</code> method must be called to mark the array's end.
*
* @return this
* @throws JSONException If the nesting is too deep, or if the object is
* started in the wrong place (for example as a key or after the end of the
* outermost array or object).
*/
public JSONWriter array() throws JSONException {
if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
this.push(null);
this.append("[");
this.comma = false;
return this;
}
throw new JSONException("Misplaced array.");
}
/**
* End something.
*
* @param mode Mode
* @param c Closing character
* @return this
* @throws JSONException If unbalanced.
*/
private JSONWriter end(char mode, char c) throws JSONException {
if (this.mode != mode) {
throw new JSONException(mode == 'a' ? "Misplaced endArray." :
"Misplaced endObject.");
}
this.pop(mode);
try {
this.writer.write(c);
} catch (IOException e) {
throw new JSONException(e);
}
this.comma = true;
return this;
}
/**
* End an array. This method most be called to balance calls to
* <code>array</code>.
*
* @return this
* @throws JSONException If incorrectly nested.
*/
public JSONWriter endArray() throws JSONException {
return this.end('a', ']');
}
/**
* End an object. This method most be called to balance calls to
* <code>object</code>.
*
* @return this
* @throws JSONException If incorrectly nested.
*/
public JSONWriter endObject() throws JSONException {
return this.end('k', '}');
}
/**
* Append a key. The key will be associated with the next value. In an
* object, every value must be preceded by a key.
*
* @param string A key string.
* @return this
* @throws JSONException If the key is out of place. For example, keys
* do not belong in arrays or if the key is null.
*/
public JSONWriter key(String string) throws JSONException {
if (string == null) {
throw new JSONException("Null key.");
}
if (this.mode == 'k') {
try {
stack[top - 1].putOnce(string, Boolean.TRUE);
if (this.comma) {
this.writer.write(',');
}
this.writer.write(JSONObject.quote(string));
this.writer.write(':');
this.comma = false;
this.mode = 'o';
return this;
} catch (IOException e) {
throw new JSONException(e);
}
}
throw new JSONException("Misplaced key.");
}
/**
* Begin appending a new object. All keys and values until the balancing
* <code>endObject</code> will be appended to this object. The
* <code>endObject</code> method must be called to mark the object's end.
*
* @return this
* @throws JSONException If the nesting is too deep, or if the object is
* started in the wrong place (for example as a key or after the end of the
* outermost array or object).
*/
public JSONWriter object() throws JSONException {
if (this.mode == 'i') {
this.mode = 'o';
}
if (this.mode == 'o' || this.mode == 'a') {
this.append("{");
this.push(new JSONObject());
this.comma = false;
return this;
}
throw new JSONException("Misplaced object.");
}
/**
* Pop an array or object scope.
*
* @param c The scope to close.
* @throws JSONException If nesting is wrong.
*/
private void pop(char c) throws JSONException {
if (this.top <= 0) {
throw new JSONException("Nesting error.");
}
char m = this.stack[this.top - 1] == null ? 'a' : 'k';
if (m != c) {
throw new JSONException("Nesting error.");
}
this.top -= 1;
this.mode = this.top == 0 ?
'd' : this.stack[this.top - 1] == null ? 'a' : 'k';
}
/**
* Push an array or object scope.
*
* @param c The scope to open.
* @throws JSONException If nesting is too deep.
*/
private void push(JSONObject jo) throws JSONException {
if (this.top >= maxdepth) {
throw new JSONException("Nesting too deep.");
}
this.stack[this.top] = jo;
this.mode = jo == null ? 'a' : 'k';
this.top += 1;
}
/**
* Append either the value <code>true</code> or the value
* <code>false</code>.
*
* @param b A boolean.
* @return this
* @throws JSONException
*/
public JSONWriter value(boolean b) throws JSONException {
return this.append(b ? "true" : "false");
}
/**
* Append a double value.
*
* @param d A double.
* @return this
* @throws JSONException If the number is not finite.
*/
public JSONWriter value(double d) throws JSONException {
return this.value(new Double(d));
}
/**
* Append a long value.
*
* @param l A long.
* @return this
* @throws JSONException
*/
public JSONWriter value(long l) throws JSONException {
return this.append(Long.toString(l));
}
/**
* Append an object value.
*
* @param object The object to append. It can be null, or a Boolean, Number,
* String, JSONObject, or JSONArray, or an object that implements JSONString.
* @return this
* @throws JSONException If the value is out of sequence.
*/
public JSONWriter value(Object object) throws JSONException {
return this.append(JSONObject.valueToString(object));
}
}
@@ -1,15 +1,11 @@
package dev.brighten.pl.utils;
import cc.funkemunky.carbon.utils.json.JSONException;
import cc.funkemunky.carbon.utils.json.JSONObject;
package dev.brighten.antivpn.utils.json;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
public class JsonReader {
private static String readAll(Reader rd) throws IOException {
public static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
@@ -0,0 +1,534 @@
package dev.brighten.antivpn.utils.json;
/*
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.
*/
import java.util.Iterator;
/**
* This provides static methods to convert an XML text into a JSONObject,
* and to covert a JSONObject into an XML text.
*
* @author JSON.org
* @version 2011-02-11
*/
public class XML {
/**
* The Character '&'.
*/
public static final Character AMP = new Character('&');
/**
* The Character '''.
*/
public static final Character APOS = new Character('\'');
/**
* The Character '!'.
*/
public static final Character BANG = new Character('!');
/**
* The Character '='.
*/
public static final Character EQ = new Character('=');
/**
* The Character '>'.
*/
public static final Character GT = new Character('>');
/**
* The Character '<'.
*/
public static final Character LT = new Character('<');
/**
* The Character '?'.
*/
public static final Character QUEST = new Character('?');
/**
* The Character '"'.
*/
public static final Character QUOT = new Character('"');
/**
* The Character '/'.
*/
public static final Character SLASH = new Character('/');
/**
* Replace special characters with XML escapes:
* <pre>
* &amp; <small>(ampersand)</small> is replaced by &amp;amp;
* &lt; <small>(less than)</small> is replaced by &amp;lt;
* &gt; <small>(greater than)</small> is replaced by &amp;gt;
* &quot; <small>(double quote)</small> is replaced by &amp;quot;
* </pre>
*
* @param string The string to be escaped.
* @return The escaped string.
*/
public static String escape(String string) {
StringBuffer sb = new StringBuffer();
for (int i = 0, length = string.length(); i < length; i++) {
char c = string.charAt(i);
switch (c) {
case '&':
sb.append("&amp;");
break;
case '<':
sb.append("&lt;");
break;
case '>':
sb.append("&gt;");
break;
case '"':
sb.append("&quot;");
break;
case '\'':
sb.append("&apos;");
break;
default:
sb.append(c);
}
}
return sb.toString();
}
/**
* Throw an exception if the string contains whitespace.
* Whitespace is not allowed in tagNames and attributes.
*
* @param string
* @throws JSONException
*/
public static void noSpace(String string) throws JSONException {
int i, length = string.length();
if (length == 0) {
throw new JSONException("Empty string.");
}
for (i = 0; i < length; i += 1) {
if (Character.isWhitespace(string.charAt(i))) {
throw new JSONException("'" + string +
"' contains a space character.");
}
}
}
/**
* Scan the content following the named tag, attaching it to the context.
*
* @param x The XMLTokener containing the source string.
* @param context The JSONObject that will include the new material.
* @param name The tag name.
* @return true if the close tag is processed.
* @throws JSONException
*/
private static boolean parse(XMLTokener x, JSONObject context,
String name) throws JSONException {
char c;
int i;
JSONObject jsonobject = null;
String string;
String tagName;
Object token;
// Test for and skip past these forms:
// <!-- ... -->
// <! ... >
// <![ ... ]]>
// <? ... ?>
// Report errors for these forms:
// <>
// <=
// <<
token = x.nextToken();
// <!
if (token == BANG) {
c = x.next();
if (c == '-') {
if (x.next() == '-') {
x.skipPast("-->");
return false;
}
x.back();
} else if (c == '[') {
token = x.nextToken();
if (token.equals("CDATA")) {
if (x.next() == '[') {
string = x.nextCDATA();
if (string.length() > 0) {
context.accumulate("content", string);
}
return false;
}
}
throw x.syntaxError("Expected 'CDATA['");
}
i = 1;
do {
token = x.nextMeta();
if (token == null) {
throw x.syntaxError("Missing '>' after '<!'.");
} else if (token == LT) {
i += 1;
} else if (token == GT) {
i -= 1;
}
} while (i > 0);
return false;
} else if (token == QUEST) {
// <?
x.skipPast("?>");
return false;
} else if (token == SLASH) {
// Close tag </
token = x.nextToken();
if (name == null) {
throw x.syntaxError("Mismatched close tag " + token);
}
if (!token.equals(name)) {
throw x.syntaxError("Mismatched " + name + " and " + token);
}
if (x.nextToken() != GT) {
throw x.syntaxError("Misshaped close tag");
}
return true;
} else if (token instanceof Character) {
throw x.syntaxError("Misshaped tag");
// Open tag <
} else {
tagName = (String) token;
token = null;
jsonobject = new JSONObject();
for (; ; ) {
if (token == null) {
token = x.nextToken();
}
// attribute = value
if (token instanceof String) {
string = (String) token;
token = x.nextToken();
if (token == EQ) {
token = x.nextToken();
if (!(token instanceof String)) {
throw x.syntaxError("Missing value");
}
jsonobject.accumulate(string,
XML.stringToValue((String) token));
token = null;
} else {
jsonobject.accumulate(string, "");
}
// Empty tag <.../>
} else if (token == SLASH) {
if (x.nextToken() != GT) {
throw x.syntaxError("Misshaped tag");
}
if (jsonobject.length() > 0) {
context.accumulate(tagName, jsonobject);
} else {
context.accumulate(tagName, "");
}
return false;
// Content, between <...> and </...>
} else if (token == GT) {
for (; ; ) {
token = x.nextContent();
if (token == null) {
if (tagName != null) {
throw x.syntaxError("Unclosed tag " + tagName);
}
return false;
} else if (token instanceof String) {
string = (String) token;
if (string.length() > 0) {
jsonobject.accumulate("content",
XML.stringToValue(string));
}
// Nested element
} else if (token == LT) {
if (parse(x, jsonobject, tagName)) {
if (jsonobject.length() == 0) {
context.accumulate(tagName, "");
} else if (jsonobject.length() == 1 &&
jsonobject.opt("content") != null) {
context.accumulate(tagName,
jsonobject.opt("content"));
} else {
context.accumulate(tagName, jsonobject);
}
return false;
}
}
}
} else {
throw x.syntaxError("Misshaped tag");
}
}
}
}
/**
* Try to convert a string into a number, boolean, or null. If the string
* can't be converted, return the string. This is much less ambitious than
* JSONObject.stringToValue, especially because it does not attempt to
* convert plus forms, octal forms, hex forms, or E forms lacking decimal
* points.
*
* @param string A String.
* @return A simple JSON value.
*/
public static Object stringToValue(String string) {
if (string.equals("")) {
return string;
}
if (string.equalsIgnoreCase("true")) {
return Boolean.TRUE;
}
if (string.equalsIgnoreCase("false")) {
return Boolean.FALSE;
}
if (string.equalsIgnoreCase("null")) {
return JSONObject.NULL;
}
if (string.equals("0")) {
return new Integer(0);
}
// If it might be a number, try converting it. If that doesn't work,
// return the string.
try {
char initial = string.charAt(0);
boolean negative = false;
if (initial == '-') {
initial = string.charAt(1);
negative = true;
}
if (initial == '0' && string.charAt(negative ? 2 : 1) == '0') {
return string;
}
if ((initial >= '0' && initial <= '9')) {
if (string.indexOf('.') >= 0) {
return Double.valueOf(string);
} else if (string.indexOf('e') < 0 && string.indexOf('E') < 0) {
Long myLong = new Long(string);
if (myLong.longValue() == myLong.intValue()) {
return new Integer(myLong.intValue());
} else {
return myLong;
}
}
}
} catch (Exception ignore) {
}
return string;
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject. Some information may be lost in this transformation
* because JSON is a data format and XML is a document format. XML uses
* elements, attributes, and content text, while JSON uses unordered
* collections of name/value pairs and arrays of values. JSON does not
* does not like to distinguish between elements and attributes.
* Sequences of similar elements are represented as JSONArrays. Content
* text may be placed in a "content" member. Comments, prologs, DTDs, and
* <code>&lt;[ [ ]]></code> are ignored.
*
* @param string The source string.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException
*/
public static JSONObject toJSONObject(String string) throws JSONException {
JSONObject jo = new JSONObject();
XMLTokener x = new XMLTokener(string);
while (x.more() && x.skipPast("<")) {
parse(x, jo, null);
}
return jo;
}
/**
* Convert a JSONObject into a well-formed, element-normal XML string.
*
* @param object A JSONObject.
* @return A string.
* @throws JSONException
*/
public static String toString(Object object) throws JSONException {
return toString(object, null);
}
/**
* Convert a JSONObject into a well-formed, element-normal XML string.
*
* @param object A JSONObject.
* @param tagName The optional name of the enclosing tag.
* @return A string.
* @throws JSONException
*/
public static String toString(Object object, String tagName)
throws JSONException {
StringBuffer sb = new StringBuffer();
int i;
JSONArray ja;
JSONObject jo;
String key;
Iterator keys;
int length;
String string;
Object value;
if (object instanceof JSONObject) {
// Emit <tagName>
if (tagName != null) {
sb.append('<');
sb.append(tagName);
sb.append('>');
}
// Loop thru the keys.
jo = (JSONObject) object;
keys = jo.keys();
while (keys.hasNext()) {
key = keys.next().toString();
value = jo.opt(key);
if (value == null) {
value = "";
}
if (value instanceof String) {
string = (String) value;
} else {
string = null;
}
// Emit content in body
if (key.equals("content")) {
if (value instanceof JSONArray) {
ja = (JSONArray) value;
length = ja.length();
for (i = 0; i < length; i += 1) {
if (i > 0) {
sb.append('\n');
}
sb.append(escape(ja.get(i).toString()));
}
} else {
sb.append(escape(value.toString()));
}
// Emit an array of similar keys
} else if (value instanceof JSONArray) {
ja = (JSONArray) value;
length = ja.length();
for (i = 0; i < length; i += 1) {
value = ja.get(i);
if (value instanceof JSONArray) {
sb.append('<');
sb.append(key);
sb.append('>');
sb.append(toString(value));
sb.append("</");
sb.append(key);
sb.append('>');
} else {
sb.append(toString(value, key));
}
}
} else if (value.equals("")) {
sb.append('<');
sb.append(key);
sb.append("/>");
// Emit a new tag <k>
} else {
sb.append(toString(value, key));
}
}
if (tagName != null) {
// Emit the </tagname> close tag
sb.append("</");
sb.append(tagName);
sb.append('>');
}
return sb.toString();
// XML does not have good support for arrays. If an array appears in a place
// where XML is lacking, synthesize an <array> element.
} else {
if (object.getClass().isArray()) {
object = new JSONArray(object);
}
if (object instanceof JSONArray) {
ja = (JSONArray) object;
length = ja.length();
for (i = 0; i < length; i += 1) {
sb.append(toString(ja.opt(i), tagName == null ? "array" : tagName));
}
return sb.toString();
} else {
string = (object == null) ? "null" : escape(object.toString());
return (tagName == null) ? "\"" + string + "\"" :
(string.length() == 0) ? "<" + tagName + "/>" :
"<" + tagName + ">" + string + "</" + tagName + ">";
}
}
}
}
@@ -0,0 +1,373 @@
package dev.brighten.antivpn.utils.json;
/*
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.
*/
/**
* The XMLTokener extends the JSONTokener to provide additional methods
* for the parsing of XML texts.
*
* @author JSON.org
* @version 2010-12-24
*/
public class XMLTokener extends JSONTokener {
/**
* The table of entity values. It initially contains Character values for
* amp, apos, gt, lt, quot.
*/
public static final java.util.HashMap entity;
static {
entity = new java.util.HashMap(8);
entity.put("amp", XML.AMP);
entity.put("apos", XML.APOS);
entity.put("gt", XML.GT);
entity.put("lt", XML.LT);
entity.put("quot", XML.QUOT);
}
/**
* Construct an XMLTokener from a string.
*
* @param s A source string.
*/
public XMLTokener(String s) {
super(s);
}
/**
* Get the text in the CDATA block.
*
* @return The string up to the <code>]]&gt;</code>.
* @throws JSONException If the <code>]]&gt;</code> is not found.
*/
public String nextCDATA() throws JSONException {
char c;
int i;
StringBuffer sb = new StringBuffer();
for (; ; ) {
c = next();
if (end()) {
throw syntaxError("Unclosed CDATA");
}
sb.append(c);
i = sb.length() - 3;
if (i >= 0 && sb.charAt(i) == ']' &&
sb.charAt(i + 1) == ']' && sb.charAt(i + 2) == '>') {
sb.setLength(i);
return sb.toString();
}
}
}
/**
* Get the next XML outer token, trimming whitespace. There are two kinds
* of tokens: the '<' character which begins a markup tag, and the content
* text between markup tags.
*
* @return A string, or a '<' Character, or null if there is no more
* source text.
* @throws JSONException
*/
public Object nextContent() throws JSONException {
char c;
StringBuffer sb;
do {
c = next();
} while (Character.isWhitespace(c));
if (c == 0) {
return null;
}
if (c == '<') {
return XML.LT;
}
sb = new StringBuffer();
for (; ; ) {
if (c == '<' || c == 0) {
back();
return sb.toString().trim();
}
if (c == '&') {
sb.append(nextEntity(c));
} else {
sb.append(c);
}
c = next();
}
}
/**
* Return the next entity. These entities are translated to Characters:
* <code>&amp; &apos; &gt; &lt; &quot;</code>.
*
* @param ampersand An ampersand character.
* @return A Character or an entity String if the entity is not recognized.
* @throws JSONException If missing ';' in XML entity.
*/
public Object nextEntity(char ampersand) throws JSONException {
StringBuffer sb = new StringBuffer();
for (; ; ) {
char c = next();
if (Character.isLetterOrDigit(c) || c == '#') {
sb.append(Character.toLowerCase(c));
} else if (c == ';') {
break;
} else {
throw syntaxError("Missing ';' in XML entity: &" + sb);
}
}
String string = sb.toString();
Object object = entity.get(string);
return object != null ? object : ampersand + string + ";";
}
/**
* Returns the next XML meta token. This is used for skipping over <!...>
* and <?...?> structures.
*
* @return Syntax characters (<code>< > / = ! ?</code>) are returned as
* Character, and strings and names are returned as Boolean. We don't care
* what the values actually are.
* @throws JSONException If a string is not properly closed or if the XML
* is badly structured.
*/
public Object nextMeta() throws JSONException {
char c;
char q;
do {
c = next();
} while (Character.isWhitespace(c));
switch (c) {
case 0:
throw syntaxError("Misshaped meta tag");
case '<':
return XML.LT;
case '>':
return XML.GT;
case '/':
return XML.SLASH;
case '=':
return XML.EQ;
case '!':
return XML.BANG;
case '?':
return XML.QUEST;
case '"':
case '\'':
q = c;
for (; ; ) {
c = next();
if (c == 0) {
throw syntaxError("Unterminated string");
}
if (c == q) {
return Boolean.TRUE;
}
}
default:
for (; ; ) {
c = next();
if (Character.isWhitespace(c)) {
return Boolean.TRUE;
}
switch (c) {
case 0:
case '<':
case '>':
case '/':
case '=':
case '!':
case '?':
case '"':
case '\'':
back();
return Boolean.TRUE;
}
}
}
}
/**
* Get the next XML Token. These tokens are found inside of angle
* brackets. It may be one of these characters: <code>/ > = ! ?</code> or it
* may be a string wrapped in single quotes or double quotes, or it may be a
* name.
*
* @return a String or a Character.
* @throws JSONException If the XML is not well formed.
*/
public Object nextToken() throws JSONException {
char c;
char q;
StringBuffer sb;
do {
c = next();
} while (Character.isWhitespace(c));
switch (c) {
case 0:
throw syntaxError("Misshaped element");
case '<':
throw syntaxError("Misplaced '<'");
case '>':
return XML.GT;
case '/':
return XML.SLASH;
case '=':
return XML.EQ;
case '!':
return XML.BANG;
case '?':
return XML.QUEST;
// Quoted string
case '"':
case '\'':
q = c;
sb = new StringBuffer();
for (; ; ) {
c = next();
if (c == 0) {
throw syntaxError("Unterminated string");
}
if (c == q) {
return sb.toString();
}
if (c == '&') {
sb.append(nextEntity(c));
} else {
sb.append(c);
}
}
default:
// Name
sb = new StringBuffer();
for (; ; ) {
sb.append(c);
c = next();
if (Character.isWhitespace(c)) {
return sb.toString();
}
switch (c) {
case 0:
return sb.toString();
case '>':
case '/':
case '=':
case '!':
case '?':
case '[':
case ']':
back();
return sb.toString();
case '<':
case '"':
case '\'':
throw syntaxError("Bad character in a name");
}
}
}
}
/**
* Skip characters until past the requested string.
* If it is not found, we are left at the end of the source with a result of false.
*
* @param to A string to skip past.
* @throws JSONException
*/
public boolean skipPast(String to) throws JSONException {
boolean b;
char c;
int i;
int j;
int offset = 0;
int length = to.length();
char[] circle = new char[length];
/*
* First fill the circle buffer with as many characters as are in the
* to string. If we reach an early end, bail.
*/
for (i = 0; i < length; i += 1) {
c = next();
if (c == 0) {
return false;
}
circle[i] = c;
}
/*
* We will loop, possibly for all of the remaining characters.
*/
for (; ; ) {
j = offset;
b = true;
/*
* Compare the circle buffer with the to string.
*/
for (i = 0; i < length; i += 1) {
if (circle[j] != to.charAt(i)) {
b = false;
break;
}
j += 1;
if (j >= length) {
j -= length;
}
}
/*
* If we exit the loop with b intact, then victory is ours.
*/
if (b) {
return true;
}
/*
* Get the next character. If there isn't one, then defeat is ours.
*/
c = next();
if (c == 0) {
return false;
}
/*
* Shove the character in the circle buffer and advance the
* circle offset. The offset is mod n.
*/
circle[offset] = c;
offset += 1;
if (offset >= length) {
offset -= length;
}
}
}
}
-674
View File
@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
+8
View File
@@ -0,0 +1,8 @@
# AntiVPN
An antivpn plugin utilizing the KauriVPN API
## The Fastest Available
Just a simple plugin using an incredibly fast and accurate API.
## SpigotMC Page
Rate and support the project on SpigotMC: https://www.spigotmc.org/resources/kaurivpn-anti-proxy-tor-and-vpn-free-api.93355/
+90
View File
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.6.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Velocity</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>velocity</id>
<url>https://nexus.velocitypowered.com/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.6.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-velocity</artifactId>
<version>2.2.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<!-- Replace this with your package! -->
<shadedPattern>dev.brighten.antivpn.velocity.org.bstats</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>
</project>
@@ -0,0 +1,39 @@
package dev.brighten.antivpn.velocity;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.command.CommandExecutor;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.Optional;
@RequiredArgsConstructor
public class VelocityCommandExecutor implements CommandExecutor {
private final CommandSource sender;
@Override
public void sendMessage(String message) {
sender.sendMessage(LegacyComponentSerializer.builder().character('&').build().deserialize(message));
}
@Override
public boolean hasPermission(String permission) {
return sender.hasPermission(permission);
}
@Override
public Optional<APIPlayer> getPlayer() {
if(!isPlayer()) return Optional.empty();
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((Player) sender).getUniqueId());
}
@Override
public boolean isPlayer() {
return sender instanceof Player;
}
}
@@ -0,0 +1,179 @@
package dev.brighten.antivpn.velocity;
import dev.brighten.antivpn.api.VPNConfig;
import dev.brighten.antivpn.velocity.util.ConfigDefault;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class VelocityConfig implements VPNConfig {
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
"license", VelocityPlugin.INSTANCE), kickStringDefault =
new ConfigDefault<>("Proxies are not allowed on our server",
"kickMessage", VelocityPlugin.INSTANCE),
defaultDatabaseType = new ConfigDefault<>("MySQL",
"database.type", VelocityPlugin.INSTANCE),
defaultDatabaseName = new ConfigDefault<>("kaurivpn",
"database.database", VelocityPlugin.INSTANCE),
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", VelocityPlugin.INSTANCE),
defaultUsername = new ConfigDefault<>("root",
"database.username", VelocityPlugin.INSTANCE),
defaultPassword = new ConfigDefault<>("password",
"database.password", VelocityPlugin.INSTANCE),
defaultIp = new ConfigDefault<>("localhost", "database.ip", VelocityPlugin.INSTANCE),
defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" +
" &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message",
VelocityPlugin.INSTANCE);
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
"cachedResults", VelocityPlugin.INSTANCE),
defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled",
VelocityPlugin.INSTANCE), defaultCommandsEnable = new ConfigDefault<>(false,
"commands.enabled", VelocityPlugin.INSTANCE), defaultKickPlayers
= new ConfigDefault<>(true, "kickPlayers", VelocityPlugin.INSTANCE),
defaultUseCredentials = new ConfigDefault<>(true,
"database.useCredentials", VelocityPlugin.INSTANCE),
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled",
VelocityPlugin.INSTANCE),
defaultMetrics = new ConfigDefault<>(true, "bstats", VelocityPlugin.INSTANCE);
private final ConfigDefault<Integer>
defaultPort = new ConfigDefault<>(-1, "database.port", VelocityPlugin.INSTANCE);
private final ConfigDefault<List<String>> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(),
"prefixWhitelists", VelocityPlugin.INSTANCE), defaultCommands = new ConfigDefault<>(
Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute",
VelocityPlugin.INSTANCE);
private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg;
private List<String> prefixWhitelists, commands;
private int port;
private boolean cacheResults, useCredentials, databaseEnabled, commandsEnabled, kickPlayers, alertToStaff, metrics;
@Override
public String getLicense() {
return license;
}
@Override
public boolean cachedResults() {
return cacheResults;
}
@Override
public String getKickString() {
return kickMessage;
}
@Override
public String alertMessage() {
return alertMsg;
}
@Override
public boolean alertToStaff() {
return alertToStaff;
}
@Override
public boolean runCommands() {
return commandsEnabled;
}
@Override
public List<String> commands() {
return commands;
}
@Override
public boolean kickPlayersOnDetect() {
return kickPlayers;
}
@Override
public List<String> getPrefixWhitelists() {
return prefixWhitelists;
}
@Override
public boolean isDatabaseEnabled() {
return databaseEnabled;
}
@Override
public boolean useDatabaseCreds() {
return useCredentials;
}
@Override
public String mongoDatabaseURL() {
return mongoURL;
}
@Override
public String getDatabaseType() {
return databaseType;
}
@Override
public String getDatabaseName() {
return databaseName;
}
@Override
public String getUsername() {
return username;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getIp() {
return ip;
}
@Override
public int getPort() {
if(port == -1) {
switch (getDatabaseType().toLowerCase()) {
case "mongodb":
case "mongo":
case "mongod":
return 27017;
case "sql":
case "mysql":
return 3306;
}
}
return port;
}
@Override
public boolean metrics() {
return metrics;
}
public void update() {
license = licenseDefault.get();
kickMessage = kickStringDefault.get();
cacheResults = cacheResultsDefault.get();
prefixWhitelists = prefixWhitelistsDefault.get();
databaseEnabled = defaultDatabaseEnabled.get();
useCredentials = defaultUseCredentials.get();
databaseType = defaultDatabaseType.get();
databaseName = defaultDatabaseName.get();
mongoURL = defaultMongoURL.get();
username = defaultUsername.get();
password = defaultPassword.get();
ip = defaultIp.get();
port = defaultPort.get();
commandsEnabled = defaultCommandsEnable.get();
commands = defaultCommands.get();
kickPlayers = defaultKickPlayers.get();
alertToStaff = defaultAlertToStaff.get();
alertMsg = defaultAlertMsg.get();
metrics = defaultMetrics.get();
}
}
@@ -0,0 +1,98 @@
package dev.brighten.antivpn.velocity;
import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.scheduler.ScheduledTask;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.velocity.util.StringUtils;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class VelocityListener extends VPNExecutor {
private ScheduledTask cacheResetTask;
@Override
public void registerListeners() {
VelocityPlugin.INSTANCE.getServer().getEventManager()
.register(VelocityPlugin.INSTANCE, this);
VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class,
event -> {
if(event.getResult().isAllowed()) {
if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress()
.getAddress().getHostAddress())
|| AntiVPN.getInstance().getConfig().getPrefixWhitelists().stream()
.anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return;
checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(),
AntiVPN.getInstance().getConfig().cachedResults(), result -> {
if(result.isSuccess() && result.isProxy()) {
if(AntiVPN.getInstance().getConfig().kickPlayersOnDetect())
event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
.build().deserialize(AntiVPN.getInstance().getConfig().getKickString()));
VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
if(AntiVPN.getInstance().getConfig().alertToStaff()) //Ensuring the user wishes to alert to staff
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.filter(APIPlayer::isAlertsEnabled)
.forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getConfig().alertMessage()
.replace("%player%", event.getPlayer().getUsername())
.replace("%reason%", result.getMethod())
.replace("%country%", result.getCountryName())
.replace("%city%", result.getCity())));
//In case the user wants to run their own commands instead of using the built in kicking
if(AntiVPN.getInstance().getConfig().runCommands()) {
for (String command : AntiVPN.getInstance().getConfig().commands()) {
VelocityPlugin.INSTANCE.getServer().getCommandManager()
.executeAsync(VelocityPlugin.INSTANCE.getServer()
.getConsoleCommandSource(),
StringUtils.translateAlternateColorCodes('&',
command.replace("%player%",
event.getPlayer().getUsername())));
}
}
AntiVPN.getInstance().detections++;
} else if(!result.isSuccess()) {
VelocityPlugin.INSTANCE.getLogger()
.log(Level.WARNING,
"The API query was not a success! " +
"You may need to upgrade your license on https://funkemunky.cc/shop");
}
AntiVPN.getInstance().checked++;
});
}
});
}
@Override
public void runCacheReset() {
cacheResetTask = VelocityPlugin.INSTANCE.getServer().getScheduler()
.buildTask(VelocityPlugin.INSTANCE, this::resetCache)
.repeat(20, TimeUnit.MINUTES)
.schedule();
}
@Override
public void shutdown() {
if(cacheResetTask != null) {
cacheResetTask.cancel();
cacheResetTask = null;
}
threadExecutor.shutdown();
VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this);
}
@Override
public void log(String log, Object... objects) {
VelocityPlugin.INSTANCE.getLogger().log(Level.INFO, String.format(log, objects));
}
}
@@ -0,0 +1,31 @@
package dev.brighten.antivpn.velocity;
import com.velocitypowered.api.proxy.Player;
import dev.brighten.antivpn.api.APIPlayer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
public class VelocityPlayer extends APIPlayer {
private final Player player;
public VelocityPlayer(Player player) {
super(player.getUniqueId(), player.getUsername(), player.getRemoteAddress().getAddress());
this.player = player;
}
@Override
public void sendMessage(String message) {
player.sendMessage(LegacyComponentSerializer.builder().character('&').build().deserialize(message));
}
@Override
public void kickPlayer(String reason) {
player.disconnect(LegacyComponentSerializer.builder().character('&').build().deserialize(reason));
}
@Override
public boolean hasPermission(String permission) {
return player.hasPermission(permission);
}
}
@@ -0,0 +1,35 @@
package dev.brighten.antivpn.velocity;
import com.velocitypowered.api.proxy.Player;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.PlayerExecutor;
import java.util.*;
import java.util.stream.Collectors;
public class VelocityPlayerExecutor implements PlayerExecutor {
private final Map<Player, VelocityPlayer> cachedPlayers = new WeakHashMap<>();
@Override
public Optional<APIPlayer> getPlayer(String name) {
Optional<Player> player = VelocityPlugin.INSTANCE.getServer().getPlayer(name);
return player.map(value -> cachedPlayers.computeIfAbsent(value, VelocityPlayer::new));
}
@Override
public Optional<APIPlayer> getPlayer(UUID uuid) {
Optional<Player> player = VelocityPlugin.INSTANCE.getServer().getPlayer(uuid);
return player.map(value -> cachedPlayers.computeIfAbsent(value, VelocityPlayer::new));
}
@Override
public List<APIPlayer> getOnlinePlayers() {
return VelocityPlugin.INSTANCE.getServer().getAllPlayers().stream()
.map(pl -> cachedPlayers.computeIfAbsent(pl, VelocityPlayer::new))
.collect(Collectors.toList());
}
}
@@ -0,0 +1,103 @@
package dev.brighten.antivpn.velocity;
import com.google.inject.Inject;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.velocity.util.Config;
import lombok.Getter;
import lombok.val;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bstats.velocity.Metrics;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.logging.Logger;
import java.util.stream.IntStream;
@Getter
@Plugin(id = "kaurivpn", name = "KauriVPN", version = "1.5.0", authors = {"funkemunky"})
public class VelocityPlugin {
private final ProxyServer server;
private final Logger logger;
private Metrics.Factory metricsFactory;
public static VelocityPlugin INSTANCE;
@Inject
@DataDirectory
private Path configDir;
private Config config;
@Inject
public VelocityPlugin(ProxyServer server, Logger logger, Metrics.Factory metricsFactory) {
this.server = server;
this.logger = logger;
this.metricsFactory = metricsFactory;
}
@Subscribe
public void onInit(ProxyInitializeEvent event) {
INSTANCE = this;
logger.info("Loading config...");
config = new Config();
//Loading plugin
logger.info("Starting AntiVPN services...");
AntiVPN.start(new VelocityConfig(), new VelocityListener(), new VelocityPlayerExecutor(), configDir.toFile());
if(AntiVPN.getInstance().getConfig().metrics()) {
logger.info("Starting metrics...");
Metrics metrics = metricsFactory.make(this, 12791);
}
logger.info("Registering commands...");
for (Command command : AntiVPN.getInstance().getCommands()) {
server.getCommandManager().register(server.getCommandManager().metaBuilder(command.name())
.aliases(command.aliases()).build(), (SimpleCommand) invocation -> {
CommandSource sender = invocation.source();
if(!invocation.source().hasPermission("antivpn.command.*")
&& !invocation.source().hasPermission(command.permission())) {
invocation.source().sendMessage(Component.text("No permission").toBuilder()
.color(TextColor.color(255,0,0)).build());
return;
}
val children = command.children();
String[] args = invocation.arguments();
if(children.length > 0 && args.length > 0) {
for (Command child : children) {
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
if(!sender.hasPermission("antivpn.command.*")
&& !sender.hasPermission(child.permission())) {
invocation.source().sendMessage(Component.text("No permission")
.toBuilder().color(TextColor.color(255,0,0)).build());
return;
}
sender.sendMessage(LegacyComponentSerializer.builder().character('&').build()
.deserialize(child.execute(new VelocityCommandExecutor(sender), IntStream
.range(0, args.length - 1)
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
return;
}
}
}
sender.sendMessage(LegacyComponentSerializer.builder().character('&').build()
.deserialize(command.execute(new VelocityCommandExecutor(sender), args)));
});
}
}
}
@@ -0,0 +1,414 @@
package dev.brighten.antivpn.velocity.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
public final class Configuration
{
private static final char SEPARATOR = '.';
final Map<String, Object> self;
private final Configuration defaults;
public Configuration()
{
this( null );
}
public Configuration(Configuration defaults)
{
this( new LinkedHashMap<String, Object>(), defaults );
}
Configuration(Map<?, ?> map, Configuration defaults)
{
this.self = new LinkedHashMap<>();
this.defaults = defaults;
for ( Map.Entry<?, ?> entry : map.entrySet() )
{
String key = ( entry.getKey() == null ) ? "null" : entry.getKey().toString();
if ( entry.getValue() instanceof Map )
{
this.self.put( key, new Configuration( (Map) entry.getValue(), ( defaults == null ) ? null : defaults.getSection( key ) ) );
} else
{
this.self.put( key, entry.getValue() );
}
}
}
private Configuration getSectionFor(String path)
{
int index = path.indexOf( SEPARATOR );
if ( index == -1 )
{
return this;
}
String root = path.substring( 0, index );
Object section = self.get( root );
if ( section == null )
{
section = new Configuration( ( defaults == null ) ? null : defaults.getSection( root ) );
self.put( root, section );
}
return (Configuration) section;
}
private String getChild(String path)
{
int index = path.indexOf( SEPARATOR );
return ( index == -1 ) ? path : path.substring( index + 1 );
}
/*------------------------------------------------------------------------*/
@SuppressWarnings("unchecked")
public <T> T get(String path, T def)
{
Configuration section = getSectionFor( path );
Object val;
if ( section == this )
{
val = self.get( path );
} else
{
val = section.get( getChild( path ), def );
}
if ( val == null && def instanceof Configuration )
{
self.put( path, def );
}
return ( val != null ) ? (T) val : def;
}
public boolean contains(String path)
{
return get( path, null ) != null;
}
public Object get(String path)
{
return get( path, getDefault( path ) );
}
public Object getDefault(String path)
{
return ( defaults == null ) ? null : defaults.get( path );
}
public void set(String path, Object value)
{
if ( value instanceof Map )
{
value = new Configuration( (Map) value, ( defaults == null ) ? null : defaults.getSection( path ) );
}
Configuration section = getSectionFor( path );
if ( section == this )
{
if ( value == null )
{
self.remove( path );
} else
{
self.put( path, value );
}
} else
{
section.set( getChild( path ), value );
}
}
/*------------------------------------------------------------------------*/
public Configuration getSection(String path)
{
Object def = getDefault( path );
return (Configuration) get( path, ( def instanceof Configuration ) ? def : new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) ) );
}
/**
* Gets keys, not deep by default.
*
* @return top level keys for this section
*/
public Collection<String> getKeys()
{
return new LinkedHashSet<>( self.keySet() );
}
/*------------------------------------------------------------------------*/
public byte getByte(String path)
{
Object def = getDefault( path );
return getByte( path, ( def instanceof Number ) ? ( (Number) def ).byteValue() : 0 );
}
public byte getByte(String path, byte def)
{
Object val = get( path, def );
return ( val instanceof Number ) ? ( (Number) val ).byteValue() : def;
}
public List<Byte> getByteList(String path)
{
List<?> list = getList( path );
List<Byte> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof Number )
{
result.add( ( (Number) object ).byteValue() );
}
}
return result;
}
public short getShort(String path)
{
Object def = getDefault( path );
return getShort( path, ( def instanceof Number ) ? ( (Number) def ).shortValue() : 0 );
}
public short getShort(String path, short def)
{
Object val = get( path, def );
return ( val instanceof Number ) ? ( (Number) val ).shortValue() : def;
}
public List<Short> getShortList(String path)
{
List<?> list = getList( path );
List<Short> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof Number )
{
result.add( ( (Number) object ).shortValue() );
}
}
return result;
}
public int getInt(String path)
{
Object def = getDefault( path );
return getInt( path, ( def instanceof Number ) ? ( (Number) def ).intValue() : 0 );
}
public int getInt(String path, int def)
{
Object val = get( path, def );
return ( val instanceof Number ) ? ( (Number) val ).intValue() : def;
}
public List<Integer> getIntList(String path)
{
List<?> list = getList( path );
List<Integer> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof Number )
{
result.add( ( (Number) object ).intValue() );
}
}
return result;
}
public long getLong(String path)
{
Object def = getDefault( path );
return getLong( path, ( def instanceof Number ) ? ( (Number) def ).longValue() : 0 );
}
public long getLong(String path, long def)
{
Object val = get( path, def );
return ( val instanceof Number ) ? ( (Number) val ).longValue() : def;
}
public List<Long> getLongList(String path)
{
List<?> list = getList( path );
List<Long> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof Number )
{
result.add( ( (Number) object ).longValue() );
}
}
return result;
}
public float getFloat(String path)
{
Object def = getDefault( path );
return getFloat( path, ( def instanceof Number ) ? ( (Number) def ).floatValue() : 0 );
}
public float getFloat(String path, float def)
{
Object val = get( path, def );
return ( val instanceof Number ) ? ( (Number) val ).floatValue() : def;
}
public List<Float> getFloatList(String path)
{
List<?> list = getList( path );
List<Float> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof Number )
{
result.add( ( (Number) object ).floatValue() );
}
}
return result;
}
public double getDouble(String path)
{
Object def = getDefault( path );
return getDouble( path, ( def instanceof Number ) ? ( (Number) def ).doubleValue() : 0 );
}
public double getDouble(String path, double def)
{
Object val = get( path, def );
return ( val instanceof Number ) ? ( (Number) val ).doubleValue() : def;
}
public List<Double> getDoubleList(String path)
{
List<?> list = getList( path );
List<Double> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof Number )
{
result.add( ( (Number) object ).doubleValue() );
}
}
return result;
}
public boolean getBoolean(String path)
{
Object def = getDefault( path );
return getBoolean( path, ( def instanceof Boolean ) ? (Boolean) def : false );
}
public boolean getBoolean(String path, boolean def)
{
Object val = get( path, def );
return ( val instanceof Boolean ) ? (Boolean) val : def;
}
public List<Boolean> getBooleanList(String path)
{
List<?> list = getList( path );
List<Boolean> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof Boolean )
{
result.add( (Boolean) object );
}
}
return result;
}
public char getChar(String path)
{
Object def = getDefault( path );
return getChar( path, ( def instanceof Character ) ? (Character) def : '\u0000' );
}
public char getChar(String path, char def)
{
Object val = get( path, def );
return ( val instanceof Character ) ? (Character) val : def;
}
public List<Character> getCharList(String path)
{
List<?> list = getList( path );
List<Character> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof Character )
{
result.add( (Character) object );
}
}
return result;
}
public String getString(String path)
{
Object def = getDefault( path );
return getString( path, ( def instanceof String ) ? (String) def : "" );
}
public String getString(String path, String def)
{
Object val = get( path, def );
return ( val instanceof String ) ? (String) val : def;
}
public List<String> getStringList(String path)
{
List<?> list = getList( path );
List<String> result = new ArrayList<>();
for ( Object object : list )
{
if ( object instanceof String )
{
result.add( (String) object );
}
}
return result;
}
/*------------------------------------------------------------------------*/
public List<?> getList(String path)
{
Object def = getDefault( path );
return getList( path, ( def instanceof List<?> ) ? (List<?>) def : Collections.EMPTY_LIST );
}
public List<?> getList(String path, List<?> def)
{
Object val = get( path, def );
return ( val instanceof List<?> ) ? (List<?>) val : def;
}
}
@@ -0,0 +1,60 @@
package dev.brighten.antivpn.velocity.config;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
public abstract class ConfigurationProvider
{
private static final Map<Class<? extends ConfigurationProvider>, ConfigurationProvider> providers = new HashMap<>();
static
{
try
{
providers.put( YamlConfiguration.class, new YamlConfiguration() );
} catch ( NoClassDefFoundError ex )
{
// Ignore, no SnakeYAML
}
try
{
providers.put( JsonConfiguration.class, new JsonConfiguration() );
} catch ( NoClassDefFoundError ex )
{
// Ignore, no Gson
}
}
public static ConfigurationProvider getProvider(Class<? extends ConfigurationProvider> provider)
{
return providers.get( provider );
}
/*------------------------------------------------------------------------*/
public abstract void save(Configuration config, File file) throws IOException;
public abstract void save(Configuration config, Writer writer);
public abstract Configuration load(File file) throws IOException;
public abstract Configuration load(File file, Configuration defaults) throws IOException;
public abstract Configuration load(Reader reader);
public abstract Configuration load(Reader reader, Configuration defaults);
public abstract Configuration load(InputStream is);
public abstract Configuration load(InputStream is, Configuration defaults);
public abstract Configuration load(String string);
public abstract Configuration load(String string, Configuration defaults);
}
@@ -0,0 +1,114 @@
package dev.brighten.antivpn.velocity.config;
import com.google.common.base.Charsets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class JsonConfiguration extends ConfigurationProvider
{
private final Gson json = new GsonBuilder().serializeNulls().setPrettyPrinting().registerTypeAdapter( Configuration.class, new JsonSerializer<Configuration>()
{
@Override
public JsonElement serialize(Configuration src, Type typeOfSrc, JsonSerializationContext context)
{
return context.serialize( ( (Configuration) src ).self );
}
} ).create();
@Override
public void save(Configuration config, File file) throws IOException
{
try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), Charsets.UTF_8 ) )
{
save( config, writer );
}
}
@Override
public void save(Configuration config, Writer writer)
{
json.toJson( config.self, writer );
}
@Override
public Configuration load(File file) throws IOException
{
return load( file, null );
}
@Override
public Configuration load(File file, Configuration defaults) throws IOException
{
try ( FileInputStream is = new FileInputStream( file ) )
{
return load( is, defaults );
}
}
@Override
public Configuration load(Reader reader)
{
return load( reader, null );
}
@Override
@SuppressWarnings("unchecked")
public Configuration load(Reader reader, Configuration defaults)
{
Map<String, Object> map = json.fromJson( reader, LinkedHashMap.class );
if ( map == null )
{
map = new LinkedHashMap<>();
}
return new Configuration( map, defaults );
}
@Override
public Configuration load(InputStream is)
{
return load( is, null );
}
@Override
public Configuration load(InputStream is, Configuration defaults)
{
return load( new InputStreamReader( is, Charsets.UTF_8 ), defaults );
}
@Override
public Configuration load(String string)
{
return load( string, null );
}
@Override
@SuppressWarnings("unchecked")
public Configuration load(String string, Configuration defaults)
{
Map<String, Object> map = json.fromJson( string, LinkedHashMap.class );
if ( map == null )
{
map = new LinkedHashMap<>();
}
return new Configuration( map, defaults );
}
}
@@ -0,0 +1,136 @@
package dev.brighten.antivpn.velocity.config;
import com.google.common.base.Charsets;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.representer.Represent;
import org.yaml.snakeyaml.representer.Representer;
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class YamlConfiguration extends ConfigurationProvider
{
private final ThreadLocal<Yaml> yaml = new ThreadLocal<Yaml>()
{
@Override
protected Yaml initialValue()
{
Representer representer = new Representer()
{
{
representers.put( Configuration.class, new Represent()
{
@Override
public Node representData(Object data)
{
return represent( ( (Configuration) data ).self );
}
} );
}
};
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK );
return new Yaml( new Constructor(), representer, options );
}
};
@Override
public void save(Configuration config, File file) throws IOException
{
try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), Charsets.UTF_8 ) )
{
save( config, writer );
}
}
@Override
public void save(Configuration config, Writer writer)
{
yaml.get().dump( config.self, writer );
}
@Override
public Configuration load(File file) throws IOException
{
return load( file, null );
}
@Override
public Configuration load(File file, Configuration defaults) throws IOException
{
try ( FileInputStream is = new FileInputStream( file ) )
{
return load( is, defaults );
}
}
@Override
public Configuration load(Reader reader)
{
return load( reader, null );
}
@Override
@SuppressWarnings("unchecked")
public Configuration load(Reader reader, Configuration defaults)
{
Map<String, Object> map = yaml.get().loadAs( reader, LinkedHashMap.class );
if ( map == null )
{
map = new LinkedHashMap<>();
}
return new Configuration( map, defaults );
}
@Override
public Configuration load(InputStream is)
{
return load( is, null );
}
@Override
@SuppressWarnings("unchecked")
public Configuration load(InputStream is, Configuration defaults)
{
Map<String, Object> map = yaml.get().loadAs( is, LinkedHashMap.class );
if ( map == null )
{
map = new LinkedHashMap<>();
}
return new Configuration( map, defaults );
}
@Override
public Configuration load(String string)
{
return load( string, null );
}
@Override
@SuppressWarnings("unchecked")
public Configuration load(String string, Configuration defaults)
{
Map<String, Object> map = yaml.get().loadAs( string, LinkedHashMap.class );
if ( map == null )
{
map = new LinkedHashMap<>();
}
return new Configuration( map, defaults );
}
}
@@ -0,0 +1,111 @@
package dev.brighten.antivpn.velocity.util;
import com.google.common.io.ByteStreams;
import dev.brighten.antivpn.velocity.VelocityPlugin;
import dev.brighten.antivpn.velocity.config.Configuration;
import dev.brighten.antivpn.velocity.config.ConfigurationProvider;
import dev.brighten.antivpn.velocity.config.YamlConfiguration;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Author: nitramleo (Martin)
* Date created: 10-Aug-18
*/
public class Config {
private File file;
private Configuration configuration;
public Config() {
File dataFolder = VelocityPlugin.INSTANCE.getConfigDir().toFile();
this.file = new File(dataFolder, "config.yml");
try {
if (!this.file.exists()) {
if (!dataFolder.exists()) {
dataFolder.mkdir();
}
this.file.createNewFile();
}
this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(this.file);
}
catch (IOException e) {
e.printStackTrace();
}
}
public void load() {
File dataFolder = VelocityPlugin.INSTANCE.getConfigDir().toFile();
this.file = new File(dataFolder, "config.yml");
try {
this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(this.file);
}
catch (IOException e) {
e.printStackTrace();
}
}
public void save() {
try {
ConfigurationProvider.getProvider( YamlConfiguration.class).save(this.configuration, this.file);
}
catch (IOException e) {
e.printStackTrace();
}
}
public Configuration getConfiguration() {
return this.configuration;
}
public File getFile() {
return this.file;
}
public double getDouble(final String path) {
if (this.configuration.get(path) != null) {
return this.configuration.getDouble(path);
}
return 0.0;
}
public int getInt(final String path) {
if (this.configuration.get(path) != null) {
return this.configuration.getInt(path);
}
return 0;
}
public Object get(final String path) {
return this.configuration.get(path);
}
public void set(final String path, final Object object) {
configuration.set(path, object);
}
public boolean getBoolean(final String path) {
return this.configuration.get(path) != null && this.configuration.getBoolean(path);
}
public String getString(final String path) {
if (this.configuration.get(path) != null) {
return StringUtils.translateAlternateColorCodes('&', this.configuration.getString(path));
}
return "String at path: " + path + " not found!";
}
public List<String> getStringList(final String path) {
if (this.configuration.get(path) != null) {
final ArrayList<String> strings = new ArrayList<String>();
for (final String string : this.configuration.getStringList(path)) {
strings.add(StringUtils.translateAlternateColorCodes('&', string));
}
return strings;
}
return Arrays.asList("String List at path: " + path + " not found!");
}
}
@@ -0,0 +1,28 @@
package dev.brighten.antivpn.velocity.util;
import dev.brighten.antivpn.velocity.VelocityPlugin;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class ConfigDefault<A> {
private final A defaultValue;
private final String path;
private final VelocityPlugin plugin;
public A get() {
if(plugin.getConfig().get(path) != null)
return (A) plugin.getConfig().get(path);
else {
plugin.getConfig().set(path, defaultValue);
plugin.getConfig().save();
return defaultValue;
}
}
public A set(A value) {
plugin.getConfig().set(path, value);
return value;
}
}
@@ -0,0 +1,17 @@
package dev.brighten.antivpn.velocity.util;
public class StringUtils {
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
char[] b = textToTranslate.toCharArray();
for(int i = 0; i < b.length - 1; ++i) {
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) {
b[i] = 167;
b[i + 1] = Character.toLowerCase(b[i + 1]);
}
}
return new String(b);
}
}
@@ -0,0 +1 @@
{"id":"kaurivpn","name":"KauriVPN","version":"${project.version}","authors":["funkemunky"],"dependencies":[],"main":"dev.brighten.antivpn.velocity.VelocityPlugin"}
+25 -15
View File
@@ -4,9 +4,23 @@
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>
<groupId>dev.brighten.plugin</groupId>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>AntiVPN</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<version>1.6.1</version>
<modules>
<module>Common</module>
<module>Bungee</module>
<module>Bukkit</module>
<module>Assembly</module>
<module>Velocity</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<build>
<plugins>
@@ -18,6 +32,14 @@
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
<useIncrementalCompilation>false</useIncrementalCompilation>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
@@ -32,7 +54,7 @@
<repositories>
<repository>
<id>funkemunky-releases</id>
<url>http://nexus.funkemunky.cc/content/repositories/releases/</url>
<url>https://nexus.funkemunky.cc/content/repositories/releases/</url>
</repository>
</repositories>
@@ -43,18 +65,6 @@
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.8.8</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>cc.funkemunky.plugins</groupId>
<artifactId>Atlas</artifactId>
<version>1.6.6</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -1,74 +0,0 @@
package dev.brighten.pl;
import cc.funkemunky.api.Atlas;
import cc.funkemunky.api.utils.MiscUtils;
import dev.brighten.pl.handlers.AlertsHandler;
import dev.brighten.pl.handlers.VPNHandler;
import dev.brighten.pl.vpn.VPNAPI;
import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.java.JavaPlugin;
public class AntiVPN extends JavaPlugin {
public static AntiVPN INSTANCE;
public VPNAPI vpnAPI;
public VPNHandler vpnHandler;
public AlertsHandler alertsHandler;
public void onEnable() {
INSTANCE = this;
enable();
}
public void onDisable() {
disable();
}
public void enable() {
saveDefaultConfig();
print(true, "scanner");
Atlas.getInstance().initializeScanner(this, true, true);
print(true, "vpn api and handlers");
vpnAPI = new VPNAPI();
vpnHandler = new VPNHandler();
vpnHandler.run();
print(true, "alerts handler");
alertsHandler = new AlertsHandler();
MiscUtils.printToConsole("&aCompleted startup.");
}
public void disable() {
print(false, "listeners");
HandlerList.unregisterAll(this);
print(false, "tasks");
Bukkit.getScheduler().cancelTasks(this);
print("Save", "database");
AntiVPN.INSTANCE.vpnAPI.database.saveDatabase();
print(false, "threads");
AntiVPN.INSTANCE.vpnAPI.vpnThread.shutdown();
print(false, "handlers");
AntiVPN.INSTANCE.vpnAPI.vpnThread = null;
AntiVPN.INSTANCE.vpnHandler = null;
AntiVPN.INSTANCE.alertsHandler = null;
MiscUtils.printToConsole("&aCompleted shutdown.");
}
private void print(boolean enable, String task) {
MiscUtils.printToConsole((enable ? "&7Enabling " : "&7Disabling ") + task + "...");
}
private void print(String custom, String task) {
MiscUtils.printToConsole("&7" + custom + "ing " + task + "...");
}
}
@@ -1,19 +0,0 @@
package dev.brighten.pl.commands;
import cc.funkemunky.api.commands.ancmd.Command;
import cc.funkemunky.api.commands.ancmd.CommandAdapter;
import cc.funkemunky.api.utils.Init;
import dev.brighten.pl.AntiVPN;
@Init(commands = true)
public class AlertsCommand {
@Command(name = "kaurivpn.alerts", description = "toggle vpn alerts",
display = "alerts", playerOnly = true, permission = {"kvpn.alerts", "kvpn.command.alerts"})
public void onCommand(CommandAdapter cmd) {
boolean toggled = AntiVPN.INSTANCE.alertsHandler.toggleAlerts(cmd.getPlayer());
cmd.getSender().sendMessage(toggled
? "&aYou are now viewing vpn detection alerts."
: "&cYou are no longer viewing vpn detection alerts.");
}
}
@@ -1,17 +0,0 @@
package dev.brighten.pl.commands;
import cc.funkemunky.api.Atlas;
import cc.funkemunky.api.commands.ancmd.Command;
import cc.funkemunky.api.commands.ancmd.CommandAdapter;
import cc.funkemunky.api.utils.Init;
@Init(commands = true)
public class VpnCommand {
@Command(name = "kaurivpn", description = "The Kauri AntiVPN main command.",
aliases = {"antivpn", "avpn", "kvpn"}, permission = "kvpn.command")
public void onCommand(CommandAdapter cmd) {
Atlas.getInstance().getCommandManager().runHelpMessage(cmd,
cmd.getSender(), Atlas.getInstance().getCommandManager().getDefaultScheme());
}
}
@@ -1,44 +0,0 @@
package dev.brighten.pl.handlers;
import cc.funkemunky.api.utils.JsonMessage;
import dev.brighten.pl.utils.Config;
import dev.brighten.pl.utils.StringUtils;
import dev.brighten.pl.vpn.VPNResponse;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public class AlertsHandler {
private Set<Player> hasAlerts = new HashSet<>();
public void sendAlert(UUID uuid, VPNResponse response) {
JsonMessage message = new JsonMessage();
OfflinePlayer player = Bukkit.getOfflinePlayer(uuid);
message.addText(StringUtils.formatString(Config.alertMessage, response)
.replace("%player%", player.getName()))
.addHoverText(Config.alertHoverMessage.stream()
.map(string -> StringUtils.formatString(string, response)
.replace("%player%", player.getName())).toArray(String[]::new));
hasAlerts.parallelStream().filter(Objects::nonNull)
.forEach(message::sendToPlayer);
}
//TODO When updated Atlas releases, add this functionality.
public void sendBungeeAlert(UUID uuid, VPNResponse response) {
//Empty for now.
}
public boolean toggleAlerts(Player player) {
boolean contains;
if(contains = hasAlerts.contains(player)) hasAlerts.remove(player);
else hasAlerts.add(player);
return !contains;
}
}
@@ -1,79 +0,0 @@
package dev.brighten.pl.handlers;
import cc.funkemunky.api.Atlas;
import cc.funkemunky.api.utils.MiscUtils;
import cc.funkemunky.api.utils.RunUtils;
import cc.funkemunky.api.utils.Tuple;
import dev.brighten.pl.AntiVPN;
import dev.brighten.pl.listeners.impl.VPNCheckEvent;
import dev.brighten.pl.utils.Config;
import dev.brighten.pl.utils.StringUtils;
import dev.brighten.pl.vpn.VPNResponse;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class VPNHandler {
private LinkedList<Tuple<UUID, String>> queue = new LinkedList<>();
private AtomicBoolean checking = new AtomicBoolean(false);
private List<Tuple<UUID, String>> toAdd = new ArrayList<>();
public Map<UUID, String> toKick = new HashMap<>();
public void run() {
AntiVPN.INSTANCE.vpnAPI.vpnThread.scheduleAtFixedRate(() -> {
if(!checking.get()) {
Tuple<UUID, String> element;
checking.set(true);
while(queue.size() > 0 && (element = queue.poll()) != null) {
val response = AntiVPN.INSTANCE.vpnAPI.getResponse(element.two);
if(response != null && response.isSuccess()) {
VPNCheckEvent event = new VPNCheckEvent(response);
if(Config.fireEvent)
RunUtils.task(() -> Bukkit.getPluginManager().callEvent(event), AntiVPN.INSTANCE);
if(response.isProxy()) {
if(Config.alertStaff) alert(response, element.one);
if(Config.kickPlayers) kick(response, element.one);
}
} else MiscUtils.printToConsole((response != null) + "?");
}
checking.set(false);
queue.addAll(toAdd);
toAdd.clear();
}
}, 0L, 20L, TimeUnit.MILLISECONDS);
}
private void alert(VPNResponse response, UUID uuid) {
if(Config.alertBungee) {
AntiVPN.INSTANCE.alertsHandler.sendBungeeAlert(uuid, response); //Empty method until Atlas v1.7 releases.
} else {
AntiVPN.INSTANCE.alertsHandler.sendAlert(uuid, response);
}
}
private void kick(VPNResponse response, UUID uuid) {
if(Config.kickBungee) {
Atlas.getInstance().getBungeeManager().getBungeeAPI()
.kickPlayer(uuid, StringUtils.formatString(Config.kickMessage, response));
} else {
Player player = Bukkit.getPlayer(uuid);
RunUtils.task(() -> {
String message = StringUtils.formatString(Config.kickMessage, response);
if(player != null)
player.kickPlayer(message);
else toKick.put(uuid, message);
});
}
}
public void checkPlayer(Player player) {
if(!checking.get())
queue.add(new Tuple<>(player.getUniqueId(), player.getAddress().getAddress().getHostAddress()));
else toAdd.add(new Tuple<>(player.getUniqueId(), player.getAddress().getAddress().getHostAddress()));
}
}
@@ -1,24 +0,0 @@
package dev.brighten.pl.listeners;
import cc.funkemunky.api.utils.Init;
import dev.brighten.pl.AntiVPN;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@Init
public class JoinListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR)
public void onJoin(PlayerJoinEvent event) {
if(AntiVPN.INSTANCE.vpnHandler.toKick.containsKey(event.getPlayer().getUniqueId())) {
event.getPlayer().kickPlayer(AntiVPN.INSTANCE.vpnHandler.toKick
.compute(event.getPlayer().getUniqueId(),
(key, val) -> AntiVPN.INSTANCE.vpnHandler.toKick.remove(key)));
return;
}
AntiVPN.INSTANCE.vpnHandler.checkPlayer(event.getPlayer());
}
}
@@ -1,21 +0,0 @@
package dev.brighten.pl.listeners.impl;
import dev.brighten.pl.vpn.VPNResponse;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.util.UUID;
@RequiredArgsConstructor
@Getter
public class VPNCheckEvent extends Event {
private UUID uuid;
private final VPNResponse response;
private static HandlerList handlerList = new HandlerList();
public HandlerList getHandlers() {
return handlerList;
}
}
@@ -1,40 +0,0 @@
package dev.brighten.pl.utils;
import cc.funkemunky.api.utils.ConfigSetting;
import cc.funkemunky.api.utils.Init;
import java.util.ArrayList;
import java.util.List;
@Init
public class Config {
@ConfigSetting(name = "license")
public static String license = "";
@ConfigSetting(name = "kick-players")
public static boolean kickPlayers = true;
@ConfigSetting(name = "kick-message")
public static String kickMessage = "";
@ConfigSetting(name = "kick-bungee")
public static boolean kickBungee = false;
@ConfigSetting(name = "alert-staff")
public static boolean alertStaff = true;
@ConfigSetting(name = "alert-message")
public static String alertMessage = "";
@ConfigSetting(name = "alert-hover")
public static List<String> alertHoverMessage = new ArrayList<>();
@ConfigSetting(name = "alert-bungee")
public static boolean alertBungee = false;
@ConfigSetting(name = "hide-ips")
public static boolean hideIps = false;
@ConfigSetting(name = "fire-event")
public static boolean fireEvent = true;
}
@@ -1,27 +0,0 @@
package dev.brighten.pl.utils;
import cc.funkemunky.api.utils.Color;
import cc.funkemunky.carbon.utils.json.JSONException;
import dev.brighten.pl.vpn.VPNResponse;
import lombok.val;
public class StringUtils {
public static String formatString(String string, VPNResponse response) {
String message = Color.translate(string);
if (response.isSuccess()) {
try {
val json = response.toJson();
for (String key : json.keySet()) {
message = message.replace("%" + key + "%", String.valueOf(json.get(key)));
}
return message;
} catch (JSONException e) {
e.printStackTrace();
}
}
return message;
}
}
@@ -1,105 +0,0 @@
package dev.brighten.pl.vpn;
import cc.funkemunky.api.utils.MathUtils;
import cc.funkemunky.api.utils.MiscUtils;
import cc.funkemunky.api.utils.RunUtils;
import cc.funkemunky.carbon.db.Database;
import cc.funkemunky.carbon.db.StructureSet;
import cc.funkemunky.carbon.db.flatfile.FlatfileDatabase;
import cc.funkemunky.carbon.utils.Pair;
import cc.funkemunky.carbon.utils.json.JSONException;
import cc.funkemunky.carbon.utils.json.JSONObject;
import dev.brighten.pl.AntiVPN;
import dev.brighten.pl.utils.Config;
import dev.brighten.pl.utils.JsonReader;
import lombok.val;
import org.bukkit.entity.Player;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
public class VPNAPI {
public Database database;
public ScheduledExecutorService vpnThread;
public VPNAPI() {
MiscUtils.printToConsole("&cLoading VPNHandler&7...");
MiscUtils.printToConsole("&7Setting up Carbon database &eVPN-Cache&7...");
database = new FlatfileDatabase("VPN-Cache");
MiscUtils.printToConsole("&7Registering listener...");
vpnThread = Executors.newScheduledThreadPool(2);
//Running saveDatabase task.
MiscUtils.printToConsole("&7Running database saving task...");
RunUtils.taskTimerAsync(database::saveDatabase, AntiVPN.INSTANCE, 0, 20 * 60 * 2);
}
public VPNResponse getResponse(Player player) {
return getResponse(player.getAddress().getAddress().getHostAddress());
}
public void cacheReponse(VPNResponse response) {
if(response.isSuccess()) {
try {
//Removing old value if it contains it.
if(database.contains(response.getIp())) database.remove(response.getIp());
val json = response.toJson();
val pairs = json.keySet().stream().map(key -> {
try {
return new Pair<>(key, json.get(key));
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}).filter(Objects::nonNull).toArray(Pair[]::new);
StructureSet set = database.createStructure(response.getIp(), pairs);
if(MathUtils.getDelta(set.getObjects().size(), pairs.length) > 1) {
MiscUtils.printToConsole("&cThere was an error saving response for IP &f"
+ response.getIp() + "&c. &7Removing from database...");
database.remove(response.getIp());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
public VPNResponse getIfCached(String ipAddress) {
if(database.contains(ipAddress)) {
return VPNResponse.fromSet(database.get(ipAddress));
} else {
return null;
}
}
public VPNResponse getResponse(String ipAddress) {
try {
val response = getIfCached(ipAddress);
if(response != null) return response;
String url = "https://funkemunky.cc/vpn?license="
+ (Config.license.length() == 0 ? "none" : Config.license) + "&ip=" + ipAddress;
JSONObject object = JsonReader.readJsonFromUrl(url);
val toCacheAndReturn = VPNResponse.fromJson(object.toString());
cacheReponse(toCacheAndReturn);
return toCacheAndReturn;
} catch (IOException | JSONException e) {
e.printStackTrace();
}
return null;
}
}
@@ -1,67 +0,0 @@
package dev.brighten.pl.vpn;
import cc.funkemunky.carbon.db.StructureSet;
import cc.funkemunky.carbon.utils.json.JSONException;
import cc.funkemunky.carbon.utils.json.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class VPNResponse {
private String ip, countryName, countryCode, method, state, city, isp, timeZone, locationString;
private boolean proxy, usedAdvanced, cached, success;
private double score;
private int queriesLeft;
public JSONObject toJson() throws JSONException {
JSONObject json = new JSONObject();
json.put("ip", ip);
json.put("countryName", countryName);
json.put("countryCode", countryCode);
json.put("state", state);
json.put("city", city);
json.put("method", method);
json.put("isp", isp);
json.put("score", score);
json.put("proxy", proxy);
json.put("success", success);
json.put("timeZone", timeZone);
json.put("success", true);
json.put("queriesLeft", queriesLeft);
json.put("locationString", locationString);
json.put("usedAdvanced", usedAdvanced);
json.put("cached", cached);
return json;
}
public static VPNResponse fromJson(String json) throws JSONException {
JSONObject jsonObject = new JSONObject(json);
return new VPNResponse(jsonObject.getString("ip"), jsonObject.getString("countryName"),
jsonObject.has("method") ? jsonObject.getString("method") : "N/A",
jsonObject.getString("countryCode"), jsonObject.getString("state"),
jsonObject.getString("city"), jsonObject.getString("isp"),
jsonObject.getString("timeZone"), jsonObject.getString("locationString"),
jsonObject.getBoolean("proxy"), jsonObject.getBoolean("usedAdvanced"),
jsonObject.getBoolean("cached"), jsonObject.getBoolean("success"),
jsonObject.has("score") ? jsonObject.getDouble("score") : -1,
jsonObject.getInt("queriesLeft"));
}
public static VPNResponse fromSet(StructureSet set) {
return new VPNResponse(set.getField("ip"), set.getField("countryName"),
set.containsKey("method") ? set.getField("method") : "N/A",
set.getField("countryCode"), set.getField("state"),
set.getField("city"), set.getField("isp"),
set.getField("timeZone"), set.getField("locationString"),
set.getField("proxy"), set.getField("usedAdvanced"),
set.getField("cached"), set.getField("success"),
set.containsKey("score") ? set.getDouble("score") : -1,
set.getField("queriesLeft"));
}
}
-51
View File
@@ -1,51 +0,0 @@
#
# You will need a license from https://funkemunky.cc/shop to use the AntiVPN to its full potential.
# Your server will have 200 free queries per month. Once that ends, the plugin will no longer query.
# You can also use your Kauri Anticheat license here.
#
license: ""
#
# Enable kicking #
# You can disable the function of removing players from the server.
kick-players: true
# Kick Message #
# The message sent to the player when they are kicked.
kick-message: |-
&r
&6&lKauri AntiVPN
&7You have been kicked for using a VPN.
&r
&f&o%method%
#
# Run Command in Bungee #
# This will allow the command to be run in Bungee.
kick-bungee: false
#
# VPN Use Alerts #
# A great way to find nefarious players quickly (i.e. cheaters) without them knowing what is going on.
alert-staff: true
#
# Alert Message #
# This will be the message sent to staff vpn detection.
# Requires the permission antivpn.alert to view alerts.
alert-message: "&8[&c!&8] &7Found &f%player% &7to be using a VPN."
# Alert Hover #
# The lines here will be shown when hovering on the alert message
# Placeholders: %method%, %ip%, %countryCode%, %countryName%, %score%, %queriesLeft%, %proxy%, %isp%, %timeZone%
alert-hover:
- "&eMethod: &f%method%"
- "&eIP: &f%ip%"
- "&eCountry: &f%countryName%"
#
# Alert Bungee #
# Requires AntiVPN to be on all servers using it and will require Atlas bungee mode to be enabled.
# All configuration for alerts is local. No setup required on Bungee.
alert-bungee: false
#
# Hide IP #
# This will hide the IP from all users. If false, IPs are only shown to users with permission antivpn.showip.
hide-ips: false
#
# Fire Event #
# If disabled, the VPNCheckEvent will no longer be fired. Only disable this if you have no plugins depending on it.
fire-event: true
-9
View File
@@ -1,9 +0,0 @@
name: KauriVPN
main: dev.brighten.pl.AntiVPN
author: funkemunky
depend: [Atlas]
version: ${project.version}
permissions:
antivpn.showip:
default: false
description: Permission node to show IPs.