blob: 9c2a7e9fde478b7eddb52fa8cdd0d0a4414517f4 [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
*
* 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 org.onosproject.xmpp.core.ctl.handlers;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.QName;
import org.onosproject.xmpp.core.XmppConstants;
import org.onosproject.xmpp.core.ctl.XmppValidator;
import org.onosproject.xmpp.core.ctl.exception.UnsupportedStanzaTypeException;
import org.onosproject.xmpp.core.ctl.exception.XmppValidationException;
import org.onosproject.xmpp.core.stream.XmppStreamClose;
import org.onosproject.xmpp.core.stream.XmppStreamError;
import org.onosproject.xmpp.core.stream.XmppStreamOpen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.Presence;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.Iterator;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Translates XML Element to XMPP Packet.
*/
public class XmppDecoder extends MessageToMessageDecoder {
private final Logger logger = LoggerFactory.getLogger(getClass());
private XmppValidator validator = new XmppValidator();
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, Object object, List out) throws Exception {
if (object instanceof Element) {
Element root = (Element) object;
try {
Packet packet = recognizeAndReturnXmppPacket(root);
validate(packet);
out.add(packet);
} catch (UnsupportedStanzaTypeException e) {
throw e;
} catch (Exception e) {
throw new XmppValidationException(false);
}
} else if (object instanceof XMLEvent) {
XMLEvent event = (XMLEvent) object;
if (event.isStartElement()) {
final StartElement element = event.asStartElement();
if (element.getName().getLocalPart().equals(XmppConstants.STREAM_QNAME)) {
DocumentFactory df = DocumentFactory.getInstance();
QName qname = (element.getName().getPrefix() == null) ?
df.createQName(element.getName().getLocalPart(),
element.getName().getNamespaceURI()) :
df.createQName(element.getName().getLocalPart(),
element.getName().getPrefix(), element.getName().getNamespaceURI());
Element newElement = df.createElement(qname);
Iterator nsIt = element.getNamespaces();
// add all relevant XML namespaces to Element
while (nsIt.hasNext()) {
Namespace ns = (Namespace) nsIt.next();
newElement.addNamespace(ns.getPrefix(), ns.getNamespaceURI());
}
Iterator attrIt = element.getAttributes();
// add all attributes to Element
while (attrIt.hasNext()) {
Attribute attr = (Attribute) attrIt.next();
newElement.addAttribute(attr.getName().getLocalPart(), attr.getValue());
}
XmppStreamOpen xmppStreamOpen = new XmppStreamOpen(newElement);
validator.validateStream(xmppStreamOpen);
out.add(xmppStreamOpen);
}
} else if (event.isEndElement()) {
out.add(new XmppStreamClose());
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.info("Exception caught: {}", cause.getMessage());
if (cause.getCause() instanceof XmppValidationException) {
if (((XmppValidationException) cause.getCause()).isStreamValidationException()) {
XmppStreamError.Condition condition = XmppStreamError.Condition.bad_format;
XmppStreamError error = new XmppStreamError(condition);
ctx.channel().writeAndFlush(error);
ctx.channel().writeAndFlush(new XmppStreamClose());
return;
}
}
logger.info("Not a StreamValidationException. Sending exception upstream.");
ctx.fireExceptionCaught(cause);
}
private void validate(Packet packet) throws UnsupportedStanzaTypeException, XmppValidationException {
validator.validate(packet);
}
protected Packet recognizeAndReturnXmppPacket(Element root)
throws UnsupportedStanzaTypeException, IllegalArgumentException {
checkNotNull(root);
Packet packet = null;
if (root.getName().equals(XmppConstants.IQ_QNAME)) {
packet = new IQ(root);
} else if (root.getName().equals(XmppConstants.MESSAGE_QNAME)) {
packet = new Message(root);
} else if (root.getName().equals(XmppConstants.PRESENCE_QNAME)) {
packet = new Presence(root);
} else {
throw new UnsupportedStanzaTypeException("Unrecognized XMPP Packet");
}
logger.info("XMPP Packet received\n" + root.asXML());
return packet;
}
}