001/**
002 *
003 * Copyright 2017-2022 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.IqData;
023import org.jivesoftware.smack.packet.StandardExtensionElement;
024import org.jivesoftware.smack.packet.XmlElement;
025import org.jivesoftware.smack.packet.XmlEnvironment;
026import org.jivesoftware.smack.parsing.SmackParsingException;
027import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
028import org.jivesoftware.smack.provider.IqProvider;
029import org.jivesoftware.smack.util.PacketParserUtils;
030import org.jivesoftware.smack.util.ParserUtils;
031import org.jivesoftware.smack.xml.XmlPullParser;
032import org.jivesoftware.smack.xml.XmlPullParserException;
033
034import org.jivesoftware.smackx.jingle.element.Jingle;
035import org.jivesoftware.smackx.jingle.element.JingleAction;
036import org.jivesoftware.smackx.jingle.element.JingleContent;
037import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
038import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
039import org.jivesoftware.smackx.jingle.element.JingleReason;
040import org.jivesoftware.smackx.jingle.element.JingleReason.Reason;
041import org.jivesoftware.smackx.jingle.element.UnknownJingleContentDescription;
042import org.jivesoftware.smackx.jingle.element.UnknownJingleContentTransport;
043
044import org.jxmpp.jid.FullJid;
045
046public class JingleProvider extends IqProvider<Jingle> {
047
048    private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName());
049
050    @Override
051    public Jingle parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
052        Jingle.Builder builder = Jingle.builder(iqData);
053
054        String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME);
055        if (actionString != null) {
056            JingleAction action = JingleAction.fromString(actionString);
057            builder.setAction(action);
058        }
059
060        FullJid initiator = ParserUtils.getFullJidAttribute(parser, Jingle.INITIATOR_ATTRIBUTE_NAME);
061        builder.setInitiator(initiator);
062
063        FullJid responder = ParserUtils.getFullJidAttribute(parser, Jingle.RESPONDER_ATTRIBUTE_NAME);
064        builder.setResponder(responder);
065
066        String sessionId = parser.getAttributeValue("", Jingle.SESSION_ID_ATTRIBUTE_NAME);
067        builder.setSessionId(sessionId);
068
069
070        outerloop: while (true) {
071            XmlPullParser.Event eventType = parser.next();
072            switch (eventType) {
073            case START_ELEMENT:
074                String tagName = parser.getName();
075                switch (tagName) {
076                case JingleContent.ELEMENT:
077                    JingleContent content = parseJingleContent(parser, parser.getDepth());
078                    builder.addJingleContent(content);
079                    break;
080                case JingleReason.ELEMENT:
081                    JingleReason reason = parseJingleReason(parser);
082                    builder.setReason(reason);
083                    break;
084                default:
085                    LOGGER.severe("Unknown Jingle element: " + tagName);
086                    break;
087                }
088                break;
089            case END_ELEMENT:
090                if (parser.getDepth() == initialDepth) {
091                    break outerloop;
092                }
093                break;
094            default:
095                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
096                break;
097            }
098        }
099
100        return builder.build();
101    }
102
103    public static JingleContent parseJingleContent(XmlPullParser parser, final int initialDepth)
104                    throws XmlPullParserException, IOException, SmackParsingException {
105        JingleContent.Builder builder = JingleContent.getBuilder();
106
107        String creatorString = parser.getAttributeValue("", JingleContent.CREATOR_ATTRIBUTE_NAME);
108        JingleContent.Creator creator = JingleContent.Creator.valueOf(creatorString);
109        builder.setCreator(creator);
110
111        String disposition = parser.getAttributeValue("", JingleContent.DISPOSITION_ATTRIBUTE_NAME);
112        builder.setDisposition(disposition);
113
114        String name = parser.getAttributeValue("", JingleContent.NAME_ATTRIBUTE_NAME);
115        builder.setName(name);
116
117        String sendersString = parser.getAttributeValue("", JingleContent.SENDERS_ATTRIBUTE_NAME);
118        if (sendersString != null) {
119            JingleContent.Senders senders = JingleContent.Senders.valueOf(sendersString);
120            builder.setSenders(senders);
121        }
122
123        outerloop: while (true) {
124            XmlPullParser.Event eventType = parser.next();
125            switch (eventType) {
126            case START_ELEMENT:
127                String tagName = parser.getName();
128                String namespace = parser.getNamespace();
129                switch (tagName) {
130                case JingleContentDescription.ELEMENT: {
131                    JingleContentDescription description;
132                    JingleContentDescriptionProvider<?> provider = JingleContentProviderManager.getJingleContentDescriptionProvider(namespace);
133                    if (provider == null) {
134                        StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
135                        description = new UnknownJingleContentDescription(standardExtensionElement);
136                    }
137                    else {
138                        description = provider.parse(parser);
139                    }
140                    builder.setDescription(description);
141                    break;
142                }
143                case JingleContentTransport.ELEMENT: {
144                    JingleContentTransport transport;
145                    JingleContentTransportProvider<?> provider = JingleContentProviderManager.getJingleContentTransportProvider(namespace);
146                    if (provider == null) {
147                        StandardExtensionElement standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
148                        transport = new UnknownJingleContentTransport(standardExtensionElement);
149                    }
150                    else {
151                        transport = provider.parse(parser);
152                    }
153                    builder.setTransport(transport);
154                    break;
155                }
156                default:
157                    LOGGER.severe("Unknown Jingle content element: " + tagName);
158                    break;
159                }
160                break;
161            case END_ELEMENT:
162                if (parser.getDepth() == initialDepth) {
163                    break outerloop;
164                }
165                break;
166            default:
167                // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
168                break;
169            }
170        }
171
172        return builder.build();
173    }
174
175    public static JingleReason parseJingleReason(XmlPullParser parser)
176                    throws XmlPullParserException, IOException, SmackParsingException {
177        ParserUtils.assertAtStartTag(parser);
178        final int initialDepth = parser.getDepth();
179        final String jingleNamespace = parser.getNamespace();
180
181        JingleReason.Reason reason = null;
182        XmlElement element = null;
183        String text = null;
184
185        // 'sid' is only set if the reason is 'alternative-session'.
186        String sid = null;
187
188        outerloop: while (true) {
189            XmlPullParser.TagEvent event = parser.nextTag();
190            switch (event) {
191            case START_ELEMENT:
192                String elementName = parser.getName();
193                String namespace = parser.getNamespace();
194                if (namespace.equals(jingleNamespace)) {
195                    switch (elementName) {
196                    case "text":
197                        text = parser.nextText();
198                        break;
199                    case "alternative-session":
200                        parser.next();
201                        sid = parser.nextText();
202                        break;
203                    default:
204                        reason = Reason.fromString(elementName);
205                        break;
206                    }
207                } else {
208                    element = PacketParserUtils.parseExtensionElement(elementName, namespace, parser, null);
209                }
210                break;
211            case END_ELEMENT:
212                if (parser.getDepth() == initialDepth) {
213                    break outerloop;
214                }
215                break;
216            }
217        }
218
219        JingleReason res;
220        if (sid != null) {
221            res = new JingleReason.AlternativeSession(sid, text, element);
222        } else {
223            res = new JingleReason(reason, text, element);
224        }
225        return res;
226    }
227}