package edu.washington.cs.publickey.xmpp.server;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Mode;
import edu.washington.cs.publickey.Tools;
import edu.washington.cs.publickey.storage.PersistentStorage;
import edu.washington.cs.publickey.xmpp.XMPPNetwork;
public class PublicKeyXmppServer {
public final static String key_username = "xmpp_username";
public final static String key_password = "xmpp_password";
public final static String key_xmpp_network = "xmpp_network";
private String username;
private String password;
private final PersistentStorage storage;
private static FileWriter log;
private XMPPConnection connection;
private final LinkedList<PublicKeyXmppServerProtocol> protocolHandlers = new LinkedList<PublicKeyXmppServerProtocol>();
static {
try {
log = new FileWriter(new File("xmpp_server_debug.log"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public PublicKeyXmppServer(Properties props, PersistentStorage storage) {
loadProperties(props);
log("Starting IM client for " + username, true);
this.storage = storage;
ConnectionConfiguration connConfig = new ConnectionConfiguration(network.getServerAddr(), network.getServerPort(), network.getServiceName());
this.connect(connConfig);
connection.addPacketListener(new PublicKeyListener(), new Tools.AcceptAllFilter());
Thread protocolHandlerCleaner = new Thread(new Runnable() {
public void run() {
boolean quit = false;
while (!quit) {
synchronized (PublicKeyXmppServer.this) {
for (Iterator<PublicKeyXmppServerProtocol> iter = protocolHandlers.iterator(); iter.hasNext();) {
PublicKeyXmppServerProtocol p = iter.next();
if (p.isTimedOut()) {
p.close();
iter.remove();
}
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
quit = true;
}
}
}
});
protocolHandlerCleaner.setName("XMPP protocol listener killer");
protocolHandlerCleaner.setDaemon(true);
protocolHandlerCleaner.start();
}
private void connect(ConnectionConfiguration connConfig) {
connection = new XMPPConnection(connConfig);
// connect to the xmpp server
try {
connection.connect();
log("Connected to " + connection.getHost(), true);
} catch (XMPPException ex) {
log("Failed to connect to " + connection.getHost(), true);
return;
}
// login
try {
connection.login(username, password);
log("Logged in as " + connection.getUser(), true);
} catch (XMPPException ex) {
log("Failed to log in as " + connection.getUser(), true);
return;
}
// set presence
Presence p = new Presence(Presence.Type.available);
p.setMode(Presence.Mode.available);
connection.sendPacket(p);
// set allow messages from all users
connection.getRoster().setSubscriptionMode(Roster.SubscriptionMode.accept_all);
PacketFilter presenceFilter = new PacketTypeFilter(Presence.class);
PresencePacketListener presencePacketListener = new PresencePacketListener();
connection.addPacketListener(presencePacketListener, presenceFilter);
return;
}
private XMPPNetwork network;
private void loadProperties(Properties props) {
try {
this.username = props.getProperty(key_username);
this.password = props.getProperty(key_password);
this.network = XMPPNetwork.getFromName(props.getProperty(key_xmpp_network));
} catch (Throwable t) {
System.err.println("error loading properties: " + t.getMessage());
t.printStackTrace();
System.exit(1);
}
}
public XMPPNetwork getNetwork() {
return network;
}
public void log(String mesg, boolean toStdOut) {
try {
if (log != null) {
log.write(mesg + "\n");
log.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (toStdOut) {
System.out.println(mesg);
}
}
private class PublicKeyListener implements PacketListener {
public void processPacket(Packet packet) {
Object clientHello = packet.getProperty(Tools.PUBLICKEY_PAYLOAD_KEY__PublicKeyFriend);
// log("got message:" + packet.toXML(), false);
if (clientHello != null) {
log("got client hello from: " + packet.getFrom(), true);
try {
PublicKeyXmppServerProtocol handler = new PublicKeyXmppServerProtocol(packet, connection, storage, PublicKeyXmppServer.this);
synchronized (PublicKeyXmppServer.this) {
protocolHandlers.add(handler);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private class PresencePacketListener implements PacketListener {
public void processPacket(Packet packet) {
Presence presence = (Presence) packet;
String from = presence.getFrom();
// System.out.println("got packet from: " + from + "\n" +
// packet.toXML());
if (presence.getType() == Presence.Type.subscribe) {
// Accept all subscription requests.
Presence response = new Presence(Presence.Type.subscribed);
response.setTo(from);
response.setPacketID(packet.getPacketID());
connection.sendPacket(response);
System.out.println("got presense subscribe from: " + packet.getFrom());
} else if (presence.getType() == Presence.Type.available) {
// Send back that we are available as well
Presence response = new Presence(Presence.Type.available);
response.setMode(Mode.available);
response.setTo(from);
response.setPacketID(packet.getPacketID());
connection.sendPacket(response);
System.out.println("got presense available from: " + packet.getFrom());
// System.out.println("sending presence available: \n" +
// response.toXML());
} else if (presence.getType() == Presence.Type.unsubscribe) {
// Accept all unsubscription requests.
Presence response = new Presence(Presence.Type.unsubscribed);
response.setTo(from);
response.setPacketID(packet.getPacketID());
connection.sendPacket(response);
System.out.println("got presense unsubscribe from: " + packet.getFrom());
// System.out.println("sending presence unsubscribed: \n" +
// response.toXML());
}
}
}
public static interface LimitedXMPPConnection {
public void sendMessage();
}
public void shutdown() {
if (connection.isConnected()) {
log("logging off xmpp: " + username, true);
connection.disconnect();
}
}
}