2014-03-18 00:51:34 +01:00
|
|
|
/**
|
|
|
|
*
|
2019-02-10 19:01:47 +01:00
|
|
|
* Copyright 2014 Andriy Tsykholyas, 2015-2019 Florian Schmaus
|
2014-03-18 00:51:34 +01:00
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
|
2017-06-14 17:12:43 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
|
2014-11-07 21:12:01 +01:00
|
|
|
import org.jivesoftware.smack.packet.NamedElement;
|
2019-02-10 19:01:47 +01:00
|
|
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
2014-03-18 00:51:34 +01:00
|
|
|
import org.jivesoftware.smack.provider.IQProvider;
|
|
|
|
import org.jivesoftware.smack.util.StringUtils;
|
2019-05-06 22:06:13 +02:00
|
|
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
|
|
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2014-03-18 00:51:34 +01:00
|
|
|
import org.jivesoftware.smackx.hoxt.packet.AbstractHttpOverXmpp;
|
|
|
|
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
|
2015-01-01 23:57:44 +01:00
|
|
|
import org.jivesoftware.smackx.shim.provider.HeadersProvider;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2014-03-18 00:51:34 +01:00
|
|
|
/**
|
2018-03-31 14:17:30 +02:00
|
|
|
* Abstract parent for Req and Resp stanza providers.
|
2014-03-18 00:51:34 +01:00
|
|
|
*
|
|
|
|
* @author Andriy Tsykholyas
|
|
|
|
* @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
|
|
|
|
*/
|
2014-10-07 21:15:20 +02:00
|
|
|
public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmpp> extends IQProvider<H> {
|
2014-03-18 00:51:34 +01:00
|
|
|
|
|
|
|
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";
|
|
|
|
|
|
|
|
/**
|
2015-06-29 12:10:01 +02:00
|
|
|
* Parses HeadersExtension element if any.
|
2014-03-18 00:51:34 +01:00
|
|
|
*
|
2015-06-29 12:10:01 +02:00
|
|
|
* @param parser parser
|
|
|
|
* @return HeadersExtension or null if no headers
|
2019-10-30 12:02:36 +01:00
|
|
|
* @throws XmlPullParserException if an error in the XML parser occurred.
|
|
|
|
* @throws IOException if an I/O error occurred.
|
2019-08-30 12:08:30 +02:00
|
|
|
* @throws SmackParsingException if the Smack parser (provider) encountered invalid input.
|
2014-03-18 00:51:34 +01:00
|
|
|
*/
|
2019-02-10 19:01:47 +01:00
|
|
|
protected HeadersExtension parseHeaders(XmlPullParser parser) throws IOException, XmlPullParserException, SmackParsingException {
|
2015-06-29 12:10:01 +02:00
|
|
|
HeadersExtension headersExtension = null;
|
|
|
|
/* We are either at start of headers, start of data or end of req/res */
|
2019-05-06 22:06:13 +02:00
|
|
|
if (parser.next() == XmlPullParser.Event.START_ELEMENT && parser.getName().equals(HeadersExtension.ELEMENT)) {
|
2015-06-29 12:10:01 +02:00
|
|
|
headersExtension = HeadersProvider.INSTANCE.parse(parser);
|
|
|
|
parser.next();
|
2014-03-18 00:51:34 +01:00
|
|
|
}
|
2015-06-29 12:10:01 +02:00
|
|
|
|
|
|
|
return headersExtension;
|
2014-03-18 00:51:34 +01:00
|
|
|
}
|
|
|
|
|
2015-06-29 12:10:01 +02:00
|
|
|
/**
|
|
|
|
* Parses Data element if any.
|
|
|
|
*
|
|
|
|
* @param parser parser
|
|
|
|
* @return Data or null if no data
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2019-10-30 12:02:36 +01:00
|
|
|
* @throws XmlPullParserException if an error in the XML parser occurred.
|
|
|
|
* @throws IOException if an I/O error occurred.
|
2015-06-29 12:10:01 +02:00
|
|
|
*/
|
|
|
|
protected AbstractHttpOverXmpp.Data parseData(XmlPullParser parser) throws XmlPullParserException, IOException {
|
2014-11-07 21:12:01 +01:00
|
|
|
NamedElement child = null;
|
2014-03-18 00:51:34 +01:00
|
|
|
boolean done = false;
|
2015-06-29 12:10:01 +02:00
|
|
|
AbstractHttpOverXmpp.Data data = null;
|
|
|
|
/* We are either at start of data or end of req/res */
|
2019-05-06 22:06:13 +02:00
|
|
|
if (parser.getEventType() == XmlPullParser.Event.START_ELEMENT) {
|
2015-06-29 12:10:01 +02:00
|
|
|
while (!done) {
|
2019-05-06 22:06:13 +02:00
|
|
|
XmlPullParser.Event eventType = parser.next();
|
2014-03-18 00:51:34 +01:00
|
|
|
|
2019-05-06 22:06:13 +02:00
|
|
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
2015-06-29 12:10:01 +02:00
|
|
|
switch (parser.getName()) {
|
|
|
|
case ELEMENT_TEXT:
|
|
|
|
child = parseText(parser);
|
|
|
|
break;
|
|
|
|
case ELEMENT_BASE_64:
|
|
|
|
child = parseBase64(parser);
|
|
|
|
break;
|
|
|
|
case ELEMENT_CHUNKED_BASE_64:
|
|
|
|
child = parseChunkedBase64(parser);
|
|
|
|
break;
|
|
|
|
case ELEMENT_XML:
|
|
|
|
child = parseXml(parser);
|
|
|
|
break;
|
|
|
|
case ELEMENT_IBB:
|
|
|
|
child = parseIbb(parser);
|
|
|
|
break;
|
|
|
|
case ELEMENT_SIPUB:
|
|
|
|
// TODO: sipub is allowed by xep-0332, but is not
|
|
|
|
// implemented yet
|
|
|
|
throw new UnsupportedOperationException("sipub is not supported yet");
|
|
|
|
case ELEMENT_JINGLE:
|
|
|
|
// TODO: jingle is allowed by xep-0332, but is not
|
|
|
|
// implemented yet
|
|
|
|
throw new UnsupportedOperationException("jingle is not supported yet");
|
|
|
|
default:
|
|
|
|
// other elements are not allowed
|
|
|
|
throw new IllegalArgumentException("unsupported child tag: " + parser.getName());
|
|
|
|
}
|
2019-05-06 22:06:13 +02:00
|
|
|
} else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
2015-06-29 12:10:01 +02:00
|
|
|
if (parser.getName().equals(ELEMENT_DATA)) {
|
|
|
|
done = true;
|
|
|
|
}
|
2014-03-18 00:51:34 +01:00
|
|
|
}
|
|
|
|
}
|
2015-06-29 12:10:01 +02:00
|
|
|
data = new AbstractHttpOverXmpp.Data(child);
|
2014-03-18 00:51:34 +01:00
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2015-07-01 13:39:04 +02:00
|
|
|
private static AbstractHttpOverXmpp.Text parseText(XmlPullParser parser) throws XmlPullParserException, IOException {
|
2014-03-18 00:51:34 +01:00
|
|
|
String text = null;
|
|
|
|
boolean done = false;
|
|
|
|
|
|
|
|
while (!done) {
|
2019-05-06 22:06:13 +02:00
|
|
|
XmlPullParser.Event eventType = parser.next();
|
2014-03-18 00:51:34 +01:00
|
|
|
|
2019-05-06 22:06:13 +02:00
|
|
|
if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
2014-03-18 00:51:34 +01:00
|
|
|
if (parser.getName().equals(ELEMENT_TEXT)) {
|
|
|
|
done = true;
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
|
|
|
|
}
|
2019-05-06 22:06:13 +02:00
|
|
|
} else if (eventType == XmlPullParser.Event.TEXT_CHARACTERS) {
|
2014-03-18 00:51:34 +01:00
|
|
|
text = parser.getText();
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new AbstractHttpOverXmpp.Text(text);
|
|
|
|
}
|
|
|
|
|
2015-07-01 13:39:04 +02:00
|
|
|
private static AbstractHttpOverXmpp.Xml parseXml(XmlPullParser parser)
|
|
|
|
throws XmlPullParserException, IOException {
|
2014-03-18 00:51:34 +01:00
|
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
boolean done = false;
|
|
|
|
boolean startClosed = true;
|
|
|
|
|
|
|
|
while (!done) {
|
2019-05-06 22:06:13 +02:00
|
|
|
XmlPullParser.Event eventType = parser.next();
|
2014-03-18 00:51:34 +01:00
|
|
|
|
2019-05-06 22:06:13 +02:00
|
|
|
if ((eventType == XmlPullParser.Event.END_ELEMENT) && parser.getName().equals(ELEMENT_XML)) {
|
2014-03-18 00:51:34 +01:00
|
|
|
done = true;
|
|
|
|
} else { // just write everything else as text
|
|
|
|
|
2019-05-06 22:06:13 +02:00
|
|
|
if (eventType == XmlPullParser.Event.START_ELEMENT) {
|
2014-03-18 00:51:34 +01:00
|
|
|
|
|
|
|
if (!startClosed) {
|
|
|
|
builder.append('>');
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.append('<');
|
|
|
|
builder.append(parser.getName());
|
|
|
|
appendXmlAttributes(parser, builder);
|
|
|
|
startClosed = false;
|
2019-05-06 22:06:13 +02:00
|
|
|
} else if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
2014-03-18 00:51:34 +01:00
|
|
|
|
|
|
|
if (startClosed) {
|
|
|
|
builder.append("</");
|
|
|
|
builder.append(parser.getName());
|
|
|
|
builder.append('>');
|
|
|
|
} else {
|
|
|
|
builder.append("/>");
|
|
|
|
startClosed = true;
|
|
|
|
}
|
2019-05-06 22:06:13 +02:00
|
|
|
} else if (eventType == XmlPullParser.Event.TEXT_CHARACTERS) {
|
2014-03-18 00:51:34 +01:00
|
|
|
|
|
|
|
if (!startClosed) {
|
|
|
|
builder.append('>');
|
|
|
|
startClosed = true;
|
|
|
|
}
|
2016-01-13 15:04:01 +01:00
|
|
|
builder.append(StringUtils.escapeForXmlText(parser.getText()));
|
2014-03-18 00:51:34 +01:00
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new AbstractHttpOverXmpp.Xml(builder.toString());
|
|
|
|
}
|
|
|
|
|
2015-07-01 13:39:04 +02:00
|
|
|
private static void appendXmlAttributes(XmlPullParser parser, StringBuilder builder) {
|
2014-03-18 00:51:34 +01:00
|
|
|
// 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("=\"");
|
2016-01-13 15:04:01 +01:00
|
|
|
builder.append(StringUtils.escapeForXml(parser.getAttributeValue(i)));
|
2014-03-18 00:51:34 +01:00
|
|
|
builder.append('"');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-01 13:39:04 +02:00
|
|
|
private static AbstractHttpOverXmpp.Base64 parseBase64(XmlPullParser parser) throws XmlPullParserException,
|
|
|
|
IOException {
|
2014-03-18 00:51:34 +01:00
|
|
|
String text = null;
|
|
|
|
boolean done = false;
|
|
|
|
|
|
|
|
while (!done) {
|
2019-05-06 22:06:13 +02:00
|
|
|
XmlPullParser.Event eventType = parser.next();
|
2014-03-18 00:51:34 +01:00
|
|
|
|
2019-05-06 22:06:13 +02:00
|
|
|
if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
2014-03-18 00:51:34 +01:00
|
|
|
|
|
|
|
if (parser.getName().equals(ELEMENT_BASE_64)) {
|
|
|
|
done = true;
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
|
|
|
|
}
|
2019-05-06 22:06:13 +02:00
|
|
|
} else if (eventType == XmlPullParser.Event.TEXT_CHARACTERS) {
|
2014-03-18 00:51:34 +01:00
|
|
|
text = parser.getText();
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("unexpected eventType: " + eventType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new AbstractHttpOverXmpp.Base64(text);
|
|
|
|
}
|
|
|
|
|
2015-07-01 13:39:04 +02:00
|
|
|
private static AbstractHttpOverXmpp.ChunkedBase64 parseChunkedBase64(XmlPullParser parser)
|
|
|
|
throws XmlPullParserException, IOException {
|
2014-03-18 00:51:34 +01:00
|
|
|
String streamId = parser.getAttributeValue("", ATTRIBUTE_STREAM_ID);
|
|
|
|
AbstractHttpOverXmpp.ChunkedBase64 child = new AbstractHttpOverXmpp.ChunkedBase64(streamId);
|
|
|
|
boolean done = false;
|
|
|
|
|
|
|
|
while (!done) {
|
2019-05-06 22:06:13 +02:00
|
|
|
XmlPullParser.Event eventType = parser.next();
|
2014-03-18 00:51:34 +01:00
|
|
|
|
2019-05-06 22:06:13 +02:00
|
|
|
if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
2014-03-18 00:51:34 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-07-01 13:39:04 +02:00
|
|
|
private static AbstractHttpOverXmpp.Ibb parseIbb(XmlPullParser parser) throws XmlPullParserException, IOException {
|
2014-03-18 00:51:34 +01:00
|
|
|
String sid = parser.getAttributeValue("", ATTRIBUTE_SID);
|
|
|
|
AbstractHttpOverXmpp.Ibb child = new AbstractHttpOverXmpp.Ibb(sid);
|
|
|
|
boolean done = false;
|
|
|
|
|
|
|
|
while (!done) {
|
2019-05-06 22:06:13 +02:00
|
|
|
XmlPullParser.Event eventType = parser.next();
|
2014-03-18 00:51:34 +01:00
|
|
|
|
2019-05-06 22:06:13 +02:00
|
|
|
if (eventType == XmlPullParser.Event.END_ELEMENT) {
|
2014-03-18 00:51:34 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|