diff --git a/Assembly/dependency-reduced-pom.xml b/Assembly/dependency-reduced-pom.xml index 14a0aeb..df32d86 100644 --- a/Assembly/dependency-reduced-pom.xml +++ b/Assembly/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ AntiVPN dev.brighten.antivpn - 1.6.1 + 1.7 4.0.0 Assembly diff --git a/Assembly/pom.xml b/Assembly/pom.xml index 3a9ef52..3894153 100644 --- a/Assembly/pom.xml +++ b/Assembly/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.6.1 + 1.7 4.0.0 diff --git a/Bukkit/dependency-reduced-pom.xml b/Bukkit/dependency-reduced-pom.xml index 0f4c4bb..94d1665 100644 --- a/Bukkit/dependency-reduced-pom.xml +++ b/Bukkit/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ AntiVPN dev.brighten.antivpn - 1.6.1 + 1.7 4.0.0 Bukkit @@ -56,7 +56,7 @@ dev.brighten.antivpn Common - 1.6.1 + 1.7 provided @@ -71,6 +71,10 @@ h2 com.h2database + + snakeyaml + org.yaml + mongo-java-driver org.mongodb diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index 76a044d..df0dca9 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.6.1 + 1.7 4.0.0 @@ -69,7 +69,7 @@ dev.brighten.antivpn Common - 1.6.1 + 1.7 provided diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitConfig.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitConfig.java index 03e010d..d4daec3 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitConfig.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitConfig.java @@ -23,6 +23,8 @@ public class BukkitConfig implements VPNConfig { "database.username", BukkitPlugin.pluginInstance), defaultPassword = new ConfigDefault<>("password", "database.password", BukkitPlugin.pluginInstance), + + defaultIp = new ConfigDefault<>("localhost", "database.ip", BukkitPlugin.pluginInstance), defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" + " &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message", @@ -43,10 +45,14 @@ public class BukkitConfig implements VPNConfig { private final ConfigDefault> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(), "prefixWhitelists", BukkitPlugin.pluginInstance), defaultCommands = new ConfigDefault<>( Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute", + BukkitPlugin.pluginInstance), + defBlockedCountries = new ConfigDefault<>(new ArrayList<>(), "blockedCountries", + BukkitPlugin.pluginInstance), + defAllowedCountries = new ConfigDefault<>(new ArrayList<>(), "allowedCountries", BukkitPlugin.pluginInstance); private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg; - private List prefixWhitelists, commands; + private List prefixWhitelists, commands, allowedCountries, blockedCountries; private int port; private boolean cacheResults, databaseEnabled, useCredentials, commandsEnabled, kickPlayers, alertToStaff, metrics; @@ -135,6 +141,16 @@ public class BukkitConfig implements VPNConfig { return ip; } + @Override + public List allowedCountries() { + return allowedCountries; + } + + @Override + public List blockedCountries() { + return blockedCountries; + } + @Override public int getPort() { if(port == -1) { @@ -177,5 +193,7 @@ public class BukkitConfig implements VPNConfig { alertToStaff = defaultAlertToStaff.get(); alertMsg = defaultAlertMsg.get(); metrics = defaultMetrics.get(); + blockedCountries = defBlockedCountries.get(); + allowedCountries = defAllowedCountries.get(); } } diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java index b0da53a..8282a13 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java @@ -3,6 +3,8 @@ package dev.brighten.antivpn.bukkit; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.bukkit.util.ConfigDefault; import dev.brighten.antivpn.command.Command; +import dev.brighten.antivpn.utils.MiscUtils; +import dev.brighten.antivpn.utils.config.Configuration; import lombok.val; import net.md_5.bungee.api.ChatColor; import org.bstats.bukkit.Metrics; @@ -16,6 +18,7 @@ import org.bukkit.plugin.SimplePluginManager; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; +import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; @@ -35,7 +38,12 @@ public class BukkitPlugin extends JavaPlugin { //Loading config Bukkit.getLogger().info("Loading config..."); - saveDefaultConfig(); + Configuration config = new Configuration(); + File configFile = new File(getDataFolder(), "config.yml"); + if(!configFile.exists()){ + configFile.getParentFile().mkdirs(); + MiscUtils.copy(getResource( "config.yml"), configFile); + } Bukkit.getLogger().info("Starting AntiVPN services..."); AntiVPN.start(new BukkitConfig(), new BukkitListener(), new BukkitPlayerExecutor(), getDataFolder()); diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/util/ConfigDefault.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/util/ConfigDefault.java index 8fd0d8f..d5c2477 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/util/ConfigDefault.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/util/ConfigDefault.java @@ -1,8 +1,12 @@ package dev.brighten.antivpn.bukkit.util; +import dev.brighten.antivpn.utils.MiscUtils; +import dev.brighten.antivpn.utils.config.Configuration; import lombok.AllArgsConstructor; import org.bukkit.plugin.Plugin; +import java.io.File; + @AllArgsConstructor public class ConfigDefault { diff --git a/Bukkit/src/main/resources/config.yml b/Bukkit/src/main/resources/config.yml new file mode 100644 index 0000000..2f61a3f --- /dev/null +++ b/Bukkit/src/main/resources/config.yml @@ -0,0 +1,46 @@ +##################################################### +# 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: + enabled: false # Enable to cache queries and save alerts state beyond restarts + useCredentials: true + type: MySQL #Options Mongo and MySQL + database: kaurivpn # The database name you would like to use + mongoURL: '' # Can be used if you prefer to authenticate via Mongo URL + username: root # Your database username + password: password # Your database password + ip: localhost #The IP of your database goes here + port: -1 #-1 will use default port of databases (MySQL:3306, Mongo:27017). Otherwise, enter alternative ports here. +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)' +# 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 diff --git a/Bungee/dependency-reduced-pom.xml b/Bungee/dependency-reduced-pom.xml index 7a673e6..4d9ac21 100644 --- a/Bungee/dependency-reduced-pom.xml +++ b/Bungee/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ AntiVPN dev.brighten.antivpn - 1.6.1 + 1.7 4.0.0 Bungee @@ -50,7 +50,7 @@ dev.brighten.antivpn Common - 1.6.1 + 1.7 provided @@ -65,6 +65,10 @@ h2 com.h2database + + snakeyaml + org.yaml + mongo-java-driver org.mongodb diff --git a/Bungee/pom.xml b/Bungee/pom.xml index dd4a350..baa19f5 100644 --- a/Bungee/pom.xml +++ b/Bungee/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.6.1 + 1.7 4.0.0 @@ -63,7 +63,7 @@ dev.brighten.antivpn Common - 1.6.1 + 1.7 provided diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeConfig.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeConfig.java index 2d3f0a5..9e0598a 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeConfig.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeConfig.java @@ -41,10 +41,14 @@ public class BungeeConfig implements VPNConfig { private final ConfigDefault> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(), "prefixWhitelists", BungeePlugin.pluginInstance), defaultCommands = new ConfigDefault<>( Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute", - BungeePlugin.pluginInstance); + BungeePlugin.pluginInstance), + defBlockedCountries = new ConfigDefault<>(new ArrayList<>(), "blockedCountries", + BungeePlugin.pluginInstance), + defAllowedCountries = new ConfigDefault<>(new ArrayList<>(), "allowedCountries", + BungeePlugin.pluginInstance); private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg; - private List prefixWhitelists, commands; + private List prefixWhitelists, commands, allowedCountries, blockedCountries; private int port; private boolean cacheResults, useCredentials, databaseEnabled, commandsEnabled, kickPlayers, alertToStaff, metrics; @@ -133,6 +137,16 @@ public class BungeeConfig implements VPNConfig { return ip; } + @Override + public List allowedCountries() { + return allowedCountries; + } + + @Override + public List blockedCountries() { + return blockedCountries; + } + @Override public int getPort() { if(port == -1) { @@ -175,5 +189,7 @@ public class BungeeConfig implements VPNConfig { alertToStaff = defaultAlertToStaff.get(); alertMsg = defaultAlertMsg.get(); metrics = defaultMetrics.get(); + blockedCountries = defBlockedCountries.get(); + allowedCountries = defAllowedCountries.get(); } } diff --git a/Common/pom.xml b/Common/pom.xml index c543a95..8bef495 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.6.1 + 1.7 4.0.0 @@ -64,6 +64,12 @@ 2.1.210 compile + + org.yaml + snakeyaml + 1.15 + compile + org.mongodb mongo-java-driver diff --git a/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java b/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java index 1a53d85..3927507 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java @@ -38,6 +38,10 @@ public interface VPNConfig { String getIp(); + List allowedCountries(); + + List blockedCountries(); + int getPort(); boolean metrics(); diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java b/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java index 7e6bc9a..1496970 100644 --- a/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java +++ b/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java @@ -1,9 +1,6 @@ package dev.brighten.antivpn.utils; -import java.io.Closeable; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; +import java.io.*; import java.lang.reflect.Method; import java.net.Inet4Address; import java.net.InetAddress; @@ -33,6 +30,24 @@ public class MiscUtils { } } + 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(); diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/config/Configuration.java b/Common/src/main/java/dev/brighten/antivpn/utils/config/Configuration.java new file mode 100644 index 0000000..b9408cf --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/config/Configuration.java @@ -0,0 +1,507 @@ +package dev.brighten.antivpn.utils.config; + +import java.util.*; + +public final class Configuration +{ + + private static final char SEPARATOR = '.'; + final Map self; + final Map> comments; + private final Configuration defaults; + + public Configuration() + { + this( null ); + } + + public Configuration(Configuration defaults) + { + this( new LinkedHashMap(), 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 list = new ArrayList<>(); + Collections.addAll(list, contents.split("\n")); + + int currentLayer = 0; + String currentPath = ""; + + int lineNumber = 0; + for(Iterator 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 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 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 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 getByteList(String path) + { + List list = getList( path ); + List 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 getShortList(String path) + { + List list = getList( path ); + List 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 getIntList(String path) + { + List list = getList( path ); + List 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 getLongList(String path) + { + List list = getList( path ); + List 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 getFloatList(String path) + { + List list = getList( path ); + List 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 getDoubleList(String path) + { + List list = getList( path ); + List 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 getBooleanList(String path) + { + List list = getList( path ); + List 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 getCharList(String path) + { + List list = getList( path ); + List 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 getStringList(String path) + { + List list = getList( path ); + List 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; + } +} \ No newline at end of file diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/config/ConfigurationProvider.java b/Common/src/main/java/dev/brighten/antivpn/utils/config/ConfigurationProvider.java new file mode 100644 index 0000000..47f8a48 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/config/ConfigurationProvider.java @@ -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, 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 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); +} \ No newline at end of file diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/config/YamlConfiguration.java b/Common/src/main/java/dev/brighten/antivpn/utils/config/YamlConfiguration.java new file mode 100644 index 0000000..81bdf33 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/config/YamlConfiguration.java @@ -0,0 +1,161 @@ +package dev.brighten.antivpn.utils.config; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.*; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +public class YamlConfiguration extends ConfigurationProvider +{ + + private final ThreadLocal yaml = new ThreadLocal() + { + @Override + protected Yaml initialValue() + { + + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK ); + + return new Yaml(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); + + List 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 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(contents); + } 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 ); + } + + @Override + @SuppressWarnings("unchecked") + public Configuration load(Reader reader, Configuration defaults) + { + Map map = yaml.get().loadAs( reader, LinkedHashMap.class ); + if ( map == null ) + { + map = new LinkedHashMap<>(); + } + return new Configuration( map, defaults ); + } + + @Override + public Configuration load(InputStream is) + { + return load( is, null ); + } + + @Override + @SuppressWarnings("unchecked") + public Configuration load(InputStream is, Configuration defaults) + { + Map map = yaml.get().loadAs( is, LinkedHashMap.class ); + if ( map == null ) + { + map = new LinkedHashMap<>(); + } + return new Configuration( map, defaults ); + } + + @Override + public Configuration load(String string) + { + return load( string, null ); + } + + @Override + @SuppressWarnings("unchecked") + public Configuration load(String string, Configuration defaults) + { + Map map = yaml.get().loadAs( string, LinkedHashMap.class ); + if ( map == null ) + { + map = new LinkedHashMap<>(); + } + return new Configuration( map, defaults ); + } +} \ No newline at end of file diff --git a/Velocity/pom.xml b/Velocity/pom.xml index 0132b68..5178e79 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.6.1 + 1.7 4.0.0 @@ -33,7 +33,7 @@ dev.brighten.antivpn Common - 1.6.1 + 1.7 provided diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityConfig.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityConfig.java index 94fe0ea..32f21a7 100644 --- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityConfig.java +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityConfig.java @@ -41,10 +41,14 @@ public class VelocityConfig implements VPNConfig { private final ConfigDefault> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(), "prefixWhitelists", VelocityPlugin.INSTANCE), defaultCommands = new ConfigDefault<>( Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute", - VelocityPlugin.INSTANCE); + VelocityPlugin.INSTANCE), + defBlockedCountries = new ConfigDefault<>(new ArrayList<>(), "blockedCountries", + VelocityPlugin.INSTANCE), + defAllowedCountries = new ConfigDefault<>(new ArrayList<>(), "allowedCountries", + VelocityPlugin.INSTANCE); private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg; - private List prefixWhitelists, commands; + private List prefixWhitelists, commands, allowedCountries, blockedCountries; private int port; private boolean cacheResults, useCredentials, databaseEnabled, commandsEnabled, kickPlayers, alertToStaff, metrics; @@ -133,6 +137,16 @@ public class VelocityConfig implements VPNConfig { return ip; } + @Override + public List allowedCountries() { + return allowedCountries; + } + + @Override + public List blockedCountries() { + return blockedCountries; + } + @Override public int getPort() { if(port == -1) { @@ -175,5 +189,7 @@ public class VelocityConfig implements VPNConfig { alertToStaff = defaultAlertToStaff.get(); alertMsg = defaultAlertMsg.get(); metrics = defaultMetrics.get(); + blockedCountries = defBlockedCountries.get(); + allowedCountries = defAllowedCountries.get(); } } diff --git a/pom.xml b/pom.xml index 228c66d..2b45b4a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ dev.brighten.antivpn AntiVPN pom - 1.6.1 + 1.7 Common