package fr.Alphart.BAT.Modules.Ban;
import static fr.Alphart.BAT.I18n.I18n._;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import net.md_5.bungee.event.EventHandler;
import com.google.common.base.Charsets;
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import fr.Alphart.BAT.BAT;
import fr.Alphart.BAT.Modules.BATCommand;
import fr.Alphart.BAT.Modules.IModule;
import fr.Alphart.BAT.Modules.ModuleConfiguration;
import fr.Alphart.BAT.Modules.Core.Core;
import fr.Alphart.BAT.Utils.FormatUtils;
import fr.Alphart.BAT.Utils.UUIDNotFoundException;
import fr.Alphart.BAT.Utils.Utils;
import fr.Alphart.BAT.database.DataSourceHandler;
import fr.Alphart.BAT.database.SQLQueries;
public class Ban implements IModule, Listener {
private final String name = "ban";
private ScheduledTask task;
private BanCommand commandHandler;
private final BanConfig config;
public Ban(){
config = new BanConfig();
}
@Override
public List<BATCommand> getCommands() {
return commandHandler.getCmds();
}
@Override
public String getName() {
return name;
}
@Override
public String getMainCommand() {
return "ban";
}
@Override
public ModuleConfiguration getConfig() {
return config;
}
@Override
public boolean load() {
// Init table
try (Connection conn = BAT.getConnection()) {
final Statement statement = conn.createStatement();
if (DataSourceHandler.isSQLite()) {
for (final String query : SQLQueries.Ban.SQLite.createTable) {
statement.executeUpdate(query);
}
} else {
statement.executeUpdate(SQLQueries.Ban.createTable);
}
statement.close();
} catch (final SQLException e) {
DataSourceHandler.handleException(e);
}
// Register commands
commandHandler = new BanCommand(this);
commandHandler.loadCmds();
// Launch tempban task
final BanExpirationTask banExpirationTask = new BanExpirationTask(this);
task = ProxyServer.getInstance().getScheduler().schedule(BAT.getInstance(), banExpirationTask, 0, 10, TimeUnit.SECONDS);
// Check if the online players are banned (if the module has been reloaded)
for(final ProxiedPlayer player : ProxyServer.getInstance().getPlayers()){
final List<String> serversToCheck = player.getServer() != null
? Arrays.asList(player.getServer().getInfo().getName(), GLOBAL_SERVER)
: Arrays.asList(GLOBAL_SERVER);
for(final String server : serversToCheck){
if(isBan(player, server)){
if (server.equals(player.getPendingConnection().getListener().getDefaultServer()) || server.equals(GLOBAL_SERVER)) {
player.disconnect(getBanMessage(player.getPendingConnection(), server));
continue;
}
player.sendMessage(getBanMessage(player.getPendingConnection(), server));
player.connect(ProxyServer.getInstance().getServerInfo(player.getPendingConnection().getListener().getDefaultServer()));
}
}
}
return true;
}
@Override
public boolean unload() {
task.cancel();
return true;
}
public class BanConfig extends ModuleConfiguration {
public BanConfig() {
init(name);
}
}
public BaseComponent[] getBanMessage(final PendingConnection pConn, final String server){
String reason = "";
Timestamp expiration = null;
Timestamp begin = null;
String staff = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try (Connection conn = BAT.getConnection()) {
statement = conn.prepareStatement(DataSourceHandler.isSQLite()
? SQLQueries.Ban.SQLite.getBanMessage
: SQLQueries.Ban.getBanMessage);
try{
final UUID pUUID;
if(pConn.getUniqueId() != null){
pUUID = pConn.getUniqueId();
}
else{
pUUID = java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + pConn.getName() ).getBytes(Charsets.UTF_8));
}
statement.setString(1, pUUID.toString().replace("-", ""));
statement.setString(2, pConn.getAddress().getAddress().getHostAddress());
statement.setString(3, server);
}catch(final UUIDNotFoundException e){
BAT.getInstance().getLogger().severe("Error during retrieving of the UUID of " + pConn.getName() + ". Please report this error :");
e.printStackTrace();
}
resultSet = statement.executeQuery();
if(resultSet.next()) {
if(DataSourceHandler.isSQLite()){
begin = new Timestamp(resultSet.getLong("strftime('%s',ban_begin)") * 1000);
String endStr = resultSet.getString("ban_end"); // SQLite see this row as null but it doesn't seem to make the same with ban message though it's almost the same code ...
expiration = (endStr == null) ? null : new Timestamp(Long.parseLong(endStr));
}else{
begin = resultSet.getTimestamp("ban_begin");
expiration = resultSet.getTimestamp("ban_end");
}
reason = (resultSet.getString("ban_reason") != null) ? resultSet.getString("ban_reason") : IModule.NO_REASON;
staff = resultSet.getString("ban_staff");
}else{
throw new SQLException("No active ban found.");
}
} catch (final SQLException e) {
DataSourceHandler.handleException(e);
} finally {
DataSourceHandler.close(statement, resultSet);
}
if(expiration != null){
return TextComponent.fromLegacyText(_("isBannedTemp",
new String[]{ reason, (expiration.getTime() < System.currentTimeMillis()) ? "a few moments" : FormatUtils.getDuration(expiration.getTime()),
Core.defaultDF.format(begin), staff }));
}else{
return TextComponent.fromLegacyText(_("isBanned", new String[]{ reason, Core.defaultDF.format(begin), staff }));
}
}
/**
* Check if both ip and name of this player are banned
*
* @param player
* @param server
* @return true if name or ip is banned
*/
public boolean isBan(final ProxiedPlayer player, final String server) {
final String ip = Core.getPlayerIP(player.getName());
if (isBan(player.getName(), server) || isBan(ip, server)) {
return true;
}
return false;
}
/**
* Check if this entity (player or ip) is banned
*
* @param bannedEntity
* | can be an ip or a player name
* @param server
* | if server equals to (any) check if the player is ban on a
* server
* @return
*/
public boolean isBan(final String bannedEntity, final String server) {
PreparedStatement statement = null;
ResultSet resultSet = null;
try (Connection conn = BAT.getConnection()) {
// If this is an ip which may be banned
if (Utils.validIP(bannedEntity)) {
final String ip = bannedEntity;
statement = conn.prepareStatement((ANY_SERVER.equals(server)) ? SQLQueries.Ban.isBanIP
: SQLQueries.Ban.isBanServerIP);
statement.setString(1, ip);
if (!ANY_SERVER.equals(server)) {
statement.setString(2, server);
}
}
// If this is a player which may be banned
else {
final String pName = bannedEntity;
final String uuid = Core.getUUID(pName);
statement = conn.prepareStatement((ANY_SERVER.equals(server)) ? SQLQueries.Ban.isBan
: SQLQueries.Ban.isBanServer);
statement.setString(1, uuid);
if (!ANY_SERVER.equals(server)) {
statement.setString(2, server);
}
}
resultSet = statement.executeQuery();
// If there are a result
if (resultSet.next()) {
return true;
}
} catch (final SQLException e) {
DataSourceHandler.handleException(e);
} finally {
DataSourceHandler.close(statement, resultSet);
}
return false;
}
/**
* Ban this entity (player or ip) <br>
*
* @param bannedEntity
* | can be an ip or a player name
* @param server
* ; set to "(global)", to global ban
* @param staff
* @param duration
* ; set to 0 for ban def
* @param reason
* | optional
* @return
*/
public String ban(final String bannedEntity, final String server, final String staff,
final long expirationTimestamp, final String reason) {
try (Connection conn = BAT.getConnection()) {
// If the bannedEntity is an ip
if (Utils.validIP(bannedEntity)) {
final String ip = bannedEntity;
final PreparedStatement statement = conn.prepareStatement(SQLQueries.Ban.createBanIP);
statement.setString(1, ip);
statement.setString(2, staff);
statement.setString(3, server);
statement.setTimestamp(4, (expirationTimestamp > 0) ? new Timestamp(expirationTimestamp) : null);
statement.setString(5, (NO_REASON.equals(reason)) ? null : reason);
statement.executeUpdate();
statement.close();
for (final ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) {
if (Utils.getPlayerIP(player).equals(ip) && (GLOBAL_SERVER.equals(server) || server.equalsIgnoreCase(player.getServer().getInfo().getName())) ) {
BAT.kick(player, _("wasBannedNotif", new String[] { reason }));
}
}
if (BAT.getInstance().getRedis().isRedisEnabled()) {
for (final UUID pUUID : RedisBungee.getApi().getPlayersOnline()) {
if (RedisBungee.getApi().getPlayerIp(pUUID).equals(ip) && (GLOBAL_SERVER.equals(server) || server.equalsIgnoreCase(RedisBungee.getApi().getServerFor(pUUID).getName()))) {
BAT.getInstance().getRedis().sendGKickPlayer(pUUID, _("wasBannedNotif", new String[] { reason }));
}
}
}
if (expirationTimestamp > 0) {
return _("banTempBroadcast", new String[] { ip, FormatUtils.getDuration(expirationTimestamp),
staff, server, reason });
} else {
return _("banBroadcast", new String[] { ip, staff, server, reason });
}
}
// Otherwise it's a player
else {
final String pName = bannedEntity;
final String sUUID = Core.getUUID(pName);
final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(pName);
final PreparedStatement statement = conn.prepareStatement(SQLQueries.Ban.createBan);
statement.setString(1, sUUID);
statement.setString(2, staff);
statement.setString(3, server);
statement.setTimestamp(4, (expirationTimestamp > 0) ? new Timestamp(expirationTimestamp) : null);
statement.setString(5, (NO_REASON.equals(reason)) ? null : reason);
statement.executeUpdate();
statement.close();
// Kick player if he's online and on the server where he's
// banned
if (player != null
&& (server.equals(GLOBAL_SERVER) || player.getServer().getInfo().getName().equalsIgnoreCase(server))) {
BAT.kick(player, _("wasBannedNotif", new String[] { reason }));
} else if (BAT.getInstance().getRedis().isRedisEnabled()) {
UUID pUUID = RedisBungee.getApi().getUuidFromName(pName);
if (RedisBungee.getApi().isPlayerOnline(pUUID)
&& ((server.equals(GLOBAL_SERVER) || RedisBungee.getApi().getServerFor(pUUID).getName().equalsIgnoreCase(server)))) {
BAT.getInstance().getRedis().sendGKickPlayer(pUUID, _("wasBannedNotif", new String[] { reason }));
}
}
if (expirationTimestamp > 0) {
return _("banTempBroadcast", new String[] { pName, FormatUtils.getDuration(expirationTimestamp),
staff, server, reason });
} else {
return _("banBroadcast", new String[] { pName, staff, server, reason });
}
}
} catch (final SQLException e) {
return DataSourceHandler.handleException(e);
}
}
/**
* Ban the ip of an online player
*
* @param server
* ; set to "(global)", to global ban
* @param staff
* @param duration
* ; set to 0 for ban def
* @param reason
* | optional
* @param ip
*/
public String banIP(final ProxiedPlayer player, final String server, final String staff,
final long expirationTimestamp, final String reason) {
ban(Utils.getPlayerIP(player), server, staff, expirationTimestamp, reason);
return _("banBroadcast", new String[] { player.getName() + "'s IP", staff, server, reason });
}
public String banRedisIP(final UUID pUUID, final String server, final String staff,
final long expirationTimestamp, final String reason) {
if (BAT.getInstance().getRedis().isRedisEnabled() && RedisBungee.getApi().isPlayerOnline(pUUID)) {
ban(RedisBungee.getApi().getPlayerIp(pUUID).getHostAddress(), server, staff, expirationTimestamp, reason);
return _("banBroadcast", new String[] { RedisBungee.getApi().getNameFromUuid(pUUID) + "'s IP", staff, server, reason });
} else {
return null;
}
}
/**
* Unban an entity (player or ip)
*
* @param bannedEntity
* | can be an ip or a player name
* @param server
* | if equals to (any), unban from all servers | if equals to
* (global), remove global ban
* @param staff
* @param reason
* @param unBanIP
*/
public String unBan(final String bannedEntity, final String server, final String staff, final String reason) {
PreparedStatement statement = null;
try (Connection conn = BAT.getConnection()) {
// If the bannedEntity is an ip
if (Utils.validIP(bannedEntity)) {
final String ip = bannedEntity;
if (ANY_SERVER.equals(server)) {
statement = (DataSourceHandler.isSQLite()) ? conn.prepareStatement(SQLQueries.Ban.SQLite.unBanIP)
: conn.prepareStatement(SQLQueries.Ban.unBanIP);
statement.setString(1, reason);
statement.setString(2, staff);
statement.setString(3, ip);
} else {
statement = (DataSourceHandler.isSQLite()) ? conn
.prepareStatement(SQLQueries.Ban.SQLite.unBanIPServer) : conn
.prepareStatement(SQLQueries.Ban.unBanIPServer);
statement.setString(1, reason);
statement.setString(2, staff);
statement.setString(3, ip);
statement.setString(4, server);
}
statement.executeUpdate();
return _("unbanBroadcast", new String[] { ip, staff, server, reason });
}
// Otherwise it's a player
else {
final String pName = bannedEntity;
final String UUID = Core.getUUID(pName);
if (ANY_SERVER.equals(server)) {
statement = (DataSourceHandler.isSQLite()) ? conn.prepareStatement(SQLQueries.Ban.SQLite.unBan)
: conn.prepareStatement(SQLQueries.Ban.unBan);
statement.setString(1, reason);
statement.setString(2, staff);
statement.setString(3, UUID);
} else {
statement = (DataSourceHandler.isSQLite()) ? conn
.prepareStatement(SQLQueries.Ban.SQLite.unBanServer) : conn
.prepareStatement(SQLQueries.Ban.unBanServer);
statement.setString(1, reason);
statement.setString(2, staff);
statement.setString(3, UUID);
statement.setString(4, server);
}
statement.executeUpdate();
return _("unbanBroadcast", new String[] { pName, staff, server, reason });
}
} catch (final SQLException e) {
return DataSourceHandler.handleException(e);
} finally {
DataSourceHandler.close(statement);
}
}
/**
* Unban the ip of this entity
*
* @param entity
* @param server
* | if equals to (any), unban from all servers | if equals to
* (global), remove global ban
* @param staff
* @param reason
* | optional
* @param duration
* ; set to 0 for ban def
*/
public String unBanIP(final String entity, final String server, final String staff, final String reason) {
if (Utils.validIP(entity)) {
return unBan(entity, server, staff, reason);
} else {
unBan(Core.getPlayerIP(entity), server, staff, reason);
return _("unbanBroadcast", new String[] { entity + "'s IP", staff, server, reason });
}
}
/**
* Get all ban data of an entity <br>
* <b>Should be runned async to optimize performance</b>
*
* @param entity
* @return List of BanEntry of the player
*/
public List<BanEntry> getBanData(final String entity) {
final List<BanEntry> banList = new ArrayList<BanEntry>();
PreparedStatement statement = null;
ResultSet resultSet = null;
try (Connection conn = BAT.getConnection()) {
// If the entity is an ip
if (Utils.validIP(entity)) {
statement = conn.prepareStatement((DataSourceHandler.isSQLite())
? SQLQueries.Ban.SQLite.getBanIP
: SQLQueries.Ban.getBanIP);
statement.setString(1, entity);
resultSet = statement.executeQuery();
}
// Otherwise if it's a player
else {
statement = conn.prepareStatement((DataSourceHandler.isSQLite())
? SQLQueries.Ban.SQLite.getBan
: SQLQueries.Ban.getBan);
statement.setString(1, Core.getUUID(entity));
resultSet = statement.executeQuery();
}
while (resultSet.next()) {
final Timestamp beginDate;
final Timestamp endDate;
final Timestamp unbanDate;
if(DataSourceHandler.isSQLite()){
beginDate = new Timestamp(resultSet.getLong("strftime('%s',ban_begin)") * 1000);
String endStr = resultSet.getString("ban_end");
endDate = (endStr == null) ? null : new Timestamp(Long.parseLong(endStr));
long unbanLong = resultSet.getLong("strftime('%s',ban_unbandate)") * 1000;
unbanDate = (unbanLong == 0) ? null : new Timestamp(unbanLong);
}else{
beginDate = resultSet.getTimestamp("ban_begin");
endDate = resultSet.getTimestamp("ban_end");
unbanDate = resultSet.getTimestamp("ban_unbandate");
}
// Make it compatible with sqlite (date: get an int with the sfrt and then construct a tiemstamp)
final String server = resultSet.getString("ban_server");
String reason = resultSet.getString("ban_reason");
if(reason == null){
reason = NO_REASON;
}
final String staff = resultSet.getString("ban_staff");
final boolean active = (resultSet.getBoolean("ban_state") ? true : false);
String unbanReason = resultSet.getString("ban_unbanreason");
if(unbanReason == null){
unbanReason = NO_REASON;
}
final String unbanStaff = resultSet.getString("ban_unbanstaff");
banList.add(new BanEntry(entity, server, reason, staff, beginDate, endDate, unbanDate, unbanReason, unbanStaff, active));
}
} catch (final SQLException e) {
DataSourceHandler.handleException(e);
} finally {
DataSourceHandler.close(statement, resultSet);
}
return banList;
}
public List<BanEntry> getManagedBan(final String staff){
final List<BanEntry> banList = new ArrayList<BanEntry>();
PreparedStatement statement = null;
ResultSet resultSet = null;
try (Connection conn = BAT.getConnection()) {
statement = conn.prepareStatement((DataSourceHandler.isSQLite())
? SQLQueries.Ban.SQLite.getManagedBan
: SQLQueries.Ban.getManagedBan);
statement.setString(1, staff);
statement.setString(2, staff);
resultSet = statement.executeQuery();
while (resultSet.next()) {
final Timestamp beginDate;
final Timestamp endDate;
final Timestamp unbanDate;
if(DataSourceHandler.isSQLite()){
beginDate = new Timestamp(resultSet.getLong("strftime('%s',ban_begin)") * 1000);
String endStr = resultSet.getString("ban_end");
endDate = (endStr == null) ? null : new Timestamp(Long.parseLong(endStr));
long unbanLong = resultSet.getLong("strftime('%s',ban_unbandate)") * 1000;
unbanDate = (unbanLong == 0) ? null : new Timestamp(unbanLong);
}else{
beginDate = resultSet.getTimestamp("ban_begin");
endDate = resultSet.getTimestamp("ban_end");
unbanDate = resultSet.getTimestamp("ban_unbandate");
}
// Make it compatible with sqlite (date: get an int with the sfrt and then construct a tiemstamp)
final String server = resultSet.getString("ban_server");
String reason = resultSet.getString("ban_reason");
if(reason == null){
reason = NO_REASON;
}
String entity = (resultSet.getString("ban_ip") != null)
? resultSet.getString("ban_ip")
: Core.getPlayerName(resultSet.getString("UUID"));
// If the UUID search failed
if(entity == null){
entity = "UUID:" + resultSet.getString("UUID");
}
final boolean active = (resultSet.getBoolean("ban_state") ? true : false);
String unbanReason = resultSet.getString("ban_unbanreason");
if(unbanReason == null){
unbanReason = NO_REASON;
}
final String unbanStaff = resultSet.getString("ban_unbanstaff");
banList.add(new BanEntry(entity, server, reason, staff, beginDate, endDate, unbanDate, unbanReason, unbanStaff, active));
}
} catch (final SQLException e) {
DataSourceHandler.handleException(e);
} finally {
DataSourceHandler.close(statement, resultSet);
}
return banList;
}
// Event listener
@EventHandler
public void onServerConnect(final ServerConnectEvent e) {
final ProxiedPlayer player = e.getPlayer();
final String target = e.getTarget().getName();
if (isBan(player, target)) {
if (target.equals(player.getPendingConnection().getListener().getDefaultServer())) {
// If it's player's join server kick him
if(e.getPlayer().getServer() == null){
e.setCancelled(true);
// Need to delay for avoiding the "bit cannot be cast to fm exception" and to annoy the banned player :p
ProxyServer.getInstance().getScheduler().schedule(BAT.getInstance(), new Runnable() {
@Override
public void run() {
e.getPlayer().disconnect(getBanMessage(player.getPendingConnection(), target));
}
}, 500, TimeUnit.MILLISECONDS);
}else{
e.setCancelled(true);
e.getPlayer().sendMessage(getBanMessage(player.getPendingConnection(), target));
}
return;
}
player.sendMessage(getBanMessage(player.getPendingConnection(), target));
if (player.getServer() == null) {
player.connect(ProxyServer.getInstance().getServerInfo(
player.getPendingConnection().getListener().getDefaultServer()));
}
e.setCancelled(true);
}
}
@EventHandler
public void onPlayerLogin(final LoginEvent ev) {
ev.registerIntent(BAT.getInstance());
BAT.getInstance().getProxy().getScheduler().runAsync(BAT.getInstance(), new Runnable()
{
public void run() {
boolean isBanPlayer = false;
PreparedStatement statement = null;
ResultSet resultSet = null;
UUID uuid = null;
try(Connection conn = BAT.getConnection()){
statement = conn.prepareStatement("SELECT ban_id FROM `BAT_ban` WHERE ban_state = 1 AND UUID = ? AND ban_server = '" + GLOBAL_SERVER + "';");
// If this is an online mode server, the uuid will be already set
if(ev.getConnection().getUniqueId() != null){
uuid = ev.getConnection().getUniqueId();
}
// Otherwise it's an offline mode server, so we're gonna generate the UUID using player name (hashing)
else{
uuid = java.util.UUID.nameUUIDFromBytes(("OfflinePlayer:" + ev.getConnection().getName() ).getBytes(Charsets.UTF_8));
}
statement.setString(1, uuid.toString().replaceAll( "-", "" ));
resultSet = statement.executeQuery();
if (resultSet.next()){
isBanPlayer = true;
}
} catch (SQLException e) {
DataSourceHandler.handleException(e);
} finally {
DataSourceHandler.close(statement, resultSet);
}
if ((isBanPlayer) || (isBan(ev.getConnection().getAddress().getAddress().getHostAddress(), GLOBAL_SERVER))) {
BaseComponent[] bM = getBanMessage(ev.getConnection(), GLOBAL_SERVER);
ev.setCancelReason(TextComponent.toLegacyText(bM));
ev.setCancelled(true);
}
ev.completeIntent(BAT.getInstance());
}
});
}
}