AbstractHttpOverXmppProvider.java

  1. /**
  2.  *
  3.  * Copyright 2014 Andriy Tsykholyas
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.hoxt.provider;

  18. import org.jivesoftware.smack.packet.NamedElement;
  19. import org.jivesoftware.smack.provider.IQProvider;
  20. import org.jivesoftware.smack.util.StringUtils;
  21. import org.jivesoftware.smackx.hoxt.packet.AbstractHttpOverXmpp;
  22. import org.jivesoftware.smackx.shim.packet.HeadersExtension;
  23. import org.jivesoftware.smackx.shim.provider.HeadersProvider;
  24. import org.xmlpull.v1.XmlPullParser;
  25. import org.xmlpull.v1.XmlPullParserException;

  26. import java.io.IOException;

  27. /**
  28.  * Abstract parent for Req and Resp packet providers.
  29.  *
  30.  * @author Andriy Tsykholyas
  31.  * @see <a href="http://xmpp.org/extensions/xep-0332.html">XEP-0332: HTTP over XMPP transport</a>
  32.  */
  33. public abstract class AbstractHttpOverXmppProvider<H extends AbstractHttpOverXmpp> extends IQProvider<H> {

  34.     private static final String ELEMENT_DATA = "data";
  35.     private static final String ELEMENT_TEXT = "text";
  36.     private static final String ELEMENT_BASE_64 = "base64";
  37.     private static final String ELEMENT_CHUNKED_BASE_64 = "chunkedBase64";
  38.     private static final String ELEMENT_XML = "xml";
  39.     static final String ELEMENT_IBB = "ibb";
  40.     static final String ELEMENT_SIPUB = "sipub";
  41.     static final String ELEMENT_JINGLE = "jingle";

  42.     private static final String ATTRIBUTE_STREAM_ID = "streamId";
  43.     private static final String ATTRIBUTE_SID = "sid";
  44.     static final String ATTRIBUTE_VERSION = "version";

  45.     /**
  46.      * Parses Headers and Data elements.
  47.      *
  48.      * @param parser      parser
  49.      * @param elementName name of concrete implementation of this element
  50.      * @param body        parent Body element
  51.      * @throws Exception
  52.      */
  53.     protected void parseHeadersAndData(XmlPullParser parser, String elementName, AbstractHttpOverXmpp body) throws Exception {
  54.         boolean done = false;

  55.         while (!done) {
  56.             int eventType = parser.next();

  57.             if (eventType == XmlPullParser.START_TAG) {
  58.                 if (parser.getName().equals(HeadersExtension.ELEMENT)) {
  59.                     HeadersExtension headersExtension = HeadersProvider.INSTANCE.parse(parser);
  60.                     body.setHeaders(headersExtension);
  61.                 } else if (parser.getName().endsWith(ELEMENT_DATA)) {
  62.                     AbstractHttpOverXmpp.Data data = parseData(parser);
  63.                     body.setData(data);
  64.                 } else {
  65.                     throw new IllegalArgumentException("unexpected tag:" + parser.getName() + "'");
  66.                 }
  67.             } else if (eventType == XmlPullParser.END_TAG) {
  68.                 if (parser.getName().equals(elementName)) {
  69.                     done = true;
  70.                 }
  71.             }
  72.         }
  73.     }

  74.     private AbstractHttpOverXmpp.Data parseData(XmlPullParser parser) throws XmlPullParserException, IOException {
  75.         NamedElement child = null;
  76.         boolean done = false;

  77.         while (!done) {
  78.             int eventType = parser.next();

  79.             if (eventType == XmlPullParser.START_TAG) {
  80.                 if (parser.getName().equals(ELEMENT_TEXT)) {
  81.                     child = parseText(parser);
  82.                 } else if (parser.getName().equals(ELEMENT_BASE_64)) {
  83.                     child = parseBase64(parser);
  84.                 } else if (parser.getName().equals(ELEMENT_CHUNKED_BASE_64)) {
  85.                     child = parseChunkedBase64(parser);
  86.                 } else if (parser.getName().equals(ELEMENT_XML)) {
  87.                     child = parseXml(parser);
  88.                 } else if (parser.getName().equals(ELEMENT_IBB)) {
  89.                     child = parseIbb(parser);
  90.                 } else if (parser.getName().equals(ELEMENT_SIPUB)) {
  91.                     // TODO: sipub is allowed by xep-0332, but is not implemented yet
  92.                     throw new UnsupportedOperationException("sipub is not supported yet");
  93.                 } else if (parser.getName().equals(ELEMENT_JINGLE)) {
  94.                     // TODO: jingle is allowed by xep-0332, but is not implemented yet
  95.                     throw new UnsupportedOperationException("jingle is not supported yet");
  96.                 } else {
  97.                     // other elements are not allowed
  98.                     throw new IllegalArgumentException("unsupported child tag: " + parser.getName());
  99.                 }
  100.             } else if (eventType == XmlPullParser.END_TAG) {
  101.                 if (parser.getName().equals(ELEMENT_DATA)) {
  102.                     done = true;
  103.                 }
  104.             }
  105.         }

  106.         AbstractHttpOverXmpp.Data data = new AbstractHttpOverXmpp.Data(child);
  107.         return data;
  108.     }

  109.     private AbstractHttpOverXmpp.Text parseText(XmlPullParser parser) throws XmlPullParserException, IOException {
  110.         String text = null;
  111.         boolean done = false;

  112.         while (!done) {
  113.             int eventType = parser.next();

  114.             if (eventType == XmlPullParser.END_TAG) {
  115.                 if (parser.getName().equals(ELEMENT_TEXT)) {
  116.                     done = true;
  117.                 } else {
  118.                     throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
  119.                 }
  120.             } else if (eventType == XmlPullParser.TEXT) {
  121.                 text = parser.getText();
  122.             } else {
  123.                 throw new IllegalArgumentException("unexpected eventType: " + eventType);
  124.             }
  125.         }

  126.         return new AbstractHttpOverXmpp.Text(text);
  127.     }

  128.     private AbstractHttpOverXmpp.Xml parseXml(XmlPullParser parser) throws XmlPullParserException, IOException {
  129.         StringBuilder builder = new StringBuilder();
  130.         boolean done = false;
  131.         boolean startClosed = true;

  132.         while (!done) {
  133.             int eventType = parser.next();

  134.             if ((eventType == XmlPullParser.END_TAG) && parser.getName().equals(ELEMENT_XML)) {
  135.                 done = true;
  136.             } else { // just write everything else as text

  137.                 if (eventType == XmlPullParser.START_TAG) {

  138.                     if (!startClosed) {
  139.                         builder.append('>');
  140.                     }

  141.                     builder.append('<');
  142.                     builder.append(parser.getName());
  143.                     appendXmlAttributes(parser, builder);
  144.                     startClosed = false;
  145.                 } else if (eventType == XmlPullParser.END_TAG) {

  146.                     if (startClosed) {
  147.                         builder.append("</");
  148.                         builder.append(parser.getName());
  149.                         builder.append('>');
  150.                     } else {
  151.                         builder.append("/>");
  152.                         startClosed = true;
  153.                     }
  154.                 } else if (eventType == XmlPullParser.TEXT) {

  155.                     if (!startClosed) {
  156.                         builder.append('>');
  157.                         startClosed = true;
  158.                     }
  159.                     builder.append(StringUtils.escapeForXML(parser.getText()));
  160.                 } else {
  161.                     throw new IllegalArgumentException("unexpected eventType: " + eventType);
  162.                 }
  163.             }
  164.         }

  165.         return new AbstractHttpOverXmpp.Xml(builder.toString());
  166.     }

  167.     private void appendXmlAttributes(XmlPullParser parser, StringBuilder builder) {
  168.         // NOTE: for now we ignore namespaces
  169.         int count = parser.getAttributeCount();

  170.         if (count > 0) {

  171.             for (int i = 0; i < count; i++) {
  172.                 builder.append(' ');
  173.                 builder.append(parser.getAttributeName(i));
  174.                 builder.append("=\"");
  175.                 builder.append(StringUtils.escapeForXML(parser.getAttributeValue(i)));
  176.                 builder.append('"');
  177.             }
  178.         }
  179.     }

  180.     private AbstractHttpOverXmpp.Base64 parseBase64(XmlPullParser parser) throws XmlPullParserException, IOException {
  181.         String text = null;
  182.         boolean done = false;

  183.         while (!done) {
  184.             int eventType = parser.next();

  185.             if (eventType == XmlPullParser.END_TAG) {

  186.                 if (parser.getName().equals(ELEMENT_BASE_64)) {
  187.                     done = true;
  188.                 } else {
  189.                     throw new IllegalArgumentException("unexpected end tag of: " + parser.getName());
  190.                 }
  191.             } else if (eventType == XmlPullParser.TEXT) {
  192.                 text = parser.getText();
  193.             } else {
  194.                 throw new IllegalArgumentException("unexpected eventType: " + eventType);
  195.             }
  196.         }

  197.         return new AbstractHttpOverXmpp.Base64(text);
  198.     }

  199.     private AbstractHttpOverXmpp.ChunkedBase64 parseChunkedBase64(XmlPullParser parser) throws XmlPullParserException, IOException {
  200.         String streamId = parser.getAttributeValue("", ATTRIBUTE_STREAM_ID);
  201.         AbstractHttpOverXmpp.ChunkedBase64 child = new AbstractHttpOverXmpp.ChunkedBase64(streamId);
  202.         boolean done = false;

  203.         while (!done) {
  204.             int eventType = parser.next();

  205.             if (eventType == XmlPullParser.END_TAG) {
  206.                 if (parser.getName().equals(ELEMENT_CHUNKED_BASE_64)) {
  207.                     done = true;
  208.                 } else {
  209.                     throw new IllegalArgumentException("unexpected end tag: " + parser.getName());
  210.                 }
  211.             } else {
  212.                 throw new IllegalArgumentException("unexpected event type: " + eventType);
  213.             }
  214.         }
  215.         return child;
  216.     }

  217.     private AbstractHttpOverXmpp.Ibb parseIbb(XmlPullParser parser) throws XmlPullParserException, IOException {
  218.         String sid = parser.getAttributeValue("", ATTRIBUTE_SID);
  219.         AbstractHttpOverXmpp.Ibb child = new AbstractHttpOverXmpp.Ibb(sid);
  220.         boolean done = false;

  221.         while (!done) {
  222.             int eventType = parser.next();

  223.             if (eventType == XmlPullParser.END_TAG) {
  224.                 if (parser.getName().equals(ELEMENT_IBB)) {
  225.                     done = true;
  226.                 } else {
  227.                     throw new IllegalArgumentException("unexpected end tag: " + parser.getName());
  228.                 }
  229.             } else {
  230.                 throw new IllegalArgumentException("unexpected event type: " + eventType);
  231.             }
  232.         }
  233.         return child;
  234.     }
  235. }