package org.exist.irc;
import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.jibble.pircbot.*;
public class IRCSession extends PircBot {
private static final int PING_PERIOD = 20000;
private static final int TIMEOUT = 240000;
private static final String EV_MESSAGE = "message";
private static final String EV_NOTICE = "notice";
private static final String EV_JOIN = "join";
private static final String EV_PART = "part";
private static final String EV_USERS_LIST = "users";
private static final String EV_PING = "ping";
private static char[] CHUNK = new char[8192];
static {
Arrays.fill(CHUNK, ' ');
}
// server and channel settings
private String server;
private String channel;
private String sessionId;
private HttpServletResponse response = null;
private Writer responseWriter = null;
private StringWriter writer = new StringWriter();
private boolean fillChunks = false;
private boolean disconnect = false;
private long lastPing;
private boolean isRunning = false;
private Pattern cmdRegex = Pattern.compile("^/\\s*(\\w+)\\s+(.*)$");
private Matcher matcher = cmdRegex.matcher("");
public IRCSession(String server, String channel, String nick, boolean modProxyHack) throws IOException, NickAlreadyInUseException, IrcException {
super();
this.server = server;
this.channel = channel;
this.sessionId = Integer.toString(hashCode());
this.fillChunks = modProxyHack;
this.setVersion("XIRCProxy 0.1");
this.setLogin("XIRCProxy");
this.setName(nick);
this.setVerbose(true);
this.setEncoding("UTF-8");
connect();
}
protected void connect() throws IOException, IrcException, NickAlreadyInUseException {
log("Connecting to " + server);
connect(server);
log("Join channel: " + channel);
joinChannel(channel);
}
public String getSessionId() {
return sessionId;
}
public boolean started() {
return isRunning;
}
public void run(HttpServletResponse servletResponse) {
setResponseObject(servletResponse);
log("Listening to chat events ...");
disconnect = false;
lastPing = System.currentTimeMillis();
isRunning = true;
while (!disconnect) {
synchronized (this) {
try {
wait(PING_PERIOD);
} catch (InterruptedException e) {
}
}
if (!disconnect) {
long now = System.currentTimeMillis();
if (now - lastPing > TIMEOUT) {
log("No response from client, disconnecting user " + getNick() + " from channel " + channel + " ...");
partChannel(channel, "Connection Timed Out");
quitServer();
} else {
pingClient();
}
}
}
isRunning = false;
flush();
log("Exiting ...");
synchronized(this) {
notifyAll();
}
}
public synchronized void closeTunnel() {
disconnect = true;
notifyAll();
try {
wait();
} catch (InterruptedException e) {
}
}
public synchronized boolean setResponseObject(HttpServletResponse servletResponse) {
this.response = servletResponse;
response.setContentType("text/html");
response.setBufferSize(64);
try {
ServletOutputStream os = response.getOutputStream();
responseWriter = new PrintWriter(new OutputStreamWriter(os, "UTF-8"), false);
responseWriter.write("<html><head>");
responseWriter.write("<title>IRCProxy</title>");
responseWriter.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />");
responseWriter.write("</head><body>");
writeEvent("Connection to proxy opened.", EV_NOTICE);
flush();
} catch (IOException e) {
log("Exception while opening push stream: " + e.getMessage());
return true;
}
return false;
}
public void quit() {
quit("Client Quit");
}
private void quit(String partMessage) {
partChannel(channel, partMessage);
quitServer();
}
public synchronized void send(String message) {
matcher.reset(message);
if (matcher.find()) {
String cmd = matcher.group(1);
message = matcher.group(2);
processCommand(cmd, message);
} else {
sendMessage(channel, message);
writeMessage(getNick(), message);
}
}
public synchronized void attemptReconnect() throws IOException, IrcException {
log("Reconnecting ...");
disconnect = true;
lastPing = System.currentTimeMillis();
writer = new StringWriter();
notifyAll();
if (!isConnected()) {
connect();
}
}
protected void onConnect() {
writeEvent("Connected to server " + this.server + ".", EV_NOTICE);
flush();
}
protected synchronized void onDisconnect() {
disconnect = true;
notifyAll();
}
protected void onMessage(String channel, String sender, String login, String hostname, String message) {
log("Message from " + sender);
writeMessage(sender, message);
}
protected void onJoin(String channel, String sender, String login, String hostname) {
try {
writer.write("<script language=\"JavaScript\" type=\"text/javascript\">");
writer.write(jsCall("dispatchEvent", new String[] {
EV_JOIN, sender,
sender + " [" + hostname + "] has joined " + channel
}));
writer.write("</script>\n\n");
flush();
} catch (Exception e) {
e.printStackTrace();
closeConnection(e.getMessage());
}
}
protected void onPart(String channel, String sender, String login, String hostname) {
try {
writer.write("<script language=\"JavaScript\" type=\"text/javascript\">");
writer.write(jsCall("dispatchEvent", new String[] {
EV_PART, sender,
sender + " [" + hostname + "] has left " + channel
}));
writer.write("</script>\n\n");
flush();
} catch (Exception e) {
e.printStackTrace();
closeConnection(e.getMessage());
}
}
protected void onQuit(String sourceNick, String sourceLogin, String sourceHostname, String reason) {
try {
writer.write("<script language=\"JavaScript\" type=\"text/javascript\">");
writer.write(jsCall("dispatchEvent", new String[] {
EV_PART, sourceNick,
sourceNick + " [" + sourceLogin + '@' + sourceHostname + "] has quit: \"" + reason + '"'
}));
writer.write("</script>\n\n");
flush();
} catch (Exception e) {
e.printStackTrace();
closeConnection(e.getMessage());
}
}
protected void onNotice(String sourceNick, String sourceLogin, String sourceHostname, String target, String notice) {
writeEvent("Notice from " + sourceNick + " [" + sourceHostname + "] to " + target + ": " + notice, EV_NOTICE);
}
protected void onUserList(String channel, User[] users) {
String args[] = new String[users.length + 1];
args[0] = EV_USERS_LIST;
for (int i = 0; i < users.length; i++) {
args[i + 1] = users[i].getNick();
}
try {
writer.write("<script language=\"JavaScript\" type=\"text/javascript\">");
writer.write(jsCall("dispatchEvent", args));
writer.write("</script>\n\n");
flush();
} catch (Exception e) {
e.printStackTrace();
closeConnection(e.getMessage());
}
}
protected void onServerResponse(int i, String string) {
if (i == ReplyConstants.RPL_MOTDSTART || i == ReplyConstants.RPL_MOTD ||
i == ReplyConstants.RPL_ENDOFMOTD) {
writeEvent(string, EV_NOTICE);
}
}
public void pingResponse() {
log("Received ping reponse ...");
lastPing = System.currentTimeMillis();
}
private void pingClient() {
String js = jsCall("dispatchEvent", new String[] { EV_PING });
writeLine(js);
}
private void writeMessage(String sender, String message) {
String js = jsCall("dispatchEvent", new String[] { EV_MESSAGE, sender, message });
writeLine(js);
}
private void writeEvent(String message, String cls) {
String js = jsCall("dispatchEvent", new String[] { cls, message });
writeLine(js);
}
private void writeLine(String data) {
try {
writer.write("<script language=\"JavaScript\" type=\"text/javascript\">");
writer.write(data);
writer.write("</script>\n\n");
flush();
} catch (Exception e) {
e.printStackTrace();
closeConnection(e.getMessage());
}
}
private String jsCall(String func, String[] params) {
StringBuffer buf = new StringBuffer();
buf.append("top.").append(func).append("('");
buf.append(sessionId);
buf.append('\'');
for (int i = 0; i < params.length; i++) {
buf.append(", '");
buf.append(escape(params[i]));
buf.append('\'');
}
buf.append(");");
return buf.toString();
}
private void flush() {
if (response == null)
return;
String data = writer.toString();
writer = new StringWriter();
try {
if (data.length() > 0) {
responseWriter.write(data);
if (fillChunks)
responseWriter.write(CHUNK);
}
responseWriter.flush();
} catch (IOException e) {
log("Exception while flushing servlet output: " + e.getMessage());
outputStreamClosed();
}
}
private void outputStreamClosed() {
log("HttpServletResponse closed.");
response = null;
responseWriter = null;
}
private void closeConnection(String message) {
if (writer != null) {
writer.write("<h1>Error</h1>");
writer.write("<p>Error found: " + message + "</p>");
writer.write("<p>Closing connection.</p>");
flush();
}
partChannel(channel);
quitServer();
}
private String escape(String in) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < in.length(); i++) {
char ch = in.charAt(i);
switch (ch) {
case '\\' :
break;
case '"' :
case '\'' :
buf.append(""");
break;
case '<' :
buf.append("<");
break;
case '>' :
buf.append(">");
break;
case '&' :
buf.append("&");
break;
default:
buf.append(ch);
break;
}
}
return buf.toString();
}
private void processCommand(String command, String message) {
if ("MSG".equalsIgnoreCase(command)) {
String target = new StringTokenizer(message).nextToken();
sendMessage(target, message.substring(target.length() + 1));
} else if ("QUIT".equalsIgnoreCase(command)) {
writeMessage(getNick(), "QUIT: " + message);
quit(message);
} else if ("NICK".equalsIgnoreCase(command)) {
writeMessage(getNick(), "Trying to change nick to " + message);
changeNick(message);
send("/list");
}
}
public static void main(String[] args) {
Pattern cmdRegex = Pattern.compile("^/\\s*(\\w+)\\s+(.*)$");
Matcher matcher = cmdRegex.matcher("");
matcher.reset("/quit abcdefg");
if (matcher.find()) {
System.out.println("Command: " + matcher.group(1) + " - " + matcher.group(2));
} else {
System.out.println("Nothing");
}
}
}