diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleActionEnum.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleActionEnum.java new file mode 100644 index 000000000..7c8f7365e --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleActionEnum.java @@ -0,0 +1,49 @@ +package org.jivesoftware.smackx.jingle; + +/** + * The "action" in the jingle packet, as an enum. + * + * Changed to reflect XEP-166 rev: 20JUN07 + * + * @author Jeff Williams + */ +public enum JingleActionEnum { + + UNKNOWN("unknown"), + CONTENT_ACCEPT("content-accept"), + CONTENT_ADD("content-add"), + CONTENT_MODIFY("content-modify"), + CONTENT_REMOVE("content-remove"), + SESSION_ACCEPT("session-accept"), + SESSION_INFO("session-info"), + SESSION_INITIATE("session-initiate"), + SESSION_TERMINATE("session-terminate"), + TRANSPORT_INFO("transport-info"); + + private String actionCode; + + private JingleActionEnum(String inActionCode) { + actionCode = inActionCode; + } + + /** + * Returns the String value for an Action. + */ + + public String toString() { + return actionCode; + } + + /** + * Returns the Action enum for a String action value. + */ + public static JingleActionEnum getAction(String inActionCode) { + for (JingleActionEnum jingleAction : JingleActionEnum.values()) { + if (jingleAction.actionCode.equals(inActionCode)) { + return jingleAction; + } + } + return null; + } + +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleException.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleException.java new file mode 100644 index 000000000..acf45a9c2 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleException.java @@ -0,0 +1,52 @@ +package org.jivesoftware.smackx.jingle; + + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.packet.JingleError; + + /** + * A Jingle exception. + * + * @author Alvaro Saurin + */ + public class JingleException extends XMPPException { + + private final JingleError error; + + /** + * Default constructor. + */ + public JingleException() { + super(); + error = null; + } + + /** + * Constructor with an error message. + * + * @param msg The message. + */ + public JingleException(String msg) { + super(msg); + error = null; + } + + /** + * Constructor with an error response. + * + * @param error The error message. + */ + public JingleException(JingleError error) { + super(); + this.error = error; + } + + /** + * Return the error message. + * + * @return the error + */ + public JingleError getError() { + return error; + } + } \ No newline at end of file diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleNegotiatorState.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleNegotiatorState.java new file mode 100644 index 000000000..40247eff3 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleNegotiatorState.java @@ -0,0 +1,10 @@ +package org.jivesoftware.smackx.jingle; + +/** + * @author Jeff Williams + */ +public enum JingleNegotiatorState { + PENDING, + FAILED, + SUCCEEDED +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionState.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionState.java new file mode 100644 index 000000000..4d066c1a4 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionState.java @@ -0,0 +1,49 @@ +package org.jivesoftware.smackx.jingle; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.packet.Jingle; + +/** + * Implement the Jingle Session state using the State Behavioral pattern. + * (From the book Design Patterns, AKA GoF.) + * These classes also employ the Flyweight and Singleton patterns as recommended for the State pattern by GoF. + * + * There seems to be three ways to go with the State pattern in Java: interface, abstract class and enums. + * Most of the accepted models use abstract classes. It wasn't clear to me that any of the three models was + * superior, so I went with the most common example. + * + * @author Jeff Williams + */ +public abstract class JingleSessionState { + + /** + * Called when entering the state. + */ + public static JingleSessionState getInstance() { + // Since we can never instantiate this class there is nothing to return (ever). + return null; + } + + /** + * Called when entering the state. + */ + public abstract void enter(); + + /** + * Called when exiting the state. + */ + public abstract void exit(); + + /** + * Process an incoming Jingle Packet. + * When you look at the GoF State pattern this method roughly corresponds to example on p310: ProcessOctect(). + */ + public abstract IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action); + + /** + * For debugging just emit the short name of the class. + */ + public String toString() { + return this.getClass().getSimpleName(); + } +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateActive.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateActive.java new file mode 100644 index 000000000..b08f15be4 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateActive.java @@ -0,0 +1,92 @@ +package org.jivesoftware.smackx.jingle; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.packet.Jingle; +import org.jivesoftware.smackx.packet.JingleError; + +/** + * @author Jeff Williams + * @see JingleSessionState + */ +public class JingleSessionStateActive extends JingleSessionState { + private static JingleSessionStateActive INSTANCE = null; + + protected JingleSessionStateActive() { + // Prevent instantiation of the class. + } + + /** + * A thread-safe means of getting the one instance of this class. + * @return The singleton instance of this class. + */ + public synchronized static JingleSessionState getInstance() { + if (INSTANCE == null) { + INSTANCE = new JingleSessionStateActive(); + } + return INSTANCE; + } + + public void enter() { + // TODO Auto-generated method stub + + } + + public void exit() { + // TODO Auto-generated method stub + + } + + public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) { + IQ response = null; + + switch (action) { + + case CONTENT_ACCEPT: + break; + + case CONTENT_ADD: + break; + + case CONTENT_MODIFY: + break; + + case CONTENT_REMOVE: + break; + + case SESSION_INFO: + break; + + case SESSION_TERMINATE: + receiveSessionTerminateAction(session, jingle); + break; + + case TRANSPORT_INFO: + break; + + default: + // Anything other action is an error. + response = session.createJingleError(jingle, JingleError.OUT_OF_ORDER); + break; + } + return response; + } + + /** + * Receive and process the action. + */ + private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) { + + // According to XEP-166 the only thing we can do is ack. + IQ response = session.createAck(jingle); + + try { + session.terminate("Closed remotely"); + } catch (XMPPException e) { + e.printStackTrace(); + } + + return response; + } + +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateEnded.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateEnded.java new file mode 100644 index 000000000..0c34b13c2 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateEnded.java @@ -0,0 +1,51 @@ +package org.jivesoftware.smackx.jingle; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.packet.Jingle; +import org.jivesoftware.smackx.packet.JingleError; + +/** + * @author Jeff Williams + * @see JingleSessionState + */ +public class JingleSessionStateEnded extends JingleSessionState { + private static JingleSessionStateEnded INSTANCE = null; + + protected JingleSessionStateEnded() { + // Prevent instantiation of the class. + } + + /** + * A thread-safe means of getting the one instance of this class. + * @return The singleton instance of this class. + */ + public synchronized static JingleSessionState getInstance() { + if (INSTANCE == null) { + INSTANCE = new JingleSessionStateEnded(); + } + + return INSTANCE; + } + + public void enter() { + System.out.println("Session Ended"); + System.out.println("-------------------------------------------------------------------"); + + } + + public void exit() { + // TODO Auto-generated method stub + + } + + /** + * Pretty much nothing is valid for receiving once we've ended the session. + */ + public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) { + IQ response = null; + + response = session.createJingleError(jingle, JingleError.MALFORMED_STANZA); + + return response; + } +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStatePending.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStatePending.java new file mode 100644 index 000000000..a779a5f85 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStatePending.java @@ -0,0 +1,117 @@ +package org.jivesoftware.smackx.jingle; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.packet.Jingle; + +/** + * @author Jeff Williams + * @see JingleSessionState + */ + +public class JingleSessionStatePending extends JingleSessionState { + private static JingleSessionStatePending INSTANCE = null; + + protected JingleSessionStatePending() { + // Prevent instantiation of the class. + } + + /** + * A thread-safe means of getting the one instance of this class. + * @return The singleton instance of this class. + */ + public synchronized static JingleSessionState getInstance() { + if (INSTANCE == null) { + INSTANCE = new JingleSessionStatePending(); + } + return INSTANCE; + } + + public void enter() { + // TODO Auto-generated method stub + + } + + public void exit() { + // TODO Auto-generated method stub + + } + + public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) { + IQ response = null; + + switch (action) { + + case CONTENT_ACCEPT: + response = receiveContentAcceptAction(jingle); + break; + + case CONTENT_MODIFY: + break; + + case CONTENT_REMOVE: + break; + + case SESSION_ACCEPT: + response = receiveSessionAcceptAction(session, jingle); + break; + + case SESSION_INFO: + break; + + case SESSION_TERMINATE: + response = receiveSessionTerminateAction(session, jingle); + break; + + case TRANSPORT_INFO: + break; + + default: + // Anything other action is an error. + //response = createJingleError(inJingle, JingleError.OUT_OF_ORDER); + break; + } + + return response; + } + + /** + * Receive and process the action. + */ + private IQ receiveContentAcceptAction(Jingle inJingle) { + + // According to XEP-167 the only thing we can do is ack. + //setSessionState(JingleSessionStateEnum.ACTIVE); + //return createAck(inJingle); + + // This is now handled by the media negotiator for the matching segment. + return null; + } + + /** + * Receive and process the action. + */ + private IQ receiveSessionAcceptAction(JingleSession session, Jingle inJingle) { + + // According to XEP-166 the only thing we can do is ack. + session.setSessionState(JingleSessionStateActive.getInstance()); + return session.createAck(inJingle); + } + + /** + * Receive and process the action. + */ + private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) { + + // According to XEP-166 the only thing we can do is ack. + IQ response = session.createAck(jingle); + + try { + session.terminate("Closed remotely"); + } catch (XMPPException e) { + e.printStackTrace(); + } + + return response; + } +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateUnknown.java b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateUnknown.java new file mode 100644 index 000000000..e1e942e04 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/jingle/JingleSessionStateUnknown.java @@ -0,0 +1,195 @@ +package org.jivesoftware.smackx.jingle; + +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smackx.jingle.media.JingleMediaManager; +import org.jivesoftware.smackx.jingle.media.MediaNegotiator; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.jingle.nat.JingleTransportManager; +import org.jivesoftware.smackx.jingle.nat.TransportNegotiator; +import org.jivesoftware.smackx.jingle.nat.TransportResolver; +import org.jivesoftware.smackx.packet.Jingle; +import org.jivesoftware.smackx.packet.JingleContent; +import org.jivesoftware.smackx.packet.JingleDescription; +import org.jivesoftware.smackx.packet.JingleError; +import org.jivesoftware.smackx.packet.JingleTransport; + +/** + * @author Jeff Williams + * @see JingleSessionState + */ +public class JingleSessionStateUnknown extends JingleSessionState { + private static JingleSessionStateUnknown INSTANCE = null; + + protected JingleSessionStateUnknown() { + // Prevent instantiation of the class. + } + + /** + * A thread-safe means of getting the one instance of this class. + * @return The singleton instance of this class. + */ + public synchronized static JingleSessionState getInstance() { + if (INSTANCE == null) { + INSTANCE = new JingleSessionStateUnknown(); + } + return INSTANCE; + } + + public void enter() { + // TODO Auto-generated method stub + + } + + public void exit() { + // TODO Auto-generated method stub + + } + + public IQ processJingle(JingleSession session, Jingle jingle, JingleActionEnum action) { + IQ response = null; + + switch (action) { + case SESSION_INITIATE: + response = receiveSessionInitiateAction(session, jingle); + break; + + case SESSION_TERMINATE: + response = receiveSessionTerminateAction(session, jingle); + break; + + default: + // Anything other than session-initiate is an error. + response = session.createJingleError(jingle, JingleError.MALFORMED_STANZA); + break; + } + + return response; + } + + /** + * In the UNKNOWN state we received a action. + * This method processes that action. + */ + + private IQ receiveSessionInitiateAction(JingleSession session, Jingle inJingle) { + + IQ response = null; + boolean shouldAck = true; + + // According to XEP-166 when we get a session-initiate we need to check for: + // 1. Initiator unknown + // 2. Receiver redirection + // 3. Does not support Jingle + // 4. Does not support any formats + // 5. Does not support any formats + // If all of the above are OK then we send an IQ type = result to ACK the session-initiate. + + // 1. Initiator unknown + // TODO + + // 2. Receiver redirection + // TODO + + // 3. Does not support Jingle + // Handled by Smack's lower layer. + + // 4. Does not support any formats + // TODO + + // 5. Does not support any formats + // TODO + + if (!shouldAck) { + + response = session.createJingleError(inJingle, JingleError.NEGOTIATION_ERROR); + + } else { + + // Create the Ack + response = session.createAck(inJingle); + + session.setSessionState(JingleSessionStatePending.getInstance()); + + // Now set up all of the initial content negotiators for the session. + for (JingleContent jingleContent : inJingle.getContentsList()) { + // First create the content negotiator for this section. + ContentNegotiator contentNeg = new ContentNegotiator(session, jingleContent.getCreator(), jingleContent + .getName()); + + // Get the media negotiator that goes with the of this content. + JingleDescription jingleDescription = jingleContent.getDescription(); + + // Loop through each media manager looking for the ones that matches the incoming + // session-initiate choices. + // (Set the first media manager as the default, so that in case things don't match we can still negotiate.) + JingleMediaManager chosenMediaManager = session.getMediaManagers().get(0); + for (JingleMediaManager mediaManager : session.getMediaManagers()) { + boolean matches = true; + for (PayloadType mediaPayloadType : mediaManager.getPayloads()) { + for (PayloadType descPayloadType2 : jingleDescription.getPayloadTypesList()) { + if (mediaPayloadType.getId() != descPayloadType2.getId()) { + matches = false; + } + } + if (matches) { + chosenMediaManager = mediaManager; + } + } + } + + // Create the media negotiator for this content description. + contentNeg.setMediaNegotiator(new MediaNegotiator(session, chosenMediaManager, jingleDescription + .getPayloadTypesList(), contentNeg)); + + // For each transport type in this content, try to find the corresponding transport manager. + // Then create a transport negotiator for that transport. + for (JingleTransport jingleTransport : jingleContent.getJingleTransportsList()) { + for (JingleMediaManager mediaManager : session.getMediaManagers()) { + + JingleTransportManager transportManager = mediaManager.getTransportManager(); + TransportResolver resolver = null; + try { + resolver = transportManager.getResolver(session); + } catch (XMPPException e) { + e.printStackTrace(); + } + + if (resolver.getType().equals(TransportResolver.Type.rawupd)) { + contentNeg.setTransportNegotiator(new TransportNegotiator.RawUdp(session, resolver, contentNeg)); + } + if (resolver.getType().equals(TransportResolver.Type.ice)) { + contentNeg.setTransportNegotiator(new TransportNegotiator.Ice(session, resolver, contentNeg)); + } + } + } + + // Add the content negotiator to the session. + session.addContentNegotiator(contentNeg); + } + + // Now setup to track the media negotiators, so that we know when (if) to send a session-accept. + session.setupListeners(); + } + + return response; + } + + /** + * Receive and process the action. + */ + private IQ receiveSessionTerminateAction(JingleSession session, Jingle jingle) { + + // According to XEP-166 the only thing we can do is ack. + IQ response = session.createAck(jingle); + + try { + session.terminate("Closed remotely"); + } catch (XMPPException e) { + e.printStackTrace(); + } + + return response; + } + +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/packet/JingleContent.java b/jingle/extension/source/org/jivesoftware/smackx/packet/JingleContent.java new file mode 100644 index 000000000..a07f021e1 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/packet/JingleContent.java @@ -0,0 +1,186 @@ +/** + * $RCSfile: JingleContent.java,v $ + * $Revision: 1.2 $ + * $Date: 2007/07/02 22:45:36 $ + * + * Copyright 2003-2005 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.packet; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.jivesoftware.smack.packet.PacketExtension; + +/** + * Jingle content. + * + * @author Jeff Williams + */ +public class JingleContent implements PacketExtension { + + public static final String NODENAME = "content"; + public static final String CREATOR = "creator"; + public static final String NAME = "name"; + + private String creator; + private String name; + + private JingleDescription description; + private final List transports = new ArrayList(); + + /** + * Creates a content description.. + */ + public JingleContent(String creator, String name) { + super(); + this.creator = creator; + this.name = name; + } + + public String getCreator() { + return creator; + } + + public String getName() { + return name; + } + + /** + * Returns the XML element name of the element. + * + * @return the XML element name of the element. + */ + public String getElementName() { + return NODENAME; + } + + /** + * Return the namespace. + * + * @return The namespace + */ + public String getNamespace() { + // There is no namespace for + return ""; + } + + /** + * Sets the description for this Jingle content. + * + * @param description + * The description + */ + public void setDescription(JingleDescription description) { + this.description = description; + } + + /** + * Gets the description for this Jingle content. + * + * @return The description. + */ + public JingleDescription getDescription() { + return description; + } + + /** + * Adds a JingleTransport type to the packet. + * + * @param transport + * the JignleTransport to add. + */ + public void addJingleTransport(final JingleTransport transport) { + synchronized (transports) { + transports.add(transport); + } + } + + /** + * Adds a list of transports to add to the packet. + * + * @param transports + * the transports to add. + */ + public void addTransports(final List transports) { + synchronized (transports) { + for (JingleTransport transport : transports) { + addJingleTransport(transport); + } + } + } + + /** + * Returns an Iterator for the JingleTransports in the packet. + * + * @return an Iterator for the JingleTransports in the packet. + */ + public Iterator getJingleTransports() { + return Collections.unmodifiableList(getJingleTransportsList()).iterator(); + } + + /** + * Returns a list for the JingleTransports in the packet. + * + * @return a list for the JingleTransports in the packet. + */ + public List getJingleTransportsList() { + synchronized (transports) { + return new ArrayList(transports); + } + } + + /** + * Returns a count of the JingleTransports in the Jingle packet. + * + * @return the number of the JingleTransports in the Jingle packet. + */ + public int getJingleTransportsCount() { + synchronized (transports) { + return transports.size(); + } + } + + /** + * Convert a Jingle description to XML. + * + * @return a string with the XML representation + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + + synchronized (transports) { + + buf.append("<").append(getElementName()); + + buf.append(" creator='" + creator + "' name='" + name + "'>"); + + // Add the description. + if (description != null) { + buf.append(description.toXML()); + } + + // Add all of the transports. + for (JingleTransport transport : transports) { + buf.append(transport.toXML()); + } + buf.append(""); + } + return buf.toString(); + } + +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/packet/JingleDescription.java b/jingle/extension/source/org/jivesoftware/smackx/packet/JingleDescription.java new file mode 100644 index 000000000..5c82d9480 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/packet/JingleDescription.java @@ -0,0 +1,197 @@ +/** + * $RCSfile: JingleDescription.java,v $ + * $Revision: 1.1 $ + * $Date: 2007/07/02 17:41:08 $ + * + * Copyright 2003-2005 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.packet; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.jingle.media.PayloadType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Jingle content description. + * + * @author Alvaro Saurin + */ +public abstract class JingleDescription implements PacketExtension { + + // static + + public static final String NODENAME = "description"; + + // non-static + + private final List payloads = new ArrayList(); + + /** + * Creates a content description.. + */ + public JingleDescription() { + super(); + } + + /** + * Returns the XML element name of the element. + * + * @return the XML element name of the element. + */ + public String getElementName() { + return NODENAME; + } + + /** + * Return the namespace. + * + * @return The namespace + */ + public abstract String getNamespace(); + + /** + * Adds a audio payload type to the packet. + * + * @param pt the audio payload type to add. + */ + public void addPayloadType(final PayloadType pt) { + synchronized (payloads) { + if (pt == null) { + System.err.println("Null payload type"); + } else { + payloads.add(pt); + } + } + } + + /** + * Adds a list of payloads to the packet. + * + * @param pts the payloads to add. + */ + public void addAudioPayloadTypes(final List pts) { + synchronized (payloads) { + Iterator ptIter = pts.iterator(); + while (ptIter.hasNext()) { + PayloadType.Audio pt = (PayloadType.Audio) ptIter.next(); + addPayloadType(new PayloadType.Audio(pt)); + } + } + } + + /** + * Returns an Iterator for the audio payloads in the packet. + * + * @return an Iterator for the audio payloads in the packet. + */ + public Iterator getPayloadTypes() { + return Collections.unmodifiableList(getPayloadTypesList()).iterator(); + } + + /** + * Returns a list for the audio payloads in the packet. + * + * @return a list for the audio payloads in the packet. + */ + public List getPayloadTypesList() { + synchronized (payloads) { + return new ArrayList(payloads); + } + } + + /** + * Return the list of Payload types contained in the description. + * + * @return a list of PayloadType.Audio + */ + public List getAudioPayloadTypesList() { + ArrayList result = new ArrayList(); + Iterator jinglePtsIter = getPayloadTypes(); + + while (jinglePtsIter.hasNext()) { + PayloadType jpt = (PayloadType) jinglePtsIter.next(); + if (jpt instanceof PayloadType.Audio) { + PayloadType.Audio jpta = (PayloadType.Audio) jpt; + result.add(jpta); + } + } + + return result; + } + + /** + * Returns a count of the audio payloads in the Jingle packet. + * + * @return the number of audio payloads in the Jingle packet. + */ + public int getPayloadTypesCount() { + synchronized (payloads) { + return payloads.size(); + } + } + + /** + * Convert a Jingle description to XML. + * + * @return a string with the XML representation + */ + public String toXML() { + StringBuilder buf = new StringBuilder(); + + synchronized (payloads) { + if (payloads.size() > 0) { + buf.append("<").append(getElementName()); + buf.append(" xmlns=\"").append(getNamespace()).append("\" >"); + + for (PayloadType payloadType : payloads) { + if (payloadType != null) { + buf.append(payloadType.toXML()); + } + } + buf.append(""); + } + } + + return buf.toString(); + } + + /** + * Jingle audio description + */ + public static class Audio extends JingleDescription { + + public static final String NAMESPACE = "http://www.xmpp.org/extensions/xep-0167.html#ns"; + + public Audio() { + super(); + } + + /** + * Utility constructor, with a PayloadType + */ + public Audio(final PayloadType pt) { + super(); + addPayloadType(pt); + } + + public String getNamespace() { + return NAMESPACE; + } + } +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/provider/JingleContentProvider.java b/jingle/extension/source/org/jivesoftware/smackx/provider/JingleContentProvider.java new file mode 100644 index 000000000..ca1f52184 --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/provider/JingleContentProvider.java @@ -0,0 +1,58 @@ +/** + * $RCSfile: JingleContentProvider.java,v $ + * $Revision: 1.2 $ + * $Date: 2007/07/04 00:52:33 $ + * + * Copyright 2003-2005 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.provider; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smackx.packet.JingleContent; +import org.xmlpull.v1.XmlPullParser; + +/** + * Jingle provider + * + * @author Jeff Williams + */ +public class JingleContentProvider implements PacketExtensionProvider { + + /** + * Creates a new provider. ProviderManager requires that every + * PacketExtensionProvider has a public, no-argument constructor + */ + public JingleContentProvider() { + super(); + } + + /** + * Parse a JingleContent extension. + */ + public PacketExtension parseExtension(final XmlPullParser parser) throws Exception { + PacketExtension result = null; + + String elementName = parser.getName(); + String creator = parser.getAttributeValue("", JingleContent.CREATOR); + String name = parser.getAttributeValue("", JingleContent.NAME); + + // Try to get an Audio content info + result = new JingleContent(creator, name); + + return result; + } + +} diff --git a/jingle/extension/source/org/jivesoftware/smackx/provider/JingleDescriptionProvider.java b/jingle/extension/source/org/jivesoftware/smackx/provider/JingleDescriptionProvider.java new file mode 100644 index 000000000..475d5504c --- /dev/null +++ b/jingle/extension/source/org/jivesoftware/smackx/provider/JingleDescriptionProvider.java @@ -0,0 +1,143 @@ +/** + * $RCSfile: JingleDescriptionProvider.java,v $ + * $Revision: 1.1 $ + * $Date: 2007/07/02 17:41:11 $ + * + * Copyright 2003-2005 Jive Software. + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.provider; + +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smackx.jingle.media.PayloadType; +import org.jivesoftware.smackx.packet.JingleDescription; +import org.xmlpull.v1.XmlPullParser; + +/** + * Parser for a Jingle description + * + * @author Alvaro Saurin + */ +public abstract class JingleDescriptionProvider implements PacketExtensionProvider { + + /** + * Default constructor + */ + public JingleDescriptionProvider() { + super(); + } + + /** + * Parse a iq/jingle/description/payload-type element. + * + * @param parser + * the input to parse + * @return a payload type element + * @throws Exception + */ + protected PayloadType parsePayload(final XmlPullParser parser) throws Exception { + int ptId = 0; + String ptName; + int ptChannels = 0; + + try { + ptId = Integer.parseInt(parser.getAttributeValue("", "id")); + } catch (Exception e) { + } + + ptName = parser.getAttributeValue("", "name"); + + try { + ptChannels = Integer.parseInt(parser.getAttributeValue("", "channels")); + } catch (Exception e) { + } + + return new PayloadType(ptId, ptName, ptChannels); + } + + /** + * Parse a iq/jingle/description element. + * + * @param parser + * the input to parse + * @return a description element + * @throws Exception + */ + public PacketExtension parseExtension(final XmlPullParser parser) throws Exception { + boolean done = false; + JingleDescription desc = getInstance(); + + while (!done) { + int eventType = parser.next(); + String name = parser.getName(); + + if (eventType == XmlPullParser.START_TAG) { + if (name.equals(PayloadType.NODENAME)) { + desc.addPayloadType(parsePayload(parser)); + } else { + throw new Exception("Unknow element \"" + name + "\" in content."); + } + } else if (eventType == XmlPullParser.END_TAG) { + if (name.equals(JingleDescription.NODENAME)) { + done = true; + } + } + } + return desc; + } + + /** + * Return a new instance of this class. Subclasses must overwrite this + * method. + */ + protected abstract JingleDescription getInstance(); + + /** + * Jingle audio + */ + public static class Audio extends JingleDescriptionProvider { + + /** + * Default constructor + */ + public Audio() { + super(); + } + + /** + * Parse an audio payload type. + */ + public PayloadType parsePayload(final XmlPullParser parser) throws Exception { + PayloadType pte = super.parsePayload(parser); + PayloadType.Audio pt = new PayloadType.Audio(pte); + int ptClockRate = 0; + + try { + ptClockRate = Integer.parseInt(parser.getAttributeValue("", "clockrate")); + } catch (Exception e) { + } + pt.setClockRate(ptClockRate); + + return pt; + } + + /** + * Get a new instance of this object. + */ + protected JingleDescription getInstance() { + return new JingleDescription.Audio(); + } + } +}