diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md
index 05ffa0b5c..d2764a2d0 100644
--- a/documentation/extensions/index.md
+++ b/documentation/extensions/index.md
@@ -56,6 +56,7 @@ Smack Extensions and currently supported XEPs of smack-extensions
| Stream Initation | [XEP-0095](http://xmpp.org/extensions/xep-0095.html) | Initiating a data stream between any two XMPP entities. |
| [SI File Transfer](filetransfer.md) | [XEP-0096](http://xmpp.org/extensions/xep-0096.html) | Transfer files between two users over XMPP. |
| [Entity Capabilities](caps.md) | [XEP-0115](http://xmpp.org/extensions/xep-0115.html) | Broadcasting and dynamic discovery of entity capabilities. |
+| [Jingle](jingle.html) | [XEP-0116](http://xmpp.org/extensions/xep-0166.html) | Initiate and manage sessions between two XMPP entities. |
| Data Forms Validation | [XEP-0122](http://xmpp.org/extensions/xep-0122.html) | Enables an application to specify additional validation guidelines . |
| Service Administration | [XEP-0133](http://xmpp.org/extensions/xep-0133.html) | Recommended best practices for service-level administration of servers and components using Ad-Hoc Commands. |
| Stream Compression | [XEP-0138](http://xmpp.org/extensions/xep-0138.html) | Support for optional compression of the XMPP stream.
diff --git a/documentation/extensions/jingle.md b/documentation/extensions/jingle.md
new file mode 100644
index 000000000..926c3aef8
--- /dev/null
+++ b/documentation/extensions/jingle.md
@@ -0,0 +1,72 @@
+Jingle
+======
+
+**XEP related:** [XEP-0116: Jingle](http://xmpp.org/extensions/xep-0166.html)
+
+Jingle Element Structure
+------------------------
+
+```
+jingle
+│ action (REQUIRED, XEP-0166 § 7.2)
+| content-accept
+| content-add
+| content-modify
+| content-reject
+| content-remove
+| description-info
+| security-info
+| session-accept
+| session-info
+| session-initiate
+| transport-accept
+| transport-info
+| transport-reject
+| transport-replace
+│ initator (RECOMMENDED for session initiate, NOT RECOMMENDED otherwise, full JID, XEP-0166 § 7.1)
+│ responder (RECOMMENDED for session accept, NOT RECOMMENDED otherwise, full JID. XEP-0166 § 7.1)
+│ sid (REQUIRED, SHOULD match XML Nmtoken production)
+│
+├── (optional, XEP-0166 § 7.4)
+│ │
+│ └──(alternative─session│busy│..)
+│
+└── (one or more, XEP-0166 § 7.3)
+ │ creator (REQUIRED, must be one of)
+ | initiator
+ | responder
+ │ disposition (OPTIONAL)
+ │ name (REQUIRED)
+ │ senders (OPTIONAL, except when content-modify then REQUIRED)
+ | both (default)
+ | initiator
+ | none
+ | responder
+ │
+ ├──description
+ │ │ media
+ │ │ xmlns
+ │ │
+ │ ├──payload─type
+ │ │
+ │ └──file (XEP─0234)
+ │
+ └──transport
+ │ xmlns
+ │ pwd (OPTIONAL, XEP-0176 Jingle ICE)
+ │ ufrag (OPTIONAL, XEP-0176 Jingle ICE)
+ │ mode (XEP-0234 Jingle File Transfer)
+ │ sid (XEP-0234 Jingle File Transfer)
+ │
+ └──candidate
+ component
+ foundation
+ generation
+ id
+ ip
+ network
+ port
+ priority
+ protocol
+ type
+```
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleHandler.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleHandler.java
new file mode 100644
index 000000000..4f183fc7c
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleHandler.java
@@ -0,0 +1,26 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.jingle.element.Jingle;
+
+public interface JingleHandler {
+
+ IQ handleRequest(Jingle jingle);
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java
new file mode 100644
index 000000000..03fd4fe40
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleManager.java
@@ -0,0 +1,136 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import org.jivesoftware.smack.Manager;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
+import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.IQ.Type;
+import org.jivesoftware.smackx.jingle.element.Jingle;
+import org.jivesoftware.smackx.jingle.element.JingleContent;
+import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
+import org.jxmpp.jid.FullJid;
+import org.jxmpp.jid.Jid;
+
+public final class JingleManager extends Manager {
+
+ private static final Logger LOGGER = Logger.getLogger(JingleManager.class.getName());
+
+ private static final Map INSTANCES = new WeakHashMap<>();
+
+ public static synchronized JingleManager getInstanceFor(XMPPConnection connection) {
+ JingleManager jingleManager = INSTANCES.get(connection);
+ if (jingleManager == null) {
+ jingleManager = new JingleManager(connection);
+ INSTANCES.put(connection, jingleManager);
+ }
+ return jingleManager;
+ }
+
+ private final Map descriptionHandlers = new ConcurrentHashMap<>();
+
+ private final Map jingleSessionHandlers = new ConcurrentHashMap<>();
+
+ private JingleManager(XMPPConnection connection) {
+ super(connection);
+
+ connection.registerIQRequestHandler(
+ new AbstractIqRequestHandler(Jingle.ELEMENT, Jingle.NAMESPACE, Type.set, Mode.async) {
+ @Override
+ public IQ handleIQRequest(IQ iqRequest) {
+ final Jingle jingle = (Jingle) iqRequest;
+
+ if (jingle.getContents().isEmpty()) {
+ Jid from = jingle.getFrom();
+ assert (from != null);
+ FullJid fullFrom = from.asFullJidOrThrow();
+ String sid = jingle.getSid();
+ FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(fullFrom, sid);
+ JingleSessionHandler jingleSessionHandler = jingleSessionHandlers.get(fullJidAndSessionId);
+ if (jingleSessionHandler == null) {
+ // TODO handle non existing jingle session handler.
+ return null;
+ }
+ return jingleSessionHandler.handleRequest(jingle, sid);
+ }
+
+ if (jingle.getContents().size() > 1) {
+ LOGGER.severe("Jingle IQs with more then one content element are currently not supported by Smack");
+ return null;
+ }
+
+ JingleContent content = jingle.getContents().get(0);
+ JingleContentDescription description = content.getDescription();
+ JingleHandler jingleDescriptionHandler = descriptionHandlers.get(
+ description.getNamespace());
+ if (jingleDescriptionHandler == null) {
+ // TODO handle non existing content description handler.
+ return null;
+ }
+ return jingleDescriptionHandler.handleRequest(jingle);
+ }
+ });
+ }
+
+ public JingleHandler registerDescriptionHandler(String namespace, JingleHandler handler) {
+ return descriptionHandlers.put(namespace, handler);
+ }
+
+ public JingleSessionHandler registerJingleSessionHandler(FullJid otherJid, String sessionId, JingleSessionHandler sessionHandler) {
+ FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(otherJid, sessionId);
+ return jingleSessionHandlers.put(fullJidAndSessionId, sessionHandler);
+ }
+
+ public JingleSessionHandler unregisterJingleSessionhandler(FullJid otherJid, String sessionId, JingleSessionHandler sessionHandler) {
+ FullJidAndSessionId fullJidAndSessionId = new FullJidAndSessionId(otherJid, sessionId);
+ return jingleSessionHandlers.remove(fullJidAndSessionId);
+ }
+
+ private static final class FullJidAndSessionId {
+ final FullJid fullJid;
+ final String sessionId;
+
+ private FullJidAndSessionId(FullJid fullJid, String sessionId) {
+ this.fullJid = fullJid;
+ this.sessionId = sessionId;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 31 * fullJid.hashCode();
+ hashCode = 31 * hashCode + sessionId.hashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof FullJidAndSessionId)) {
+ return false;
+ }
+ FullJidAndSessionId otherFullJidAndSessionId = (FullJidAndSessionId) other;
+ return fullJid.equals(otherFullJidAndSessionId.fullJid)
+ && sessionId.equals(otherFullJidAndSessionId.sessionId);
+ }
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java
new file mode 100644
index 000000000..e86223371
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSession.java
@@ -0,0 +1,54 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle;
+
+import org.jxmpp.jid.Jid;
+
+// TODO: Is this class still required? If not, then remove it.
+public class JingleSession {
+
+ private final Jid initiator;
+
+ private final Jid responder;
+
+ private final String sid;
+
+ public JingleSession(Jid initiator, Jid responder, String sid) {
+ this.initiator = initiator;
+ this.responder = responder;
+ this.sid = sid;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 31 + initiator.hashCode();
+ hashCode = 31 * hashCode + responder.hashCode();
+ hashCode = 31 * hashCode + sid.hashCode();
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof JingleSession)) {
+ return false;
+ }
+
+ JingleSession otherJingleSession = (JingleSession) other;
+ return initiator.equals(otherJingleSession.initiator) && responder.equals(otherJingleSession.responder)
+ && sid.equals(otherJingleSession.sid);
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSessionHandler.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSessionHandler.java
new file mode 100644
index 000000000..db3cc2f98
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/JingleSessionHandler.java
@@ -0,0 +1,26 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.jingle.element.Jingle;
+
+public interface JingleSessionHandler {
+
+ IQ handleRequest(Jingle jingle, String sessionId);
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java
new file mode 100644
index 000000000..1c5661158
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/Jingle.java
@@ -0,0 +1,204 @@
+/**
+ *
+ * Copyright 2003-2007 Jive Software, 2014-2017 Florian Schmaus
+ *
+ * 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.jingle.element;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.util.Objects;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jxmpp.jid.FullJid;
+
+/**
+ * The Jingle element.
+ *
+ * @author Florian Schmaus
+ */
+public final class Jingle extends IQ {
+
+ public static final String NAMESPACE = "urn:xmpp:jingle:1";
+
+ public static final String ACTION_ATTRIBUTE_NAME = "action";
+
+ public static final String INITIATOR_ATTRIBUTE_NAME = "initiator";
+
+ public static final String RESPONDER_ATTRIBUTE_NAME = "responder";
+
+ public static final String SESSION_ID_ATTRIBUTE_NAME = "sid";
+
+ public static final String ELEMENT = "jingle";
+
+ /**
+ * The session ID related to this session. The session ID is a unique identifier generated by the initiator. This
+ * should match the XML Nmtoken production so that XML character escaping is not needed for characters such as &.
+ */
+ private final String sessionId;
+
+ /**
+ * The jingle action. This attribute is required.
+ */
+ private final JingleAction action;
+
+ private final FullJid initiator;
+
+ private final FullJid responder;
+
+ private final JingleReason reason;
+
+ private final List contents;
+
+ private Jingle(String sessionId, JingleAction action, FullJid initiator, FullJid responder, JingleReason reason,
+ List contents) {
+ super(ELEMENT, NAMESPACE);
+ this.sessionId = StringUtils.requireNotNullOrEmpty(sessionId, "Jingle session ID must not be null");
+ this.action = Objects.requireNonNull(action, "Jingle action must not be null");
+ this.initiator = initiator;
+ this.responder = responder;
+ this.reason = reason;
+ if (contents != null) {
+ this.contents = Collections.unmodifiableList(contents);
+ }
+ else {
+ this.contents = Collections.emptyList();
+ }
+ }
+
+ /**
+ * Get the initiator. The initiator will be the full JID of the entity that has initiated the flow (which may be
+ * different to the "from" address in the IQ)
+ *
+ * @return the initiator
+ */
+ public FullJid getInitiator() {
+ return initiator;
+ }
+
+ /**
+ * Get the responder. The responder is the full JID of the entity that has replied to the initiation (which may be
+ * different to the "to" addresss in the IQ).
+ *
+ * @return the responder
+ */
+ public FullJid getResponder() {
+ return responder;
+ }
+
+ /**
+ * Returns the session ID related to the session. The session ID is a unique identifier generated by the initiator.
+ * This should match the XML Nmtoken production so that XML character escaping is not needed for characters such as
+ * &.
+ *
+ * @return Returns the session ID related to the session.
+ */
+ public String getSid() {
+ return sessionId;
+ }
+
+ /**
+ * Get the action specified in the jingle IQ.
+ *
+ * @return the action.
+ */
+ public JingleAction getAction() {
+ return action;
+ }
+
+ /**
+ * Get a List of the contents.
+ *
+ * @return the contents.
+ */
+ public List getContents() {
+ return contents;
+ }
+
+ @Override
+ protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
+ xml.optAttribute(INITIATOR_ATTRIBUTE_NAME, getInitiator());
+ xml.optAttribute(RESPONDER_ATTRIBUTE_NAME, getResponder());
+ xml.optAttribute(ACTION_ATTRIBUTE_NAME, getAction());
+ xml.optAttribute(SESSION_ID_ATTRIBUTE_NAME, getSid());
+ xml.rightAngleBracket();
+
+ xml.optElement(reason);
+
+ xml.append(contents);
+
+ return xml;
+ }
+
+ public static Builder getBuilder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String sid;
+
+ private JingleAction action;
+
+ private FullJid initiator;
+
+ private FullJid responder;
+
+ private JingleReason reason;
+
+ private List contents;
+
+ private Builder() {
+ }
+
+ public Builder setSessionId(String sessionId) {
+ this.sid = sessionId;
+ return this;
+ }
+
+ public Builder setAction(JingleAction action) {
+ this.action = action;
+ return this;
+ }
+
+ public Builder setInitiator(FullJid initator) {
+ this.initiator = initator;
+ return this;
+ }
+
+ public Builder setResponder(FullJid responder) {
+ this.responder = responder;
+ return this;
+ }
+
+ public Builder addJingleContent(JingleContent content) {
+ if (contents == null) {
+ contents = new ArrayList<>(1);
+ }
+ contents.add(content);
+ return this;
+ }
+
+ public Builder setReason(JingleReason.Reason reason) {
+ this.reason = new JingleReason(reason);
+ return this;
+ }
+
+ public Jingle build() {
+ return new Jingle(sid, action, initiator, responder, reason, contents);
+ }
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleAction.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleAction.java
new file mode 100644
index 000000000..81daeb227
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleAction.java
@@ -0,0 +1,72 @@
+/**
+ *
+ * Copyright © 2014-2017 Florian Schmaus
+ *
+ * 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.jingle.element;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The "action" in the jingle packet, as an enum.
+ *
+ *
+ * @author Florian Schmaus
+ */
+public enum JingleAction {
+
+ content_accept,
+ content_add,
+ content_modify,
+ content_reject,
+ content_remove,
+ description_info,
+ session_accept,
+ session_info,
+ session_initiate,
+ sessio_terminate,
+ transport_accept,
+ transport_info,
+ transport_reject,
+ transport_replace,
+ ;
+
+ private static final Map map = new HashMap(
+ JingleAction.values().length);
+ static {
+ for (JingleAction jingleAction : JingleAction.values()) {
+ map.put(jingleAction.toString(), jingleAction);
+ }
+ }
+
+ private final String asString;
+
+ private JingleAction() {
+ asString = this.name().replace('_', '-');
+ }
+
+ @Override
+ public String toString() {
+ return asString;
+ }
+
+ public static JingleAction fromString(String string) {
+ JingleAction jingleAction = map.get(string);
+ if (jingleAction == null) {
+ throw new IllegalArgumentException("Unknown jingle action: " + string);
+ }
+ return jingleAction;
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java
new file mode 100644
index 000000000..ca5696bff
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContent.java
@@ -0,0 +1,218 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.element;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.util.Objects;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * Jingle content element.
+ */
+public final class JingleContent implements NamedElement {
+
+ public static final String ELEMENT = "content";
+
+ public static final String CREATOR_ATTRIBUTE_NAME = "creator";
+
+ public enum Creator {
+ initiator,
+ responder,
+ }
+
+ /**
+ * Which party originally generated the content type. Defined values are 'initiator' and 'responder'. Default is
+ * 'initiator'.
+ */
+ private final Creator creator;
+
+ public static final String DISPOSITION_ATTRIBUTE_NAME = "disposition";
+
+ private final String disposition;
+
+ public static final String NAME_ATTRIBUTE_NAME = "name";
+
+ private final String name;
+
+ public static final String SENDERS_ATTRIBUTE_NAME = "senders";
+
+ public enum Senders {
+ both,
+ initiator,
+ none,
+ responder,
+ }
+
+ /**
+ * Which parties in the session will be generation the content. Defined values are 'both', 'initiator', 'none' and
+ * 'responder. Default is 'both'.
+ */
+ private final Senders senders;
+
+ private final JingleContentDescription description;
+
+ private final List transports;
+
+ /**
+ * Creates a content description..
+ */
+ private JingleContent(Creator creator, String disposition, String name, Senders senders,
+ JingleContentDescription description, List transports) {
+ this.creator = Objects.requireNonNull(creator, "Jingle content creator must not be null");
+ this.disposition = disposition;
+ this.name = StringUtils.requireNotNullOrEmpty(name, "Jingle content name must not be null or empty");
+ this.senders = senders;
+ this.description = description;
+ if (transports != null) {
+ this.transports = Collections.unmodifiableList(transports);
+ }
+ else {
+ this.transports = Collections.emptyList();
+ }
+ }
+
+ public Creator getCreator() {
+ return creator;
+ }
+
+ public String getDisposition() {
+ return disposition;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Senders getSenders() {
+ return senders;
+ }
+
+ /**
+ * Gets the description for this Jingle content.
+ *
+ * @return The description.
+ */
+ public JingleContentDescription getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns an Iterator for the JingleTransports in the packet.
+ *
+ * @return an Iterator for the JingleTransports in the packet.
+ */
+ public List getJingleTransports() {
+ return transports;
+ }
+
+ /**
+ * Returns a count of the JingleTransports in the Jingle packet.
+ *
+ * @return the number of the JingleTransports in the Jingle packet.
+ */
+ public int getJingleTransportsCount() {
+ return transports.size();
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public XmlStringBuilder toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder();
+ xml.attribute(CREATOR_ATTRIBUTE_NAME, creator);
+ xml.optAttribute(DISPOSITION_ATTRIBUTE_NAME, disposition);
+ xml.attribute(NAME_ATTRIBUTE_NAME, name);
+ xml.optAttribute(SENDERS_ATTRIBUTE_NAME, senders);
+ xml.rightAngleBracket();
+
+ xml.optAppend(description);
+
+ xml.append(transports);
+
+ xml.closeElement(this);
+ return xml;
+ }
+
+ public static Builder getBuilder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private Creator creator;
+
+ private String disposition;
+
+ private String name;
+
+ private Senders senders;
+
+ private JingleContentDescription description;
+
+ private List transports;
+
+ private Builder() {
+ }
+
+ public Builder setCreator(Creator creator) {
+ this.creator = creator;
+ return this;
+ }
+
+ public Builder setDisposition(String disposition) {
+ this.disposition = disposition;
+ return this;
+ }
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setSenders(Senders senders) {
+ this.senders = senders;
+ return this;
+ }
+
+ public Builder setDescription(JingleContentDescription description) {
+ if (this.description != null) {
+ throw new IllegalStateException("Jingle content description already set");
+ }
+ this.description = description;
+ return this;
+ }
+
+ public Builder addTransport(JingleContentTransport transport) {
+ if (transports == null) {
+ transports = new ArrayList<>(4);
+ }
+ transports.add(transport);
+ return this;
+ }
+
+ public JingleContent build() {
+ return new JingleContent(creator, disposition, name, senders, description, transports);
+ }
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescription.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescription.java
new file mode 100644
index 000000000..7a1e6463f
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescription.java
@@ -0,0 +1,68 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus.
+ *
+ * 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.jingle.element;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.jivesoftware.smack.packet.ExtensionElement;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * Jingle content description.
+ *
+ */
+public abstract class JingleContentDescription implements ExtensionElement {
+
+ public static final String ELEMENT = "description";
+
+ private final List payloads;
+
+ protected JingleContentDescription(List payloads) {
+ if (payloads != null) {
+ this.payloads = Collections.unmodifiableList(payloads);
+ }
+ else {
+ this.payloads = Collections.emptyList();
+ }
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ public List getJinglePayloadTypes() {
+ return payloads;
+ }
+
+ protected void addExtraAttributes(XmlStringBuilder xml) {
+
+ }
+
+ @Override
+ public final XmlStringBuilder toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ addExtraAttributes(xml);
+ xml.rightAngleBracket();
+
+ xml.append(payloads);
+
+ return xml;
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescriptionPayloadType.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescriptionPayloadType.java
new file mode 100644
index 000000000..ff525b0c1
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentDescriptionPayloadType.java
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright © 2014-2017 Florian Schmaus
+ *
+ * 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.jingle.element;
+
+import org.jivesoftware.smack.packet.NamedElement;
+
+/**
+ * An element found usually in 'description' elements.
+ *
+ */
+public abstract class JingleContentDescriptionPayloadType implements NamedElement {
+
+ public static final String ELEMENT = "payload-type";
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransport.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransport.java
new file mode 100644
index 000000000..5885c5ad8
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransport.java
@@ -0,0 +1,69 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.element;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.jivesoftware.smack.packet.ExtensionElement;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * A jingle transport extension.
+ *
+ */
+public abstract class JingleContentTransport implements ExtensionElement {
+
+ public static final String ELEMENT = "transport";
+
+ protected final List candidates;
+
+ protected JingleContentTransport(List candidates) {
+ if (candidates != null) {
+ this.candidates = Collections.unmodifiableList(candidates);
+ }
+ else {
+ this.candidates = Collections.emptyList();
+ }
+ }
+
+ public List getCandidates() {
+ return candidates;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ protected void addExtraAttributes(XmlStringBuilder xml) {
+
+ }
+
+ @Override
+ public final XmlStringBuilder toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ addExtraAttributes(xml);
+ xml.rightAngleBracket();
+
+ xml.append(candidates);
+
+ xml.closeElement(this);
+ return xml;
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransportCandidate.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransportCandidate.java
new file mode 100644
index 000000000..be38c8864
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleContentTransportCandidate.java
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.element;
+
+import org.jivesoftware.smack.packet.NamedElement;
+
+/**
+ * An element found usually in Jingle 'transport' elements.
+ *
+ */
+public abstract class JingleContentTransportCandidate implements NamedElement {
+
+ public static final String ELEMENT = "candidate";
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleError.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleError.java
new file mode 100644
index 000000000..6fa132893
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleError.java
@@ -0,0 +1,98 @@
+/**
+ *
+ * Copyright 2003-2005 Jive Software, 2017 Florian Schmaus.
+ *
+ * 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.jingle.element;
+
+import java.util.Locale;
+
+import org.jivesoftware.smack.packet.ExtensionElement;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+public final class JingleError implements ExtensionElement {
+
+ public static String NAMESPACE = "urn:xmpp:jingle:errors:1";
+
+ public static final JingleError OUT_OF_ORDER = new JingleError("out-of-order");
+
+ public static final JingleError UNKNOWN_SESSION = new JingleError("unknown-session");
+
+ public static final JingleError UNSUPPORTED_CONTENT = new JingleError("unsupported-content");
+
+ public static final JingleError UNSUPPORTED_TRANSPORTS = new JingleError("unsupported-transports");
+
+ private final String errorName;
+
+ /**
+ * Creates a new error with the specified code and errorName.
+ *
+ * @param message a message describing the error.
+ */
+ private JingleError(final String errorName) {
+ this.errorName = errorName;
+ }
+
+ /**
+ * Returns the name of the Jingle error.
+ *
+ * @return the name of the error.
+ */
+ public String getMessage() {
+ return errorName;
+ }
+
+ @Override
+ public XmlStringBuilder toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.closeEmptyElement();
+ return xml;
+ }
+
+ /**
+ * Returns a Action instance associated with the String value.
+ */
+ public static JingleError fromString(String value) {
+ value = value.toLowerCase(Locale.US);
+ switch (value) {
+ case "out-of-order":
+ return OUT_OF_ORDER;
+ case "unknown-session":
+ return UNKNOWN_SESSION;
+ case "unsupported-content":
+ return UNSUPPORTED_CONTENT;
+ case "unsupported-transports":
+ return UNSUPPORTED_TRANSPORTS;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getMessage();
+ }
+
+ @Override
+ public String getElementName() {
+ return errorName;
+ }
+
+ @Override
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java
new file mode 100644
index 000000000..b150832d3
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/JingleReason.java
@@ -0,0 +1,105 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.element;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * The Jingle 'reason' element.
+ *
+ * @see XEP-0166 § 7.4
+ *
+ */
+public class JingleReason implements NamedElement {
+
+ public static final String ELEMENT = "reason";
+
+ public enum Reason {
+ alternative_session,
+ busy,
+ cancel,
+ connectivity_error,
+ decline,
+ expired,
+ failed_application,
+ failed_transport,
+ general_error,
+ gone,
+ incompatible_parameters,
+ media_error,
+ security_error,
+ success,
+ timeout,
+ unsupported_applications,
+ unsupported_transports,
+ ;
+
+ private static final Map LUT = new HashMap<>(Reason.values().length);
+
+ static {
+ for (Reason reason : Reason.values()) {
+ LUT.put(reason.toString(), reason);
+ }
+ }
+
+ private final String asString;
+
+ private Reason() {
+ asString = name().replace('_', '-');
+ }
+
+ @Override
+ public String toString() {
+ return asString;
+ }
+
+ public static Reason fromString(String string) {
+ Reason reason = LUT.get(string);
+ if (reason == null) {
+ throw new IllegalArgumentException("Unknown reason: " + string);
+ }
+ return reason;
+ }
+ }
+
+ private final Reason reason;
+
+ public JingleReason(Reason reason) {
+ this.reason = reason;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public XmlStringBuilder toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.rightAngleBracket();
+
+ xml.emptyElement(reason);
+
+ xml.closeElement(this);
+ return xml;
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/package-info.java
new file mode 100644
index 000000000..49fdcc362
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/element/package-info.java
@@ -0,0 +1,21 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.
+ */
+
+/**
+ * Stanzas and Extension Elements for XEP-0166: Jingle.
+ */
+package org.jivesoftware.smackx.jingle.element;
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/package-info.java
new file mode 100644
index 000000000..2e7a2ec90
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/package-info.java
@@ -0,0 +1,21 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.
+ */
+
+/**
+ * Smack's API for XEP-0166: Jingle.
+ */
+package org.jivesoftware.smackx.jingle;
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentDescriptionProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentDescriptionProvider.java
new file mode 100644
index 000000000..ac69e7783
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentDescriptionProvider.java
@@ -0,0 +1,29 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.provider;
+
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
+import org.xmlpull.v1.XmlPullParser;
+
+public abstract class JingleContentDescriptionProvider
+ extends ExtensionElementProvider {
+
+ @Override
+ public abstract D parse(XmlPullParser parser, int initialDepth) throws Exception;
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentProviderManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentProviderManager.java
new file mode 100644
index 000000000..a4592e45d
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentProviderManager.java
@@ -0,0 +1,45 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.provider;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class JingleContentProviderManager {
+
+ private static final Map> jingleContentDescriptionProviders = new ConcurrentHashMap<>();
+
+ private static final Map> jingleContentTransportProviders = new ConcurrentHashMap<>();
+
+ public static JingleContentDescriptionProvider> addJingleContentDescrptionProvider(String namespace,
+ JingleContentDescriptionProvider> provider) {
+ return jingleContentDescriptionProviders.put(namespace, provider);
+ }
+
+ public static JingleContentDescriptionProvider> getJingleContentDescriptionProvider(String namespace) {
+ return jingleContentDescriptionProviders.get(namespace);
+ }
+
+ public static JingleContentTransportProvider> addJingleContentDescrptionProvider(String namespace,
+ JingleContentTransportProvider> provider) {
+ return jingleContentTransportProviders.put(namespace, provider);
+ }
+
+ public static JingleContentTransportProvider> getJingleContentTransportProvider(String namespace) {
+ return jingleContentTransportProviders.get(namespace);
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentTransportProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentTransportProvider.java
new file mode 100644
index 000000000..e9e1774f5
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleContentTransportProvider.java
@@ -0,0 +1,29 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.provider;
+
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
+import org.xmlpull.v1.XmlPullParser;
+
+public abstract class JingleContentTransportProvider
+ extends ExtensionElementProvider {
+
+ @Override
+ public abstract T parse(XmlPullParser parser, int initialDepth) throws Exception;
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleErrorProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleErrorProvider.java
new file mode 100644
index 000000000..12be5370d
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleErrorProvider.java
@@ -0,0 +1,31 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.provider;
+
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.jivesoftware.smackx.jingle.element.JingleError;
+import org.xmlpull.v1.XmlPullParser;
+
+public class JingleErrorProvider extends ExtensionElementProvider {
+
+ @Override
+ public JingleError parse(XmlPullParser parser, int initialDepth) throws Exception {
+ String errorName = parser.getName();
+ return JingleError.fromString(errorName);
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java
new file mode 100644
index 000000000..f00ebfc0b
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/JingleProvider.java
@@ -0,0 +1,149 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.jingle.provider;
+
+import java.util.logging.Logger;
+
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.ParserUtils;
+import org.jivesoftware.smackx.jingle.element.Jingle;
+import org.jivesoftware.smackx.jingle.element.JingleAction;
+import org.jivesoftware.smackx.jingle.element.JingleContent;
+import org.jivesoftware.smackx.jingle.element.JingleContentDescription;
+import org.jivesoftware.smackx.jingle.element.JingleContentTransport;
+import org.jivesoftware.smackx.jingle.element.JingleReason;
+import org.jivesoftware.smackx.jingle.element.JingleReason.Reason;
+import org.jxmpp.jid.FullJid;
+import org.xmlpull.v1.XmlPullParser;
+
+public class JingleProvider extends IQProvider {
+
+ private static final Logger LOGGER = Logger.getLogger(JingleProvider.class.getName());
+
+ @Override
+ public Jingle parse(XmlPullParser parser, int initialDepth) throws Exception {
+ Jingle.Builder builder = Jingle.getBuilder();
+
+ String actionString = parser.getAttributeValue("", Jingle.ACTION_ATTRIBUTE_NAME);
+ if (actionString != null) {
+ JingleAction action = JingleAction.fromString(actionString);
+ builder.setAction(action);
+ }
+
+ FullJid initiator = ParserUtils.getFullJidAttribute(parser, Jingle.INITIATOR_ATTRIBUTE_NAME);
+ builder.setInitiator(initiator);
+
+ FullJid responder = ParserUtils.getFullJidAttribute(parser, Jingle.RESPONDER_ATTRIBUTE_NAME);
+ builder.setResponder(responder);
+
+ String sessionId = parser.getAttributeValue("", Jingle.SESSION_ID_ATTRIBUTE_NAME);
+ builder.setSessionId(sessionId);
+
+
+ outerloop: while (true) {
+ int eventType = parser.next();
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String tagName = parser.getName();
+ switch (tagName) {
+ case JingleContent.ELEMENT:
+ JingleContent content = parseJingleContent(parser, parser.getDepth());
+ builder.addJingleContent(content);
+ break;
+ case JingleReason.ELEMENT:
+ parser.next();
+ String reasonString = parser.getName();
+ Reason reason = Reason.fromString(reasonString);
+ builder.setReason(reason);
+ break;
+ default:
+ LOGGER.severe("Unknown Jingle element: " + tagName);
+ break;
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ if (parser.getDepth() == initialDepth) {
+ break outerloop;
+ }
+ }
+ }
+
+ return builder.build();
+ }
+
+ public static JingleContent parseJingleContent(XmlPullParser parser, final int initialDepth)
+ throws Exception {
+ JingleContent.Builder builder = JingleContent.getBuilder();
+
+ String creatorString = parser.getAttributeValue("", JingleContent.CREATOR_ATTRIBUTE_NAME);
+ JingleContent.Creator creator = JingleContent.Creator.valueOf(creatorString);
+ builder.setCreator(creator);
+
+ String disposition = parser.getAttributeValue("", JingleContent.DISPOSITION_ATTRIBUTE_NAME);
+ builder.setDisposition(disposition);
+
+ String name = parser.getAttributeValue("", JingleContent.NAME_ATTRIBUTE_NAME);
+ builder.setName(name);
+
+ String sendersString = parser.getAttributeValue("", JingleContent.SENDERS_ATTRIBUTE_NAME);
+ if (sendersString != null) {
+ JingleContent.Senders senders = JingleContent.Senders.valueOf(sendersString);
+ builder.setSenders(senders);
+ }
+
+ outerloop: while (true) {
+ int eventType = parser.next();
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ String tagName = parser.getName();
+ String namespace = parser.getNamespace();
+ switch (tagName) {
+ case JingleContentDescription.ELEMENT: {
+ JingleContentDescriptionProvider> provider = JingleContentProviderManager.getJingleContentDescriptionProvider(namespace);
+ if (provider == null) {
+ // TODO handle this case (DefaultExtensionElement wrapped in something?)
+ break;
+ }
+ JingleContentDescription description = provider.parse(parser);
+ builder.setDescription(description);
+ break;
+ }
+ case JingleContentTransport.ELEMENT: {
+ JingleContentTransportProvider> provider = JingleContentProviderManager.getJingleContentTransportProvider(namespace);
+ if (provider == null) {
+ // TODO handle this case (DefaultExtensionElement wrapped in something?)
+ break;
+ }
+ JingleContentTransport transport = provider.parse(parser);
+ builder.addTransport(transport);
+ break;
+ }
+ default:
+ LOGGER.severe("Unknown Jingle content element: " + tagName);
+ break;
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ if (parser.getDepth() == initialDepth) {
+ break outerloop;
+ }
+ }
+ }
+
+ return builder.build();
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/package-info.java
new file mode 100644
index 000000000..feb0e8d86
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/provider/package-info.java
@@ -0,0 +1,21 @@
+/**
+ *
+ * Copyright 2017 Florian Schmaus
+ *
+ * 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.
+ */
+
+/**
+ * Providers and parsers for XEP-0166: Jingle.
+ */
+package org.jivesoftware.smackx.jingle.provider;
diff --git a/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.providers b/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.providers
index 41705f0ca..fc23362a1 100644
--- a/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.providers
+++ b/smack-extensions/src/main/resources/org.jivesoftware.smack.extensions/extensions.providers
@@ -544,4 +544,35 @@
org.jivesoftware.smackx.bob.provider.BoBIQProvider
+
+
+ jingle
+ urn:xmpp:jingle:1
+ org.jivesoftware.smackx.jingle.provider.JingleProvider
+
+
+
+ out-of-order
+ urn:xmpp:jingle:errors:1
+ org.jivesoftware.smackx.jingle.provider.JingleErrorProvider
+
+
+
+ unknown-session
+ urn:xmpp:jingle:errors:1
+ org.jivesoftware.smackx.jingle.provider.JingleErrorProvider
+
+
+
+ unsupported-content
+ urn:xmpp:jingle:errors:1
+ org.jivesoftware.smackx.jingle.provider.JingleErrorProvider
+
+
+
+ unsupported-transports
+ urn:xmpp:jingle:errors:1
+ org.jivesoftware.smackx.jingle.provider.JingleErrorProvider
+
+