es.udc.pfc.xmpp.handler.XEP0114Decoder.java Source code

Java tutorial

Introduction

Here is the source code for es.udc.pfc.xmpp.handler.XEP0114Decoder.java

Source

/**
 * Copyright 2012 Jos Martnez
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package es.udc.pfc.xmpp.handler;

import static com.google.common.base.Preconditions.checkNotNull;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.util.CharsetUtil;

import com.google.common.hash.Hashing;

import es.udc.pfc.xmpp.stanza.Stanza;
import es.udc.pfc.xmpp.stanza.XMPPNamespaces;
import es.udc.pfc.xmpp.xml.XMLElement;

/**
 * XEP-0114 Stream Decoder.
 */
public class XEP0114Decoder extends SimpleChannelHandler {

    private static final QName STREAM_NAME = new QName(XMPPNamespaces.STREAM, "stream", "stream");

    private static enum Status {
        CONNECT, AUTHENTICATE, READY, DISCONNECTED;
    }

    private final String serverName;
    private final String secret;
    private Status status;
    private String streamID;

    public XEP0114Decoder(String serverName, String secret) throws XMLStreamException {
        super();

        this.serverName = checkNotNull(serverName);
        this.secret = checkNotNull(secret);

        status = Status.CONNECT;
    }

    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        Channels.write(ctx.getChannel(), ChannelBuffers.copiedBuffer(
                "<stream:stream xmlns='jabber:component:accept' xmlns:stream='http://etherx.jabber.org/streams' to='"
                        + serverName + "'>",
                CharsetUtil.UTF_8));

        //ctx.sendUpstream(e);
    }

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        if (e.getMessage() instanceof XMLEvent) {
            final XMLEvent event = (XMLEvent) e.getMessage();

            switch (status) {
            case CONNECT:
                if (event.isStartElement()) {
                    final StartElement element = event.asStartElement();

                    if (STREAM_NAME.equals(element.getName())
                            && XMPPNamespaces.ACCEPT.equals(element.getNamespaceURI(null))) {
                        if (!serverName.equals(element.getAttributeByName(new QName("from")).getValue())) {
                            throw new Exception("server name mismatch");
                        }
                        streamID = element.getAttributeByName(new QName("id")).getValue();

                        status = Status.AUTHENTICATE;
                        Channels.write(ctx.getChannel(),
                                ChannelBuffers.copiedBuffer("<handshake>"
                                        + Hashing.sha1().hashString(streamID + secret, CharsetUtil.UTF_8).toString()
                                        + "</handshake>", CharsetUtil.UTF_8));
                    }
                } else {
                    throw new Exception("Expected stream:stream element");
                }
                break;
            case AUTHENTICATE:
            case READY:
                if (event.isEndElement()) {
                    final EndElement element = event.asEndElement();

                    if (STREAM_NAME.equals(element.getName())) {
                        Channels.disconnect(ctx.getChannel());
                        return;
                    }
                }
                break;
            case DISCONNECTED:
                throw new Exception("received DISCONNECTED");
            }
        } else if (e.getMessage() instanceof XMLElement) {
            final XMLElement element = (XMLElement) e.getMessage();

            switch (status) {
            case AUTHENTICATE:
                if (!"handshake".equals(element.getTagName()))
                    throw new Exception("expected handshake");
                status = Status.READY;
                System.out.println("logged in");

                ctx.getPipeline().get(XMPPStreamHandler.class).loggedIn();

                break;
            case READY:
                final Stanza stanza = Stanza.fromElement(element);
                if (stanza == null)
                    throw new Exception("Unknown stanza");

                Channels.fireMessageReceived(ctx, stanza);
                break;
            default:
                throw new Exception("unexpected handleElement");
            }
        } else {
            ctx.sendUpstream(e);
        }
    }

    @Override
    public void disconnectRequested(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
        Channels.write(ctx, e.getFuture(), ChannelBuffers.copiedBuffer("</stream:stream>", CharsetUtil.UTF_8));
    }

}