Compare commits

...

165 Commits

Author SHA1 Message Date
funkemunky 2bdd7d2c34 Cleaned up files 2023-12-04 08:55:01 -05:00
funkemunky 31a9412c0a Updated libraries, and cleaned up code for performance purposes. 2023-12-04 08:54:48 -05:00
Dawson Hessler 7f96c49ce8 Delete nightly.yml 2023-10-30 10:41:45 -04:00
Dawson Hessler 7b3f9fc6ae Update nightly.yml 2023-10-30 10:38:01 -04:00
Dawson Hessler edd08b27ce Update maven.yml 2023-10-30 10:37:36 -04:00
Dawson Hessler 158045217e Create nightly.yml 2023-10-30 10:37:01 -04:00
Dawson 63bdb0a4da Merge pull request #45 from funkemunky/dependabot/maven/Common/com.google.guava-guava-32.0.0-jre
Bump com.google.guava:guava from 31.1-jre to 32.0.0-jre in /Common
2023-10-30 10:27:05 -04:00
Dawson b9e23ba34e Updating to only run on commits and pull requests to main 2023-10-30 10:26:26 -04:00
dependabot[bot] 9f6b0f8b27 Bump com.google.guava:guava from 31.1-jre to 32.0.0-jre in /Common
Bumps [com.google.guava:guava](https://github.com/google/guava) from 31.1-jre to 32.0.0-jre.
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

---
updated-dependencies:
- dependency-name: com.google.guava:guava
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-30 14:24:01 +00:00
Dawson fec1dcdef1 Merge pull request #29 from funkemunky/dependabot/maven/Common/org.yaml-snakeyaml-2.0
Bump snakeyaml from 1.30 to 2.0 in /Common
2023-10-30 10:23:21 -04:00
Dawson Hessler c062e3d910 Tested and correcting dependency issues 2023-10-30 10:20:49 -04:00
Dawson Hessler a79fb1fe9a Updating more vulnerable depends 2023-10-30 10:14:15 -04:00
Dawson Hessler d224efce3c Updating depends to make it work 2023-10-30 10:12:14 -04:00
Dawson Hessler a2554c2bba Merge branch 'master' into dependabot/maven/Common/org.yaml-snakeyaml-2.0 2023-10-30 10:06:28 -04:00
Dawson 70bfb4e83d Merge pull request #42 from C0D3-M4513R/master
Fix PR #35
2023-10-30 10:06:07 -04:00
Dawson bc666447c5 Merge pull request #43 from C0D3-M4513R/patch-bukkit-banned-whitelist
Add null checks to ip check on login
2023-10-30 10:05:51 -04:00
Dawson Hessler 26cfc3e3f8 Update maven.yml 2023-10-30 10:03:04 -04:00
Dawson Hessler 7974b24271 Revert "Revert "Revert "Update maven.yml"""
This reverts commit 87cdd57383.
2023-10-30 10:02:30 -04:00
Dawson Hessler 87cdd57383 Revert "Revert "Update maven.yml""
This reverts commit 6fe928ca14.
2023-10-30 10:02:17 -04:00
Dawson Hessler 6fe928ca14 Revert "Update maven.yml"
This reverts commit d461b5945b.
2023-10-30 10:02:02 -04:00
Dawson Hessler dae9111a34 Revert "Merge branch 'master' of https://github.com/funkemunky/AntiVPN"
This reverts commit f4d6fc2b4b, reversing
changes made to d461b5945b.
2023-10-30 10:01:50 -04:00
Dawson Hessler f4d6fc2b4b Merge branch 'master' of https://github.com/funkemunky/AntiVPN 2023-10-30 10:00:12 -04:00
Dawson Hessler d461b5945b Update maven.yml 2023-10-30 09:59:50 -04:00
Dawson c6303ec1b2 Merge pull request #44 from funkemunky/funkemunky-patch-1
Updating build action to run on pull request
2023-10-30 09:57:55 -04:00
Dawson Hessler ca8fb24134 Caching maven repos properly 2023-10-30 09:54:58 -04:00
Dawson 5e37d2c371 Updating to run on pull request 2023-10-30 09:50:47 -04:00
Dawson Hessler 48c6dd63ee Revert "Merge pull request #35 from C0D3-M4513R/patch-1"
This reverts commit db1cdad4e1, reversing
changes made to 9f66570088.
2023-10-30 09:48:04 -04:00
C0D3 M4513R 50e7059597 Add null checks to ip check on login
Supersedes: #41
Fixes: #39
2023-10-18 05:57:22 +02:00
C0D3 M4513R 464b02f416 Fix PR #35 2023-10-18 05:45:27 +02:00
Dawson db1cdad4e1 Merge pull request #35 from C0D3-M4513R/patch-1 2023-10-17 18:58:31 -04:00
Dawson 9f66570088 Merge pull request #37 from C0D3-M4513R/command-suggestion 2023-10-17 18:57:47 -04:00
Dawson 5f0b2796b3 Merge pull request #40 from alexkarezin/master 2023-10-17 18:56:42 -04:00
Alex Karezin be5eb4e953 Update README.md by adding a link to repository map
Adding a link to the high-level diagrams including module, library dependency and others (https://sourcespy.com/github/funkemunkyantivpn/). Built directly from source and updated on schedule. Intended to simplify developer's introduction to the project.

In the spirit of transparency - I am the author of the diagrams. Hope contributors find it useful.
2023-07-31 14:43:20 -04:00
C0D3 M4513R dde81b0495 Bump version 2023-07-14 13:35:50 +02:00
C0D3 M4513R cbc00b79e2 Add command suggestion for empty args 2023-07-14 13:31:36 +02:00
C0D3 M4513R 3b2a463e58 Update PlanCommand.java 2023-07-14 13:17:47 +02:00
Dawson Hessler 96e48594d8 Fixing velocity loading issues 2023-07-12 08:45:39 -04:00
Dawson Hessler c1ab71c7ed Merge branch 'master' of https://github.com/funkemunky/AntiVPN 2023-07-06 20:26:07 -04:00
Dawson Hessler 4bda24f10c Bump to 1.9.0 2023-07-06 20:26:02 -04:00
Dawson 259cff4402 Merge pull request #32 from AlexProgrammerDE/patch-1
Remove unused import
2023-07-06 20:15:51 -04:00
Dawson Hessler c54e90dca1 Removing usages of System.out.print 2023-07-06 20:13:47 -04:00
Alex 4f1e3848de Remove unused import 2023-07-04 17:30:39 +02:00
Dawson 1606ad192e Working on spongepowered version of plugin 2023-05-25 10:24:42 -04:00
Dawson Hessler 40308869c0 1.8.4 Added cache for logins for faster response 2023-05-23 08:09:41 -04:00
Dawson Hessler 6959f35d0c Fixing whitelist issues in bukkit and bungee 2023-04-18 06:45:28 -04:00
Dawson Hessler 21b6924cce Fixing error with inserted type mismatch in H2 and MySQL 2023-04-18 06:42:07 -04:00
Dawson Hessler 9c843cd061 Updating to 1.8.3.1
Turns out we accidentally already used the 1.8.3 version
2023-03-13 15:08:04 -04:00
Dawson Hessler 91a09f6940 Updating version to 1.8.3 2023-03-13 15:04:14 -04:00
Dawson Hessler 36b44200c4 Adding response expiry since I just realized it never expires 2023-03-13 15:00:39 -04:00
dependabot[bot] 903dd8e73e Bump snakeyaml from 1.30 to 2.0 in /Common
Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.30 to 2.0.
- [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.0..snakeyaml-1.30)

---
updated-dependencies:
- dependency-name: org.yaml:snakeyaml
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 19:31:59 +00:00
Dawson e6bc601372 Fixing compile bug 2023-02-28 14:31:40 -05:00
Dawson 9dbf0e8635 Merge branch 'master' of https://github.com/funkemunky/AntiVPN 2022-11-02 10:39:50 -04:00
Dawson dd1b6afbb7 Working on adding sponge support 2022-11-02 10:39:48 -04:00
Dawson Hessler 14e266b978 Fixing load issue with snakeyaml on 1.12+ servers 2022-09-02 09:34:47 -04:00
Dawson Hessler cc289f41ff Instead of deleting old files, make new one 2022-08-29 07:34:53 -04:00
Dawson Hessler bf5b81b750 1.8.2.1, fixed H2 and removed debug 2022-08-29 07:19:42 -04:00
Dawson a480f13302 Merge pull request #26 from funkemunky/reload-commmand
Shrunk jar file size, fixed errors, added database reload
2022-08-28 13:18:09 -04:00
Dawson Hessler 4a95b51350 Replaced incorrect SQLLite messaging with H2 2022-08-28 13:17:30 -04:00
Dawson Hessler 9dc312186b Shrunk jar file size, fixed errors, added database reload
- Fixed H2 database error on index creation when loading plugin by using dynamic library downloader/loader from Lucko's Helper.
- Shrunk jar file size extensively so it can be uploaded to Spigot directly.
- Updated h2database driver to 2.1.214 to patch vulnerability
- Updated mysql database driver to 8.0.30 to patch vulnerability
- Updated MongoDB java driver to 3.12.11.
2022-08-28 13:14:13 -04:00
Dawson 3f9a2100a9 Merge pull request #25 from funkemunky/reload-commmand
New /antivpn reload and Message Configuration
2022-08-28 12:14:35 -04:00
Dawson Hessler 4c82755935 Adding comment and added some default messages to help formatting on generation 2022-08-28 12:13:57 -04:00
Dawson Hessler 0048cf6b8c 1.8.2 2022-08-28 12:12:24 -04:00
Dawson Hessler 795c869fc0 Fixed reloading and adding of messages into config 2022-08-28 12:11:41 -04:00
Dawson Hessler 95a00a4d0a Adding ability to configure "no permission" message 2022-08-28 11:53:46 -04:00
Dawson Hessler a6f26d4ba7 Merge branch 'master' into reload-commmand 2022-08-28 11:42:58 -04:00
Dawson 7a0786e29f Shit 2022-08-24 11:43:29 -04:00
Dawson df4a14086b Updating workflow
- Now uploading alpha jars on workflow.
- Caching depends to make it run faster
2022-08-19 11:12:34 -04:00
Dawson f55fa88c2b Merge pull request #24 from funkemunky/dependabot/maven/Common/mysql-mysql-connector-java-8.0.28
Bump mysql-connector-java from 8.0.27 to 8.0.28 in /Common
2022-08-19 11:10:29 -04:00
Dawson 4f79522010 Fixing memory leak 2022-08-19 11:09:52 -04:00
Dawson Hessler 7654cca651 Adding reload command 2022-08-05 14:14:42 -04:00
Dawson Hessler e01cbf95f2 Fixing whitelist ip and uuid checking 2022-07-10 12:59:45 -04:00
Dawson Hessler db49d400a0 Fixing whitelist checking for users 2022-07-10 12:57:05 -04:00
dependabot[bot] b39cc3e19c Bump mysql-connector-java from 8.0.27 to 8.0.28 in /Common
Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 8.0.27 to 8.0.28.
- [Release notes](https://github.com/mysql/mysql-connector-j/releases)
- [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES)
- [Commits](https://github.com/mysql/mysql-connector-j/compare/8.0.27...8.0.28)

---
updated-dependencies:
- dependency-name: mysql:mysql-connector-java
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-21 04:21:04 +00:00
Dawson ff25c75055 Update .gitignore 2022-06-17 13:05:52 -04:00
Dawson 733e797a17 Implemented /kaurivpn plan 2022-06-06 10:15:40 -04:00
Dawson 0c903794e5 Cleaning up, adding QueryResponse
- Putting API classes under its own package.
- Moved API calls from AntiVPN into new FunkemunkyAPI class.
- Added QueryResponse object and FunkemunkyAPI#getQueryResponse get grab plan information.
2022-06-06 09:32:48 -04:00
Dawson bddf26359d 1.7.1.1 2022-06-05 08:45:52 -04:00
funkemunky 4424b2b9a5 Fixing multiple bugs
- Fixes /antivpn alerts error caused by VpnStrings not being initialized. All of that initialized is now done globally inside AntiVPN class instead of individually per platform.
- Fixed bug where MySQL will only load H2.
- H2 is now a separate object and will not create an sql error on startup from the index creation process.
2022-04-11 12:58:43 -04:00
Dawson 60043dd07a Merge pull request #22 from funkemunky/tab-completion
Tab completion and Velocity Patch
2022-04-06 09:21:46 -04:00
funkemunky 314e554ce0 Fixing velocity loading, v1.7.1 update 2022-04-06 09:20:07 -04:00
funkemunky 110e696995 Cleaning up imports part 2 2022-04-06 08:56:24 -04:00
funkemunky d12f1c983c Cleaning up imports 2022-04-06 08:56:15 -04:00
funkemunky cf9de8115e Adding velocity command tab completion and wrapping 2022-04-06 08:55:51 -04:00
funkemunky 338f64962e Cleaning up BukkitCommand wrapping, removung unnecessary code
- We just update the BukkitCommand wrapping to be consistent with other parts of the project.
- Removing the configuration loading function that was specific to Bukkit as we now use a global configuration API within Common
2022-04-06 08:47:51 -04:00
funkemunky 71604d5b45 Adding bungee tab complete 2022-04-06 08:40:18 -04:00
Dawson Hessler 5a69e49fb9 Relocating shaded depends to prevent incompatibilities 2022-04-01 11:50:32 -04:00
Dawson fe6d5a3635 Merge pull request #19 from funkemunky/country-banning
Country banning
2022-04-01 11:34:37 -04:00
Dawson Hessler b5caf9604d Adding vanilla kick option 2022-04-01 11:27:56 -04:00
Dawson Hessler 315d4eaa3f Running country checks before running proxy checks 2022-04-01 11:13:21 -04:00
Dawson Hessler f98ab77944 Fixing Bukkit NullPointerException 2022-04-01 11:12:21 -04:00
funkemunky 0fccd9e296 Debugging velocity 2022-03-30 09:47:40 -04:00
funkemunky 0db8b93a7c Updating Bukkit and Velocity 2022-03-30 09:12:59 -04:00
funkemunky ea979cd729 Adding kick command to bungee and adding comments 2022-03-30 08:59:50 -04:00
Dawson Hessler ba72ad2a44 Implementing new configuration system 2022-03-29 16:20:07 -04:00
Dawson Hessler 8edef241e4 Adding universal config API and adding allowed/blocked country config 2022-03-18 10:33:14 -04:00
funkemunky 2fbbe5b3c8 Fixing bukkit prefix not working 2022-03-17 21:41:33 -04:00
Dawson 325e19dca5 Merge pull request #16 from Kek5chen/master
attempted fix of wrong alert state
2022-02-21 09:16:25 -05:00
Dawson 8ad6c3aaa2 Merge pull request #13 from funkemunky/dependabot/maven/Common/com.h2database-h2-2.1.210
Bump h2 from 1.4.200 to 2.1.210 in /Common
2022-02-21 09:16:12 -05:00
Dawson f8765ff95f Merge pull request #17 from funkemunky/fixing-ip-whitelist
Fixing ip whitelist
2022-02-21 09:16:00 -05:00
funkemunky 619b61fe55 Fixed ip whitelisting 2022-02-21 09:15:43 -05:00
funkemunky a6aac8fce7 Using regex to check ipv4 and fixing allowlist ips 2022-02-21 08:19:46 -05:00
funkemunky 2afb31b073 Adding maven compile check 2022-02-21 07:55:06 -05:00
Kekschen 66d193148e Fixed setting values into cache 2022-02-20 22:06:28 +01:00
Kekschen 58b48dceb4 Updated APIPlayer Caching
---Untested Code---

Updated the BukkitPlayer caching objects to use UUIDs instead of Player objects as Player objects generate a new hash when rejoining the server and get out of sync therefore will horde memory and stay unavailable till the programs termination.
2022-02-20 21:59:54 +01:00
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
dependabot[bot] 898e32972b Bump h2 from 1.4.200 to 2.1.210 in /Common
Bumps [h2](https://github.com/h2database/h2database) from 1.4.200 to 2.1.210.
- [Release notes](https://github.com/h2database/h2database/releases)
- [Commits](https://github.com/h2database/h2database/compare/version-1.4.200...version-2.1.210)

---
updated-dependencies:
- dependency-name: com.h2database:h2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-21 23:54:53 +00: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
89 changed files with 6934 additions and 640 deletions
+31
View File
@@ -0,0 +1,31 @@
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 17.0.2
uses: actions/setup-java@v1
with:
java-version: 17.0
- name: Cache Maven packages
uses: actions/cache@v2
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
- name: Compile
run: mvn -B -Pclean install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload AntiVPN
uses: actions/upload-artifact@v2
with:
name: AntiVPN
path: Assembly/target/Assembly-*.jar
+3
View File
@@ -13,6 +13,9 @@ local.properties
.settings/
.loadpath
.recommenders
*.iml
.idea/
# External tool builders
.externalToolBuilders/
-42
View File
@@ -1,42 +0,0 @@
<?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.1.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.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
+18 -3
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.1.1</version>
<version>1.9.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -21,7 +21,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
@@ -29,7 +29,16 @@
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<relocations>
<relocation>
<pattern>org.yaml.snakeyaml</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.com.google.common</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
@@ -44,6 +53,12 @@
<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>
-29
View File
@@ -1,29 +0,0 @@
<?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.1.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Bukkit</artifactId>
<dependencies>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.13.2</artifactId>
<version>1.13.2</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.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
+39 -2
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.1.1</version>
<version>1.9.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -23,6 +23,37 @@
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<!-- Replace this with your package! -->
<shadedPattern>dev.brighten.antivpn.bukkit.org.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>org.yaml.snakeyaml</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.com.google.common</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
@@ -47,9 +78,15 @@
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.1.1</version>
<version>1.9.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>2.2.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -1,9 +1,14 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
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 {
@@ -11,12 +16,25 @@ public class BukkitCommandExecutor implements CommandExecutor {
private final CommandSender sender;
@Override
public void sendMessage(String message) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message));
public void sendMessage(String message, Object... objects) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
String.format(message, objects)));
}
@Override
public boolean hasPermission(String permission) {
return sender.hasPermission(permission);
}
@Override
public Optional<APIPlayer> getPlayer() {
if(!isPlayer()) return Optional.empty();
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((Player)sender).getUniqueId());
}
@Override
public boolean isPlayer() {
return sender instanceof Player;
}
}
@@ -1,37 +0,0 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.api.VPNConfig;
import dev.brighten.antivpn.bukkit.util.ConfigDefault;
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);
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
"cachedResults", BukkitPlugin.pluginInstance);
private String license, kickMessage;
private boolean cacheResults;
@Override
public String getLicense() {
return license;
}
@Override
public boolean cachedResults() {
return cacheResults;
}
@Override
public String getKickString() {
return kickMessage;
}
public void update() {
license = licenseDefault.get();
kickMessage = kickStringDefault.get();
cacheResults = cacheResultsDefault.get();
}
}
@@ -1,23 +1,34 @@
package dev.brighten.antivpn.bukkit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.message.VpnString;
import dev.brighten.antivpn.web.objects.VPNResponse;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.event.Event;
import org.bukkit.entity.Player;
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.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import java.util.Optional;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class BukkitListener extends VPNExecutor implements Listener {
private final Cache<UUID, VPNResponse> responseCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(2000)
.build();
private BukkitTask cacheResetTask;
@Override
public void registerListeners() {
BukkitPlugin.pluginInstance.getServer().getPluginManager()
@@ -25,45 +36,156 @@ public class BukkitListener extends VPNExecutor implements Listener {
}
@Override
public void runCacheReset() {
cacheResetTask = new BukkitRunnable() {
public void run() {
resetCache();
}
}.runTaskTimerAsynchronously(BukkitPlugin.pluginInstance, 24000, 24000); //Reset cache every 20 minutes
public void shutdown() {
HandlerList.unregisterAll(this);
threadExecutor.shutdown();
}
@Override
public void shutdown() {
if(cacheResetTask != null && !cacheResetTask.isCancelled()) cacheResetTask.cancel();
public void log(Level level, String log, Object... objects) {
Bukkit.getLogger().log(level, String.format(log, objects));
}
@Override
public void log(String log, Object... objects) {
log(Level.INFO, String.format(log, objects));
}
@EventHandler
public void onListener(final AsyncPlayerPreLoginEvent event) {
checkIp(event.getAddress().getHostAddress(), AntiVPN.getInstance().getConfig().cachedResults(), result -> {
if(result.isSuccess() && result.isProxy()) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
event.setKickMessage(ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getConfig().getKickString()));
Optional.ofNullable(Bukkit.getPlayer(event.getUniqueId())).ifPresent(player -> {
new BukkitRunnable() {
public void run() {
if(!player.hasPermission("antivpn.bypass"))
player.kickPlayer(ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getConfig().getKickString()));
System.out.println(player.getPlayer().getName()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
}
}.runTask(BukkitPlugin.pluginInstance);
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");
});
});
} 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");
}
@EventHandler
public void onListener(final PlayerLoginEvent event) {
//If they're exempt, don't check.
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.getAddress().getHostAddress())
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
.anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return;
if(responseCache.asMap().containsKey(event.getPlayer().getUniqueId())) {
VPNResponse cached = responseCache.getIfPresent(event.getPlayer().getUniqueId());
if (cached != null && cached.isProxy()) {
event.setResult(PlayerLoginEvent.Result.KICK_BANNED);
event.setKickMessage(org.bukkit.ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getVpnConfig().getKickString()));
return;
}
});
}
final Player player = event.getPlayer();
checkIp(event.getAddress().getHostAddress(),
AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> {
if(result.isSuccess()) {
//We need to run on main thread or kicking and running commands will cause errors
new BukkitRunnable() {
public void run() {
//If the player is whitelisted, we don't want to kick them
if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) {
log("UUID is whitelisted: %s",
event.getPlayer().getUniqueId().toString());
return;
}
{
//If the IP is whitelisted, we don't want to kick them
InetSocketAddress address = event.getPlayer().getAddress();
if (address != null){
InetAddress address1 = address.getAddress();
if (address1 != null && AntiVPN.getInstance().getExecutor().isWhitelisted(address1.getHostAddress())) {
log("IP is whitelisted: %s",
address1.getHostAddress());
return;
}
}
}
// If the countryList() size is zero, no need to check.
// Running country check first
if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty()
// This bit of code will decide whether or not to kick the player
// If it contains the code and it is set to whitelist, it will not kick as they are equal
// and vise versa. However, if the contains does not match the state, it will kick.
&& AntiVPN.getInstance().getVpnConfig().countryList()
.contains(result.getCountryCode())
!= AntiVPN.getInstance().getVpnConfig().whitelistCountries()) {
//Using our built in kicking system if no commands are configured
if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) {
final String kickReason = AntiVPN.getInstance().getVpnConfig()
.countryVanillaKickReason();
// Kicking our player
event.getPlayer().kickPlayer(ChatColor
.translateAlternateColorCodes('&',
kickReason
.replace("%player%", event.getPlayer().getName())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode())));
} else {
for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
final String formattedCommand = ChatColor.translateAlternateColorCodes('&',
cmd.replace("%player%", event.getPlayer().getName())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode()));
// Runs our command from console
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand);
}
}
} else if(result.isProxy()) {
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect())
player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getVpnConfig().getKickString()));
log(Level.INFO, event.getPlayer().getName()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
//Ensuring the user wishes to alert to staff
if(AntiVPN.getInstance().getVpnConfig().alertToStaff())
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.filter(APIPlayer::isAlertsEnabled)
.forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().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().getVpnConfig().runCommands()) {
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
ChatColor.translateAlternateColorCodes('&',
command.replace("%player%",
event.getPlayer().getName())));
}
}
AntiVPN.getInstance().detections++;
}
}
}.runTask(BukkitPlugin.pluginInstance);
} else {
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++;
});
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
}
}
@@ -5,12 +5,13 @@ import dev.brighten.antivpn.api.PlayerExecutor;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
public class BukkitPlayerExecutor implements PlayerExecutor {
private final Map<UUID, BukkitPlayer> cachedPlayers = new HashMap<>();
@Override
public Optional<APIPlayer> getPlayer(String name) {
final Player player = Bukkit.getPlayer(name);
@@ -19,7 +20,7 @@ public class BukkitPlayerExecutor implements PlayerExecutor {
return Optional.empty();
}
return Optional.of(new BukkitPlayer(player));
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
}
@Override
@@ -30,11 +31,20 @@ public class BukkitPlayerExecutor implements PlayerExecutor {
return Optional.empty();
}
return Optional.of(new BukkitPlayer(player));
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
}
@Override
public List<APIPlayer> getOnlinePlayers() {
return Bukkit.getOnlinePlayers().stream().map(BukkitPlayer::new).collect(Collectors.toList());
public void unloadPlayer(UUID uuid) {
cachedPlayers.remove(uuid);
}
@Override
public List<APIPlayer> getOnlinePlayers() {
return Bukkit.getOnlinePlayers().stream()
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), k -> new BukkitPlayer(pl)))
.collect(Collectors.toList());
}
}
@@ -1,40 +1,52 @@
package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.bukkit.command.BukkitCommand;
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.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
System.out.println("Loading config...");
saveDefaultConfig();
Bukkit.getLogger().info("Starting AntiVPN services...");
AntiVPN.start(new BukkitListener(), new BukkitPlayerExecutor(), getDataFolder());
System.out.println("Starting AntiVPN services...");
AntiVPN.start(new BukkitConfig(), new BukkitListener(), new BukkitPlayerExecutor());
// Loading our bStats metrics to be pushed to https://bstats.org
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
Bukkit.getLogger().info("Starting bStats metrics...");
Metrics metrics = new Metrics(this, 12615);
metrics.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);
}
System.out.println("Setting up and registering commands...");
Bukkit.getLogger().info("Setting up and registering commands...");
// We need access to the commandMap to register our commands without using the "proper" method
if (pluginInstance.getServer().getPluginManager() instanceof SimplePluginManager) {
SimplePluginManager manager = (SimplePluginManager) pluginInstance.getServer().getPluginManager();
try {
@@ -46,72 +58,34 @@ public class BukkitPlugin extends JavaPlugin {
}
}
// Registering commands
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])) {
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])) {
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;
}
};
// Wraps our general command API to Bukkit specific calls
BukkitCommand newCommand = new BukkitCommand(command);
// Adding to our own list for later referencing
registeredCommands.add(newCommand);
// This tells Bukkit to register our command for use.
commandMap.register(pluginInstance.getName(), newCommand);
}
//TODO Finish system before implementing on startup
/*Bukkit.getLogger().info("Getting strings...");
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), BukkitPlugin.pluginInstance)
.get());
AntiVPN.getInstance().getMessageHandler().reloadStrings();*/
reloadConfig();
}
@Override
public void onDisable() {
System.out.println("Stopping plugin services...");
Bukkit.getLogger().info("Stopping plugin services...");
AntiVPN.getInstance().stop();
System.out.println("Unregistering commands...");
Bukkit.getLogger().info("Unregistering commands...");
try {
Field field = SimpleCommandMap.class.getDeclaredField("knownCommands");
field.setAccessible(true);
@@ -124,10 +98,10 @@ public class BukkitPlugin extends JavaPlugin {
e.printStackTrace();
}
System.out.println("Unregistering listeners...");
Bukkit.getLogger().info("Unregistering listeners...");
HandlerList.unregisterAll(this);
System.out.println("Cancelling any running tasks...");
Bukkit.getLogger().info("Cancelling any running tasks...");
Bukkit.getScheduler().cancelTasks(this);
}
}
@@ -0,0 +1,77 @@
package dev.brighten.antivpn.bukkit.command;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.bukkit.BukkitCommandExecutor;
import dev.brighten.antivpn.command.Command;
import lombok.val;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class BukkitCommand extends org.bukkit.command.Command {
private final Command command;
public BukkitCommand(Command command) {
super(command.name(), command.description(), command.usage(), Arrays.asList(command.aliases()));
this.command = command;
}
@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.translateAlternateColorCodes('&',
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
return true;
}
val children = command.children();
if(children.length > 0 && args.length > 0) {
for (Command child : children) {
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
if(!sender.hasPermission("antivpn.command.*")
&& !sender.hasPermission(child.permission())) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
return true;
}
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
child.execute(new BukkitCommandExecutor(sender), IntStream
.range(0, args.length - 1)
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
return true;
}
}
}
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
command.execute(new BukkitCommandExecutor(sender), args)));
return true;
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
name: AntiVPN
name: KauriVPN
main: dev.brighten.antivpn.bukkit.BukkitPlugin
version: ${project.version}
author: funkemunky
-29
View File
@@ -1,29 +0,0 @@
<?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.1.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Bungee</artifactId>
<dependencies>
<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.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
+38 -2
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.1.1</version>
<version>1.9.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -23,6 +23,36 @@
<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>
<relocation>
<pattern>org.yaml.snakeyaml</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
</relocation>
<relocation>
<pattern>com.google</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.com.google</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
@@ -41,7 +71,7 @@
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.1.1</version>
<version>1.9.2</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -50,6 +80,12 @@
<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>
@@ -1,24 +0,0 @@
package dev.brighten.antivpn.bungee;
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;
@RequiredArgsConstructor
public class BungeeCommandExecutor implements CommandExecutor {
private final CommandSender executor;
@Override
public void sendMessage(String message) {
executor.sendMessage(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&', message)));
}
@Override
public boolean hasPermission(String permission) {
return executor.hasPermission(permission);
}
}
@@ -1,37 +0,0 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.api.VPNConfig;
import dev.brighten.antivpn.bungee.util.ConfigDefault;
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);
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
"cachedResults", BungeePlugin.pluginInstance);
private String license, kickMessage;
private boolean cacheResults;
@Override
public String getLicense() {
return license;
}
@Override
public boolean cachedResults() {
return cacheResults;
}
@Override
public String getKickString() {
return kickMessage;
}
public void update() {
license = licenseDefault.get();
kickMessage = kickStringDefault.get();
cacheResults = cacheResultsDefault.get();
}
}
@@ -1,16 +1,22 @@
package dev.brighten.antivpn.bungee;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.web.objects.VPNResponse;
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.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.TabCompleteEvent;
import net.md_5.bungee.api.event.PreLoginEvent;
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.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -18,18 +24,17 @@ public class BungeeListener extends VPNExecutor implements Listener {
private ScheduledTask cacheResetTask;
private final Cache<UUID, VPNResponse> responseCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(2000)
.build();
@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) {
@@ -40,23 +45,129 @@ public class BungeeListener extends VPNExecutor implements Listener {
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListener(this);
}
@Override
public void log(Level level, String log, Object... objects) {
BungeeCord.getInstance().getLogger().log(Level.INFO, String.format(log, objects));
}
@Override
public void log(String log, Object... objects) {
log(Level.INFO, String.format(log, objects));
}
@EventHandler
public void onListener(final PreLoginEvent event) {
if(!responseCache.asMap().containsKey(event.getConnection().getUniqueId())) return;
VPNResponse cached = responseCache.getIfPresent(event.getConnection().getUniqueId());
if(cached != null && cached.isProxy()) {
event.setCancelled(true);
event.setCancelReason(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
AntiVPN.getInstance().getVpnConfig().getKickString())));
AntiVPN.getInstance().getExecutor().log(Level.INFO,
"%s was kicked from pre-login proxy cache.",
event.getConnection().getName());
}
}
@EventHandler
public void onListener(final PostLoginEvent event) {
if(event.getPlayer().hasPermission("antivpn.bypass")) return;
if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
|| AntiVPN.getInstance().getVpnConfig().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()) {
event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
AntiVPN.getInstance().getConfig().getKickString())));
System.out.println(event.getPlayer().getName()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
} else if(!result.isSuccess()) {
AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> {
if(result.isSuccess()) {
//If the player is whitelisted, we don't want to kick them
if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) {
AntiVPN.getInstance().getExecutor().log("UUID is whitelisted: %s",
event.getPlayer().getUniqueId().toString());
return;
}
//If the IP is whitelisted, we don't want to kick them
if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getAddress().getAddress()
.getHostAddress())) {
AntiVPN.getInstance().getExecutor().log("IP is whitelisted: %s",
event.getPlayer().getAddress().getAddress().getHostAddress());
return;
}
responseCache.put(event.getPlayer().getUniqueId(), result);
if(AntiVPN.getInstance().getVpnConfig().countryList().size() > 0
// This bit of code will decide whether or not to kick the player
// If it contains the code and it is set to whitelist, it will not kick as they are equal
// and vise versa. However, if the contains does not match the state, it will kick.
&& AntiVPN.getInstance().getVpnConfig().countryList()
.contains(result.getCountryCode()) != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) {
//Using our built in kicking system if no commands are configured
if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().size() == 0) {
final String kickReason = AntiVPN.getInstance().getVpnConfig()
.countryVanillaKickReason();
// Kicking our player
event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
kickReason
.replace("%player%", event.getPlayer().getName())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode()))));
} else {
for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
final String formattedCommand = ChatColor.translateAlternateColorCodes('&',
cmd.replace("%player%", event.getPlayer().getName())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode()));
// Runs our command from console
BungeeCord.getInstance().getPluginManager().dispatchCommand(
BungeeCord.getInstance().getConsole(), formattedCommand);
}
}
} else if(result.isProxy()) {
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect())
event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
AntiVPN.getInstance().getVpnConfig().getKickString())));
BungeeCord.getInstance().getLogger().info(event.getPlayer().getName()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) //Ensuring the user wishes to alert to staff
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.filter(APIPlayer::isAlertsEnabled)
.forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().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().getVpnConfig().runCommands()) {
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
BungeeCord.getInstance().getPluginManager()
.dispatchCommand(BungeeCord.getInstance().getConsole(),
ChatColor.translateAlternateColorCodes('&',
command.replace("%player%", event.getPlayer().getName())));
}
}
AntiVPN.getInstance().detections++;
}
} else {
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++;
});
}
@EventHandler
public void onLeave(PlayerDisconnectEvent event) {
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
}
}
@@ -5,20 +5,20 @@ import dev.brighten.antivpn.api.PlayerExecutor;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
public class BungeePlayerExecutor implements PlayerExecutor {
private final Map<UUID, BungeePlayer> cachedPlayers = new HashMap<>();
@Override
public Optional<APIPlayer> getPlayer(String name) {
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(name);
if(player == null) return Optional.empty();
return Optional.of(new BungeePlayer(player));
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), key -> new BungeePlayer(player)));
}
@Override
@@ -27,11 +27,18 @@ public class BungeePlayerExecutor implements PlayerExecutor {
if(player == null) return Optional.empty();
return Optional.of(new BungeePlayer(player));
return Optional.of(cachedPlayers.computeIfAbsent(uuid, key -> new BungeePlayer(player)));
}
@Override
public void unloadPlayer(UUID uuid) {
this.cachedPlayers.remove(uuid);
}
@Override
public List<APIPlayer> getOnlinePlayers() {
return BungeeCord.getInstance().getPlayers().stream().map(BungeePlayer::new).collect(Collectors.toList());
return BungeeCord.getInstance().getPlayers().stream()
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new BungeePlayer(pl)))
.collect(Collectors.toList());
}
}
@@ -1,82 +1,47 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.bungee.util.Config;
import dev.brighten.antivpn.bungee.command.BungeeCommand;
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.stream.IntStream;
import java.util.concurrent.TimeUnit;
public class BungeePlugin extends Plugin {
public static BungeePlugin pluginInstance;
@Getter
private Config config;
private static BaseComponent[] noPermission = new ComponentBuilder("No permission").color(ChatColor.RED)
.create();
private SingleLineChart vpnDetections, ipsChecked;
@Override
public void onEnable() {
pluginInstance = this;
//Setting up config
config = new Config();
BungeeCord.getInstance().getLogger().info("Loading config...");
//Loading plugin
AntiVPN.start(new BungeeConfig(), new BungeeListener(), new BungeePlayerExecutor());
BungeeCord.getInstance().getLogger().info("Starting AntiVPN services...");
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
if(AntiVPN.getInstance().getVpnConfig().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])) {
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().getPluginManager().registerCommand(pluginInstance, new BungeeCommand(command));
}
}
@@ -0,0 +1,79 @@
package dev.brighten.antivpn.bungee.command;
import dev.brighten.antivpn.AntiVPN;
import lombok.val;
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.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
import java.util.Arrays;
import java.util.stream.IntStream;
public class BungeeCommand extends Command implements TabExecutor {
private final dev.brighten.antivpn.command.Command command;
public BungeeCommand(dev.brighten.antivpn.command.Command command) {
super(command.name(), command.permission(), command.aliases());
this.command = command;
}
@Override
public void execute(CommandSender sender, String[] args) {
if(!sender.hasPermission("antivpn.command.*")
&& !sender.hasPermission(command.permission())) {
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
return;
}
val children = command.children();
if(children.length > 0 && args.length > 0) {
for (dev.brighten.antivpn.command.Command child : children) {
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
if(!sender.hasPermission("antivpn.command.*")
&& !sender.hasPermission(child.permission())) {
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
return;
}
sender.sendMessage(TextComponent
.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
child.execute(new BungeeCommandExecutor(sender), IntStream
.range(0, args.length - 1)
.mapToObj(i -> args[i + 1]).toArray(String[]::new)))));
return;
}
}
}
sender.sendMessage(TextComponent
.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
command.execute(new BungeeCommandExecutor(sender), args))));
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
val children = command.children();
if(children.length > 0 && args.length > 0) {
for (dev.brighten.antivpn.command.Command child : children) {
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
return child.tabComplete(new BungeeCommandExecutor(sender), "alias", IntStream
.range(0, args.length - 1)
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
}
}
}
return command.tabComplete(new BungeeCommandExecutor(sender), "alias", args);
}
}
@@ -0,0 +1,41 @@
package dev.brighten.antivpn.bungee.command;
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, Object... objects) {
sender.sendMessage(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&', String.format(message, objects))));
}
@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;
}
}
@@ -1,114 +0,0 @@
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!");
}
}
@@ -1,28 +0,0 @@
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;
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
name: AntiVPN
name: KauriVPN
main: dev.brighten.antivpn.bungee.BungeePlugin
description: A simple and fast antivpn plugin.
version: ${project.version}
+88 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.1.1</version>
<version>1.9.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -16,4 +16,91 @@
<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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
<relocations>
<relocation>
<pattern>org.yaml.snakeyaml</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.com.google.common</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>funkemunky-releases</id>
<url>https://nexus.funkemunky.cc/content/repositories/releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.12.14</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -5,47 +5,183 @@ 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.command.impl.LookupCommand;
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 dev.brighten.antivpn.database.VPNDatabase;
import dev.brighten.antivpn.database.local.H2VPN;
import dev.brighten.antivpn.database.mongo.MongoVPN;
import dev.brighten.antivpn.database.sql.MySqlVPN;
import dev.brighten.antivpn.depends.LibraryLoader;
import dev.brighten.antivpn.depends.MavenLibrary;
import dev.brighten.antivpn.message.MessageHandler;
import dev.brighten.antivpn.utils.ConfigDefault;
import dev.brighten.antivpn.utils.MiscUtils;
import dev.brighten.antivpn.utils.config.Configuration;
import dev.brighten.antivpn.utils.config.ConfigurationProvider;
import dev.brighten.antivpn.utils.config.YamlConfiguration;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter(AccessLevel.PRIVATE)
@MavenLibrary(groupId = "com.h2database", artifactId ="h2", version = "2.2.224")
@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14")
@MavenLibrary(groupId = "com.mysql", artifactId = "mysql-connector-j", version = "8.2.0")
public class AntiVPN {
private static AntiVPN INSTANCE;
private VPNConfig config;
private VPNConfig vpnConfig;
private VPNExecutor executor;
private PlayerExecutor playerExecutor;
private VPNDatabase database;
private MessageHandler messageHandler;
private Configuration config;
private List<Command> commands = new ArrayList<>();
public int detections, checked;
private File pluginFolder;
public static void start(VPNConfig config, VPNExecutor executor, PlayerExecutor playerExecutor) {
public static void start(VPNExecutor executor, PlayerExecutor playerExecutor, File pluginFolder) {
//Initializing
INSTANCE = new AntiVPN();
INSTANCE.config = config;
INSTANCE.pluginFolder = pluginFolder;
INSTANCE.executor = executor;
INSTANCE.playerExecutor = playerExecutor;
LibraryLoader.loadAll(INSTANCE);
try {
File configFile = new File(pluginFolder, "config.yml");
if(!configFile.exists()){
configFile.getParentFile().mkdirs();
MiscUtils.copy(INSTANCE.getResource( "config.yml"), configFile);
}
INSTANCE.config = ConfigurationProvider.getProvider(YamlConfiguration.class)
.load(configFile);
} catch (IOException e) {
e.printStackTrace();
}
INSTANCE.vpnConfig = new VPNConfig();
INSTANCE.executor.registerListeners();
INSTANCE.config.update();
INSTANCE.vpnConfig.update();
INSTANCE.messageHandler = new MessageHandler();
switch(INSTANCE.vpnConfig.getDatabaseType().toLowerCase()) {
case "h2":
case "local":
case "flatfile": {
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
INSTANCE.database = new H2VPN();
INSTANCE.database.init();
break;
}
case "mysql":
case "sql":{
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
INSTANCE.database = new MySqlVPN();
INSTANCE.database.init();
break;
}
case "mongo":
case "mongodb":
case "mongod": {
INSTANCE.database = new MongoVPN();
INSTANCE.database.init();
break;
}
default: {
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " +
"Options: [MySQL]");
break;
}
}
//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);
}
});
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), AntiVPN.getInstance())
.get());
AntiVPN.getInstance().getMessageHandler().reloadStrings();
}
public InputStream getResource(String filename) {
if (filename == null) {
throw new IllegalArgumentException("Filename cannot be null");
} else {
try {
URL url = executor.getClass().getClassLoader().getResource(filename);
if (url == null) {
return null;
} else {
URLConnection connection = url.openConnection();
connection.setUseCaches(false);
return connection.getInputStream();
}
} catch (IOException var4) {
return null;
}
}
}
public void stop() {
executor.shutdown();
if(database != null) database.shutdown();
}
public void reloadDatabase() {
database.shutdown();
switch(AntiVPN.getInstance().getVpnConfig().getDatabaseType().toLowerCase()) {
case "h2":
case "local":
case "flatfile": {
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
INSTANCE.database = new H2VPN();
INSTANCE.database.init();
break;
}
case "mysql":
case "sql":{
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
INSTANCE.database = new MySqlVPN();
INSTANCE.database.init();
break;
}
case "mongo":
case "mongodb":
case "mongod": {
INSTANCE.database = new MongoVPN();
INSTANCE.database.init();
break;
}
default: {
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " +
"Options: [MySQL]");
break;
}
}
}
public static AntiVPN getInstance() {
@@ -54,17 +190,26 @@ public class AntiVPN {
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));
public void saveConfig() {
try {
ConfigurationProvider.getProvider(YamlConfiguration.class)
.save(getConfig(), new File(pluginFolder.getPath() + File.separator + "config.yml"));
} catch (IOException e) {
e.printStackTrace();
}
}
return VPNResponse.fromJson(result);
public void reloadConfig() {
try {
config = ConfigurationProvider.getProvider(YamlConfiguration.class)
.load(new File(pluginFolder.getPath() + File.separator + "config.yml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void registerCommands() {
commands.add(new AntiVPNCommand());
commands.add(new LookupCommand());
}
}
@@ -1,21 +1,36 @@
package dev.brighten.antivpn.api;
import dev.brighten.antivpn.AntiVPN;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.net.InetAddress;
import java.util.UUID;
@Getter
@RequiredArgsConstructor
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);
}
}
@@ -10,5 +10,7 @@ public interface PlayerExecutor {
Optional<APIPlayer> getPlayer(UUID uuid);
void unloadPlayer(UUID uuid);
List<APIPlayer> getOnlinePlayers();
}
@@ -1,13 +1,290 @@
package dev.brighten.antivpn.api;
public interface VPNConfig {
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.utils.ConfigDefault;
String getLicense();
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
boolean cachedResults();
public class VPNConfig {
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
"license", AntiVPN.getInstance()), kickStringDefault =
new ConfigDefault<>("Proxies are not allowed on our server",
"kickMessage", AntiVPN.getInstance()),
defaultDatabaseType = new ConfigDefault<>("H2",
"database.type", AntiVPN.getInstance()),
defaultDatabaseName = new ConfigDefault<>("kaurivpn",
"database.database", AntiVPN.getInstance()),
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", AntiVPN.getInstance()),
defaultUsername = new ConfigDefault<>("root",
"database.username", AntiVPN.getInstance()),
defaultPassword = new ConfigDefault<>("password",
"database.password", AntiVPN.getInstance()),
defaultCountryKickReason = new ConfigDefault<>(
"&cSorry, but our server does not allow connections from\n&f%country%",
"countries.vanillaKickReason", AntiVPN.getInstance()),
defaultIp = new ConfigDefault<>("localhost", "database.ip", AntiVPN.getInstance()),
defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" +
" &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message",
AntiVPN.getInstance());
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
"cachedResults", AntiVPN.getInstance()),
defaultUseCredentials = new ConfigDefault<>(true,
"database.useCredentials", AntiVPN.getInstance()),
defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled",
AntiVPN.getInstance()), defaultCommandsEnable = new ConfigDefault<>(false,
"commands.enabled", AntiVPN.getInstance()), defaultKickPlayers
= new ConfigDefault<>(true, "kickPlayers", AntiVPN.getInstance()),
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled",
AntiVPN.getInstance()),
defaultWhitelistCountries = new ConfigDefault<>(true, "countries.whitelist",
AntiVPN.getInstance()),
defaultMetrics = new ConfigDefault<>(true, "bstats", AntiVPN.getInstance());
private final ConfigDefault<Integer>
defaultPort = new ConfigDefault<>(-1, "database.port", AntiVPN.getInstance());
private final ConfigDefault<List<String>> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(),
"prefixWhitelists", AntiVPN.getInstance()), defaultCommands = new ConfigDefault<>(
Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute",
AntiVPN.getInstance()),
defCountryKickCommands = new ConfigDefault<>(Collections.emptyList(),
"countries.commands", AntiVPN.getInstance()),
defCountrylist = new ConfigDefault<>(new ArrayList<>(), "countries.list",
AntiVPN.getInstance());
String getKickString();
private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg,
countryVanillaKickReason;
private List<String> prefixWhitelists, commands, countryList, countryKickCommands;
private int port;
private boolean cacheResults, databaseEnabled, useCredentials, commandsEnabled, kickPlayers, alertToStaff,
metrics, whitelistCountries;
void update();
/**
* License from https://funkemunky.cc/shop to be used for more queries.
* @return String
*/
public String getLicense() {
return license;
}
/**
* If true, results will be cached to reduce queries to https://funkemunky.cc
* @return boolean
*/
public boolean cachedResults() {
return cacheResults;
}
/**
* Will be used for vanilla kick message when {@link VPNConfig#runCommands()} is true.
* @return String
*/
public String getKickString() {
return kickMessage;
}
/**
* Message to send staff on proxy detection.
* @return String
*/
public String alertMessage() {
return alertMsg;
}
/**
* If true, staff will be alerted on proxy detection.
* @return boolean
*/
public boolean alertToStaff() {
return alertToStaff;
}
/**
* If true, will run {@link VPNConfig#commands()} on detect. If not, it will use vanilla kicking methods.
* @return boolean
*/
public boolean runCommands() {
return commandsEnabled;
}
/**
* Commands to run on proxy detection.
* @return List
*/
public List<String> commands() {
return commands;
}
/**
* If false, no commands nor kick will be run on proxy detection.
* @return boolean
*/
public boolean kickPlayersOnDetect() {
return kickPlayers;
}
/**
* Returns Strings of which are checked against the beginning of player names. Used to
* allow Geyser-connected players to join.
* @return List
*/
public List<String> getPrefixWhitelists() {
return prefixWhitelists;
}
/**
* Returns true if we want to use a database
* @return boolean
*/
public boolean isDatabaseEnabled() {
return databaseEnabled;
}
/**
* Whether or not the database we want to connect to requires credentials.
* @return boolean
*/
public boolean useDatabaseCreds() {
return useCredentials;
}
/**
* Only for Mongo only. URL used for connecting to database. Overrides other fields
* @return String
*/
public String mongoDatabaseURL() {
return mongoURL;
}
/**
* Database type. Either MySQL and Mongo.
* @return String
*/
public String getDatabaseType() {
return databaseType;
}
/**
* Database name
* @return String
*/
public String getDatabaseName() {
return databaseName;
}
/**
* Database username
* @return String
*/
public String getUsername() {
return username;
}
/**
* Database Password
* @return String
*/
public String getPassword() {
return password;
}
/**
* Database IP
* @return String
*/
public String getIp() {
return ip;
}
/**
* Returns the list of ISO country codes we need to check.
* @return List
*/
public List<String> countryList() {
return countryList;
}
/**
* If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist them.
* @return boolean
*/
public boolean whitelistCountries() {
return whitelistCountries;
}
/**
* Returns our configured commands to run on player country detection.
* @return List
*/
public List<String> countryKickCommands() {
return countryKickCommands;
}
/**
* Returns the vanilla kick reason for bad country locations
* @return String
*/
public String countryVanillaKickReason() {
return countryVanillaKickReason;
}
/**
* Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port
* based on {@link VPNConfig#getDatabaseType()} lowerCase().
* @return int
*/
public int getPort() {
if(port == -1) {
switch (getDatabaseType().toLowerCase()) {
case "mongodb":
case "mongo":
case "mongod":
return 27017;
case "sql":
case "mysql":
return 3306;
}
}
return port;
}
/**
* If true, https://bstats.org metrics will be collected to improve KauriVPN.
* @return boolean
*/
public boolean metrics() {
return metrics;
}
/**
* Grabs all information from the config.yml
*/
public void update() {
license = licenseDefault.get();
kickMessage = kickStringDefault.get();
cacheResults = cacheResultsDefault.get();
prefixWhitelists = prefixWhitelistsDefault.get();
databaseEnabled = defaultDatabaseEnabled.get();
useCredentials = defaultUseCredentials.get();
databaseType = defaultDatabaseType.get();
databaseName = defaultDatabaseName.get();
mongoURL = defaultMongoURL.get();
username = defaultUsername.get();
password = defaultPassword.get();
ip = defaultIp.get();
port = defaultPort.get();
commandsEnabled = defaultCommandsEnable.get();
commands = defaultCommands.get();
kickPlayers = defaultKickPlayers.get();
alertToStaff = defaultAlertToStaff.get();
alertMsg = defaultAlertMsg.get();
metrics = defaultMetrics.get();
countryList = defCountrylist.get();
whitelistCountries = defaultWhitelistCountries.get();
countryKickCommands = defCountryKickCommands.get();
countryVanillaKickReason = defaultCountryKickReason.get();
}
}
@@ -1,56 +1,94 @@
package dev.brighten.antivpn.api;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.utils.VPNResponse;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.web.FunkemunkyAPI;
import dev.brighten.antivpn.web.objects.VPNResponse;
import lombok.Getter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.logging.Level;
public abstract class VPNExecutor {
public static ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
public static ScheduledExecutorService threadExecutor = Executors.newScheduledThreadPool(2);
private static final Map<String, VPNResponse> responseCache = new HashMap<>();
@Getter
private final Set<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
@Getter
private final Set<String> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
private final Cache<String, VPNResponse> responseCache = CacheBuilder.newBuilder()
.expireAfterWrite(20, TimeUnit.MINUTES)
.maximumSize(4000)
.build();
public abstract void registerListeners();
public abstract void runCacheReset();
public void resetCache() {
responseCache.clear();
}
public abstract void shutdown();
public void checkIp(String ip, boolean cachedResults, Consumer<VPNResponse> result) {
threadExecutor.execute(() -> result.accept(responseCache.compute(ip, (key, val) -> {
if(val == null) {
try {
return AntiVPN.getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults);
} catch (JSONException | IOException e) {
e.printStackTrace();
}
}
public abstract void log(Level level, String log, Object... objects);
return val;
})));
public abstract void log(String log, Object... objects);
public boolean isWhitelisted(UUID uuid) {
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
return AntiVPN.getInstance().getDatabase().isWhitelisted(uuid);
}
return whitelisted.contains(uuid);
}
public VPNResponse checkIp(String ip, boolean cachedResults) {
return responseCache.compute(ip, (key, val) -> {
if(val == null) {
try {
return AntiVPN.getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults);
} catch (JSONException | IOException e) {
e.printStackTrace();
}
}
public boolean isWhitelisted(String ip) {
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
return AntiVPN.getInstance().getDatabase().isWhitelisted(ip);
}
return whitelistedIps.contains(ip);
}
return val;
public void checkIp(String ip, boolean cachedResults, Consumer<VPNResponse> result) {
threadExecutor.execute(() -> {
if(cachedResults) {
try {
result.accept(responseCache.get(ip, () -> checkIp(ip)));
} catch (ExecutionException e) {
log("Failed to process checkIp() method! Reason: " + e.getMessage());
result.accept(VPNResponse.FAILED_RESPONSE);
}
} else {
result.accept(checkIp(ip));
}
});
}
public VPNResponse checkIp(String ip) {
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
if(cachedRes.isPresent()) {
return cachedRes.get();
}
else {
try {
VPNResponse response = FunkemunkyAPI
.getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
if (response.isSuccess()) {
AntiVPN.getInstance().getDatabase().cacheResponse(response);
} else {
log("Query to VPN API failed! Reason: " + response.getFailureReason());
}
return response;
} catch (JSONException | IOException e) {
log("Query to VPN API failed! Reason: " + e.getMessage());
return VPNResponse.FAILED_RESPONSE;
}
}
}
}
@@ -1,8 +1,14 @@
package dev.brighten.antivpn.command;
import dev.brighten.antivpn.api.APIPlayer;
import java.util.Optional;
public interface CommandExecutor {
void sendMessage(String message);
void sendMessage(String message, Object... objects);
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,166 @@
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().getVpnConfig().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":
case "insert": {
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(args[1]);
AntiVPN.getInstance().getDatabase().setWhitelisted(args[1], true);
return String.format("&aAdded &6%s &ato the exemption allowlist.", args[1]);
}
case "remove":
case "delete": {
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(args[1]);
AntiVPN.getInstance().getDatabase().setWhitelisted(args[1], false);
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":
case "insert": {
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();
}
}
@@ -4,9 +4,11 @@ import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.CommandExecutor;
import dev.brighten.antivpn.utils.StringUtil;
import jdk.nashorn.internal.lookup.Lookup;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class AntiVPNCommand extends Command {
@@ -42,7 +44,8 @@ public class AntiVPNCommand extends Command {
@Override
public Command[] children() {
return new Command[] {new LookupCommand()};
return new Command[] {new LookupCommand(), new AllowlistCommand(), new AlertsCommand(),
new ClearCacheCommand(), new PlanCommand(), new ReloadCommand()};
}
@Override
@@ -52,11 +55,17 @@ public class AntiVPNCommand extends Command {
messages.add(StringUtil.line("&8"));
messages.add("&6&lAntiVPN Help Page");
messages.add("");
for (Command child : AntiVPN.getInstance().getCommands()) {
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(), description()));
+ child.usage(), child.description()));
}
messages.add(StringUtil.line("&8"));
return String.join("\n", messages);
@@ -0,0 +1,56 @@
package dev.brighten.antivpn.command.impl;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.CommandExecutor;
import java.util.Collections;
import java.util.List;
public class 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();
return "&aCleared all cached API response information!";
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
return Collections.emptyList();
}
}
@@ -8,7 +8,6 @@ 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;
@@ -68,13 +67,13 @@ public class LookupCommand extends Command {
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("&e%s&8: &f%s", "Proxy", result.isProxy()
? "&a" + result.getMethod() : "&cNo");
executor.sendMessage("&e%s&8: &f%s", "ISP", result.getIsp());
executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName());
executor.sendMessage("&e%s&8: &f%s", "City", result.getCity());
executor.sendMessage("&e%s&8: &f%s", "Coordinates", result.getLatitude()
+ "&7/&f" + result.getLongitude());
executor.sendMessage(StringUtil.line("&8"));
}
});
@@ -0,0 +1,100 @@
package dev.brighten.antivpn.command.impl;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.CommandExecutor;
import dev.brighten.antivpn.utils.StringUtil;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.web.FunkemunkyAPI;
import dev.brighten.antivpn.web.objects.QueryResponse;
import java.io.IOException;
import java.util.List;
public class PlanCommand extends Command {
@Override
public String permission() {
return "antivpn.command.plan";
}
@Override
public String name() {
return "plan";
}
@Override
public String[] aliases() {
return new String[] {"queries", "query"};
}
@Override
public String description() {
return "Info related to KauriVPN Plan";
}
@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) {
VPNExecutor.threadExecutor.execute(() -> {
QueryResponse result;
try {
if(AntiVPN.getInstance().getVpnConfig().getLicense().equals("")) {
result = FunkemunkyAPI.getQueryResponse();
} else {
result = FunkemunkyAPI.getQueryResponse(AntiVPN.getInstance().getVpnConfig().getLicense());
if(!result.isValidPlan()) {
executor.sendMessage("&cThe license &f%s &cis not a valid license, " +
"checking your Free plan information...",
AntiVPN.getInstance().getVpnConfig().getLicense());
result = FunkemunkyAPI.getQueryResponse();
}
}
String plan = result.getPlanType();
if(plan.equals("IP")) plan+= " (Free)";
String queryMax = result.getQueriesMax() == Long.MAX_VALUE
? "Unlimited" : String.valueOf(result.getQueriesMax());
executor.sendMessage(StringUtil.line("&8"));
executor.sendMessage("&6&lKauriVPN Plan Information");
executor.sendMessage("");
executor.sendMessage("&e%s&8: &f%s", "Plan", plan);
executor.sendMessage("&e%s&8: &f%s&7/&f%s", "Queries Used",
result.getQueries(), queryMax);
executor.sendMessage(StringUtil.line("&8"));
} catch(JSONException e) {
e.printStackTrace();
executor.sendMessage("&cThere was a JSONException thrown while looking up your query " +
"information. Check console for more details.");
} catch (IOException e) {
e.printStackTrace();
executor.sendMessage("&cThere was a IOException thrown while looking up your query " +
"information. Check console for more details.");
}
});
return "&7Looking up your query information...";
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
return null;
}
}
@@ -0,0 +1,65 @@
package dev.brighten.antivpn.command.impl;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.command.Command;
import dev.brighten.antivpn.command.CommandExecutor;
import java.util.Collections;
import java.util.List;
public class ReloadCommand extends Command {
@Override
public String permission() {
return "antivpn.command.reload";
}
@Override
public String name() {
return "reload";
}
@Override
public String[] aliases() {
return new String[0];
}
@Override
public String description() {
return "Reload the plugin";
}
@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) {
// Loading changes from the config.yml
AntiVPN.getInstance().reloadConfig();
// Updating the cache of these values in VPNConfig
AntiVPN.getInstance().getVpnConfig().update();
AntiVPN.getInstance().getMessageHandler().reloadStrings();
AntiVPN.getInstance().reloadDatabase();
return AntiVPN.getInstance().getMessageHandler().getString("command-reload-complete").getMessage();
}
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
return Collections.emptyList();
}
}
@@ -0,0 +1,44 @@
package dev.brighten.antivpn.database;
import dev.brighten.antivpn.web.objects.VPNResponse;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;
public interface VPNDatabase {
Optional<VPNResponse> getStoredResponse(String ip);
void cacheResponse(VPNResponse toCache);
void deleteResponse(String ip);
boolean isWhitelisted(UUID uuid);
boolean isWhitelisted(String 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,276 @@
package dev.brighten.antivpn.database.local;
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.web.objects.VPNResponse;
import lombok.SneakyThrows;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class H2VPN implements VPNDatabase {
public H2VPN() {
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
//Refreshing whitelisted players
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
AntiVPN.getInstance().getExecutor().getWhitelisted()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
//Refreshing whitlisted IPs
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
}, 2, 30, TimeUnit.SECONDS);
}
@Override
public Optional<VPNResponse> getStoredResponse(String ip) {
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed())
return Optional.empty();
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"),
rs.getTimestamp("inserted").getTime(), -1);
if(System.currentTimeMillis() - response.getLastAccess() > TimeUnit.HOURS.toMillis(1)) {
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
return Optional.empty();
}
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().getVpnConfig().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();
}
@Override
public void deleteResponse(String ip) {
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
return;
Query.prepare("delete from `responses` where `ip` = ?").append(ip).execute();
}
@SneakyThrows
@Override
public boolean isWhitelisted(UUID uuid) {
if (!AntiVPN.getInstance().getVpnConfig().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().getVpnConfig().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().getVpnConfig().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().getVpnConfig().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().getVpnConfig().isDatabaseEnabled())
return;
AntiVPN.getInstance().getExecutor().log("Initializing H2...");
MySQL.initH2();
AntiVPN.getInstance().getExecutor().log("Creating tables...");
//Running check for old table types to update
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 {
Query.prepare("create index if not exists `uuid_1` on `whitelisted` (`uuid`)").execute();
Query.prepare("create index if not exists `ip_1` on `responses` (`ip`)").execute();
Query.prepare("create index if not exists `proxy_1` on `responses` (`proxy`)").execute();
Query.prepare("create index if not exists `inserted_1` on `responses` (`inserted`)").execute();
Query.prepare("create index if not exists `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().getVpnConfig().isDatabaseEnabled())
return;
MySQL.shutdown();
}
}
@@ -0,0 +1,246 @@
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 com.mongodb.client.model.UpdateOptions;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.database.VPNDatabase;
import dev.brighten.antivpn.web.objects.VPNResponse;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class MongoVPN implements VPNDatabase {
private MongoCollection<Document> settingsDocument, cacheDocument;
private MongoClient client;
public MongoVPN() {
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
//Refreshing whitelisted players
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
AntiVPN.getInstance().getExecutor().getWhitelisted()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
//Refreshing whitlisted IPs
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
}, 2, 30, TimeUnit.SECONDS);
}
@Override
public Optional<VPNResponse> getStoredResponse(String ip) {
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
if(rdoc != null) {
long lastUpdate = rdoc.get("lastAccess", 0L);
if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) {
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
return Optional.empty();
}
return Optional.of(VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip)
.countryName(rdoc.getString("countryName"))
.countryCode(rdoc.getString("countryCode"))
.city(rdoc.getString("city"))
.isp(rdoc.getString("isp"))
.method(rdoc.getString("method"))
.timeZone(rdoc.getString("timeZone"))
.proxy(rdoc.getBoolean("proxy"))
.cached(rdoc.getBoolean("cached"))
.success(true)
.latitude(rdoc.getDouble("latitude"))
.longitude(rdoc.getDouble("longitude"))
.lastAccess(rdoc.get("lastAccess", 0L))
.build());
}
return Optional.empty();
}
@Override
public void cacheResponse(VPNResponse toCache) {
Document rdoc = new Document("ip", toCache.getIp());
rdoc.put("asn", toCache.getAsn());
rdoc.put("countryName", toCache.getCountryName());
rdoc.put("countryCode", toCache.getCountryCode());
rdoc.put("city", toCache.getCity());
rdoc.put("isp", toCache.getIsp());
rdoc.put("method", toCache.getMethod());
rdoc.put("timeZone", toCache.getTimeZone());
rdoc.put("proxy", toCache.isProxy());
rdoc.put("cached", toCache.isCached());
rdoc.put("success", toCache.isSuccess());
rdoc.put("latitude", toCache.getLatitude());
rdoc.put("longitude", toCache.getLongitude());
rdoc.put("lastAccess", System.currentTimeMillis());
VPNExecutor.threadExecutor.execute(() -> {
Bson update = new Document("$set", rdoc);
cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update,
new UpdateOptions().upsert(true));
});
}
@Override
public void deleteResponse(String ip) {
cacheDocument.deleteMany(Filters.eq("ip", ip));
}
@Override
public boolean isWhitelisted(UUID uuid) {
return settingsDocument
.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.eq("uuid", uuid.toString()))).first() != null;
}
@Override
public boolean isWhitelisted(String ip) {
return settingsDocument
.find(Filters.and(Filters.eq("setting", "whitelist"),
Filters.eq("ip", ip))).first() != null;
}
@Override
public void setWhitelisted(UUID uuid, boolean whitelisted) {
if(whitelisted) {
Document wdoc = new Document("setting", "whitelist");
wdoc.put("uuid", uuid.toString());
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
} else {
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
.and(
Filters.eq("setting", "whitelist"),
Filters.eq("uuid", uuid.toString()))));
}
}
@Override
public void setWhitelisted(String ip, boolean whitelisted) {
if(whitelisted) {
Document wdoc = new Document("setting", "whitelist").append("ip", ip);
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip);
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
} else {
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip);
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
.and(
Filters.eq("setting", "whitelist"),
Filters.eq("ip", ip))));
}
}
@Override
public 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().getVpnConfig().mongoDatabaseURL().isEmpty()) { //URL
ConnectionString cs = new ConnectionString(AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL());
MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(cs).build();
client = MongoClients.create(settings);
} else {
MongoClientSettings.Builder settingsBld = MongoClientSettings.builder().readPreference(ReadPreference.nearest())
.applyToClusterSettings(builder -> builder.
hosts(Collections.singletonList(
new ServerAddress(
AntiVPN.getInstance().getVpnConfig().getIp(),
AntiVPN.getInstance().getVpnConfig().getPort())
)));
if(AntiVPN.getInstance().getVpnConfig().useDatabaseCreds()) {
settingsBld.credential(MongoCredential
.createCredential(AntiVPN.getInstance().getVpnConfig().getUsername(),
AntiVPN.getInstance().getVpnConfig().getDatabaseName(),
AntiVPN.getInstance().getVpnConfig().getPassword().toCharArray()));
}
client = MongoClients.create(settingsBld.build());
}
MongoDatabase antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getVpnConfig().getDatabaseName());
settingsDocument = antivpnDatabase.getCollection("settings");
if(settingsDocument.listIndexes().first() == null) {
AntiVPN.getInstance().getExecutor().log("Created index for settings collection!");
settingsDocument.createIndex(Indexes.ascending("ip"));
}
cacheDocument = antivpnDatabase.getCollection("cache");
}
@Override
public void shutdown() {
settingsDocument = null;
cacheDocument = null;
client.close();
}
}
@@ -0,0 +1,347 @@
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.web.objects.VPNResponse;
import lombok.SneakyThrows;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class MySqlVPN implements VPNDatabase {
public MySqlVPN() {
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
//Refreshing whitelisted players
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
AntiVPN.getInstance().getExecutor().getWhitelisted()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
//Refreshing whitlisted IPs
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
}, 2, 30, TimeUnit.SECONDS);
}
@Override
public Optional<VPNResponse> getStoredResponse(String ip) {
if (isDisabled())
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"),
rs.getTimestamp("inserted").getTime(), -1);
if(System.currentTimeMillis() - response.getLastAccess() > TimeUnit.HOURS.toMillis(1)) {
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
return Optional.empty();
}
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 (isDisabled())
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();
}
@Override
public void deleteResponse(String ip) {
if(!isDisabled())
return;
Query.prepare("delete from `responses` where `ip` = ?").append(ip).execute();
}
@SneakyThrows
@Override
public boolean isWhitelisted(UUID uuid) {
if (isDisabled())
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 (isDisabled())
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 (isDisabled())
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 (isDisabled())
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().getVpnConfig().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());
}
}
private boolean isDisabled() {
return !AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed();
}
@Override
public void shutdown() {
if (!AntiVPN.getInstance().getVpnConfig().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,140 @@
package dev.brighten.antivpn.database.sql.utils;
import dev.brighten.antivpn.AntiVPN;
import org.h2.jdbc.JdbcSQLNonTransientConnectionException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
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().getVpnConfig().getIp()
+ ":" + AntiVPN.getInstance().getVpnConfig().getPort()
+ "/?useSSL=true&autoReconnect=true",
AntiVPN.getInstance().getVpnConfig().getUsername(),
AntiVPN.getInstance().getVpnConfig().getPassword());
conn.setAutoCommit(true);
Query.use(conn);
Query.prepare("CREATE DATABASE IF NOT EXISTS `"
+ AntiVPN.getInstance().getVpnConfig().getDatabaseName() + "`").execute();
Query.prepare("USE `" + AntiVPN.getInstance().getVpnConfig().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();
}
}
private static boolean attemptedTwice = false;
public static void initH2() {
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases");
File databaseFile = new File(dataFolder, "database");
try {
Constructor jdbcConnection = Class.forName("org.h2.jdbc.JdbcConnection")
.getConstructor(String.class, Properties.class, String.class, Object.class, boolean.class);
conn = new NonClosableConnection((Connection)jdbcConnection.newInstance("jdbc:h2:file:" +
databaseFile.getAbsolutePath(),
new Properties(), AntiVPN.getInstance().getVpnConfig().getUsername(),
AntiVPN.getInstance().getVpnConfig().getPassword(), false));
conn.setAutoCommit(true);
Query.use(conn);
AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established.");
} catch (SQLException ex) {
AntiVPN.getInstance().getExecutor().log("H2 exception on initialize");
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
AntiVPN.getInstance().getExecutor().log("No H2 library found!");
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) {
AntiVPN.getInstance().getExecutor().log("Java exception on initialize");
e.printStackTrace();
} catch(InvocationTargetException e) {
if(attemptedTwice) return;
if(e.getCause() instanceof JdbcSQLNonTransientConnectionException) {
File[] files = dataFolder.listFiles();
if(files == null) {
e.printStackTrace();
return;
}
File oldDbs = new File(dataFolder, "old");
boolean made = false;
if(!oldDbs.isDirectory()) {
made = oldDbs.mkdir();
} else made = true;
if(!made) {
throw new RuntimeException(String.format("Unable to upgrade H2 files since this application " +
"was unable to create a new directory %s (insufficient permissions?)!", oldDbs.getPath()));
}
AntiVPN.getInstance().getExecutor().log("Upgrading h2 files...");
for (File file : files) {
if(file.getName().endsWith(".db")) {
try {
Files.copy(file.toPath(), new File(oldDbs, file.getName()).toPath());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
if(file.delete()) {
AntiVPN.getInstance().getExecutor().log("Successfully deleted old " + file.getName());
} else throw new RuntimeException("Unable to delete old database file " + file.getName());
}
}
initH2();
} else e.printStackTrace();
}
attemptedTwice = true;
}
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;
}
attemptedTwice = false;
} 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,200 @@
/*
* This file is part of helper, 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.depends;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.utils.NonnullByDefault;
import dev.brighten.antivpn.utils.Supplier;
import dev.brighten.antivpn.utils.Suppliers;
import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.Objects;
/**
* Resolves {@link MavenLibrary} annotations for a class, and loads the dependency
* into the classloader.
*/
@NonnullByDefault
public final class LibraryLoader {
@SuppressWarnings("Guava")
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = Suppliers.memoize(() -> URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader()));
/**
* Resolves all {@link MavenLibrary} annotations on the given object.
*
* @param object the object to load libraries for.
*/
public static void loadAll(Object object) {
loadAll(object.getClass());
}
/**
* Resolves all {@link MavenLibrary} annotations on the given class.
*
* @param clazz the class to load libraries for.
*/
public static void loadAll(Class<?> clazz) {
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
if (libs == null) {
return;
}
for (MavenLibrary lib : libs) {
load(lib.groupId(), lib.artifactId(), lib.version(), lib.repo().url());
}
}
public static void load(String groupId, String artifactId, String version) {
load(groupId, artifactId, version, "https://repo1.maven.org/maven2");
}
public static void load(String groupId, String artifactId, String version, String repoUrl) {
load(new Dependency(groupId, artifactId, version, repoUrl));
}
public static void load(Dependency d) {
AntiVPN.getInstance().getExecutor().log(String.format("Loading dependency %s:%s:%s from %s", d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl()));
String name = d.getArtifactId() + "-" + d.getVersion();
File saveLocation = new File(getLibFolder(), name + ".jar");
if (!saveLocation.exists()) {
try {
AntiVPN.getInstance().getExecutor().log("Dependency '" + name + "' is not already in the libraries folder. Attempting to download...");
URL url = d.getUrl();
try (InputStream is = url.openStream()) {
Files.copy(is, saveLocation.toPath());
}
} catch (Exception e) {
e.printStackTrace();
}
AntiVPN.getInstance().getExecutor().log("Dependency '" + name + "' successfully downloaded.");
}
if (!saveLocation.exists()) {
throw new RuntimeException("Unable to download dependency: " + d.toString());
}
try {
URL_INJECTOR.get().addURL(saveLocation.toURI().toURL());
} catch (Exception e) {
throw new RuntimeException("Unable to load dependency: " + saveLocation.toString(), e);
}
AntiVPN.getInstance().getExecutor().log("Loaded dependency '" + name + "' successfully.");
}
private static File getLibFolder() {
File pluginDataFolder = AntiVPN.getInstance().getPluginFolder();
File libs = new File(pluginDataFolder, "libraries");
libs.mkdirs();
return libs;
}
@NonnullByDefault
public static final class Dependency {
private final String groupId;
private final String artifactId;
private final String version;
private final String repoUrl;
public Dependency(String groupId, String artifactId, String version, String repoUrl) {
this.groupId = Objects.requireNonNull(groupId, "groupId");
this.artifactId = Objects.requireNonNull(artifactId, "artifactId");
this.version = Objects.requireNonNull(version, "version");
this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl");
}
public String getGroupId() {
return this.groupId;
}
public String getArtifactId() {
return this.artifactId;
}
public String getVersion() {
return this.version;
}
public String getRepoUrl() {
return this.repoUrl;
}
public URL getUrl() throws MalformedURLException {
String repo = this.repoUrl;
if (!repo.endsWith("/")) {
repo += "/";
}
repo += "%s/%s/%s/%s-%s.jar";
String url = String.format(repo, this.groupId.replace(".", "/"), this.artifactId, this.version, this.artifactId, this.version);
return new URL(url);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Dependency)) return false;
final Dependency other = (Dependency) o;
return this.getGroupId().equals(other.getGroupId()) &&
this.getArtifactId().equals(other.getArtifactId()) &&
this.getVersion().equals(other.getVersion()) &&
this.getRepoUrl().equals(other.getRepoUrl());
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.getGroupId().hashCode();
result = result * PRIME + this.getArtifactId().hashCode();
result = result * PRIME + this.getVersion().hashCode();
result = result * PRIME + this.getRepoUrl().hashCode();
return result;
}
@Override
public String toString() {
return "LibraryLoader.Dependency(" +
"groupId=" + this.getGroupId() + ", " +
"artifactId=" + this.getArtifactId() + ", " +
"version=" + this.getVersion() + ", " +
"repoUrl=" + this.getRepoUrl() + ")";
}
}
}
@@ -0,0 +1,40 @@
/*
* This file is part of helper, 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.depends;
import java.lang.annotation.*;
/**
* Annotation to indicate the required libraries for a class.
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MavenLibraries {
MavenLibrary[] value() default {};
}
@@ -0,0 +1,67 @@
/*
* This file is part of helper, 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.depends;
import java.lang.annotation.*;
/**
* Annotation to indicate a required library for a class.
*/
@Documented
@Repeatable(MavenLibraries.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MavenLibrary {
/**
* The group id of the library
*
* @return the group id of the library
*/
String groupId();
/**
* The artifact id of the library
*
* @return the artifact id of the library
*/
String artifactId();
/**
* The version of the library
*
* @return the version of the library
*/
String version();
/**
* The repo where the library can be obtained from
*
* @return the repo where the library can be obtained from
*/
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
}
@@ -0,0 +1,45 @@
/*
* This file is part of helper, 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.depends;
import java.lang.annotation.*;
/**
* Represents a maven repository.
*/
@Documented
@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
/**
* Gets the base url of the repository.
*
* @return the base url of the repository
*/
String url();
}
@@ -0,0 +1,175 @@
/*
* This file is part of helper, 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.depends;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
/**
* Provides access to {@link URLClassLoader}#addURL.
*/
public abstract class URLClassLoaderAccess {
/**
* Creates a {@link URLClassLoaderAccess} for the given class loader.
*
* @param classLoader the class loader
* @return the access object
*/
static URLClassLoaderAccess create(URLClassLoader classLoader) {
if (Reflection.isSupported()) {
return new Reflection(classLoader);
} else if (Unsafe.isSupported()) {
return new Unsafe(classLoader);
} else {
return Noop.INSTANCE;
}
}
private final URLClassLoader classLoader;
protected URLClassLoaderAccess(URLClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Adds the given URL to the class loader.
*
* @param url the URL to add
*/
public abstract void addURL(URL url);
/**
* Accesses using reflection, not supported on Java 9+.
*/
private static class Reflection extends URLClassLoaderAccess {
private static final Method ADD_URL_METHOD;
static {
Method addUrlMethod;
try {
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
} catch (Exception e) {
addUrlMethod = null;
}
ADD_URL_METHOD = addUrlMethod;
}
private static boolean isSupported() {
return ADD_URL_METHOD != null;
}
Reflection(URLClassLoader classLoader) {
super(classLoader);
}
@Override
public void addURL(URL url) {
try {
ADD_URL_METHOD.invoke(super.classLoader, url);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}
/**
* Accesses using sun.misc.Unsafe, supported on Java 9+.
*
* @author Vaishnav Anil (<a href="https://github.com/slimjar/slimjar">...</a>)
*/
private static class Unsafe extends URLClassLoaderAccess {
private static final sun.misc.Unsafe UNSAFE;
static {
sun.misc.Unsafe unsafe;
try {
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
} catch (Throwable t) {
unsafe = null;
}
UNSAFE = unsafe;
}
private static boolean isSupported() {
return UNSAFE != null;
}
private final Collection<URL> unopenedURLs;
private final Collection<URL> pathURLs;
@SuppressWarnings("unchecked")
Unsafe(URLClassLoader classLoader) {
super(classLoader);
Collection<URL> unopenedURLs;
Collection<URL> pathURLs;
try {
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
} catch (Throwable e) {
unopenedURLs = null;
pathURLs = null;
}
this.unopenedURLs = unopenedURLs;
this.pathURLs = pathURLs;
}
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
Field field = clazz.getDeclaredField(name);
long offset = UNSAFE.objectFieldOffset(field);
return UNSAFE.getObject(object, offset);
}
@Override
public void addURL(URL url) {
this.unopenedURLs.add(url);
this.pathURLs.add(url);
}
}
private static class Noop extends URLClassLoaderAccess {
private static final Noop INSTANCE = new Noop();
private Noop() {
super(null);
}
@Override
public void addURL(URL url) {
throw new UnsupportedOperationException();
}
}
}
@@ -0,0 +1,46 @@
package dev.brighten.antivpn.message;
import dev.brighten.antivpn.AntiVPN;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class MessageHandler {
private final Map<String, VpnString> messages = new HashMap<>();
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);
getter.apply(string);
AntiVPN.getInstance().getExecutor().log("Added string " + string.getKey());
messages.put(string.getKey(), string);
}
public void initStrings(Function<VpnString, String> getter) {
addString(new VpnString("command-misc-playerRequired",
"&cYou must be a player to execute this command!"), getter);
addString(new VpnString("command-alerts-toggled",
"&7Your player proxy notifications have been set to: &e%state%"), getter);
addString(new VpnString("command-reload-complete",
"&aSuccessfully reloaded KauriVPN plugin!"), getter);
addString(new VpnString("no-permission", "&cNo permission."), getter);
}
}
@@ -0,0 +1,58 @@
package dev.brighten.antivpn.message;
import dev.brighten.antivpn.api.APIPlayer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.SneakyThrows;
import java.util.function.Function;
@Getter
public class VpnString {
private final String key;
private final String defaultMessage;
private String message;
@Setter
private Function<VpnString, String> configStringGetter;
public VpnString(String key, String defaultMessage) {
this.key = key;
this.defaultMessage = defaultMessage;
}
@SneakyThrows
public void updateString() {
if(configStringGetter == null) throw new Exception("The configStringGetter for string " + key + " is null!");
message = configStringGetter.apply(this);
}
public String getFormattedMessage(Var<String, Object>... replacements) {
String formatted = configStringGetter.apply(this);
for (Var<String, Object> replacement : replacements) {
formatted = formatted
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
}
return formatted;
}
public void sendMessage(APIPlayer player, Var<String, Object>... replacements) {
String formatted = message;
for (Var<String, Object> replacement : replacements) {
formatted = formatted
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
}
player.sendMessage(formatted);
}
@Getter
@RequiredArgsConstructor
public static class Var<S, O> {
private final String key;
private final Object replacement;
}
}
@@ -1,14 +1,14 @@
package dev.brighten.antivpn.bukkit.util;
package dev.brighten.antivpn.utils;
import dev.brighten.antivpn.AntiVPN;
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;
private final AntiVPN plugin;
public A get() {
if(plugin.getConfig().get(path) != null)
@@ -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,21 @@
/*
* Copyright (C) 2016 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package dev.brighten.antivpn.utils;
/**
* Holder for extra methods of {@code Objects} only in web. Intended to be empty for regular
* version.
*/
abstract class ExtraObjectsMethodsForWeb {}
@@ -0,0 +1,48 @@
package dev.brighten.antivpn.utils;
import java.io.*;
import java.util.regex.Pattern;
public class MiscUtils {
private static final Pattern ipv4 = Pattern.compile("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
public static void close(Closeable... closeables) {
try {
for (Closeable closeable : closeables) if (closeable != null) closeable.close();
} catch (Exception e) {
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 void copy(InputStream in, File file) {
try {
OutputStream out = new FileOutputStream(file);
int lenght;
byte[] buf = new byte[1024];
while ((lenght = in.read(buf)) > 0)
{
out.write(buf, 0, lenght);
}
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean isIpv4(String ip)
{
return ipv4.matcher(ip).matches();
}
}
@@ -0,0 +1,36 @@
/*
* This file is part of helper, 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.utils;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface NonnullByDefault {
}
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2021 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package dev.brighten.antivpn.utils;
/** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */
final class NullnessCasts {
/**
* Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that
* that conversion is safe.
*
* <p>This method is intended to help with usages of type parameters that have {
* ParametricNullness parametric nullness}. If a type parameter instead ranges over only non-null
* types (or if the type is a non-variable type, like {@code String}), then code should almost
* never use this method, preferring instead to call {@code requireNonNull} so as to benefit from
* its runtime check.
*
* <p>An example use case for this method is in implementing an {@code Iterator<T>} whose {@code
* next} field is lazily initialized. The type of that field would be {@code @Nullable T}, and the
* code would be responsible for populating a "real" {@code T} (which might still be the value
* {@code null}!) before returning it to callers. Depending on how the code is structured, a
* nullness analysis might not understand that the field has been populated. To avoid that problem
* without having to add {@code @SuppressWarnings}, the code can call this method.
*
* <p>Why <i>not</i> just add {@code SuppressWarnings}? The problem is that this method is
* typically useful for {@code return} statements. That leaves the code with two options: Either
* add the suppression to the whole method (which turns off checking for a large section of code),
* or extract a variable, and put the suppression on that. However, a local variable typically
* doesn't work: Because nullness analyses typically infer the nullness of local variables,
* there's no way to assign a {@code @Nullable T} to a field {@code T foo;} and instruct the
* analysis that that means "plain {@code T}" rather than the inferred type {@code @Nullable T}.
* (Even if supported added {@code @NonNull}, that would not help, since the problem case
* addressed by this method is the case in which {@code T} has parametric nullness -- and thus its
* value may be legitimately {@code null}.)
*/
@SuppressWarnings("nullness")
static <T extends Object> T uncheckedCastNullableTToT(T t) {
return t;
}
private NullnessCasts() {}
}
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2007 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package dev.brighten.antivpn.utils;
import com.mongodb.lang.Nullable;
import java.util.Arrays;
/**
* Helper functions that can operate on any {@code Object}.
*
* <p>See the Guava User Guide on <a
* href="https://github.com/google/guava/wiki/CommonObjectUtilitiesExplained">writing {@code Object}
* methods with {@code Objects}</a>.
*
* @author Laurence Gonsalves
* @since 2.0
*/
public final class Objects extends ExtraObjectsMethodsForWeb {
private Objects() {}
/**
* Determines whether two possibly-null objects are equal. Returns:
*
* <ul>
* <li>{@code true} if {@code a} and {@code b} are both null.
* <li>{@code true} if {@code a} and {@code b} are both non-null and they are equal according to
* {@link Object#equals(Object)}.
* <li>{@code false} in all other situations.
* </ul>
*
* <p>This assumes that any non-null objects passed to this function conform to the {@code
* equals()} contract.
*
* <p><b>Note for Java 7 and later:</b> This method should be treated as deprecated; use {@link
* java.util.Objects#equals} instead.
*/
public static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
/**
* Generates a hash code for multiple values. The hash code is generated by calling {@link
* Arrays#hashCode(Object[])}. Note that array arguments to this method, with the exception of a
* single Object array, do not get any special handling; their hash codes are based on identity
* and not contents.
*
* <p>This is useful for implementing {@link Object#hashCode()}. For example, in an object that
* has three properties, {@code x}, {@code y}, and {@code z}, one could write:
*
* <pre>{@code
* public int hashCode() {
* return Objects.hashCode(getX(), getY(), getZ());
* }
* }</pre>
*
* <p><b>Warning:</b> When a single object is supplied, the returned hash code does not equal the
* hash code of that object.
*
* <p><b>Note for Java 7 and later:</b> This method should be treated as deprecated; use {@link
* java.util.Objects#hash} instead.
*/
public static int hashCode(@Nullable Object... objects) {
return Arrays.hashCode(objects);
}
}
@@ -0,0 +1,602 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package dev.brighten.antivpn.utils;
public final class Preconditions {
private Preconditions() {
}
public static void checkArgument(boolean expression) {
if (!expression) {
throw new IllegalArgumentException();
}
}
public static void checkArgument(boolean expression, Object errorMessage) {
if (!expression) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
}
}
public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
if (!expression) {
throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1, char p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1, int p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1, long p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1, Object p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1, char p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1, int p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1, long p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1, Object p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1, char p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1, int p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1, long p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1, Object p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, char p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, int p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, long p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, Object p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2, p3));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2, p3, p4));
}
}
public static void checkState(boolean expression) {
if (!expression) {
throw new IllegalStateException();
}
}
public static void checkState(boolean expression, Object errorMessage) {
if (!expression) {
throw new IllegalStateException(String.valueOf(errorMessage));
}
}
public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
if (!expression) {
throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1, char p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1, int p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1, long p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1, Object p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1, char p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1, int p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1, long p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1, Object p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1, char p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1, int p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1, long p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1, Object p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, char p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, int p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, long p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, Object p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2, p3));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2, p3, p4));
}
}
public static <T> T checkNotNull(T reference) {
if (reference == null) {
throw new NullPointerException();
} else {
return reference;
}
}
public static <T> T checkNotNull(T reference, Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
} else {
return reference;
}
}
public static <T> T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) {
if (reference == null) {
throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
} else {
return reference;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, char p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, int p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, long p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, Object p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, char p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, int p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, long p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, Object p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, char p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, int p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, long p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, Object p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, char p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, int p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, long p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3));
} else {
return obj;
}
}
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
if (obj == null) {
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3, p4));
} else {
return obj;
}
}
public static int checkElementIndex(int index, int size) {
return checkElementIndex(index, size, "index");
}
public static int checkElementIndex(int index, int size, String desc) {
if (index >= 0 && index < size) {
return index;
} else {
throw new IndexOutOfBoundsException(badElementIndex(index, size, desc));
}
}
private static String badElementIndex(int index, int size, String desc) {
if (index < 0) {
return format("%s (%s) must not be negative", desc, index);
} else if (size < 0) {
throw new IllegalArgumentException("negative size: " + size);
} else {
return format("%s (%s) must be less than size (%s)", desc, index, size);
}
}
public static int checkPositionIndex(int index, int size) {
return checkPositionIndex(index, size, "index");
}
public static int checkPositionIndex(int index, int size, String desc) {
if (index >= 0 && index <= size) {
return index;
} else {
throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc));
}
}
private static String badPositionIndex(int index, int size, String desc) {
if (index < 0) {
return format("%s (%s) must not be negative", desc, index);
} else if (size < 0) {
throw new IllegalArgumentException("negative size: " + size);
} else {
return format("%s (%s) must not be greater than size (%s)", desc, index, size);
}
}
public static void checkPositionIndexes(int start, int end, int size) {
if (start < 0 || end < start || end > size) {
throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
}
}
private static String badPositionIndexes(int start, int end, int size) {
if (start >= 0 && start <= size) {
return end >= 0 && end <= size ? format("end index (%s) must not be less than start index (%s)", end, start) : badPositionIndex(end, size, "end index");
} else {
return badPositionIndex(start, size, "start index");
}
}
static String format(String template, Object... args) {
template = String.valueOf(template);
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
int templateStart = 0;
int i;
int placeholderStart;
for(i = 0; i < args.length; templateStart = placeholderStart + 2) {
placeholderStart = template.indexOf("%s", templateStart);
if (placeholderStart == -1) {
break;
}
builder.append(template, templateStart, placeholderStart);
builder.append(args[i++]);
}
builder.append(template, templateStart, template.length());
if (i < args.length) {
builder.append(" [");
builder.append(args[i++]);
while(i < args.length) {
builder.append(", ");
builder.append(args[i++]);
}
builder.append(']');
}
return builder.toString();
}
}
@@ -0,0 +1,11 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package dev.brighten.antivpn.utils;
@FunctionalInterface
public interface Supplier<T> extends java.util.function.Supplier<T> {
T get();
}
@@ -0,0 +1,377 @@
/*
* Copyright (C) 2007 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package dev.brighten.antivpn.utils;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
import static dev.brighten.antivpn.utils.Preconditions.checkArgument;
import static dev.brighten.antivpn.utils.Preconditions.checkNotNull;
import static java.util.Objects.requireNonNull;
/**
* Useful suppliers.
*
* <p>All methods return serializable suppliers as long as they're given serializable parameters.
*
* @author Laurence Gonsalves
* @author Harry Heymann
* @since 2.0
*/
public final class Suppliers {
private Suppliers() {}
/**
* Returns a new supplier which is the composition of the provided function and supplier. In other
* words, the new supplier's value will be computed by retrieving the value from {@code supplier},
* and then applying {@code function} to that value. Note that the resulting supplier will not
* call {@code supplier} or invoke {@code function} until it is called.
*/
public static <F extends Object, T extends Object> Supplier<T> compose(
Function<? super F, T> function, Supplier<F> supplier) {
return new SupplierComposition<>(function, supplier);
}
private static class SupplierComposition<F extends Object, T extends Object>
implements Supplier<T>, Serializable {
final Function<? super F, T> function;
final Supplier<F> supplier;
SupplierComposition(Function<? super F, T> function, Supplier<F> supplier) {
this.function = checkNotNull(function);
this.supplier = checkNotNull(supplier);
}
@Override
public T get() {
return function.apply(supplier.get());
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SupplierComposition) {
SupplierComposition<?, ?> that = (SupplierComposition<?, ?>) obj;
return function.equals(that.function) && supplier.equals(that.supplier);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(function, supplier);
}
@Override
public String toString() {
return "Suppliers.compose(" + function + ", " + supplier + ")";
}
private static final long serialVersionUID = 0;
}
/**
* Returns a supplier which caches the instance retrieved during the first call to {@code get()}
* and returns that value on subsequent calls to {@code get()}. See: <a
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
*
* <p>The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at
* most once unless the underlying {@code get()} throws an exception. The supplier's serialized
* form does not contain the cached value, which will be recalculated when {@code get()} is called
* on the reserialized instance.
*
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
* delegating calls until it returns valid data.
*
* <p>If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
* returned directly.
*/
public static <T extends Object> Supplier<T> memoize(Supplier<T> delegate) {
if (delegate instanceof NonSerializableMemoizingSupplier
|| delegate instanceof MemoizingSupplier) {
return delegate;
}
return delegate instanceof Serializable
? new MemoizingSupplier<T>(delegate)
: new NonSerializableMemoizingSupplier<T>(delegate);
}
static class MemoizingSupplier<T extends Object> implements Supplier<T>, Serializable {
final Supplier<T> delegate;
transient volatile boolean initialized;
// "value" does not need to be volatile; visibility piggy-backs
// on volatile read of "initialized".
transient T value;
MemoizingSupplier(Supplier<T> delegate) {
this.delegate = checkNotNull(delegate);
}
@Override
public T get() {
// A 2-field variant of Double Checked Locking.
if (!initialized) {
synchronized (this) {
if (!initialized) {
T t = delegate.get();
value = t;
initialized = true;
return t;
}
}
}
// This is safe because we checked `initialized.`
return uncheckedCastNullableTToT(value);
}
@Override
public String toString() {
return "Suppliers.memoize("
+ (initialized ? "<supplier that returned " + value + ">" : delegate)
+ ")";
}
private static final long serialVersionUID = 0;
}
static class NonSerializableMemoizingSupplier<T extends Object> implements Supplier<T> {
volatile Supplier<T> delegate;
volatile boolean initialized;
// "value" does not need to be volatile; visibility piggy-backs
// on volatile read of "initialized".
T value;
NonSerializableMemoizingSupplier(Supplier<T> delegate) {
this.delegate = checkNotNull(delegate);
}
@Override
public T get() {
// A 2-field variant of Double Checked Locking.
if (!initialized) {
synchronized (this) {
if (!initialized) {
/*
* requireNonNull is safe because we read and write `delegate` under synchronization.
*
* TODO(cpovirk): To avoid having to check for null, replace `delegate` with a singleton
* `Supplier` that always throws an exception.
*/
T t = requireNonNull(delegate).get();
value = t;
initialized = true;
// Release the delegate to GC.
delegate = null;
return t;
}
}
}
// This is safe because we checked `initialized.`
return uncheckedCastNullableTToT(value);
}
@Override
public String toString() {
Supplier<T> delegate = this.delegate;
return "Suppliers.memoize("
+ (delegate == null ? "<supplier that returned " + value + ">" : delegate)
+ ")";
}
}
/**
* Returns a supplier that caches the instance supplied by the delegate and removes the cached
* value after the specified time has passed. Subsequent calls to {@code get()} return the cached
* value if the expiration time has not passed. After the expiration time, a new value is
* retrieved, cached, and returned. See: <a
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
*
* <p>The returned supplier is thread-safe. The supplier's serialized form does not contain the
* cached value, which will be recalculated when {@code get()} is called on the reserialized
* instance. The actual memoization does not happen when the underlying delegate throws an
* exception.
*
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
* delegating calls until it returns valid data.
*
* @param duration the length of time after a value is created that it should stop being returned
* by subsequent {@code get()} calls
* @param unit the unit that {@code duration} is expressed in
* @throws IllegalArgumentException if {@code duration} is not positive
* @since 2.0
*/
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static <T extends Object> Supplier<T> memoizeWithExpiration(
Supplier<T> delegate, long duration, TimeUnit unit) {
return new ExpiringMemoizingSupplier<>(delegate, duration, unit);
}
@SuppressWarnings("GoodTime") // lots of violations
static class ExpiringMemoizingSupplier<T extends Object>
implements Supplier<T>, Serializable {
final Supplier<T> delegate;
final long durationNanos;
transient volatile T value;
// The special value 0 means "not yet initialized".
transient volatile long expirationNanos;
ExpiringMemoizingSupplier(Supplier<T> delegate, long duration, TimeUnit unit) {
this.delegate = checkNotNull(delegate);
this.durationNanos = unit.toNanos(duration);
checkArgument(duration > 0, "duration (%s %s) must be > 0", duration, unit);
}
@Override
public T get() {
// Another variant of Double Checked Locking.
//
// We use two volatile reads. We could reduce this to one by
// putting our fields into a holder class, but (at least on x86)
// the extra memory consumption and indirection are more
// expensive than the extra volatile reads.
long nanos = expirationNanos;
long now = System.nanoTime();
if (nanos == 0 || now - nanos >= 0) {
synchronized (this) {
if (nanos == expirationNanos) { // recheck for lost race
T t = delegate.get();
value = t;
nanos = now + durationNanos;
// In the very unlikely event that nanos is 0, set it to 1;
// no one will notice 1 ns of tardiness.
expirationNanos = (nanos == 0) ? 1 : nanos;
return t;
}
}
}
// This is safe because we checked `expirationNanos.`
return uncheckedCastNullableTToT(value);
}
@Override
public String toString() {
// This is a little strange if the unit the user provided was not NANOS,
// but we don't want to store the unit just for toString
return "Suppliers.memoizeWithExpiration(" + delegate + ", " + durationNanos + ", NANOS)";
}
private static final long serialVersionUID = 0;
}
/** Returns a supplier that always supplies {@code instance}. */
public static <T extends Object> Supplier<T> ofInstance(
T instance) {
return new SupplierOfInstance<>(instance);
}
private static class SupplierOfInstance<T extends Object>
implements Supplier<T>, Serializable {
final T instance;
SupplierOfInstance(T instance) {
this.instance = instance;
}
@Override
public T get() {
return instance;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SupplierOfInstance) {
SupplierOfInstance<?> that = (SupplierOfInstance<?>) obj;
return Objects.equal(instance, that.instance);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(instance);
}
@Override
public String toString() {
return "Suppliers.ofInstance(" + instance + ")";
}
private static final long serialVersionUID = 0;
}
/**
* Returns a supplier whose {@code get()} method synchronizes on {@code delegate} before calling
* it, making it thread-safe.
*/
public static <T extends Object> Supplier<T> synchronizedSupplier(
Supplier<T> delegate) {
return new ThreadSafeSupplier<>(delegate);
}
private static class ThreadSafeSupplier<T extends Object>
implements Supplier<T>, Serializable {
final Supplier<T> delegate;
ThreadSafeSupplier(Supplier<T> delegate) {
this.delegate = checkNotNull(delegate);
}
@Override
public T get() {
synchronized (delegate) {
return delegate.get();
}
}
@Override
public String toString() {
return "Suppliers.synchronizedSupplier(" + delegate + ")";
}
private static final long serialVersionUID = 0;
}
/**
* Returns a function that accepts a supplier and returns the result of invoking {@link
* Supplier#get} on that supplier.
*
* <p><b>Java 8 users:</b> use the method reference {@code Supplier::get} instead.
*
* @since 8.0
*/
public static <T extends Object> Function<Supplier<T>, T> supplierFunction() {
@SuppressWarnings("unchecked") // implementation is "fully variant"
SupplierFunction<T> sf = (SupplierFunction<T>) SupplierFunctionImpl.INSTANCE;
return sf;
}
private interface SupplierFunction<T extends Object> extends Function<Supplier<T>, T> {}
private enum SupplierFunctionImpl implements SupplierFunction<Object> {
INSTANCE;
// Note: This makes T a "pass-through type"
@Override
public Object apply(Supplier<Object> input) {
return input.get();
}
@Override
public String toString() {
return "Suppliers.supplierFunction()";
}
}
}
@@ -0,0 +1,507 @@
package dev.brighten.antivpn.utils.config;
import java.util.*;
public final class Configuration
{
private static final char SEPARATOR = '.';
final Map<String, Object> self;
final Map<String, List<String>> comments;
private final Configuration defaults;
public Configuration()
{
this( null );
}
public Configuration(Configuration defaults)
{
this( new LinkedHashMap<String, Object>(), defaults );
}
Configuration(Map<?, ?> map, Configuration defaults)
{
this.self = new LinkedHashMap<>();
this.defaults = defaults;
comments = new HashMap<>();
for ( Map.Entry<?, ?> entry : map.entrySet() )
{
String key = ( entry.getKey() == null ) ? "null" : entry.getKey().toString();
if ( entry.getValue() instanceof Map )
{
this.self.put( key, new Configuration( (Map) entry.getValue(), ( defaults == null ) ? null : defaults.getSection( key ) ) );
} else
{
this.self.put( key, entry.getValue() );
}
}
}
public void loadFromString(String contents) {
List<String> list = new ArrayList<>();
Collections.addAll(list, contents.split("\n"));
int currentLayer = 0;
String currentPath = "";
int lineNumber = 0;
for(Iterator<String> iterator = list.iterator(); iterator.hasNext(); lineNumber++) {
String line = iterator.next();
String trimmed = line.trim();
if(trimmed.startsWith("#") || trimmed.isEmpty()) {
addCommentLine(currentPath, line);
continue;
}
if(!line.isEmpty()) {
if(line.contains(":")) {
int layerFromLine = getLayerFromLine(line, lineNumber);
if(layerFromLine < currentLayer) {
currentPath = regressPathBy(currentLayer - layerFromLine, currentPath);
}
String key = getKeyFromLine(line);
if(currentLayer == 0) {
currentPath = key;
}
else {
currentPath += "." + key;
}
}
}
}
}
private void addCommentLine(String currentPath, String line) {
List<String> list = comments.get(currentPath);
if(list == null) {
list = new ArrayList<>();
}
list.add(line);
comments.put(currentPath, list);
}
String getKeyFromLine(String line) {
String key = null;
for(int i = 0; i < line.length(); i++) {
if(line.charAt(i) == ':') {
key = line.substring(0, i);
break;
}
}
return key == null ? null : key.trim();
}
String regressPathBy(int i, String currentPath) {
if(i <= 0) {
return currentPath;
}
String[] split = currentPath.split("\\.");
String rebuild = "";
for(int j = 0; j < split.length - i; j++) {
rebuild += split[j];
if(j <= (split.length - j)) {
rebuild += ".";
}
}
return rebuild;
}
int getLayerFromLine(String line, int lineNumber) {
double d = 0;
for(int i = 0; i < line.length(); i++) {
if(line.charAt(i) == ' ') {
d += 0.5;
}
else {
break;
}
}
return (int) d;
}
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,49 @@
package dev.brighten.antivpn.utils.config;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public abstract class ConfigurationProvider
{
public static final Map<Class<? extends ConfigurationProvider>, ConfigurationProvider> providers = new HashMap<>();
static
{
try
{
providers.put( YamlConfiguration.class, new YamlConfiguration() );
} catch ( NoClassDefFoundError ex )
{
ex.printStackTrace();
// Ignore, no SnakeYAML
}
}
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,181 @@
package dev.brighten.antivpn.utils.config;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.representer.Representer;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class YamlConfiguration extends ConfigurationProvider
{
private final ThreadLocal<Yaml> yaml = new ThreadLocal<Yaml>()
{
@Override
protected Yaml initialValue()
{
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK );
Representer representer = new Representer(options)
{
{
representers.put( Configuration.class, data -> represent( ( (Configuration) data ).self ));
}
};
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
return new Yaml( new Constructor(new LoaderOptions()), representer, options );
}
};
@Override
public void save(Configuration config, File file) throws IOException
{
try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), StandardCharsets.UTF_8 ) )
{
save( config, writer );
}
}
@Override
public void save(Configuration config, Writer writer)
{
String contents = this.yaml.get().dump(config.self);
if (contents.equals("{}\n")) {
contents = "";
}
List<String> list = new ArrayList<>();
Collections.addAll(list, contents.split("\n"));
int currentLayer = 0;
StringBuilder currentPath = new StringBuilder();
StringBuilder sb = new StringBuilder();
int lineNumber = 0;
for(Iterator<String> iterator = list.iterator(); iterator.hasNext(); lineNumber++) {
String line = iterator.next();
sb.append(line);
sb.append('\n');
if (!line.isEmpty()) {
if (line.contains(":")) {
int layerFromLine = config.getLayerFromLine(line, lineNumber);
if (layerFromLine < currentLayer) {
currentPath = new StringBuilder(config.regressPathBy(currentLayer - layerFromLine, currentPath.toString()));
}
String key = config.getKeyFromLine(line);
if (currentLayer == 0) {
currentPath = new StringBuilder(key);
} else {
currentPath.append("." + key);
}
String path = currentPath.toString();
if (config.comments.containsKey(path)) {
config.comments.get(path).forEach(string -> {
sb.append(string);
sb.append('\n');
});
}
}
}
}
try {
writer.write(sb.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public Configuration load(File file) throws IOException
{
return load( file, null );
}
@Override
public Configuration load(File file, Configuration defaults) throws IOException
{
try ( FileInputStream is = new FileInputStream( file ) )
{
return load( is, defaults );
}
}
@Override
public Configuration load(Reader reader)
{
return load( reader, null );
}
@SneakyThrows
@Override
public Configuration load(Reader reader, Configuration defaults)
{
BufferedReader input = reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(reader);
StringBuilder builder = new StringBuilder();
String line;
try {
while((line = input.readLine()) != null) {
builder.append(line);
builder.append('\n');
}
} finally {
input.close();
}
return load(builder.toString(), defaults);
}
@Override
public Configuration load(InputStream is)
{
return this.load(new InputStreamReader(is, Charset.defaultCharset()));
}
@Override
public Configuration load(InputStream is, Configuration defaults)
{
return this.load(new InputStreamReader(is, Charset.defaultCharset()), defaults);
}
@Override
public Configuration load(String string)
{
return load( string, null );
}
@Override
@SuppressWarnings("unchecked")
public Configuration load(String contents, Configuration defaults)
{
Map<String, Object> map;
LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setMaxAliasesForCollections(2147483647);
map = this.yaml.get().loadAs(contents, LinkedHashMap.class);
Configuration config = new Configuration( map, defaults );
config.loadFromString(contents);
return config;
}
}
@@ -0,0 +1,59 @@
package dev.brighten.antivpn.web;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.utils.json.JSONObject;
import dev.brighten.antivpn.utils.json.JsonReader;
import dev.brighten.antivpn.web.objects.QueryResponse;
import dev.brighten.antivpn.web.objects.VPNResponse;
import java.io.IOException;
public class FunkemunkyAPI {
/**
*
* Queries https://funkemunky.cc/vpn API and returns information on the IP
*
* @param ip String
* @param license String
* @param cachedResults boolean
* @return VPNResponse
* @throws JSONException Throws when JSON response is not formatted properly.
* @throws IOException Throws when there is an error connecting to and processing information from API.
*/
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);
}
/**
* Feeds into {@link FunkemunkyAPI#getQueryResponse(String)} using "none" as argument
* to grab query information based on the connecting IP address.
*
* @return QueryResponse
* @throws JSONException Throws when JSON response is not formatted properly.
* @throws IOException Throws when there is an error connecting to and processing information from API.
*/
public static QueryResponse getQueryResponse() throws JSONException, IOException {
return getQueryResponse("none");
}
/**
* Queries https://funkemunky.cc/vpn/queryCheck and returns information based on the
* provided licence input.
*
* @param license String
* @return QueryResponse
* @throws JSONException Throws when JSON response is not formatted properly.
* @throws IOException Throws when there is an error connecting to and processing information from API.
*/
public static QueryResponse getQueryResponse(String license) throws JSONException, IOException {
JSONObject result = JsonReader.readJsonFromUrl("https://funkemunky.cc/vpn/queryCheck?license=" + license);
return QueryResponse.fromJson(result);
}
}
@@ -0,0 +1,51 @@
package dev.brighten.antivpn.web.objects;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.utils.json.JSONObject;
import lombok.Builder;
import lombok.Data;
/**
* Used to format the JSON response from https://funkemunky.cc/vpn/queryCheck into an object for project use.
*/
@Data
@Builder(toBuilder = true)
public class QueryResponse {
private boolean validPlan;
private String planType;
private long queries;
private long queriesMax;
/**
* Takes a JSON String and feeds it into {@link QueryResponse#fromJson(JSONObject)}
*
* @param jsonString String (formatted in JSON)
* @return QueryResponse
* @throws JSONException Throws when JSON is not formatted properly.
*/
public static QueryResponse fromJson(String jsonString) throws JSONException {
return fromJson(new JSONObject(jsonString));
}
/**
* Formats response from https://funkemunky.cc/vpn/queryCheck into {@link QueryResponse} for project use.
*
* @param object JSONObject
* @return QueryResponse
* @throws JSONException Throws when JSON is not formatted properly.
*/
public static QueryResponse fromJson(JSONObject object) throws JSONException {
boolean validPlan = object.getBoolean("validPlan");
if(!validPlan) { // Nothing else will be returned from API if validPlan is false.
return QueryResponse.builder().validPlan(false).build();
}
return QueryResponse.builder().validPlan(object.getBoolean("validPlan"))
.planType(object.getString("planType"))
.queries(object.getLong("queries"))
.queriesMax(object.getLong("queryLimit")).build();
}
}
@@ -1,18 +1,16 @@
package dev.brighten.antivpn.utils;
package dev.brighten.antivpn.web.objects;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.utils.json.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.Builder;
import lombok.Data;
@Getter
@Setter
@Data
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class VPNResponse {
private String asn, ip, countryName, countryCode, city, timeZone, method, isp;
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;
@@ -31,38 +29,48 @@ public class VPNResponse {
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;
}
/**
* Feeds into {@link VPNResponse#fromJson(JSONObject)} formatting the JSON {@link String} into
* a {@link JSONObject}
*
* @param json String
* @return VPNResponse
*/
public static VPNResponse fromJson(String json) throws JSONException {
JSONObject jsonObject = new JSONObject(json);
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"), jsonObject.getBoolean("proxy"),
jsonObject.getBoolean("cached"), jsonObject.getBoolean("success"),
jsonObject.getDouble("latitude"), jsonObject.getDouble("longitude"),
jsonObject.getLong("lastAccess"), jsonObject.getInt("queriesLeft"));
return fromJson(new JSONObject(json));
}
public static final VPNResponse FAILED_RESPONSE = VPNResponse.builder()
.success(false)
.failureReason("Internal plugin API error.")
.build();
/**
* Formats response from <a href="https://funkemunky.cc/vpn">...</a> into {@link VPNResponse} for project use.
*
* @param jsonObject JSONObject
* @return VPNResponse
* @throws JSONException Throws when JSON is not formatted properly.
*/
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"), jsonObject.getBoolean("proxy"),
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 {
return VPNResponse.builder().success(false)
.failureReason(jsonObject.getString("failureReason")).build();
}
return new VPNResponse(false);
}
}
+76
View File
@@ -0,0 +1,76 @@
#####################################################
# KauriVPN Config #
# by Brighten Development #
#####################################################
# If you find that you run out of your free 20,000 queries, you may purchase a license on https://funkemunky.cc/shop
license: ''
# Message only sent with commands disabled below. Supports color codes ('&')
kickMessage: Proxies are not allowed on our server
# Caching results will lower your query usage, but results may be out of date.
cachedResults: true
# All players with any of the following characters in the beginning of their name will be whitelisted
# This is a useful feature for servers that allow players through Geyser and their IPs are not forwarded, causing
# players to be removed falsely for use of proxy.
prefixWhitelists: []
# Configure your database here.
database:
# Enable to cache queries and save alerts state beyond restarts
enabled: true
useCredentials: false
#Options Mongo, MySQL, or H2
type: H2
# The database name you would like to use
database: kaurivpn
# Can be used if you prefer to authenticate via Mongo URL
mongoURL: ''
# Your database username
username: root
# Your database password
password: password
# The IP of your database goes here
ip: localhost
# -1 will use default port of databases (MySQL:3306, Mongo:27017). Otherwise, enter alternative ports here.
port: -1
commands:
# Enable this to override the default kick function of the plugin with your own commands
enabled: false
# List of commands to run when a player is detected to be using a proxy. Supports color codes ('&')
execute:
- kick %player% VPNs are not allowed on our server!
# Enable/disable the default kicking feature of KauriVPN.
kickPlayers: true
# Configure all alerting functionality
alerts:
# You may set to 'false' to disable all alerts functionality
enabled: true
# Message to send to users with alerts enabled
# Placeholders: %country% (Country name), %player% (Player name), %reason% (Proxy detection method),
# %city% (City name).
message: '&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy &8(&f%reason%&8)
&7in location &8(&f%city%&7, &f%country%&8)'
# Configuration for country gatekeepings
countries:
# You must use ISO codes for country configuration: https://www.iban.com/country-code
# Leave empty to disable this configuration
list: []
# Set whitelist to true to only allow listed country codes, and false to deny listed country codes.
whitelist: true
# The commands to be run if the player is not allowed on the server with the above configured conditions
# Placeholders: %country% (Country name), %player% (Player name), %code% (Country ISO Code)
# Keep this empty with "[]" if you want to use the built in kicking system.
commands: []
# The kick message that will be used if commands are configured to use the built-in kicking sytem.
# PlaceHolders: %country% (Country name), %player% (Player name), %code% (Country ISO Code)
vanillaKickReason: |-
&cSorry, but our server does not allow connections from
&f%country%
# This will disable any information being sent to https://bstats.org. We recommend you keep this enabled as it helps
# us understand our users and put effort where it is needed. All information sent goes under their privacy as seen
# here: https://bstats.org/privacy-policy
bstats: true
# Here you can configure messages for KauriVPN.
messages:
command-misc-playerRequired: '&cYou must be a player to execute this command!'
command-alerts-toggled: '&7Your player proxy notifications have been set to: &e%state%'
command-reload-complete: '&aSuccessfully reloaded KauriVPN plugin!'
no-permission: '&cNo permission.'
+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.
+10
View File
@@ -0,0 +1,10 @@
[![Project Map](https://sourcespy.com/shield.svg)](https://sourcespy.com/github/funkemunkyantivpn/)
# 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/
+41
View File
@@ -0,0 +1,41 @@
<?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.9.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Sponge</artifactId>
<repositories>
<repository>
<id>sponge</id>
<url>https://repo.spongepowered.org/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>8.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.9.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
@@ -0,0 +1,31 @@
package dev.brighten.antivpn.sponge;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.sponge.util.StringUtil;
import net.kyori.adventure.text.Component;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
public class SpongePlayer extends APIPlayer {
private final ServerPlayer player;
public SpongePlayer(ServerPlayer player) {
super(player.uniqueId(), player.name(), player.connection().address().getAddress());
this.player = player;
}
@Override
public void sendMessage(String message) {
//player.sendMessage(StringUtil.translateColorCodes('&', message));
}
@Override
public void kickPlayer(String reason) {
player.kick(Component.text(StringUtil.translateColorCodes('&', reason)));
}
@Override
public boolean hasPermission(String permission) {
return player.hasPermission(permission);
}
}
@@ -0,0 +1,31 @@
package dev.brighten.antivpn.sponge;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.PlayerExecutor;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class SpongePlayerExecutor implements PlayerExecutor {
@Override
public Optional<APIPlayer> getPlayer(String name) {
return Optional.empty();
}
@Override
public Optional<APIPlayer> getPlayer(UUID uuid) {
return Optional.empty();
}
@Override
public void unloadPlayer(UUID uuid) {
}
@Override
public List<APIPlayer> getOnlinePlayers() {
return null;
}
}
@@ -0,0 +1,28 @@
package dev.brighten.antivpn.sponge;
import com.google.inject.Inject;
import org.spongepowered.api.Server;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.lifecycle.StartedEngineEvent;
import org.spongepowered.plugin.builtin.jvm.Plugin;
import java.util.logging.Logger;
@Plugin("kaurivpn")
public class SpongePlugin {
public static SpongePlugin INSTANCE;
//Plugin init
@Inject
private Logger logger;
@Listener
public void onServerStart(final StartedEngineEvent<Server> event) {
INSTANCE = this;
logger.info("Starting AntiVPN services...");
//Start AntiVPN
}
}
@@ -0,0 +1,17 @@
package dev.brighten.antivpn.sponge.util;
public class StringUtil {
public static String translateColorCodes(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);
}
}
+94
View File
@@ -0,0 +1,94 @@
<?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.9.2</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.9.2</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>
<relocation>
<pattern>org.yaml.snakeyaml</pattern>
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
@@ -0,0 +1,174 @@
package dev.brighten.antivpn.velocity;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.event.connection.DisconnectEvent;
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 dev.brighten.antivpn.web.objects.VPNResponse;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class VelocityListener extends VPNExecutor {
private ScheduledTask cacheResetTask;
private final Cache<UUID, VPNResponse> responseCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(2000)
.build();
@Override
public void registerListeners() {
VelocityPlugin.INSTANCE.getServer().getEventManager()
.register(VelocityPlugin.INSTANCE, this);
VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, DisconnectEvent.class,
event -> AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId()));
VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class,
event -> {
if (event.getResult().isAllowed()) {
if (event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
//Is exempt
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())
//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().getVpnConfig().getPrefixWhitelists().stream()
.anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return;
if(responseCache.asMap().containsKey(event.getPlayer().getUniqueId())) {
VPNResponse cached = responseCache.getIfPresent(event.getPlayer().getUniqueId());
if (cached != null && cached.isProxy()) {
event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder()
.character('&')
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
.getKickString())));
return;
}
}
checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(),
AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> {
if (result.isSuccess()) {
// If the countryList() size is zero, no need to check.
// Running country check first
if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty()
&& !(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()))
// This bit of code will decide whether or not to kick the player
// If it contains the code and it is set to whitelist, it will not kick
// as they are equal and vise versa. However, if the contains does not match
// the state, it will kick.
&& AntiVPN.getInstance().getVpnConfig().countryList()
.contains(result.getCountryCode())
!= AntiVPN.getInstance().getVpnConfig().whitelistCountries()) {
//Using our built in kicking system if no commands are configured
if (AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) {
final String kickReason = AntiVPN.getInstance().getVpnConfig()
.countryVanillaKickReason();
// Kicking our player
event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
.build().deserialize(kickReason
.replace("%player%", event.getPlayer().getUsername())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode())));
} else {
for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
final String formattedCommand = StringUtils
.translateAlternateColorCodes('&',
cmd.replace("%player%",
event.getPlayer().getUsername())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode()));
// Running the command from console
VelocityPlugin.INSTANCE.getServer().getCommandManager()
.executeAsync(VelocityPlugin.INSTANCE.getServer()
.getConsoleCommandSource(),
StringUtils.translateAlternateColorCodes('&',
formattedCommand));
}
}
} else if (result.isProxy()) {
if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect())
event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
.getKickString()));
VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
//Ensuring the user wishes to alert to staff
if (AntiVPN.getInstance().getVpnConfig().alertToStaff())
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.filter(APIPlayer::isAlertsEnabled)
.forEach(pl ->
pl.sendMessage(AntiVPN.getInstance().getVpnConfig()
.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().getVpnConfig().runCommands()) {
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
VelocityPlugin.INSTANCE.getServer().getCommandManager()
.executeAsync(VelocityPlugin.INSTANCE.getServer()
.getConsoleCommandSource(),
StringUtils.translateAlternateColorCodes('&',
command.replace("%player%",
event.getPlayer().getUsername())));
}
}
AntiVPN.getInstance().detections++;
}
} else {
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 shutdown() {
if (cacheResetTask != null) {
cacheResetTask.cancel();
cacheResetTask = null;
}
threadExecutor.shutdown();
VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this);
}
@Override
public void log(Level level, String log, Object... objects) {
VelocityPlugin.INSTANCE.getLogger().log(level, String.format(log, objects));
}
@Override
public void log(String log, Object... objects) {
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,42 @@
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<UUID, VelocityPlayer> cachedPlayers = new HashMap<>();
@Override
public Optional<APIPlayer> getPlayer(String name) {
Optional<Player> player = VelocityPlugin.INSTANCE.getServer().getPlayer(name);
return player.map(value -> cachedPlayers.computeIfAbsent(value.getUniqueId(),
key -> new VelocityPlayer(value)));
}
@Override
public Optional<APIPlayer> getPlayer(UUID uuid) {
Optional<Player> player = VelocityPlugin.INSTANCE.getServer().getPlayer(uuid);
return player.map(value -> cachedPlayers.computeIfAbsent(value.getUniqueId(),
key -> new VelocityPlayer(value)));
}
@Override
public void unloadPlayer(UUID uuid) {
cachedPlayers.remove(uuid);
}
@Override
public List<APIPlayer> getOnlinePlayers() {
return VelocityPlugin.INSTANCE.getServer().getAllPlayers().stream()
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new VelocityPlayer(pl)))
.collect(Collectors.toList());
}
}
@@ -0,0 +1,57 @@
package dev.brighten.antivpn.velocity;
import com.google.inject.Inject;
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.command.VelocityCommand;
import lombok.Getter;
import org.bstats.velocity.Metrics;
import java.nio.file.Path;
import java.util.logging.Logger;
@Getter
@Plugin(id = "kaurivpn", name = "KauriVPN", version = "1.7.1", authors = {"funkemunky"})
public class VelocityPlugin {
private final ProxyServer server;
private final Logger logger;
private final Metrics.Factory metricsFactory;
private final Path configDir;
public static VelocityPlugin INSTANCE;
@Inject
public VelocityPlugin(ProxyServer server, Logger logger, @DataDirectory Path path, Metrics.Factory metricsFactory) {
this.server = server;
this.logger = logger;
this.configDir = path;
this.metricsFactory = metricsFactory;
}
@Subscribe
public void onInit(ProxyInitializeEvent event) {
INSTANCE = this;
logger.info("Loading config...");
//Loading plugin
logger.info("Starting AntiVPN services...");
AntiVPN.start(new VelocityListener(), new VelocityPlayerExecutor(), configDir.toFile());
if(AntiVPN.getInstance().getVpnConfig().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(), new VelocityCommand(command));
}
}
}
@@ -0,0 +1,98 @@
package dev.brighten.antivpn.velocity.command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.command.Command;
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 java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class VelocityCommand implements SimpleCommand {
private final Command command;
public VelocityCommand(Command command) {
this.command = command;
}
@Override
public void execute(Invocation invocation) {
CommandSource sender = invocation.source();
if(!invocation.source().hasPermission("antivpn.command.*")
&& !invocation.source().hasPermission(command.permission())) {
invocation.source().sendMessage(LegacyComponentSerializer.builder().character('&')
.build().deserialize(AntiVPN.getInstance().getMessageHandler()
.getString("no-permission").getMessage()));
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(LegacyComponentSerializer.builder().character('&')
.build().deserialize(AntiVPN.getInstance().getMessageHandler()
.getString("no-permission").getMessage()));
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)));
}
@Override
public List<String> suggest(Invocation invocation) {
final CommandSource sender = invocation.source();
final String[] args = invocation.arguments();
val children = command.children();
if(children.length > 0 && args.length > 0) {
for (dev.brighten.antivpn.command.Command child : children) {
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
return child.tabComplete(new VelocityCommandExecutor(sender), "alias", IntStream
.range(0, args.length - 1)
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
}
}
}else if (children.length > 0){ // && args.length == 0 is always true here
return Arrays.stream(children).map(Command::name).collect(Collectors.toList());
}
return command.tabComplete(new VelocityCommandExecutor(sender), "alias", args);
}
@Override
public CompletableFuture<List<String>> suggestAsync(Invocation invocation) {
return CompletableFuture.supplyAsync(() -> this.suggest(invocation));
}
@Override
public boolean hasPermission(Invocation invocation) {
return SimpleCommand.super.hasPermission(invocation);
}
}
@@ -0,0 +1,40 @@
package dev.brighten.antivpn.velocity.command;
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, Object... objects) {
sender.sendMessage(LegacyComponentSerializer.builder().character('&').build()
.deserialize(String.format(message, objects)));
}
@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,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"}
+11 -1
View File
@@ -7,13 +7,15 @@
<groupId>dev.brighten.antivpn</groupId>
<artifactId>AntiVPN</artifactId>
<packaging>pom</packaging>
<version>1.1.1</version>
<version>1.9.2</version>
<modules>
<module>Common</module>
<module>Bungee</module>
<module>Bukkit</module>
<module>Assembly</module>
<module>Velocity</module>
<module>Sponge</module>
</modules>
<properties>
@@ -31,6 +33,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>