AbstractHttpOverXmppProvider.java
/**
*
* Copyright 2014 Andriy Tsykholyas
*
* 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.jivesoftware.smackx.hoxt.provider;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.hoxt.packet.AbstractHttpOverXmpp;
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
import org.jivesoftware.smackx.shim.provider.HeadersProvider;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
/**
* Abstract parent for Req and Resp packet providers.
*
* @author Andriy Tsykholyas
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
*/
public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmpp> extends IQProvider<H> {
private static final String ELEMENT_DATA = "data";
private static final String ELEMENT_TEXT = "text";
private static final String ELEMENT_BASE_64 = "base64";
private static final String ELEMENT_CHUNKED_BASE_64 = "chunkedBase64";
private static final String ELEMENT_XML = "xml";
static final String ELEMENT_IBB = "ibb";
static final String ELEMENT_SIPUB = "sipub";
static final String ELEMENT_JINGLE = "jingle";
private static final String ATTRIBUTE_STREAM_ID = "streamId";
private static final String ATTRIBUTE_SID = "sid";
static final String ATTRIBUTE_VERSION = "version";
/**
* Parses Headers and Data elements.
*
* @param parser parser
* @param elementName name of concrete implementation of this element
* @param body parent Body element
* @throws Exception
*/
protected void parseHeadersAndData(XmlPullParser parser, String elementName, AbstractHttpOverXmpp body) throws Exception {
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals(HeadersExtension.ELEMENT)) {
HeadersExtension headersExtension = HeadersProvider.INSTANCE.parse(parser);
body.setHeaders(headersExtension);
} else if (parser.getName().endsWith(ELEMENT_DATA)) {
AbstractHttpOverXmpp.Data data = parseData(parser);
body.setData(data);
} else {
throw new IllegalArgumentException("unexpected tag:" + parser.getName() + "'");
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(elementName)) {
done = true;
}
}
}
}
private AbstractHttpOverXmpp.Data parseData(XmlPullParser parser) throws XmlPullParserException, IOException {
NamedElement child = null;
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals(ELEMENT_TEXT)) {
child = parseText(parser);
} else if (parser.getName().equals(ELEMENT_BASE_64)) {
child = parseBase64(parser);
} else if (parser.getName().equals(ELEMENT_CHUNKED_BASE_64)) {
child = parseChunkedBase64(parser);
} else if (parser.getName().equals(ELEMENT_XML)) {
child = parseXml(parser);
} else if (parser.getName().equals(ELEMENT_IBB)) {
child = parseIbb(parser);
} else if (parser.getName().equals(ELEMENT_SIPUB)) {
// TODO: sipub is allowed by xep-0332, but is not implemented yet
throw new UnsupportedOperationException("sipub is not supported yet");
} else if (parser.getName().equals(ELEMENT_JINGLE)) {
// TODO: jingle is allowed by xep-0332, but is not implemented yet
throw new UnsupportedOperationException("jingle is not supported yet");
} else {
// other elements are not allowed
throw new IllegalArgumentException("unsupported child tag: " + parser.getName());
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(ELEMENT_DATA)) {
done = true;
}
}
}
AbstractHttpOverXmpp.Data data = new AbstractHttpOverXmpp.Data(child);
return data;
}
private AbstractHttpOverXmpp.Text parseText(XmlPullParser parser) throws XmlPullParserException, IOException {
String text = null;
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(ELEMENT_TEXT)) {
done = true;
} else {
throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
}
} else if (eventType == XmlPullParser.TEXT) {
text = parser.getText();
} else {
throw new IllegalArgumentException("unexpected eventType: " + eventType);
}
}
return new AbstractHttpOverXmpp.Text(text);
}
private AbstractHttpOverXmpp.Xml parseXml(XmlPullParser parser) throws XmlPullParserException, IOException {
StringBuilder builder = new StringBuilder();
boolean done = false;
boolean startClosed = true;
while (!done) {
int eventType = parser.next();
if ((eventType == XmlPullParser.END_TAG) && parser.getName().equals(ELEMENT_XML)) {
done = true;
} else { // just write everything else as text
if (eventType == XmlPullParser.START_TAG) {
if (!startClosed) {
builder.append('>');
}
builder.append('<');
builder.append(parser.getName());
appendXmlAttributes(parser, builder);
startClosed = false;
} else if (eventType == XmlPullParser.END_TAG) {
if (startClosed) {
builder.append("</");
builder.append(parser.getName());
builder.append('>');
} else {
builder.append("/>");
startClosed = true;
}
} else if (eventType == XmlPullParser.TEXT) {
if (!startClosed) {
builder.append('>');
startClosed = true;
}
builder.append(StringUtils.escapeForXML(parser.getText()));
} else {
throw new IllegalArgumentException("unexpected eventType: " + eventType);
}
}
}
return new AbstractHttpOverXmpp.Xml(builder.toString());
}
private void appendXmlAttributes(XmlPullParser parser, StringBuilder builder) {
// NOTE: for now we ignore namespaces
int count = parser.getAttributeCount();
if (count > 0) {
for (int i = 0; i < count; i++) {
builder.append(' ');
builder.append(parser.getAttributeName(i));
builder.append("=\"");
builder.append(StringUtils.escapeForXML(parser.getAttributeValue(i)));
builder.append('"');
}
}
}
private AbstractHttpOverXmpp.Base64 parseBase64(XmlPullParser parser) throws XmlPullParserException, IOException {
String text = null;
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(ELEMENT_BASE_64)) {
done = true;
} else {
throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
}
} else if (eventType == XmlPullParser.TEXT) {
text = parser.getText();
} else {
throw new IllegalArgumentException("unexpected eventType: " + eventType);
}
}
return new AbstractHttpOverXmpp.Base64(text);
}
private AbstractHttpOverXmpp.ChunkedBase64 parseChunkedBase64(XmlPullParser parser) throws XmlPullParserException, IOException {
String streamId = parser.getAttributeValue("", ATTRIBUTE_STREAM_ID);
AbstractHttpOverXmpp.ChunkedBase64 child = new AbstractHttpOverXmpp.ChunkedBase64(streamId);
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(ELEMENT_CHUNKED_BASE_64)) {
done = true;
} else {
throw new IllegalArgumentException("unexpected end tag: " + parser.getName());
}
} else {
throw new IllegalArgumentException("unexpected event type: " + eventType);
}
}
return child;
}
private AbstractHttpOverXmpp.Ibb parseIbb(XmlPullParser parser) throws XmlPullParserException, IOException {
String sid = parser.getAttributeValue("", ATTRIBUTE_SID);
AbstractHttpOverXmpp.Ibb child = new AbstractHttpOverXmpp.Ibb(sid);
boolean done = false;
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(ELEMENT_IBB)) {
done = true;
} else {
throw new IllegalArgumentException("unexpected end tag: " + parser.getName());
}
} else {
throw new IllegalArgumentException("unexpected event type: " + eventType);
}
}
return child;
}
}