/**
* This file is part of Oracle, licensed under the MIT License (MIT).
*
* Copyright (c) 2015 Helion3 http://helion3.com/
*
* 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 com.helion3.oracle.bans;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.profile.GameProfile;
import org.spongepowered.api.service.ban.BanService;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.util.ban.Ban;
import org.spongepowered.api.util.ban.BanTypes;
import org.spongepowered.api.util.ban.Ban.Builder;
import org.spongepowered.api.util.ban.Ban.Ip;
import org.spongepowered.api.util.ban.Ban.Profile;
import com.helion3.oracle.Oracle;
import com.helion3.oracle.players.PlayerIdentification;
import com.helion3.oracle.players.PluginPlayer;
import com.helion3.oracle.utils.JoinUtil;
public class OracleBanService implements BanService {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Collection<? extends Ban> getBans() {
Collection<? extends Ban> bans = this.getProfileBans();
bans.addAll((Collection) this.getIpBans());
return bans;
}
@Override
public Collection<Profile> getProfileBans() {
Collection<Profile> collection = new ArrayList<Profile>();
Connection conn = null;
PreparedStatement s = null;
ResultSet rs = null;
try {
conn = Oracle.dbc();
s = conn.prepareStatement("SELECT reason, HEX(player_uuid) as uuid FROM oracle_bans b LEFT JOIN oracle_players p ON b.player_id = p.player_id WHERE ip_id IS NULL AND unbanned = 0 AND player_uuid != '\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0'");
s.executeQuery();
rs = s.getResultSet();
while(rs.next()) {
// Note: we have to use the Future directly because this method expects a synchronous return
Future<GameProfile> future = Oracle.getGame().getServer().getGameProfileManager().get(PlayerIdentification.uuidFromDbString(rs.getString("uuid")));
Builder builder = Ban.builder();
builder.reason(Text.of(rs.getString("reason")));
builder.type(BanTypes.PROFILE);
builder.profile(future.get());
collection.add((Profile) builder.build());
}
} catch (SQLException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(rs != null) try { rs.close(); } catch (SQLException e) {}
if(s != null) try { s.close(); } catch (SQLException e) {}
if(conn != null) try { conn.close(); } catch (SQLException e) {}
}
return collection;
}
@Override
public Collection<Ip> getIpBans() {
Collection<Ip> collection = new ArrayList<Ip>();
Connection conn = null;
PreparedStatement s = null;
ResultSet rs = null;
try {
conn = Oracle.dbc();
s = conn.prepareStatement("SELECT reason, INET_NTOA(ip) as ip FROM oracle_bans b LEFT JOIN oracle_ips i ON b.ip_id = i.ip_id WHERE b.ip_id IS NOT NULL AND unbanned = 0");
s.executeQuery();
rs = s.getResultSet();
while(rs.next()) {
Builder builder = Ban.builder();
builder.reason(Text.of(rs.getString("reason")));
builder.type(BanTypes.IP);
builder.address(InetAddress.getByName(rs.getString("ip")));
collection.add((Ip) builder.build());
}
} catch (SQLException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} finally {
if(rs != null) try { rs.close(); } catch (SQLException e) {}
if(s != null) try { s.close(); } catch (SQLException e) {}
if(conn != null) try { conn.close(); } catch (SQLException e) {}
}
return collection;
}
@Override
public Optional<Profile> getBanFor(GameProfile profile) {
Connection conn = null;
PreparedStatement s = null;
ResultSet rs = null;
Profile ban = null;
try {
PluginPlayer pluginPlayer = PlayerIdentification.getOraclePlayer(profile.getUniqueId());
if( pluginPlayer != null ){
conn = Oracle.dbc();
s = conn.prepareStatement ("SELECT * FROM oracle_bans WHERE player_id = ? AND unbanned = 0 LIMIT 1");
s.setInt(1, pluginPlayer.getId());
s.executeQuery();
rs = s.getResultSet();
if(rs.first()) {
Builder builder = Ban.builder();
builder.reason(Text.of(rs.getString("reason")));
builder.type(BanTypes.PROFILE);
builder.profile(profile);
ban = (Ban.Profile) builder.build();
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(rs != null) try { rs.close(); } catch (SQLException e) {}
if(s != null) try { s.close(); } catch (SQLException e) {}
if(conn != null) try { conn.close(); } catch (SQLException e) {}
}
return Optional.ofNullable(ban);
}
@Override
public Optional<Ip> getBanFor(InetAddress address) {
Connection conn = null;
PreparedStatement s = null;
ResultSet rs = null;
Ip ban = null;
try {
int ipId = JoinUtil.lookupIp(address.getHostAddress());
conn = Oracle.dbc();
s = conn.prepareStatement ("SELECT * FROM oracle_bans WHERE ip_id = ? AND unbanned = 0 LIMIT 1");
s.setInt(1, ipId);
s.executeQuery();
rs = s.getResultSet();
if(rs.first()) {
Builder builder = Ban.builder();
builder.reason(Text.of(rs.getString("reason")));
builder.type(BanTypes.IP);
builder.address(address);
ban = (Ban.Ip) builder.build();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(rs != null) try { rs.close(); } catch (SQLException e) {}
if(s != null) try { s.close(); } catch (SQLException e) {}
if(conn != null) try { conn.close(); } catch (SQLException e) {}
}
return Optional.ofNullable(ban);
}
@Override
public boolean isBanned(GameProfile profile) {
return getBanFor(profile).isPresent();
}
@Override
public boolean isBanned(InetAddress address) {
return getBanFor(address).isPresent();
}
@Override
public boolean pardon(GameProfile profile) {
Connection conn = null;
PreparedStatement s = null;
PreparedStatement s2 = null;
try {
conn = Oracle.dbc();
// Insert/Get Player ID
PluginPlayer pluginPlayer = PlayerIdentification.getOraclePlayer(profile.getUniqueId());
// @todo no way to determine CommandSource
// https://github.com/SpongePowered/SpongeCommon/issues/436
int staffId = 0;
// Add unban record
s = conn.prepareStatement("INSERT INTO oracle_unbans (player_id, staff_player_id, epoch) VALUES (?, ?, ?)");
s.setInt(1, pluginPlayer.getId());
s.setInt(2, staffId);
s.setLong(3, System.currentTimeMillis() / 1000L);
s.executeUpdate();
// Mark as unbanned
s2 = conn.prepareStatement("UPDATE oracle_bans SET unbanned = 1 WHERE player_id = ?");
s2.setInt(1, pluginPlayer.getId());
s2.executeUpdate();
} catch (SQLException e){
e.printStackTrace();
} finally {
if(s != null) try { s.close(); } catch (SQLException e) {}
if(s2 != null) try { s2.close(); } catch (SQLException e) {}
if(conn != null) try { conn.close(); } catch (SQLException e) {}
}
// @todo fix this
return true;
}
@Override
public boolean pardon(InetAddress address) {
Connection conn = null;
PreparedStatement s = null;
PreparedStatement s2 = null;
try {
conn = Oracle.dbc();
// Insert/Get Player ID
int ipId = JoinUtil.lookupIp(address.getHostAddress());
// @todo no way to determine CommandSource
// https://github.com/SpongePowered/SpongeCommon/issues/436
int staffId = 0;
// Add unban record
s = conn.prepareStatement("INSERT INTO oracle_unbans (ip_id, staff_player_id, epoch) VALUES (?, ?, ?)");
s.setInt(1, ipId);
s.setInt(2, staffId);
s.setLong(3, System.currentTimeMillis() / 1000L);
s.executeUpdate();
// Mark as unbanned
s2 = conn.prepareStatement("UPDATE oracle_bans SET unbanned = 1 WHERE ip_id = ?");
s2.setInt(1, ipId);
s2.executeUpdate();
} catch (SQLException e){
e.printStackTrace();
} finally {
if(s != null) try { s.close(); } catch (SQLException e) {}
if(s2 != null) try { s2.close(); } catch (SQLException e) {}
if(conn != null) try { conn.close(); } catch (SQLException e) {}
}
// @todo fix this
return true;
}
@Override
public boolean removeBan(Ban ban) {
if (ban.getType().equals(BanTypes.PROFILE)) {
return this.pardon(((Ban.Profile) ban).getProfile());
} else if (ban.getType().equals(BanTypes.IP)) {
return this.pardon(((Ban.Ip) ban).getAddress());
}
throw new IllegalArgumentException(String.format("Ban %s had unrecognized BanType %s!", ban, ban.getType()));
}
@Override
public Optional<? extends Ban> addBan(Ban ban) {
Connection conn = null;
PreparedStatement s = null;
int staffId = 0;
if (ban.getBanCommandSource().isPresent()) {
if (ban.getBanCommandSource().get() instanceof Player) {
PluginPlayer staffPluginPlayer = PlayerIdentification.getOraclePlayer(((Player)ban.getBanCommandSource().get()).getUniqueId());
staffId = staffPluginPlayer.getId();
}
}
try {
conn = Oracle.dbc();
String reason = "";
if (ban.getReason().isPresent()) {
reason = ban.getReason().get().toPlain().toString();
}
if (ban.getType().equals(BanTypes.PROFILE)) {
Ban.Profile banProfile = (Ban.Profile) ban;
PluginPlayer pluginPlayer = PlayerIdentification.getOraclePlayer(banProfile.getProfile().getUniqueId());
if (pluginPlayer != null) {
s = conn.prepareStatement("INSERT INTO oracle_bans (player_id, staff_player_id, reason, epoch) VALUES (?, ?, ?, ?)");
s.setInt(1, pluginPlayer.getId());
s.setInt(2, staffId);
s.setString(3, reason);
s.setLong(4, System.currentTimeMillis() / 1000L);
s.executeUpdate();
}
}
else if (ban.getType().equals(BanTypes.IP)) {
Ban.Ip banIp = (Ban.Ip) ban;
// Insert/Get IP ID
int ipId = JoinUtil.lookupIp(banIp.getAddress().getHostAddress());
conn = Oracle.dbc();
s = conn.prepareStatement("INSERT INTO oracle_bans (ip_id, staff_player_id, reason, epoch) VALUES (?, ?, ?, ?)");
s.setInt(1, ipId);
s.setInt(2, staffId);
s.setString(3, reason);
s.setLong(4, System.currentTimeMillis() / 1000L);
s.executeUpdate();
}
} catch (SQLException e){
e.printStackTrace();
} finally {
if(s != null) try { s.close(); } catch (SQLException e) {}
if(conn != null) try { conn.close(); } catch (SQLException e) {}
}
return Optional.empty();
}
@Override
public boolean hasBan(Ban ban) {
if (ban.getType().equals(BanTypes.PROFILE)) {
return this.isBanned(((Ban.Profile) ban).getProfile());
} else if (ban.getType().equals(BanTypes.IP)) {
return this.isBanned(((Ban.Ip) ban).getAddress());
}
throw new IllegalArgumentException(String.format("Ban %s had unrecognized BanType %s!", ban, ban.getType()));
}
}