001/**
002 *
003 * Copyright 2017-2019 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.jingle.provider;
018
019import java.io.IOException;
020import java.util.logging.Logger;
021
022import org.jivesoftware.smack.packet.StandardExtensionElement;
023import org.jivesoftware.smack.packet.XmlEnvironment;
024import org.jivesoftware.smack.parsing.SmackParsingException;
025import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
026import org.jivesoftware.smack.provider.IQProvider;
027import org.jivesoftware.smack.util.ParserUtils;
028import org.jivesoftware.smack.xml.XmlPullParser;
029import org.jivesoftware.smack.xml.XmlPullParserException;
030
031import org.jivesoftware.smackx.jingle.element.Jingle;
032import org.jivesoftware.smackx.jingle.element.JingleAction;
033import org.jivesoftware.smackx.jingle.element.JingleContent;
034import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
035import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
036import org.jivesoftware.smackx.jingle.element.JingleReason;
037import org.jivesoftware.smackx.jingle.element.JingleReason.Reason;
038import org.jivesoftware.smackx.jingle.element.UnknownJingleContentDescription;
039import org.jivesoftware.smackx.jingle.element.UnknownJingleContentTransport;
040
041import org.jxmpp.jid.FullJid;
042
043public class JingleProvider extends IQProvider<Jingle> {
044
045    private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName());
046
047    @Override
048    public Jingle parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
049        Jingle.Builder builder = Jingle.getBuilder();
050
051        String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME);
052        if (actionString != null) {
053            JingleAction action = JingleAction.fromString(actionString);
054            builder.setAction(action);
055        }
056
057        FullJid initiator = ParserUtils.getFullJidAttribute(parser, Jingle.INITIATOR_ATTRIBUTE_NAME);
058        builder.setInitiator(initiator);
059
060        FullJid responder = ParserUtils.getFullJidAttribute(parser, Jingle.RESPONDER_ATTRIBUTE_NAME);
061        builder.setResponder(responder);
062
063        String sessionId = parser.getAttributeValue("", Jingle.SESSION_ID_ATTRIBUTE_NAME);
064        builder.setSessionId(sessionId);
065
066
067        outerloop: while (true) {
068            XmlPullParser.Event eventType = parser.next();
069            switch (eventType) {
070            case START_ELEMENT:
071                String tagName = parser.getName();
072                switch (tagName) {
073                case JingleContent.ELEMENT:
074                    JingleContent content = parseJingleContent(parser, parser.getDepth());
075                    builder.addJingleContent(content);
076                    break;
077                case JingleReason.ELEMENT:
078                    parser.next();
079                    String reasonString = parser.getName();
080                    JingleReason reason;
081                    if (reasonString.equals("alternative-session")) {
082                        parser.next();
083                        String sid = parser.nextText();
084                        reason = new JingleReason.AlternativeSession(sid);
085                    } else {
086                        reason = new JingleReason(Reason.fromString(reasonString));
087                    }
088                    builder.setReason(reason);
089                    break;
090                default:
091                    LOGGER.severe("Unknown Jingle element: " + tagName);
092                    break;
093                }
094                break;
095            case END_ELEMENT:
096                if (parser.getDepth() == initialDepth) {
097                    break outerloop;
098                }
099                break;
100            default:
101                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
102                break;
103            }
104        }
105
106        return builder.build();
107    }
108
109    public static JingleContent parseJingleContent(XmlPullParser parser, final int initialDepth)
110                    throws XmlPullParserException, IOException, SmackParsingException {
111        JingleContent.Builder builder = JingleContent.getBuilder();
112
113        String creatorString = parser.getAttributeValue("", JingleContent.CREATOR_ATTRIBUTE_NAME);
114        JingleContent.Creator creator = JingleContent.Creator.valueOf(creatorString);
115        builder.setCreator(creator);
116
117        String disposition = parser.getAttributeValue("", JingleContent.DISPOSITION_ATTRIBUTE_NAME);
118        builder.setDisposition(disposition);
119
120        String name = parser.getAttributeValue("", JingleContent.NAME_ATTRIBUTE_NAME);
121        builder.setName(name);
122
123        String sendersString = parser.getAttributeValue("", JingleContent.SENDERS_ATTRIBUTE_NAME);
124        if (sendersString != null) {
125            JingleContent.Senders senders = JingleContent.Senders.valueOf(sendersString);
126            builder.setSenders(senders);
127        }
128
129        outerloop: while (true) {
130            XmlPullParser.Event eventType = parser.next();
131            switch (eventType) {
132            case START_ELEMENT:
133                String tagName = parser.getName();
134                String namespace = parser.getNamespace();
135                switch (tagName) {
136                case JingleContentDescription.ELEMENT: {
137                    JingleContentDescription description;
138                    JingleContentDescriptionProvider<?> provider = JingleContentProviderManager.getJingleContentDescriptionProvider(namespace);
139                    if (provider == null) {
140                        StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
141                        description = new UnknownJingleContentDescription(standardExtensionElement);
142                    }
143                    else {
144                        description = provider.parse(parser);
145                    }
146                    builder.setDescription(description);
147                    break;
148                }
149                case JingleContentTransport.ELEMENT: {
150                    JingleContentTransport transport;
151                    JingleContentTransportProvider<?> provider = JingleContentProviderManager.getJingleContentTransportProvider(namespace);
152                    if (provider == null) {
153                        StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
154                        transport = new UnknownJingleContentTransport(standardExtensionElement);
155                    }
156                    else {
157                        transport = provider.parse(parser);
158                    }
159                    builder.setTransport(transport);
160                    break;
161                }
162                default:
163                    LOGGER.severe("Unknown Jingle content element: " + tagName);
164                    break;
165                }
166                break;
167            case END_ELEMENT:
168                if (parser.getDepth() == initialDepth) {
169                    break outerloop;
170                }
171                break;
172            default:
173                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
174                break;
175            }
176        }
177
178        return builder.build();
179    }
180}