package oculusPrime;
import java.io.*;
import java.net.*;
import java.util.Vector;
/**
* Start the terminal server. Start a new thread for a each connection.
*/
public class TelnetServer implements Observer {
public static enum Commands {chat, exit, bye, quit, state};
public static final boolean ADMIN_ONLY = true;
public static final String MSGPLAYERTAG = "<messageclient>";
public static final String MSGGRABBERTAG = "<messageserverhtml>";
public static final String TELNETTAG = "<telnet>";
public static final String STATETAG = "<state>";
private Vector<PrintWriter> printers = new Vector<PrintWriter>();
private Vector<Socket> socks = new Vector<Socket>();
private oculusPrime.State state = oculusPrime.State.getReference();
private oculusPrime.Settings settings = Settings.getReference();
private BanList banlist = BanList.getRefrence();
private static ServerSocket serverSocket = null;
private static Application app = null;
/** Threaded client handler */
class ConnectionHandler extends Thread {
private Socket clientSocket = null;
private BufferedReader in = null;
private PrintWriter out = null;
public ConnectionHandler(Socket socket) {
clientSocket = socket;
if (banlist.isBanned(clientSocket)){
try {
Util.debug("banned ip: " + clientSocket.toString(), this);
socket.close();
return;
} catch (Exception e) {
Util.log("ConnectionHandler(), banned IP error", e, this);
}
}
try {
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())), true);
} catch (IOException e) {
shutDown("fail aquire tcp streams: " + e.getMessage(), out, in, clientSocket);
return;
}
printers.add(out);
socks.add(clientSocket);
// send banner to terminal
sendToSocket("Welcome to Oculus Prime v" + new Updater().getCurrentVersion(), out);
String ip_address = clientSocket.getInetAddress().toString().substring(1);
sendToSocket(ip_address + " connected via socket", out);
Util.log(ip_address+" connected via socket", this);
this.start();
}
/** do the client thread */
@Override
public void run() {
if(settings.getBoolean(GUISettings.loginnotify)) app.saySpeech("lawg inn telnet");
sendToGroup(TELNETTAG+" "+printers.size() + " tcp connections active");
state.set(oculusPrime.State.values.telnetusers, printers.size());
String str = null;
while (true) {
try {
str = in.readLine();
} catch (Exception e) {
Util.debug("readLine(): " + e.getMessage(), this);
break;
}
// client is terminating?
if (str == null) {
Util.debug("read thread, closing.", this);
break;
}
try {
// parse and run it
str = str.trim();
if(str.length()>=1){
if( ! manageCommand(str, out, in, clientSocket)) {
Util.debug("doPlayer(" + str + ")", this);
doPlayer(str, out);
}
}
} catch (Exception e) {
Util.log("parse error: " + e.getLocalizedMessage(), this);
}
}
// close up, must have a closed socket
if (printers.contains(out)) shutDown("user disconnected", out, in, clientSocket);
}
} // end inner class
// close resources
private void shutDown(final String reason, PrintWriter out, BufferedReader in, Socket clientSocket) {
// log to console, and notify other users of leaving
sendToSocket("shutting down, "+reason, out);
Util.debug("closing socket [" + clientSocket + "] " + reason, this);
try {
// close resources
printers.remove(out);
if(in!=null) in.close();
if(out!=null) out.close();
if(clientSocket!=null) clientSocket.close();
} catch (Exception e) {
Util.log("shutdown: " + e.getMessage(), this);
}
sendToGroup(TELNETTAG+" "+printers.size() + " tcp connections active");
state.set(oculusPrime.State.values.telnetusers, printers.size());
}
private void doPlayer(final String str, PrintWriter out){
if(str == null || out == null) return;
final String[] cmd = str.split(" ");
String args = new String();
for(int i = 1 ; i < cmd.length ; i++) args += " " + cmd[i].trim();
PlayerCommands player = null;
try { // create command from input
player = PlayerCommands.valueOf(cmd[0]);
} catch (Exception e) {
sendToSocket("error: unknown command, " + cmd[0], out);
return;
}
if (player.equals(PlayerCommands.systemcall)) {
sendToSocket("forbidden command, " + cmd[0], out);
return;
}
// check for null vs string("")
args = args.trim();
if(args.length()==0) args = "";
app.driverCallServer(player, args);
}
/** add extra commands, macros here. Return true if the command was found */
private boolean manageCommand(final String str, PrintWriter out, BufferedReader in, Socket clientSocket){
final String[] cmd = str.split(" ");
Commands telnet = null;
try {
telnet = Commands.valueOf(cmd[0]);
} catch (Exception e) {
return false;
}
switch (telnet) {
case chat: // overrides playercommands chat
String args = new String();
for(int i = 1 ; i < cmd.length ; i++) args += " " + cmd[i].trim();
if(args.length()>1) app.driverCallServer(PlayerCommands.chat, args);
return true;
case bye:
case quit:
case exit: shutDown("user left", out, in, clientSocket);
return true;
case state:
if (cmd.length == 3) { // two args
if (cmd[1].equals("delete")) state.delete(cmd[2]);
else state.set(cmd[1], cmd[2]);
}
else if (cmd.length > 3) { // 2nd arg has spaces
String stateval = "";
for (int i=2; i<cmd.length; i++) stateval += cmd[i]+" ";
state.set(cmd[1], stateval.trim());
}
else if (cmd.length == 2) {
sendToSocket("<state> " + cmd[1] + " " + state.get(cmd[1]), out);
}
else { // no args
sendToSocket("<state> " + state.toString(), out);
}
return true;
}
// command was not managed
return false;
}
private void sendToSocket(String str, PrintWriter out) {
Boolean multiline = false;
// if (str.matches(".*<br>.*")) {
// multiline = true;
// str = (str.replaceAll("<br>", "\r\n")).trim();
// }
if (str.contains("<br>")) {
multiline = true;
str = (str.replaceAll("<br>", "\r\n")).trim();
}
if (multiline) { out.print("<multiline> "); }
out.println("<telnet> " + str+"\r");
if (multiline) { out.println("</multiline>"); }
}
@Override
/** send to socket on state change */
public void updated(String key) {
String value = state.get(key); // State.values.valueOf(key));
if(value==null) sendToGroup(STATETAG + " deleted: " + key);
else {
if (State.isNonTelnetBroadCast(key)) return;
sendToGroup(STATETAG + " " + key + " " + value);
}
}
/** send input back to all the clients currently connected */
public void sendToGroup(String str) {
Boolean multiline = false;
if (str.contains("<br>")) {
multiline = true;
str = (str.replaceAll("<br>", "\r\n")).trim();
}
PrintWriter pw = null;
for (int c = 0; c < printers.size(); c++) {
pw = printers.get(c);
if (pw.checkError()) {
printers.remove(pw);
pw.close();
} else {
if (multiline) pw.print("<multiline> ");
pw.println(str);
if (multiline) pw.println("</multiline>");
}
}
}
/** register for updates, share state with all threads */
public TelnetServer(oculusPrime.Application a) {
app = a;
state.addObserver(this);
Util.debug("telnet server started", this);
/** command connections*/
new Thread(new Runnable() {
@Override
public void run() {
while(!settings.readSetting(GUISettings.telnetport).equals(Settings.DISABLED.toString()))
startCommandListener();
}
}).start();
}
/** do forever */
private void startCommandListener(){
try {
serverSocket = new ServerSocket(settings.getInteger(GUISettings.telnetport));
} catch (Exception e) {
Util.log("server sock error: " + e.getMessage(), this);
return;
}
Util.debug("listening with socket: " + serverSocket.toString());
// serve new connections until killed
while (true) {
try {
// new user has connected
new ConnectionHandler(serverSocket.accept());
} catch (Exception e) {
try {
if(serverSocket.isBound())
serverSocket.close();
} catch (IOException ee) {
Util.log("socket error: ", ee, this);
return;
}
return;
}
}
}
public void close() {
for (int c = 0; c < printers.size(); c++) printers.get(c).close();
for (int c = 0; c < socks.size(); c++){
try {
if(socks.get(c).isConnected())
socks.get(c).close();
} catch (IOException e) {
Util.log("failed to close client socket: " + e.getMessage(), this);
}
}
try {
if(serverSocket.isBound())
serverSocket.close();
} catch (IOException e) {
Util.log("failed to close server socket: " + e.getMessage(), this);
}
}
}