/**
* bSocks 1.2-SNAPSHOT
* Copyright (C) 2013 CodingBadgers <plugins@mcbadgercraft.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package uk.codingbadgers.bsocks.threading;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import uk.codingbadgers.bsocks.bSocksModule;
import uk.codingbadgers.bsocks.commands.WebCommand;
import uk.codingbadgers.bsocks.commands.WebCommandHandler;
public class RequestHandler extends Thread {
final private Socket m_sock;
final private String m_passHash;
final private JavaPlugin m_plugin;
final private bSocksModule m_module;
/**
* Instantiates a new request handler.
*
* @param plugin the plugin instance
* @param passHash the password as an md5 hash
* @param sock the socket the connection was made on
* @param module the module instance
*/
public RequestHandler(JavaPlugin plugin, String passHash, Socket sock, bSocksModule module) {
m_sock = sock;
m_plugin = plugin;
m_passHash = passHash;
m_module = module;
}
/**
* Run the request handler thread
*/
public void run() {
JSONParser parser = new JSONParser();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(m_sock.getInputStream()));
JSONObject json = null;
String line = br.readLine();
try {
json = (JSONObject)parser.parse(line);
} catch (ParseException pe) {
m_module.log(Level.WARNING, "Error Parsing: " + line);
m_module.log(Level.WARNING, pe.toString());
br.close();
m_sock.close();
return;
}
if (json == null) {
m_module.log(Level.SEVERE, "Invalid request from " + m_sock.getInetAddress().getHostAddress());
br.close();
m_sock.close();
return;
}
if (json.get("password") == null || !json.get("password").equals(m_passHash)) {
m_module.log(Level.SEVERE, "Wrong password from " + m_sock.getInetAddress().getHostAddress());
br.close();
m_sock.close();
return;
}
String type = (String)json.get("command");
if (type.equals("message")) {
handleMessage(json);
} else if (type.equals("serverstats")) {
handleServerStatsCommand(json, m_sock);
} else if (type.equals("lookup")) {
handleLookup(json, m_sock);
} else if (type.equals("executeCommand")) {
handleExecuteCommand(json);
} else {
for (WebCommand command : WebCommandHandler.getCommands()) {
if (command.getLabel().equalsIgnoreCase(type)) {
JSONObject responce = command.handleCommand(json);
if (responce != null) {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(m_sock.getOutputStream()));
bw.write(responce.toJSONString() + "\n");
bw.flush();
bw.close();
}
break;
}
}
}
br.close();
m_sock.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Handle lookup. Looks for a player whos name starts with a given string
*
* @param command the command
* @param sock the sock
* @throws IOException Signals that an I/O exception has occurred.
*/
@SuppressWarnings("unchecked")
private void handleLookup(JSONObject command, Socket sock) throws IOException {
JSONObject responce = new JSONObject();
JSONArray players = new JSONArray();
for (OfflinePlayer player : m_plugin.getServer().getOfflinePlayers()) {
if (player.getName().startsWith((String) command.get("player"))) {
players.add(player.getName());
}
}
responce.put("players", players);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
bw.write(responce.toJSONString() + "\n");
bw.flush();
bw.close();
}
/**
* Handle server stats command. Returning information about the server and its players
*
* @param command the command
* @param sock the sock
* @throws Exception the exception
*/
@SuppressWarnings("unchecked")
private void handleServerStatsCommand(JSONObject command, Socket sock) throws Exception {
Server server = m_plugin.getServer();
Player[] onlinePlayers = server.getOnlinePlayers();
Runtime runtime = Runtime.getRuntime();
JSONObject responce = new JSONObject();
responce.put("bukkit-version", server.getBukkitVersion());
responce.put("server-name", server.getServerName());
responce.put("using-memory-bytes", (runtime.totalMemory() - runtime.freeMemory()));
responce.put("max-memory-bytes", runtime.maxMemory());
responce.put("noof-processors", runtime.availableProcessors());
responce.put("max-players", Integer.toString(server.getMaxPlayers()));
responce.put("online-players", Integer.toString(onlinePlayers.length));
JSONArray players = new JSONArray();
bSocksModule module = bSocksModule.getInstance();
for (Player player : onlinePlayers) {
JSONObject jplayer = new JSONObject();
jplayer.put("player-name", player.getName());
jplayer.put("player-display-name", player.getDisplayName());
JSONArray groups = new JSONArray();
for (String group : m_module.getPermissions().getPlayerGroups(player)) {
if (module.isValidRank(group)) {
JSONObject jgroup = new JSONObject();
jgroup.put("group", group);
groups.add(jgroup);
}
}
if (groups.isEmpty()) {
JSONObject jgroup = new JSONObject();
jgroup.put("group", module.getDefaultRank());
groups.add(jgroup);
}
jplayer.put("rank-name", groups);
players.add(jplayer);
}
responce.put("online-players", players);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
bw.write(responce.toJSONString() + "\n");
bw.flush();
bw.close();
}
/**
* Handle a message request, either sending a message to a single player, the entire server
* or the entire server excluding a given player.
*
* @param command the command
*/
private void handleMessage(JSONObject command) {
String mode = (String)command.get("mode");
// send a message to a single player
if (mode.equalsIgnoreCase("sendMessage")) {
String playerName = (String) command.get("playerName");
final String message = formatMessage((String) command.get("context"));
m_module.log(Level.INFO, "Sending message '" + message + "' to " + playerName);
final Player player = m_plugin.getServer().getPlayer(playerName);
if (player != null) {
runSync(new Runnable() {
public void run() {
player.sendMessage(message);
}
});
}
}
// send a message to all players on the server
else if (mode.equalsIgnoreCase("sendMessageAll")) {
final String message = formatMessage((String)command.get("context"));
m_module.log(Level.INFO, "Sending message '" + message + "' to all players");
runSync(new Runnable() {
public void run() {
m_plugin.getServer().broadcastMessage(message);
}
});
}
// message all players, excluding a given player
else if (mode.equalsIgnoreCase("sendMessageAllEx")) {
Player excludePlayer = m_plugin.getServer().getPlayer((String)command.get("exludedPlayer"));
final String message = formatMessage((String) command.get("context"));
m_module.log(Level.INFO, " Sending message '" + message + "' to all players apart from " + excludePlayer.getName());
Player[] players = m_plugin.getServer().getOnlinePlayers();
for (int i = 0; i < players.length; ++i) {
final Player p = players[i];
if (p != excludePlayer) {
runSync(new Runnable() {
public void run() {
p.sendMessage(message);
}
});
}
}
}
else
{
// We got told to send a message, but with an unknown mode.
m_module.log(Level.WARNING, "Unknown message mode '" + mode + "'");
}
}
/**
* Run a task on the main server thread.
*
* @param runnable
*/
private void runSync(Runnable runnable) {
Bukkit.getScheduler().runTask(m_plugin, runnable);
}
/**
* Handle execute commands.
*
* @param command the command to handle
*/
private void handleExecuteCommand(JSONObject json) {
// store the name of the sender of the command
String senderName = (String)json.get("sender");
CommandSender sender = null;
// see if its a player command or a console command
if (senderName.equalsIgnoreCase("Console"))
sender = m_plugin.getServer().getConsoleSender();
else
sender = m_plugin.getServer().getPlayer(senderName);
// couldn't find a sender, so don't process the command
if (sender == null) {
m_module.log(Level.SEVERE, "Player " + senderName + " does not exist");
return;
}
// get the command
final String command = (String)json.get("context");
final CommandSender csender = sender;
runSync(new Runnable() {
public void run() {
m_plugin.getServer().dispatchCommand(csender, command);
}
});
m_module.log(Level.INFO, "Executed command '" + command + "' as " + senderName);
}
/**
* Format a message.
*
* @param message the message to format
* @return the formatted message
*/
private String formatMessage(String message) {
message = message.trim();
message = ChatColor.translateAlternateColorCodes('&', message);
return message;
}
}