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