package com.voxeo.rayo.client.internal; import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.dom4j.Element; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelHandler; import com.rayo.client.verb.RefEvent; import com.rayo.client.xmpp.extensions.Extension; import com.rayo.client.xmpp.stanza.IQ; import com.rayo.client.xmpp.util.Dom4jParser; public class NettyServerHandler extends SimpleChannelHandler { private boolean receivedFirstStream = false; private Channel channel; private List<String> messages = new ArrayList<String>(); private String offerId; private String callId; private String conferenceId; @Override public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { super.channelBound(ctx, e); this.channel = e.getChannel(); } public void sendRayoOffer() { offerId = UUID.randomUUID().toString(); callId = UUID.randomUUID().toString(); String offer = "<presence type=\"set\" id=\"%s\" from=\"%s@localhost\" to=\"userc@localhost/voxeo\"> " + "<offer xmlns=\"urn:xmpp:rayo:1\" to=\"sip:userc@localhost:5060\" from=\"sip:test@someip.com:6089\">" + "<header name=\"Max-Forwards\" value=\"70\"/>" + "</offer></presence>"; sendResponse(channel,String.format(offer,offerId,callId)); } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { String message = (String)e.getMessage(); System.out.println(String.format("Received message %s", message)); try { Thread.sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } if (message.startsWith("<stream")) { sendResponse(channel,"<stream:stream xmlns='jabber:client' id='test' from='localhost' version='1.0' xmlns:stream='http://etherx.jabber.org/streams'>"); sendResponse(channel,"<stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>DIGEST-MD5</mechanism><mechanism>PLAIN</mechanism></mechanisms></stream:features>"); if (receivedFirstStream) { sendResponse(channel,"<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></stream:features>"); sendResponse(channel,"<stream:features><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></stream:features>"); } receivedFirstStream = true; } else if (message.startsWith("<auth")) { sendResponse(channel,"<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==</challenge>"); sendResponse(channel,"<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); } else if (message.contains("<bind")) { Element element = Dom4jParser.parseXml(message); String id = element.attributeValue("id"); sendResponse(channel,"<iq id='"+id+"' type='result' from='localhost' to='userc@localhost'><bind jid='userc@localhost/voxeo'/></iq>"); } else if (message.contains("<session")) { Element element = Dom4jParser.parseXml(message); String id = element.attributeValue("id"); sendResponse(channel,"<iq id='"+id+"' type='result' from='localhost' to='userc@localhost'><session/></iq>"); } else if (message.startsWith("<iq")) { // We may receive a buffer with several IQ messages. // This should be managed much better with the Netty framework. // But do not have enough time right now message = message.trim(); do { int iqEnd = message.indexOf("</iq>"); String iq = message.substring(0,iqEnd+5); processIQMessage(iq); if (iqEnd+5 == message.length()) { break; } else { message = message.substring(iqEnd+5); } } while(true); } } private void processIQMessage(String message) { Element element = Dom4jParser.parseXml(message); IQ iq = new IQ(element); storeIQ(iq); IQ response = null; if (iq.getChildName().equals("say") || iq.getChildName().equals("conference") || iq.getChildName().equals("transfer") || iq.getChildName().equals("ask") || iq.getChildName().equals("dial") || iq.getChildName().equals("join")) { String id = UUID.randomUUID().toString(); // send ref back RefEvent ref = new RefEvent(); ref.setJid(id); response = iq.result(Extension.create(ref)); } else { response = iq.result(); } sendResponse(channel, response.toString()); } private void sendResponse(Channel channel, String response) { System.out.println(String.format("Sending to client: %s",response)); channel.write(response); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { e.getCause().printStackTrace(); Channel ch = e.getChannel(); ch.close(); } private void storeIQ(IQ iq) { messages.add(iq.copy().setId("*").toString()); } public void assertReceived(String message) { // Transform it first to the same format dom4j creates in the server Element element = Dom4jParser.parseXml(message); IQ messageIq = new IQ(element); message = messageIq.toString(); if (!messages.contains(message)) { String errorMessage = String.format("Message %s was not received by the server", message); System.out.println("ERROR: " + errorMessage); System.out.println("List of received messages:"); System.out.println("--------------------------"); int i = 1; for (String string: messages) { System.out.println(i + ": " + string); i++; } System.out.println(errorMessage); throw new IllegalStateException(errorMessage); } } public void resetState() { receivedFirstStream = false; offerId = null; callId = null; messages.clear(); } }