package com.nabalive.server.jabber;
import com.nabalive.server.jabber.handler.IqHandler;
import com.nabalive.server.jabber.handler.MessageHandler;
import com.nabalive.server.jabber.handler.PresenceHandler;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.handler.codec.base64.Base64;
import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
import org.jboss.netty.handler.timeout.IdleStateEvent;
import org.jboss.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by IntelliJ IDEA.
* User: Julien Cheype
* Date: 11/15/11
*/
@Component
public class NabaliveServerHandler extends IdleStateAwareChannelHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ConnectionManager connectionManager;
@Autowired
IqHandler iqHandler;
@Autowired
PresenceHandler presenceHandler;
@Autowired
MessageHandler messageHandler;
AtomicLong id = new AtomicLong(System.currentTimeMillis());
Pattern response64Pattern = Pattern.compile("<response[^>]*>(.*)</response>");
//username="xxxxxx",nonce="47313",cnonce="8551452066932",nc=00000001,qop=auth,digest-uri="xmpp/www.jcheype.com",response=327aff5fc68a25524df782c8a8883e44,charset=utf-
Pattern authPattern = Pattern.compile("username=\"([^\"]*)\".*");
private Status getStatus(ChannelHandlerContext ctx) {
synchronized (ctx) {
Status status = (Status) ctx.getAttachment();
if (status == null) {
status = new Status(ctx, connectionManager.getEventListeners());
ctx.setAttachment(status);
}
return status;
}
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
final ChannelBuffer messageCB = (ChannelBuffer) e.getMessage();
String message = messageCB.toString(CharsetUtil.UTF_8);
final Status status = getStatus(ctx);
if (status.getJid() != null)
logger.debug(status.getJid().getUser() + " <<<<<<<<<<< {}", message);
else
logger.debug("<<<<<<<<<<< {}", message);
if (message.startsWith("<?xml ")) {
onStreamOpen(ctx, e, message);
} else if (message.startsWith("<auth ")) {
onAuth(ctx, e, message);
} else if (message.startsWith("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'")) {
onResponse(ctx, e, message);
} else if (message.startsWith("<iq ")) {
iqHandler.onMessage(ctx, e, status, message);
} else if (message.startsWith("<presence ")) {
presenceHandler.onMessage(ctx, e, status, message);
} else if (message.startsWith("<message ")) {
messageHandler.onMessage(ctx, e, status, message);
}
}
private void write(Channel channel, String data) {
logger.debug(">>>>>>>>>> " + data);
channel.write(ChannelBuffers.copiedBuffer(data.getBytes(CharsetUtil.UTF_8)));
}
private void onResponse(ChannelHandlerContext ctx, MessageEvent e, String message) {
final Matcher matcher = response64Pattern.matcher(message);
if (matcher.matches()) {
String base64Response = matcher.group(1);
final ChannelBuffer decode = Base64.decode(ChannelBuffers.copiedBuffer(base64Response.getBytes(CharsetUtil.UTF_8)));
String decodedAuth = decode.toString(CharsetUtil.UTF_8);
logger.debug("<<<DECODED<<<<" + decodedAuth);
Matcher matcherAuth = authPattern.matcher(decodedAuth);
if (matcherAuth.matches()) {
String username = matcherAuth.group(1);
Status status = getStatus(ctx);
status.setAuthenticated(true);
status.setUsername(username);
connectionManager.put(username, status);
}
}
final String reply1 = "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>";
write(e.getChannel(), reply1);
}
private void onAuth(ChannelHandlerContext ctx, MessageEvent e, String message) {
Random randomGenerator = new Random();
final String chanllenge = "nonce=\"" + randomGenerator.nextInt(99999) + "\",qop=\"auth\",charset=utf-8,algorithm=md5-sess";
final ChannelBuffer encode = Base64.encode(ChannelBuffers.copiedBuffer(chanllenge.getBytes(CharsetUtil.UTF_8)));
final String reply1 = "<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" +
encode.toString(CharsetUtil.UTF_8) +
"</challenge>";
write(e.getChannel(), reply1);
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
String username = getStatus(ctx).getUsername();
logger.debug("Channel closed: " + username);
if (username != null)
connectionManager.remove(username);
}
private void onStreamOpen(ChannelHandlerContext ctx, MessageEvent e, String message) {
if (!getStatus(ctx).isAuthenticated()) {
final String reply1 = "<?xml version='1.0'?><stream:stream xmlns='jabber:client' " +
"xmlns:stream='http://etherx.jabber.org/streams' id='" + id.incrementAndGet() + "' " +
"from='www.nabalive.server.jabber.com' version='1.0' xml:lang='en'>";
write(e.getChannel(), reply1);
final String reply2 = "<stream:features>\n" +
"<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>\n" +
"<mechanism>\n" +
"DIGEST-MD5\n" +
"</mechanism>\n" +
"<mechanism>\n" +
"PLAIN\n" +
"</mechanism>\n" +
"</mechanisms>\n" +
"<register xmlns='http://violet.net/features/violet-register'/>\n" +
"</stream:features>";
write(e.getChannel(), reply2);
} else {
write(e.getChannel(), "<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='1331400675' from='www.nabalive.server.jabber.com' version='1.0' xml:lang='en'>");
write(e.getChannel(), "<stream:features><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><required/></bind><unbind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></stream:features>");
}
}
@Override
public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) throws Exception {
String username = getStatus(ctx).getUsername();
logger.debug("Channel idle: " + username);
e.getChannel().close();
}
}