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