diff --git a/src/main/java/net/lethargiclion/informaban/InformaBan.java b/src/main/java/net/lethargiclion/informaban/InformaBan.java index d88ad71..6738c09 100644 --- a/src/main/java/net/lethargiclion/informaban/InformaBan.java +++ b/src/main/java/net/lethargiclion/informaban/InformaBan.java @@ -72,6 +72,7 @@ private void createDBClassList() { ebeans.add(net.lethargiclion.informaban.events.Event.class); ebeans.add(net.lethargiclion.informaban.events.TimedEvent.class); ebeans.add(net.lethargiclion.informaban.events.Ban.class); + ebeans.add(net.lethargiclion.informaban.events.IPBan.class); ebeans.add(net.lethargiclion.informaban.events.Unban.class); ebeans.add(net.lethargiclion.informaban.events.Kick.class); ebeans.add(net.lethargiclion.informaban.events.ActiveEvent.class); @@ -143,6 +144,14 @@ public void onEnable() { } else log.warning(msgFailed.format(new Object[] { "/ban" })); + PluginCommand ipban = getCommand("ipban"); + if (ipban != null) { + ipban.setExecutor(commandExecutor); + ipban.setDescription(messages.getString("command.ipban.description")); + ipban.setUsage(messages.getString("command.ipban.usage")); + } else + log.warning(msgFailed.format(new Object[] { "/ban" })); + PluginCommand unban = getCommand("unban"); if (unban != null) { unban.setExecutor(commandExecutor); diff --git a/src/main/java/net/lethargiclion/informaban/InformaBanCommandExecutor.java b/src/main/java/net/lethargiclion/informaban/InformaBanCommandExecutor.java index 7a13f60..570d458 100644 --- a/src/main/java/net/lethargiclion/informaban/InformaBanCommandExecutor.java +++ b/src/main/java/net/lethargiclion/informaban/InformaBanCommandExecutor.java @@ -25,6 +25,7 @@ import net.lethargiclion.informaban.events.ActiveBan; import net.lethargiclion.informaban.events.Ban; import net.lethargiclion.informaban.events.Event; +import net.lethargiclion.informaban.events.IPBan; import net.lethargiclion.informaban.events.Kick; import net.lethargiclion.informaban.events.Unban; @@ -34,6 +35,8 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.net.InetAddresses; + /** * InformaBan command executor class. Processes commands. * @@ -66,6 +69,8 @@ public boolean onCommand(CommandSender sender, Command command, return commandRap(sender, args); if (command.getName().equalsIgnoreCase("ban")) return commandBan(sender, args); + if (command.getName().equalsIgnoreCase("ipban")) + return commandIPBan(sender, args); if (command.getName().equalsIgnoreCase("unban")) return commandUnban(sender, args); } catch (java.util.MissingResourceException e) { @@ -103,8 +108,8 @@ private boolean commandKick(CommandSender sender, String[] args) { MessageFormat.format( plugin.messages .getString("command.kick.consoleLog"), - new Object[] { sender.getName(), - victim.getName() })); + new Object[] {sender.getName(), + victim.getName()})); // Do the kick and record it Kick k = new Kick(); @@ -143,8 +148,8 @@ private boolean commandBan(CommandSender sender, String[] args) { MessageFormat.format( plugin.messages .getString("command.ban.consoleLog"), - new Object[] { sender.getName(), - victim.getName() })); + new Object[] {sender.getName(), + victim.getName()})); // Do the ban and record it Ban b = new Ban(); @@ -160,6 +165,68 @@ private boolean commandBan(CommandSender sender, String[] args) { return false; } + /** + * Handles the /ipban command. + * + * @param sender + * The CommandSender executing this command. + * @param args + * The command arguments. + * @return False if a usage message should be displayed. + */ + private boolean commandIPBan(CommandSender sender, String[] args) { + + if (args.length == 1) + sender.sendMessage(plugin.messages + .getString("command.ban.reasonRequired")); + + String subject = null; + + if (args.length > 1) { + if (InetAddresses.isInetAddress(args[0])) { + subject = args[0]; + } else { + subject = sender.getServer().getPlayer(args[0]).getAddress().getAddress().getHostAddress(); + } + + // Check for existing IP ban + ActiveBan ab = plugin.getDatabase().find(ActiveBan.class).where() + .eq("subject", subject) + .findUnique(); + + if (ab != null) { + sender.sendMessage(plugin.messages + .getString("error.IPAlreadyBanned")); + return true; + } + + if (subject != null) { + // Set up ban message + String banReason = StringUtils.join( + Arrays.copyOfRange(args, 1, args.length), ' '); + + // Log ban to console + plugin.getLogger().info( + MessageFormat.format( + plugin.messages + .getString("command.ban.consoleLog"), + new Object[] {sender.getName(), + subject})); + + // Do the ban and record it + IPBan b = new IPBan(); + b.apply(plugin.messages, subject, sender, banReason, + Ban.PERMANENT); + plugin.getDatabase().insert(b); // Record the banning event + plugin.getDatabase().insert(b.makeActiveEvent()); // Set the actual ban + } else + sender.sendMessage(plugin.messages + .getString("error.playerNotFound")); + return true; + } + return false; + } + /** * Handles the /rap command. * @@ -179,15 +246,44 @@ private boolean commandRap(CommandSender sender, String[] args) { sender.sendMessage(MessageFormat.format( plugin.messages.getString("command.rap.clean"), name)); } + else { + sender.sendMessage(MessageFormat.format( + plugin.messages.getString("command.rap.ban"), name)); + } Iterator i = events.iterator(); while (i.hasNext()) { sender.sendMessage(i.next().toString()); } + + // Check for online player, and get bans matching IP address + Player p = plugin.getServer().getPlayer(name); + + if (p != null) { + String ipaddress = p.getAddress().getAddress().getHostAddress(); + + if (ipaddress != null) { + List ipevents = plugin.getDatabase().find(Event.class).where() + .disjunction() + .eq("subject", ipaddress) + .eq("subjectIP", ipaddress) + .findList(); + + if (!ipevents.isEmpty()) { + sender.sendMessage(MessageFormat.format( + plugin.messages.getString("command.rap.ip"), ipaddress)); + } + Iterator j = ipevents.iterator(); + + while (j.hasNext()) { + sender.sendMessage(j.next().toString()); + } + } + } return true; } return false; } - + /** * Handles the /unban command. * @@ -214,8 +310,8 @@ private boolean commandUnban(CommandSender sender, String[] args) { MessageFormat.format( plugin.messages .getString("command.unban.consoleLog"), - new Object[] { sender.getName(), - ab.getSubject() })); + new Object[] {sender.getName(), + ab.getSubject()})); // Do the unban and record it Unban b = new Unban(); diff --git a/src/main/java/net/lethargiclion/informaban/InformaBanEventListener.java b/src/main/java/net/lethargiclion/informaban/InformaBanEventListener.java index ea00b04..f2e0d31 100644 --- a/src/main/java/net/lethargiclion/informaban/InformaBanEventListener.java +++ b/src/main/java/net/lethargiclion/informaban/InformaBanEventListener.java @@ -79,6 +79,7 @@ private void checkBans(PlayerLoginEvent event) { .eq("subject", event.getAddress().getHostAddress()) .findUnique(); + if (b != null) { if (b.isActive()) { event.setKickMessage(b.getParent().getMessage()); diff --git a/src/main/java/net/lethargiclion/informaban/events/Event.java b/src/main/java/net/lethargiclion/informaban/events/Event.java index 924c2f0..47876c3 100644 --- a/src/main/java/net/lethargiclion/informaban/events/Event.java +++ b/src/main/java/net/lethargiclion/informaban/events/Event.java @@ -18,6 +18,8 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import com.google.common.net.InetAddresses; + /** * This base class represents a record of a ban, kick, jailing or other event * tracked by InformaBan. @@ -167,13 +169,17 @@ protected boolean apply(Player subject, CommandSender enforcer, } /** - * Applies this event to an offline subject. No IP is recorded. + * Applies this event to an offline subject. Accepts IP or player as subject. */ protected boolean apply(String subject, CommandSender enforcer, String reason) { if (getDateIssued() != null) return false; setDateIssued(new Date()); + if (InetAddresses.isInetAddress(subject)) { + this.subjectIP = subject; + + } this.subject = subject; this.enforcer = enforcer.getName(); this.reason = reason; diff --git a/src/main/java/net/lethargiclion/informaban/events/IPBan.java b/src/main/java/net/lethargiclion/informaban/events/IPBan.java new file mode 100644 index 0000000..9cab10c --- /dev/null +++ b/src/main/java/net/lethargiclion/informaban/events/IPBan.java @@ -0,0 +1,182 @@ +package net.lethargiclion.informaban.events; + +import java.text.DateFormat; +import java.util.ResourceBundle; + +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; + +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@Entity() +@DiscriminatorValue("IPBAN") +public class IPBan extends TimedEvent { + + public IPBan() { + } + + /** + * Enforce this ban. + * + * Note that this performs the initial enforcement of the ban, e.g. when the + * ban was initially placed. + * + * @param subject + * The player being banned. + * @param enforcer + * The player (or console) performing the ban. + * @param reason + * The reason given for the ban. + * @param duration + * The length of the ban in seconds, or 0 for a permanent ban. + * @return true if successfully enforced; false if the ban has already been + * enforced. + */ + public boolean apply(ResourceBundle messages, Player subject, + CommandSender enforcer, String reason, int duration) { + if (this.getDateIssued() != null) + return false; + + super.apply(subject, enforcer, reason, duration); + + // ResourceBundle messages = ResourceBundle.getBundle("Messages", + // InformaBan.getLocale()); + + String[] message = new String[3]; + + String serverName = "this server"; + + message[0] = String.format("%s%s has %sbanned you from %s!", + ChatColor.GOLD, enforcer.getName(), + isPermanent() ? "PERMANENTLY " : "", serverName); + message[1] = String.format("Reason: %s%s%s", ChatColor.GRAY, + ChatColor.ITALIC, reason); + message[2] = String.format( + "%sYour ban will %s.", + ChatColor.GRAY, + isPermanent() ? "NOT expire" : (String.format( + "expire in %d seconds", duration))); + + subject.kickPlayer(StringUtils.join(message, '\n')); + return true; + } + + /** + * Enforce this ban. + * + * Note that this performs the initial enforcement of the ban, e.g. when the + * ban was initially placed. + * + * @param subject + * The IP being banned. + * @param enforcer + * The player (or console) performing the ban. + * @param reason + * The reason given for the ban. + * @param duration + * The length of the ban in seconds, or 0 for a permanent ban. + * @return true if successfully enforced; false if the ban has already been + * enforced. + */ + public boolean apply(ResourceBundle messages, String subject, + CommandSender enforcer, String reason, int duration) { + if (this.getDateIssued() != null) + return false; + + super.apply(subject, enforcer, reason, duration); + + // ResourceBundle messages = ResourceBundle.getBundle("Messages", + // InformaBan.getLocale()); + + String[] message = new String[3]; + + String serverName = "this server"; + + message[0] = String.format("%s%s has %sbanned you from %s!", + ChatColor.GOLD, enforcer.getName(), + isPermanent() ? "PERMANENTLY " : "", serverName); + message[1] = String.format("Reason: %s%s%s", ChatColor.GRAY, + ChatColor.ITALIC, reason); + message[2] = String.format( + "%sYour ban will %s.", + ChatColor.GRAY, + isPermanent() ? "NOT expire" : (String.format( + "expire in %d seconds", duration))); + + Bukkit.banIP(subject); + + Player[] players = Bukkit.getOnlinePlayers(); + for (Player p: players) { + if (p.getAddress().getAddress().getHostAddress().equals(subject)) { + p.kickPlayer(StringUtils.join(message, '\n')); + } + } + return true; + } + + /** + * Fetches the string that should be used as the kick message if the subject + * of this ban attempts to log in. + * + * @return + */ + public String getMessage() { + String[] message = new String[3]; + String serverName = "this server"; + + message[0] = String + .format("%sYou are %sbanned from %s!", ChatColor.GOLD, + isPermanent() ? "PERMANENTLY " : "", serverName); + message[1] = String.format("Reason: %s%s%s", ChatColor.GRAY, + ChatColor.ITALIC, getReason()); + message[2] = String.format( + "%sYour ban (placed by %s%s%s) will %s.", + ChatColor.GRAY, + ChatColor.WHITE, + this.getEnforcer(), + ChatColor.GRAY, + getDuration() != 0 ? String.format("expire in %d seconds", + getDuration()) : "NOT expire"); + + return StringUtils.join(message, '\n'); + + } + + @Override + public String toString() { + if (isPermanent()) + return String.format("%s: %s permanently banned %s: %s", + DateFormat.getInstance().format(getDateIssued()), + getEnforcer(), getSubject(), + getReason()); + + else + return String.format("%s: %s banned %s for %d seconds: %s", + DateFormat.getInstance().format(getDateIssued()), + getEnforcer(), getSubject(), + getDuration(), getReason()); + } + + @Override + /** + * Process actions to be taken when the user's ban has expired. + * Nothing to do here for a ban. + */ + public void onExpire() { + + } + + @Override + public ActiveEvent makeActiveEvent() { + ActiveEvent ab = new ActiveBan(); + ab.setParent(this); + ab.setSubject(getSubject()); + ab.setExpiry(getExpiryDate()); + return ab; + } + +} diff --git a/src/main/java/net/lethargiclion/informaban/events/TimedEvent.java b/src/main/java/net/lethargiclion/informaban/events/TimedEvent.java index 9129edc..2c8ee5e 100644 --- a/src/main/java/net/lethargiclion/informaban/events/TimedEvent.java +++ b/src/main/java/net/lethargiclion/informaban/events/TimedEvent.java @@ -31,6 +31,14 @@ protected boolean apply(Player subject, CommandSender issuer, setDuration(duration); return true; } + + protected boolean apply(String subject, CommandSender issuer, + String reason, int duration) { + if (!super.apply(subject, issuer, reason)) { + return false; + } + return true; + } /** * Reverses the actions (if any) taken when apply() was called on the diff --git a/src/main/java/net/lethargiclion/informaban/events/Unban.java b/src/main/java/net/lethargiclion/informaban/events/Unban.java index e07ccd9..05b3e84 100644 --- a/src/main/java/net/lethargiclion/informaban/events/Unban.java +++ b/src/main/java/net/lethargiclion/informaban/events/Unban.java @@ -8,13 +8,14 @@ import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; +import com.google.common.net.InetAddresses; + @Entity() @DiscriminatorValue("UNBAN") public class Unban extends Event { - - public Unban() { - } - + + public Unban() {} + public boolean apply(ActiveBan ban, CommandSender enforcer, String reason) { @@ -25,11 +26,15 @@ public boolean apply(ActiveBan ban, // Record details super.apply(ban.subject, enforcer, reason); - // Record as unbanned - since banned player must be offline - Bukkit.getOfflinePlayer(ban.subject).setBanned(false); + if (InetAddresses.isInetAddress(ban.subject)) { + Bukkit.unbanIP(ban.subject); + } else { + // Record as unbanned - since banned player must be offline + Bukkit.getOfflinePlayer(ban.subject).setBanned(false); + } return true; } - + @Override public String toString() { return String.format("%s: %s unbanned %s: %s", diff --git a/src/main/resources/Messages.properties b/src/main/resources/Messages.properties index f8533b7..ddfc402 100644 --- a/src/main/resources/Messages.properties +++ b/src/main/resources/Messages.properties @@ -10,11 +10,13 @@ command.ib.usage = Usage: /ib reload command.kick.description = Kicks a player from the server. command.kick.usage = Usage: /kick command.ban.description = Bans a player from the server. -command.ban.usage = Usage: /ban