diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md
index 481229840e..31203908da 100644
--- a/documentation/extensions/index.md
+++ b/documentation/extensions/index.md
@@ -22,6 +22,7 @@ Currently supported XEPs of smack-tcp
| Name | XEP | Description |
|---------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| [Stream Management](streammanagement.md) | [XEP-0198](http://xmpp.org/extensions/xep-0198.html) | Allows active management of an XML Stream between two XMPP entities (stanza acknowledgement, stream resumption). |
+| Instant Stream Resumption | [XEP-xxxx](http://xmpp.org/extensions/inbox/isr.html) | Allows XMPP entities to instantaneously resume an XMPP stream.. |
Smack Extensions and currently supported XEPs of smack-extensions
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/HMAC.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/HMAC.java
new file mode 100644
index 0000000000..ae7fbc97e4
--- /dev/null
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/HMAC.java
@@ -0,0 +1,75 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Instant Stream Resumption HMAC class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx: Instant
+ * Stream Resumption
+ *
+ */
+public class HMAC {
+
+ /**
+ * Get the HMAC digest.
+ *
+ * @param msg
+ * @param keyString
+ * @param algo
+ * @return the HMAC digest
+ */
+ public static String hmacDigest(String msg, String keyString, String algo) {
+ String digest = null;
+ try {
+ SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"), algo);
+
+ String algorithm;
+ if (!algo.toLowerCase().startsWith("hmac")) {
+ algorithm = "hmac" + algo.toLowerCase();
+ } else {
+ algorithm = algo.toLowerCase();
+ }
+
+ Mac mac = Mac.getInstance(algorithm);
+ mac.init(key);
+
+ byte[] bytes = mac.doFinal(msg.getBytes("ASCII"));
+
+ StringBuffer hash = new StringBuffer();
+ for (int i = 0; i < bytes.length; i++) {
+ String hex = Integer.toHexString(0xFF & bytes[i]);
+ if (hex.length() == 1) {
+ hash.append('0');
+ }
+ hash.append(hex);
+ }
+ digest = hash.toString();
+ } catch (UnsupportedEncodingException | InvalidKeyException | NoSuchAlgorithmException e) {
+ throw new IllegalStateException("HMAC digest could not be obtained correctly.", e);
+ }
+ return digest;
+ }
+}
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/ISRUtils.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/ISRUtils.java
new file mode 100644
index 0000000000..f0ca231333
--- /dev/null
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/ISRUtils.java
@@ -0,0 +1,49 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr;
+
+import java.io.IOException;
+
+import org.jivesoftware.smack.isr.element.InstantStreamResumption;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Instant Stream Resumption utils.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx: Instant
+ * Stream Resumption
+ *
+ */
+public class ISRUtils {
+
+ /**
+ * Check if is a nonza from Instant Stream Resumption.
+ *
+ * @param parser
+ * @return true if is a nonza from Instant Stream Resumption
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ public static boolean isISRNonza(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String isrNamespace = parser.getNamespace(InstantStreamResumption.NAMESPACE_PREFIX);
+ return (isrNamespace != null && isrNamespace.equals(InstantStreamResumption.NAMESPACE))
+ || parser.getNamespace().equals(InstantStreamResumption.NAMESPACE);
+ }
+
+}
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/InstantStreamResumption.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/InstantStreamResumption.java
new file mode 100644
index 0000000000..179505e9b5
--- /dev/null
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/InstantStreamResumption.java
@@ -0,0 +1,480 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr.element;
+
+import org.jivesoftware.smack.packet.ExtensionElement;
+import org.jivesoftware.smack.packet.Nonza;
+import org.jivesoftware.smack.sm.packet.StreamManagement;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * XEP-xxxx: Instant Stream Resumption.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx: Instant
+ * Stream Resumption
+ *
+ */
+public class InstantStreamResumption {
+
+ public static final String NAMESPACE = "urn:xmpp:isr:0";
+
+ public static final String NAMESPACE_PREFIX = "isr";
+
+ /**
+ * Instant Stream Resumption feature.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx:
+ * Instant Stream Resumption
+ *
+ */
+ public static final class InstantStreamResumptionFeature implements ExtensionElement {
+
+ public static final String ELEMENT = "isr";
+ public static final InstantStreamResumptionFeature INSTANCE = new InstantStreamResumptionFeature();
+
+ private InstantStreamResumptionFeature() {
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ public CharSequence toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.closeEmptyElement();
+ return xml;
+ }
+ }
+
+ /**
+ * Instant Stream Resumption enabled nonza.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx:
+ * Instant Stream Resumption
+ *
+ */
+ public static class Enabled implements Nonza {
+
+ public static final String ELEMENT = "enabled";
+
+ private final String key;
+ private final String location;
+
+ /**
+ * Enabled nonza constructor.
+ *
+ * @param key
+ * @param location
+ */
+ public Enabled(String key, String location) {
+ this.key = key;
+ this.location = location;
+ }
+
+ /**
+ * Enabled nonza constructor.
+ *
+ * @param key
+ */
+ public Enabled(String key) {
+ this(key, null);
+ }
+
+ /**
+ * Get the key.
+ *
+ * @return the key
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Get the location.
+ *
+ * @return the location
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public String getNamespace() {
+ return StreamManagement.NAMESPACE;
+ }
+
+ @Override
+ public CharSequence toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder();
+ xml.halfOpenElement(ELEMENT);
+ xml.xmlnsAttribute(StreamManagement.NAMESPACE);
+ xml.attribute("xmlns:isr", NAMESPACE);
+ xml.attribute("isr:key", key);
+ xml.optAttribute("isr:location", location);
+ xml.closeEmptyElement();
+ return xml;
+ }
+
+ }
+
+ /**
+ * Instant Stream Resumption resume nonza.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx:
+ * Instant Stream Resumption
+ *
+ */
+ public static class InstResume implements Nonza {
+
+ public static final String ELEMENT = "inst-resume";
+
+ private final String prevId;
+ private final long handledCount;
+ private final String hash;
+ private final String algo;
+
+ /**
+ * Instant resume nonza constructor.
+ *
+ * @param prevId
+ * @param handledCount
+ * @param hash
+ * @param algo
+ */
+ public InstResume(String prevId, long handledCount, String hash, String algo) {
+ this.prevId = prevId;
+ this.handledCount = handledCount;
+ this.hash = hash;
+ this.algo = algo;
+ }
+
+ /**
+ * Get the previous id.
+ *
+ * @return the previous id
+ */
+ public String getPrevId() {
+ return prevId;
+ }
+
+ /**
+ * Get the handled count.
+ *
+ * @return the handled count
+ */
+ public long getHandledCount() {
+ return handledCount;
+ }
+
+ /**
+ * Get the hash.
+ *
+ * @return the hash
+ */
+ public String getHash() {
+ return hash;
+ }
+
+ /**
+ * Get the algorithm.
+ *
+ * @return the algorithm
+ */
+ public String getAlgo() {
+ return algo;
+ }
+
+ @Override
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public CharSequence toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.optAttribute("previd", prevId);
+ xml.optAttribute("h", Long.toString(handledCount));
+ xml.rightAngleBracket();
+
+ xml.openElement("hmac");
+
+ xml.element(new HashElement(hash, algo));
+
+ xml.closeElement("hmac");
+ xml.closeElement(this);
+ return xml;
+ }
+
+ }
+
+ /**
+ * Instant Stream Resumption resumed nonza.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx:
+ * Instant Stream Resumption
+ *
+ */
+ public static class InstResumed implements Nonza {
+
+ public static final String ELEMENT = "inst-resumed";
+
+ private final String key;
+ private final long handledCount;
+ private final String hash;
+ private final String algo;
+
+ /**
+ * Instant resumed nonza constructor.
+ *
+ * @param key
+ * @param handledCount
+ * @param hash
+ * @param algo
+ */
+ public InstResumed(String key, long handledCount, String hash, String algo) {
+ this.key = key;
+ this.handledCount = handledCount;
+ this.hash = hash;
+ this.algo = algo;
+ }
+
+ /**
+ * Instant resumed nonza constructor.
+ *
+ * @param key
+ * @param hash
+ * @param algo
+ */
+ public InstResumed(String key, String hash, String algo) {
+ this(key, -1, hash, algo);
+ }
+
+ @Override
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ /**
+ * Get the key.
+ *
+ * @return the key
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Get the handled count.
+ *
+ * @return the handled count
+ */
+ public long getHandledCount() {
+ return handledCount;
+ }
+
+ /**
+ * Get the hash.
+ *
+ * @return the hash
+ */
+ public String getHash() {
+ return hash;
+ }
+
+ /**
+ * Get the algorithm.
+ *
+ * @return the algorithm
+ */
+ public String getAlgo() {
+ return algo;
+ }
+
+ @Override
+ public CharSequence toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.optAttribute("key", key);
+ if (handledCount > 0) {
+ xml.optAttribute("h", Long.toString(handledCount));
+ }
+ xml.rightAngleBracket();
+
+ xml.openElement("hmac");
+
+ xml.element(new HashElement(hash, algo));
+
+ xml.closeElement("hmac");
+ xml.closeElement(this);
+ return xml;
+ }
+
+ }
+
+ /**
+ * Hash element.
+ *
+ * @author Fernando Ramirez
+ *
+ */
+ public static class HashElement implements ExtensionElement {
+
+ public static final String ELEMENT = "hash";
+ public static final String NAMESPACE = "urn:xmpp:hashes:1";
+
+ private final String hash;
+ private final String algo;
+
+ /**
+ * Hash element constructor.
+ *
+ * @param hash
+ * @param algo
+ */
+ public HashElement(String hash, String algo) {
+ this.hash = hash;
+ this.algo = algo;
+ }
+
+ /**
+ * Get the hash.
+ *
+ * @return the hash.
+ */
+ public String getHash() {
+ return hash;
+ }
+
+ /**
+ * Get the algorithm.
+ *
+ * @return the algorithm
+ */
+ public String getAlgo() {
+ return algo;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ public CharSequence toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.attribute("algo", algo);
+ xml.rightAngleBracket();
+ xml.escape(hash);
+ xml.closeElement(this);
+ return xml;
+ }
+
+ }
+
+ /**
+ * Instant Stream Resumption failed nonza.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx:
+ * Instant Stream Resumption
+ *
+ */
+ public static class Failed implements Nonza {
+
+ public static final String ELEMENT = "failed";
+
+ private final long handledCount;
+
+ /**
+ * Failed nonza constructor.
+ *
+ * @param handledCount
+ */
+ public Failed(long handledCount) {
+ this.handledCount = handledCount;
+ }
+
+ /**
+ * Failed nonza constructor.
+ */
+ public Failed() {
+ this(-1);
+ }
+
+ /**
+ * Get the handled count.
+ *
+ * @return the handled count
+ */
+ public long getHandledCount() {
+ return handledCount;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ public CharSequence toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ if (handledCount > 0) {
+ xml.optAttribute("h", Long.toString(handledCount));
+ }
+ xml.closeEmptyElement();
+ return xml;
+ }
+
+ }
+
+}
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/package-info.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/package-info.java
new file mode 100644
index 0000000000..b2cd1771b1
--- /dev/null
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/element/package-info.java
@@ -0,0 +1,25 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.
+ */
+/**
+ * XEP-xxxx: Instant Stream Resumption.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx: Instant
+ * Stream Resumption
+ *
+ */
+package org.jivesoftware.smack.isr.element;
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/package-info.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/package-info.java
new file mode 100644
index 0000000000..4ec242875d
--- /dev/null
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/package-info.java
@@ -0,0 +1,25 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.
+ */
+/**
+ * XEP-xxxx: Instant Stream Resumption.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx: Instant
+ * Stream Resumption
+ *
+ */
+package org.jivesoftware.smack.isr;
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/InstantStreamResumptionFeatureProvider.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/InstantStreamResumptionFeatureProvider.java
new file mode 100644
index 0000000000..042db3d7f9
--- /dev/null
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/InstantStreamResumptionFeatureProvider.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr.provider;
+
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstantStreamResumptionFeature;
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Instant Stream Resumption feature provider class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx: Instant
+ * Stream Resumption
+ *
+ */
+public class InstantStreamResumptionFeatureProvider extends ExtensionElementProvider {
+
+ @Override
+ public InstantStreamResumptionFeature parse(XmlPullParser parser, int initialDepth) throws Exception {
+ return InstantStreamResumptionFeature.INSTANCE;
+ }
+
+}
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/ParseInstantStreamResumption.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/ParseInstantStreamResumption.java
new file mode 100644
index 0000000000..54de81efb1
--- /dev/null
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/ParseInstantStreamResumption.java
@@ -0,0 +1,93 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr.provider;
+
+import java.io.IOException;
+
+import org.jivesoftware.smack.isr.element.InstantStreamResumption;
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.Enabled;
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.Failed;
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResumed;
+import org.jivesoftware.smack.util.ParserUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Parse Instant Stream Resumption class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx: Instant
+ * Stream Resumption
+ *
+ */
+public class ParseInstantStreamResumption {
+
+ public static Enabled enabled(XmlPullParser parser) throws XmlPullParserException, IOException {
+ ParserUtils.assertAtStartTag(parser);
+ String key = parser.getAttributeValue(InstantStreamResumption.NAMESPACE, "key");
+ String location = parser.getAttributeValue(InstantStreamResumption.NAMESPACE, "location");
+ parser.next();
+ ParserUtils.assertAtEndTag(parser);
+ return new Enabled(key, location);
+ }
+
+ public static Failed failed(XmlPullParser parser) throws XmlPullParserException, IOException {
+ ParserUtils.assertAtStartTag(parser);
+ Long handledCount = ParserUtils.getLongAttribute(parser, "h");
+ parser.next();
+ ParserUtils.assertAtEndTag(parser);
+
+ if (handledCount == null) {
+ return new Failed();
+ } else {
+ return new Failed(handledCount);
+ }
+ }
+
+ public static InstResumed resumed(XmlPullParser parser) throws XmlPullParserException, IOException {
+ // inst-resumed
+ ParserUtils.assertAtStartTag(parser);
+ String key = parser.getAttributeValue("", "key");
+ Long handledCount = ParserUtils.getLongAttribute(parser, "h");
+
+ // hmac
+ parser.next();
+ ParserUtils.assertAtStartTag(parser);
+
+ // hash
+ parser.next();
+ ParserUtils.assertAtStartTag(parser);
+ String algo = parser.getAttributeValue("", "algo");
+ String hash = parser.nextText();
+ ParserUtils.assertAtEndTag(parser);
+
+ // close hmac
+ parser.next();
+ ParserUtils.assertAtEndTag(parser);
+
+ // close inst-resumed
+ parser.next();
+ ParserUtils.assertAtEndTag(parser);
+
+ if (handledCount == null) {
+ return new InstResumed(key, hash, algo);
+ } else {
+ return new InstResumed(key, handledCount, hash, algo);
+ }
+ }
+
+}
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/package-info.java b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/package-info.java
new file mode 100644
index 0000000000..bb58e4e6b6
--- /dev/null
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/isr/provider/package-info.java
@@ -0,0 +1,25 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.
+ */
+/**
+ * XEP-xxxx: Instant Stream Resumption.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-xxxx: Instant
+ * Stream Resumption
+ *
+ */
+package org.jivesoftware.smack.isr.provider;
diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
index 92a2ba631b..7c196de13d 100644
--- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
+++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/XMPPTCPConnection.java
@@ -16,36 +16,99 @@
*/
package org.jivesoftware.smack.tcp;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.lang.reflect.Constructor;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+
import org.jivesoftware.smack.AbstractConnectionListener;
import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
-import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.AlreadyConnectedException;
import org.jivesoftware.smack.SmackException.AlreadyLoggedInException;
+import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
-import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.SmackException.SecurityRequiredByServerException;
+import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.SynchronizationPoint;
-import org.jivesoftware.smack.XMPPException.FailedNonzaException;
-import org.jivesoftware.smack.XMPPException.StreamErrorException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.XMPPException.FailedNonzaException;
+import org.jivesoftware.smack.XMPPException.StreamErrorException;
+import org.jivesoftware.smack.compress.packet.Compress;
import org.jivesoftware.smack.compress.packet.Compressed;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
import org.jivesoftware.smack.filter.StanzaFilter;
-import org.jivesoftware.smack.compress.packet.Compress;
+import org.jivesoftware.smack.isr.HMAC;
+import org.jivesoftware.smack.isr.ISRUtils;
+import org.jivesoftware.smack.isr.element.InstantStreamResumption;
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResume;
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResumed;
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstantStreamResumptionFeature;
+import org.jivesoftware.smack.isr.provider.ParseInstantStreamResumption;
import org.jivesoftware.smack.packet.Element;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
-import org.jivesoftware.smack.packet.StreamOpen;
-import org.jivesoftware.smack.packet.Stanza;
+import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StartTls;
+import org.jivesoftware.smack.packet.StreamOpen;
+import org.jivesoftware.smack.packet.XMPPError.Condition;
+import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Challenge;
@@ -67,8 +130,6 @@
import org.jivesoftware.smack.sm.packet.StreamManagement.StreamManagementFeature;
import org.jivesoftware.smack.sm.predicates.Predicate;
import org.jivesoftware.smack.sm.provider.ParseStreamManagement;
-import org.jivesoftware.smack.packet.Nonza;
-import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.util.ArrayBlockingQueueWithShutdown;
import org.jivesoftware.smack.util.Async;
import org.jivesoftware.smack.util.DNSUtil;
@@ -86,59 +147,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import javax.net.SocketFactory;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.lang.reflect.Constructor;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
/**
* Creates a socket connection to an XMPP server. This is the default connection
* to an XMPP server and is specified in the XMPP Core (RFC 6120).
@@ -221,6 +229,13 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
*/
private String smSessionId;
+ private String isrKey;
+
+ private String isrAlgo;
+
+ // TODO check again how to use this
+ private String isrHmacHash;
+
private final SynchronizationPoint smResumedSyncPoint = new SynchronizationPoint<>(
this, "stream resumed element");
@@ -389,7 +404,16 @@ protected synchronized void loginInternal(String username, String password, Reso
maybeEnableCompression();
if (isSmResumptionPossible()) {
- smResumedSyncPoint.sendAndWaitForResponse(new Resume(clientHandledStanzasCount, smSessionId));
+
+ if (isrKey != null && isrAlgo != null) {
+ // instant stream resumption
+ smResumedSyncPoint.sendAndWaitForResponse(new InstResume(smSessionId, clientHandledStanzasCount,
+ HMAC.hmacDigest("Initiator", isrKey, isrAlgo), isrAlgo));
+ } else {
+ // stream management resume
+ smResumedSyncPoint.sendAndWaitForResponse(new Resume(clientHandledStanzasCount, smSessionId));
+ }
+
if (smResumedSyncPoint.wasSuccessful()) {
// We successfully resumed the stream, be done here
afterSuccessfulLogin(true);
@@ -1107,31 +1131,57 @@ private void parsePackets() {
compressSyncPoint.reportSuccess();
break;
case Enabled.ELEMENT:
- Enabled enabled = ParseStreamManagement.enabled(parser);
- if (enabled.isResumeSet()) {
- smSessionId = enabled.getId();
- if (StringUtils.isNullOrEmpty(smSessionId)) {
- SmackException xmppException = new SmackException("Stream Management 'enabled' element with resume attribute but without session id received");
+ if (ISRUtils.isISRNonza(parser)) {
+ org.jivesoftware.smack.isr.element.InstantStreamResumption.Enabled enabled = ParseInstantStreamResumption
+ .enabled(parser);
+ isrKey = enabled.getKey();
+ if (isrKey == null) {
+ SmackException xmppException = new SmackException("Instant Stream Resumption 'enabled' element without key");
smEnabledSyncPoint.reportFailure(xmppException);
throw xmppException;
}
- smServerMaxResumptimTime = enabled.getMaxResumptionTime();
} else {
- // Mark this a non-resumable stream by setting smSessionId to null
- smSessionId = null;
+ Enabled enabled = ParseStreamManagement.enabled(parser);
+ if (enabled.isResumeSet()) {
+ smSessionId = enabled.getId();
+ if (StringUtils.isNullOrEmpty(smSessionId)) {
+ SmackException xmppException = new SmackException("Stream Management 'enabled' element with resume attribute but without session id received");
+ smEnabledSyncPoint.reportFailure(xmppException);
+ throw xmppException;
+ }
+ smServerMaxResumptimTime = enabled.getMaxResumptionTime();
+ } else {
+ // Mark this a non-resumable stream by
+ // setting smSessionId to null
+ smSessionId = null;
+ }
}
+
clientHandledStanzasCount = 0;
smWasEnabledAtLeastOnce = true;
smEnabledSyncPoint.reportSuccess();
LOGGER.fine("Stream Management (XEP-198): succesfully enabled");
break;
case Failed.ELEMENT:
- Failed failed = ParseStreamManagement.failed(parser);
- FailedNonzaException xmppException = new FailedNonzaException(failed, failed.getXMPPErrorCondition());
- // If only XEP-198 would specify different failure elements for the SM
- // enable and SM resume failure case. But this is not the case, so we
- // need to determine if this is a 'Failed' response for either 'Enable'
- // or 'Resume'.
+ FailedNonzaException xmppException = null;
+
+ if (ISRUtils.isISRNonza(parser)) {
+ org.jivesoftware.smack.isr.element.InstantStreamResumption.Failed failed = ParseInstantStreamResumption
+ .failed(parser);
+ processHandledCount(failed.getHandledCount());
+
+ xmppException = new FailedNonzaException(failed, Condition.undefined_condition);
+ } else {
+ Failed failed = ParseStreamManagement.failed(parser);
+ xmppException = new FailedNonzaException(failed, failed.getXMPPErrorCondition());
+ // If only XEP-198 would specify different
+ // failure elements for the SM
+ // enable and SM resume failure case. But this
+ // is not the case, so we
+ // need to determine if this is a 'Failed'
+ // response for either 'Enable'
+ // or 'Resume'.
+ }
if (smResumedSyncPoint.requestSent()) {
smResumedSyncPoint.reportFailure(xmppException);
}
@@ -1146,24 +1196,59 @@ private void parsePackets() {
lastFeaturesReceived.reportSuccess();
}
break;
+ case InstResumed.ELEMENT:
+ InstResumed instResumed = ParseInstantStreamResumption.resumed(parser);
+ isrKey = instResumed.getKey();
+ isrHmacHash = instResumed.getHash();
+ isrAlgo = instResumed.getAlgo();
+
+ // Mark SM as enabled and resumption as
+ // successful.
+ smResumedSyncPoint.reportSuccess();
+ smEnabledSyncPoint.reportSuccess();
+ // First, drop the stanzas already handled by
+ // the server
+ processHandledCount(instResumed.getHandledCount());
+ // Then re-send what is left in the unacknowledged
+ // queue
+ List stanzasListToResend = new ArrayList<>(unacknowledgedStanzas.size());
+ unacknowledgedStanzas.drainTo(stanzasListToResend);
+ for (Stanza stanza : stanzasListToResend) {
+ sendStanzaInternal(stanza);
+ }
+ // If there where stanzas resent, then request a SM
+ // ack for them.
+ // Writer's sendStreamElement() won't do it
+ // automatically based on
+ // predicates.
+ if (!stanzasListToResend.isEmpty()) {
+ requestSmAcknowledgementInternal();
+ }
+ LOGGER.fine("Stream Management (XEP-198): Stream resumed");
+ break;
case Resumed.ELEMENT:
Resumed resumed = ParseStreamManagement.resumed(parser);
if (!smSessionId.equals(resumed.getPrevId())) {
throw new StreamIdDoesNotMatchException(smSessionId, resumed.getPrevId());
}
- // Mark SM as enabled and resumption as successful.
+ // Mark SM as enabled and resumption as
+ // successful.
smResumedSyncPoint.reportSuccess();
smEnabledSyncPoint.reportSuccess();
- // First, drop the stanzas already handled by the server
+ // First, drop the stanzas already handled by
+ // the server
processHandledCount(resumed.getHandledCount());
- // Then re-send what is left in the unacknowledged queue
+ // Then re-send what is left in the unacknowledged
+ // queue
List stanzasToResend = new ArrayList<>(unacknowledgedStanzas.size());
unacknowledgedStanzas.drainTo(stanzasToResend);
for (Stanza stanza : stanzasToResend) {
sendStanzaInternal(stanza);
}
- // If there where stanzas resent, then request a SM ack for them.
- // Writer's sendStreamElement() won't do it automatically based on
+ // If there where stanzas resent, then request a SM
+ // ack for them.
+ // Writer's sendStreamElement() won't do it
+ // automatically based on
// predicates.
if (!stanzasToResend.isEmpty()) {
requestSmAcknowledgementInternal();
@@ -1752,7 +1837,17 @@ public boolean isSmAvailable() {
}
/**
- * Returns true if Stream Management was successfully negotiated with the server.
+ * Returns true if Instant Stream Resumption is supported by the server.
+ *
+ * @return true if Instant Stream Resumption is supported by the server.
+ */
+ public boolean isISRAvailable() {
+ return hasFeature(InstantStreamResumptionFeature.ELEMENT, InstantStreamResumption.NAMESPACE);
+ }
+
+ /**
+ * Returns true if Stream Management was successfully negotiated with the
+ * server.
*
* @return true if Stream Management was negotiated.
*/
diff --git a/smack-tcp/src/main/resources/org.jivesoftware.smack.tcp/smacktcp.providers b/smack-tcp/src/main/resources/org.jivesoftware.smack.tcp/smacktcp.providers
index 92238115e3..02e663fbda 100644
--- a/smack-tcp/src/main/resources/org.jivesoftware.smack.tcp/smacktcp.providers
+++ b/smack-tcp/src/main/resources/org.jivesoftware.smack.tcp/smacktcp.providers
@@ -8,4 +8,11 @@
org.jivesoftware.smack.sm.provider.StreamManagementStreamFeatureProvider
+
+
+ isr
+ urn:xmpp:isr:0
+ org.jivesoftware.smack.isr.provider.InstantStreamResumptionFeatureProvider
+
+
diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISREnabledNonzaTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISREnabledNonzaTest.java
new file mode 100644
index 0000000000..daa29e76bb
--- /dev/null
+++ b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISREnabledNonzaTest.java
@@ -0,0 +1,55 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr;
+
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.Enabled;
+import org.jivesoftware.smack.isr.provider.ParseInstantStreamResumption;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+
+public class ISREnabledNonzaTest {
+
+ private static final String enabledNonza = "";
+
+ private static final String enabledNonzaWithLocation = "";
+
+ @Test
+ public void checkISREnabledNonza() throws Exception {
+ Enabled enabled = new Enabled("a0b9162d-0981-4c7d-9174-1f55aedd1f52");
+ Assert.assertEquals(enabledNonza, enabled.toXML().toString());
+
+ Enabled enabledWithLocation = new Enabled("a0b9162d-0981-4c7d-9174-1f55aedd1f52", "isr.example.org:5222");
+ Assert.assertEquals(enabledNonzaWithLocation, enabledWithLocation.toXML().toString());
+ }
+
+ @Test
+ public void checkParseISREnabledNonza() throws Exception {
+ XmlPullParser xmlPullParser = PacketParserUtils.getParserFor(enabledNonza);
+ Enabled enabled = ParseInstantStreamResumption.enabled(xmlPullParser);
+ Assert.assertEquals(enabledNonza, enabled.toXML().toString());
+
+ XmlPullParser xmlPullParser2 = PacketParserUtils.getParserFor(enabledNonzaWithLocation);
+ Enabled enabledWithLocation = ParseInstantStreamResumption.enabled(xmlPullParser2);
+ Assert.assertEquals(enabledNonzaWithLocation, enabledWithLocation.toXML().toString());
+ }
+
+}
diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRFailedNonzaTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRFailedNonzaTest.java
new file mode 100644
index 0000000000..b3ce06a8f1
--- /dev/null
+++ b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRFailedNonzaTest.java
@@ -0,0 +1,51 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr;
+
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.Failed;
+import org.jivesoftware.smack.isr.provider.ParseInstantStreamResumption;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+
+public class ISRFailedNonzaTest {
+
+ private static final String failedNonza = "";
+ private static final String failedNonzaWithHandledCount = "";
+
+ @Test
+ public void checkISRFailedNonza() throws Exception {
+ Failed failed = new Failed();
+ Assert.assertEquals(failedNonza, failed.toXML().toString());
+
+ Failed failedWithHandledCount = new Failed(22);
+ Assert.assertEquals(failedNonzaWithHandledCount, failedWithHandledCount.toXML().toString());
+ }
+
+ @Test
+ public void checkParseISRFailedNonza() throws Exception {
+ XmlPullParser xmlPullParser = PacketParserUtils.getParserFor(failedNonza);
+ Failed failed = ParseInstantStreamResumption.failed(xmlPullParser);
+ Assert.assertEquals(failedNonza, failed.toXML().toString());
+
+ XmlPullParser xmlPullParser2 = PacketParserUtils.getParserFor(failedNonzaWithHandledCount);
+ Failed failedWithHandledCount = ParseInstantStreamResumption.failed(xmlPullParser2);
+ Assert.assertEquals(failedNonzaWithHandledCount, failedWithHandledCount.toXML().toString());
+ }
+
+}
diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRResumeNonzasTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRResumeNonzasTest.java
new file mode 100644
index 0000000000..b69de2ea4a
--- /dev/null
+++ b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRResumeNonzasTest.java
@@ -0,0 +1,58 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr;
+
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResume;
+import org.jivesoftware.smack.isr.element.InstantStreamResumption.InstResumed;
+import org.jivesoftware.smack.isr.provider.ParseInstantStreamResumption;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+
+public class ISRResumeNonzasTest {
+
+ private static final String resumeNonza = "" + "" + "" + "initator-hmac" + ""
+ + "" + "";
+
+ private static final String resumedNonza = "" + ""
+ + "" + "responder-hmac" + "" + ""
+ + "";
+
+ @Test
+ public void checkResumeNonza() throws Exception {
+ InstResume instResume = new InstResume("some-long-sm-id", 42, "initator-hmac", "sha256");
+ Assert.assertEquals(resumeNonza, instResume.toXML().toString());
+ }
+
+ @Test
+ public void checkResumedNonza() throws Exception {
+ InstResumed instResumed = new InstResumed("006b1a29-c549-41c7-a12c-2a931822f8c0", 21, "responder-hmac",
+ "sha256");
+ Assert.assertEquals(resumedNonza, instResumed.toXML().toString());
+ }
+
+ @Test
+ public void checkParseResumedNonza() throws Exception {
+ XmlPullParser xmlPullParser = PacketParserUtils.getParserFor(resumedNonza);
+ InstResumed instResumed = ParseInstantStreamResumption.resumed(xmlPullParser);
+ Assert.assertEquals(resumedNonza, instResumed.toXML().toString());
+ }
+
+}
diff --git a/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRUtilsTest.java b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRUtilsTest.java
new file mode 100644
index 0000000000..a956bdf7a3
--- /dev/null
+++ b/smack-tcp/src/test/java/org/jivesoftware/smack/isr/ISRUtilsTest.java
@@ -0,0 +1,117 @@
+/**
+ *
+ * Copyright © 2016 Fernando Ramirez
+ *
+ * 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.smack.isr;
+
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+
+public class ISRUtilsTest {
+
+ private static final String isrFailedNonza = "";
+
+ private static final String isrFailedNonzaWithHandledCount = "";
+
+ private static final String isrEnabledNonza = "";
+
+ private static final String isrEnabledNonzaWithLocation = "";
+
+ private static final String isrResumedNonza = "" + ""
+ + "" + "responder-hmac" + "" + ""
+ + "";
+
+ private static final String smFailedNonza = ""
+ + "" + "";
+
+ private static final String smEnabledNonza = "";
+
+ private static final String smEnabledNonzaResume = "";
+
+ private static final String smEnabledNonzaResumeWithLocation = "";
+
+ private static final String smResumedNonza = "";
+
+ @Test
+ public void checkIsISRNonzaValidation() throws Exception {
+ XmlPullParser parserISRFailedNonza = PacketParserUtils.getParserFor(isrFailedNonza);
+ Assert.assertTrue(ISRUtils.isISRNonza(parserISRFailedNonza));
+
+ XmlPullParser parserISRFailedNonzaWithHandledCount = PacketParserUtils
+ .getParserFor(isrFailedNonzaWithHandledCount);
+ Assert.assertTrue(ISRUtils.isISRNonza(parserISRFailedNonzaWithHandledCount));
+
+ XmlPullParser parserISREnabledNonza = PacketParserUtils.getParserFor(isrEnabledNonza);
+ Assert.assertTrue(ISRUtils.isISRNonza(parserISREnabledNonza));
+
+ XmlPullParser parserISREnabledNonzaWithLocation = PacketParserUtils.getParserFor(isrEnabledNonzaWithLocation);
+ Assert.assertTrue(ISRUtils.isISRNonza(parserISREnabledNonzaWithLocation));
+
+ XmlPullParser parserISRResumedNonza = PacketParserUtils.getParserFor(isrResumedNonza);
+ Assert.assertTrue(ISRUtils.isISRNonza(parserISRResumedNonza));
+
+ XmlPullParser parserSMFailedNonza = PacketParserUtils.getParserFor(smFailedNonza);
+ Assert.assertFalse(ISRUtils.isISRNonza(parserSMFailedNonza));
+
+ XmlPullParser parserSMEnabledNonza = PacketParserUtils.getParserFor(smEnabledNonza);
+ Assert.assertFalse(ISRUtils.isISRNonza(parserSMEnabledNonza));
+
+ XmlPullParser parserSMEnabledNonzaResume = PacketParserUtils.getParserFor(smEnabledNonzaResume);
+ Assert.assertFalse(ISRUtils.isISRNonza(parserSMEnabledNonzaResume));
+
+ XmlPullParser parserSMEnabledNonzaResumeWithLocation = PacketParserUtils
+ .getParserFor(smEnabledNonzaResumeWithLocation);
+ Assert.assertFalse(ISRUtils.isISRNonza(parserSMEnabledNonzaResumeWithLocation));
+
+ XmlPullParser parserSMResumedNonza = PacketParserUtils.getParserFor(smResumedNonza);
+ Assert.assertFalse(ISRUtils.isISRNonza(parserSMResumedNonza));
+ }
+
+ @Test
+ public void checkHMACDigest() throws Exception {
+ String msg = "Initiator";
+ String token = "006b1a29-c549-41c7-a12c-2a931822f8c0";
+
+ String sha256DigestResult = "e5e6241898f631342111958a51b61a1d75d492c782c8620b4efd0d9f172b55ca";
+ String sha1DigestResult = "583ff09b098c3f0bb3dbbc8cb0ef279755db7cde";
+ String md5DigestResult = "14b8db2ba5cdb9088e92a640485207a3";
+
+ String hmacDigest = HMAC.hmacDigest(msg, token, "SHA256");
+ Assert.assertEquals(sha256DigestResult, hmacDigest);
+
+ hmacDigest = HMAC.hmacDigest(msg, token, "sha256");
+ Assert.assertEquals(sha256DigestResult, hmacDigest);
+
+ hmacDigest = HMAC.hmacDigest(msg, token, "SHA1");
+ Assert.assertEquals(sha1DigestResult, hmacDigest);
+
+ hmacDigest = HMAC.hmacDigest(msg, token, "md5");
+ Assert.assertEquals(md5DigestResult, hmacDigest);
+
+ hmacDigest = HMAC.hmacDigest(msg, token, "hMACmd5");
+ Assert.assertEquals(md5DigestResult, hmacDigest);
+
+ }
+
+}