diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md
index dd2ca5dfb..1cf51a067 100644
--- a/documentation/extensions/index.md
+++ b/documentation/extensions/index.md
@@ -68,6 +68,7 @@ Smack Extensions and currently supported XEPs of smack-extensions
| Delayed Delivery | [XEP-0203](http://xmpp.org/extensions/xep-0203.html) | Extension for communicating the fact that an XML stanza has been delivered with a delay. |
| XMPP Over BOSH | [XEP-0206](http://xmpp.org/extensions/xep-0206.html) | Use Bidirectional-streams Over Synchronous HTTP (BOSH) to transport XMPP stanzas. |
| Attention | [XEP-0224](http://xmpp.org/extensions/xep-0224.html) | Getting attention of another user. |
+| Bits of Binary | [XEP-0231](http://xmpp.org/extensions/xep-0231.html) | Including or referring to small bits of binary data in an XML stanza. |
| Last Message Correction | [XEP-0308](http://xmpp.org/extensions/xep-0308.html) | Provides a method for indicating that a message is a correction of the last sent message. |
| [Group Chat Invitations](invitation.md) | n/a | Send invitations to other users to join a group chat room. |
| [Jive Properties](properties.md) | n/a | TODO |
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBData.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBData.java
new file mode 100644
index 000000000..2a8bf9a81
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBData.java
@@ -0,0 +1,90 @@
+/**
+ *
+ * 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.smackx.bob;
+
+import java.io.UnsupportedEncodingException;
+
+import org.jivesoftware.smack.util.stringencoder.Base64;
+
+/**
+ * Bits of Binary data class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+public class BoBData {
+
+ private final long maxAge;
+ private final String type;
+ private final byte[] content;
+
+ /**
+ * BoB data constructor.
+ *
+ * @param maxAge
+ * @param type
+ * @param content
+ */
+ public BoBData(long maxAge, String type, byte[] content) {
+ this.maxAge = maxAge;
+ this.type = type;
+ this.content = content;
+ }
+
+ /**
+ * Get the max age.
+ *
+ * @return the max age
+ */
+ public long getMaxAge() {
+ return maxAge;
+ }
+
+ /**
+ * Get the type.
+ *
+ * @return the type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Get the content.
+ *
+ * @return the content
+ */
+ public byte[] getContent() {
+ return content;
+ }
+
+ /**
+ * Get the content in a Base64 encoded String.
+ *
+ * @return the content in a Base64 encoded String
+ */
+ public String getBase64Encoded() {
+ byte[] bytes = Base64.encode(content);
+ try {
+ return new String(bytes, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("UTF-8 is not supported.");
+ }
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBHash.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBHash.java
new file mode 100644
index 000000000..3e9b77fda
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBHash.java
@@ -0,0 +1,102 @@
+/**
+ *
+ * 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.smackx.bob;
+
+/**
+ * Bits of Binary hash class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+public class BoBHash {
+
+ private final String hash;
+ private final String hashType;
+
+ /**
+ * BoB hash constructor.
+ *
+ * @param hash
+ * @param hashType
+ */
+ public BoBHash(String hash, String hashType) {
+ this.hash = hash;
+ this.hashType = hashType;
+ }
+
+ /**
+ * Get the hash.
+ *
+ * @return the hash
+ */
+ public String getHash() {
+ return hash;
+ }
+
+ /**
+ * Get the hash type.
+ *
+ * @return the hash type
+ */
+ public String getHashType() {
+ return hashType;
+ }
+
+ /**
+ * BoB hash to src attribute string.
+ *
+ * @return src attribute string
+ */
+ public String toSrc() {
+ return "cid:" + toCid();
+ }
+
+ /**
+ * BoB hash to cid attribute string.
+ *
+ * @return cid attribute string
+ */
+ public String toCid() {
+ return this.hashType + "+" + this.hash + "@bob.xmpp.org";
+ }
+
+ /**
+ * Get BoB hash from src attribute string.
+ *
+ * @param src
+ * @return the BoB hash
+ */
+ public static BoBHash fromSrc(String src) {
+ String hashType = src.substring(src.lastIndexOf("cid:") + 4, src.indexOf("+"));
+ String hash = src.substring(src.indexOf("+") + 1, src.indexOf("@bob.xmpp.org"));
+ return new BoBHash(hash, hashType);
+ }
+
+ /**
+ * Get BoB hash from cid attribute string.
+ *
+ * @param cid
+ * @return the BoB hash
+ */
+ public static BoBHash fromCid(String cid) {
+ String hashType = cid.substring(0, cid.indexOf("+"));
+ String hash = cid.substring(cid.indexOf("+") + 1, cid.indexOf("@bob.xmpp.org"));
+ return new BoBHash(hash, hashType);
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBManager.java
new file mode 100644
index 000000000..767cee02c
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBManager.java
@@ -0,0 +1,162 @@
+/**
+ *
+ * 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.smackx.bob;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.jivesoftware.smack.ConnectionCreationListener;
+import org.jivesoftware.smack.Manager;
+import org.jivesoftware.smack.SmackException.NoResponseException;
+import org.jivesoftware.smack.SmackException.NotConnectedException;
+import org.jivesoftware.smack.SmackException.NotLoggedInException;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPConnectionRegistry;
+import org.jivesoftware.smack.XMPPException.XMPPErrorException;
+import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
+import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.IQ.Type;
+import org.jivesoftware.smackx.bob.element.BoBIQ;
+import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
+import org.jxmpp.jid.Jid;
+
+/**
+ * Bits of Binary manager class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+public final class BoBManager extends Manager {
+
+ public static final String NAMESPACE = "urn:xmpp:bob";
+ public static BoBSaverManager bobSaverManager;
+
+ static {
+ XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
+ @Override
+ public void connectionCreated(XMPPConnection connection) {
+ getInstanceFor(connection, bobSaverManager);
+ }
+ });
+ }
+
+ private static final Map INSTANCES = new WeakHashMap<>();
+
+ /**
+ * Get the singleton instance of BoBManager.
+ *
+ * @param connection
+ * @param saverManager
+ * @return the instance of BoBManager
+ */
+ public static synchronized BoBManager getInstanceFor(XMPPConnection connection, BoBSaverManager saverManager) {
+ if (saverManager == null) {
+ bobSaverManager = new DefaultBoBSaverManager();
+ } else {
+ bobSaverManager = saverManager;
+ }
+
+ BoBManager bobManager = INSTANCES.get(connection);
+ if (bobManager == null) {
+ bobManager = new BoBManager(connection);
+ INSTANCES.put(connection, bobManager);
+ }
+
+ return bobManager;
+ }
+
+ private BoBManager(XMPPConnection connection) {
+ super(connection);
+ ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
+ serviceDiscoveryManager.addFeature(NAMESPACE);
+
+ connection.registerIQRequestHandler(
+ new AbstractIqRequestHandler(BoBIQ.ELEMENT, BoBIQ.NAMESPACE, Type.get, Mode.sync) {
+ @Override
+ public IQ handleIQRequest(IQ iqRequest) {
+ BoBIQ getBoBIQ = (BoBIQ) iqRequest;
+
+ BoBData bobData = bobSaverManager.getBoB(getBoBIQ.getBoBHash());
+ BoBIQ responseBoBIQ = null;
+ try {
+ responseBoBIQ = responseBoB(getBoBIQ, bobData);
+ } catch (NotConnectedException | NotLoggedInException | InterruptedException e) {
+ }
+
+ return responseBoBIQ;
+ }
+ });
+
+ connection.registerIQRequestHandler(
+ new AbstractIqRequestHandler(BoBIQ.ELEMENT, BoBIQ.NAMESPACE, Type.result, Mode.sync) {
+ @Override
+ public IQ handleIQRequest(IQ iqRequest) {
+ BoBIQ resultBoBIQ = (BoBIQ) iqRequest;
+ bobSaverManager.addBoB(resultBoBIQ.getBoBHash(), resultBoBIQ.getBoBData());
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Returns true if Bits of Binary is supported by the server.
+ *
+ * @return true if Bits of Binary is supported by the server.
+ * @throws NoResponseException
+ * @throws XMPPErrorException
+ * @throws NotConnectedException
+ * @throws InterruptedException
+ */
+ public boolean isSupportedByServer()
+ throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
+ return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(NAMESPACE);
+ }
+
+ /**
+ * Request BoB data.
+ *
+ * @param to
+ * @param bobHash
+ * @return the BoB data
+ * @throws NotLoggedInException
+ * @throws NoResponseException
+ * @throws XMPPErrorException
+ * @throws NotConnectedException
+ * @throws InterruptedException
+ */
+ public BoBData requestBoB(Jid to, BoBHash bobHash) throws NotLoggedInException, NoResponseException,
+ XMPPErrorException, NotConnectedException, InterruptedException {
+ BoBIQ requestBoBIQ = new BoBIQ(bobHash);
+ requestBoBIQ.setType(Type.get);
+ requestBoBIQ.setTo(to);
+
+ XMPPConnection connection = getAuthenticatedConnectionOrThrow();
+ BoBIQ responseBoBIQ = connection.createPacketCollectorAndSend(requestBoBIQ).nextResultOrThrow();
+ return responseBoBIQ.getBoBData();
+ }
+
+ private BoBIQ responseBoB(BoBIQ requestBoBIQ, BoBData bobData)
+ throws NotConnectedException, InterruptedException, NotLoggedInException {
+ BoBIQ responseBoBIQ = new BoBIQ(requestBoBIQ.getBoBHash(), bobData);
+ responseBoBIQ.setType(Type.result);
+ responseBoBIQ.setTo(requestBoBIQ.getFrom());
+ return responseBoBIQ;
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBSaverManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBSaverManager.java
new file mode 100644
index 000000000..a40b3dd34
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/BoBSaverManager.java
@@ -0,0 +1,33 @@
+/**
+ *
+ * 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.smackx.bob;
+
+/**
+ * Bits of Binary Saver Manager interface.
+ *
+ * @author Fernando Ramirez
+ *
+ */
+public interface BoBSaverManager {
+
+ public void addBoB(BoBHash bobHash, BoBData bobData);
+
+ public void removeBoB(BoBHash bobHash);
+
+ public BoBData getBoB(BoBHash bobHash);
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/DefaultBoBSaverManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/DefaultBoBSaverManager.java
new file mode 100644
index 000000000..9dc2eb9b8
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/DefaultBoBSaverManager.java
@@ -0,0 +1,46 @@
+/**
+ *
+ * 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.smackx.bob;
+
+import java.util.HashMap;
+
+/**
+ * Default Bits of Binary Saver Manager class.
+ *
+ * @author Fernando Ramirez
+ *
+ */
+public class DefaultBoBSaverManager implements BoBSaverManager {
+
+ HashMap bobs = new HashMap<>();
+
+ @Override
+ public void addBoB(BoBHash bobHash, BoBData bobData) {
+ bobs.put(bobHash, bobData);
+ }
+
+ @Override
+ public void removeBoB(BoBHash bobHash) {
+ bobs.remove(bobHash);
+ }
+
+ @Override
+ public BoBData getBoB(BoBHash bobHash) {
+ return bobs.get(bobHash);
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBExtension.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBExtension.java
new file mode 100644
index 000000000..f10ed5489
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBExtension.java
@@ -0,0 +1,96 @@
+/**
+ *
+ * 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.smackx.bob.element;
+
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+import org.jivesoftware.smackx.bob.BoBHash;
+import org.jivesoftware.smackx.xhtmlim.XHTMLText;
+import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension;
+
+/**
+ * Bits of Binary extension element.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+public class BoBExtension extends XHTMLExtension {
+
+ private final BoBHash bobHash;
+ private final String alt;
+ private final String paragraph;
+
+ /**
+ * Bits of Binary extension constructor.
+ *
+ * @param bobHash
+ * @param alt
+ * @param paragraph
+ */
+ public BoBExtension(BoBHash bobHash, String alt, String paragraph) {
+ this.bobHash = bobHash;
+ this.alt = alt;
+ this.paragraph = paragraph;
+ }
+
+ /**
+ * Get the BoB hash.
+ *
+ * @return the BoB hash
+ */
+ public BoBHash getBoBHash() {
+ return bobHash;
+ }
+
+ /**
+ * Get the alt field.
+ *
+ * @return the alt field
+ */
+ public String getAlt() {
+ return alt;
+ }
+
+ @Override
+ public XmlStringBuilder toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.rightAngleBracket();
+
+ xml.halfOpenElement(Message.BODY);
+ xml.xmlnsAttribute(XHTMLText.NAMESPACE);
+ xml.rightAngleBracket();
+
+ xml.openElement(XHTMLText.P);
+ xml.optEscape(paragraph);
+
+ xml.halfOpenElement(XHTMLText.IMG);
+ xml.optAttribute("alt", alt);
+ xml.attribute("src", bobHash.toSrc());
+ xml.closeEmptyElement();
+
+ xml.closeElement(XHTMLText.P);
+ xml.closeElement(Message.BODY);
+ xml.closeElement(this);
+ return xml;
+ }
+
+ public static BoBExtension from(Message message) {
+ return message.getExtension(ELEMENT, NAMESPACE);
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBIQ.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBIQ.java
new file mode 100644
index 000000000..c741a01b9
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/BoBIQ.java
@@ -0,0 +1,101 @@
+/**
+ *
+ * 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.smackx.bob.element;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.bob.BoBData;
+import org.jivesoftware.smackx.bob.BoBHash;
+import org.jivesoftware.smackx.bob.BoBManager;
+
+/**
+ * Bits of Binary IQ class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+public class BoBIQ extends IQ {
+
+ /**
+ * data element.
+ */
+ public static final String ELEMENT = "data";
+
+ /**
+ * the IQ NAMESPACE.
+ */
+ public static final String NAMESPACE = BoBManager.NAMESPACE;
+
+ private final BoBHash bobHash;
+ private final BoBData bobData;
+
+ /**
+ * Bits of Binary IQ constructor.
+ *
+ * @param bobHash
+ * @param bobData
+ */
+ public BoBIQ(BoBHash bobHash, BoBData bobData) {
+ super(ELEMENT, NAMESPACE);
+ this.bobHash = bobHash;
+ this.bobData = bobData;
+ }
+
+ /**
+ * Bits of Binary IQ constructor.
+ *
+ * @param bobHash
+ */
+ public BoBIQ(BoBHash bobHash) {
+ this(bobHash, null);
+ }
+
+ /**
+ * Get the BoB hash.
+ *
+ * @return the BoB hash
+ */
+ public BoBHash getBoBHash() {
+ return bobHash;
+ }
+
+ /**
+ * Get the BoB data.
+ *
+ * @return the BoB data
+ */
+ public BoBData getBoBData() {
+ return bobData;
+ }
+
+ @Override
+ protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
+ xml.attribute("cid", bobHash.toCid());
+
+ if (bobData != null) {
+ xml.attribute("max-age", String.valueOf(bobData.getMaxAge()));
+ xml.attribute("type", bobData.getType());
+ xml.rightAngleBracket();
+ xml.escape(bobData.getBase64Encoded());
+ } else {
+ xml.setEmptyElement();
+ }
+
+ return xml;
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/package-info.java
new file mode 100644
index 000000000..fa044be00
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/element/package-info.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Bits of Binary elements.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+package org.jivesoftware.smackx.bob.element;
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/package-info.java
new file mode 100644
index 000000000..1e03acf76
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/package-info.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Classes and interfaces of Bits of Binary.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+package org.jivesoftware.smackx.bob;
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/BoBExtensionProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/BoBExtensionProvider.java
new file mode 100644
index 000000000..a8d83b598
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/BoBExtensionProvider.java
@@ -0,0 +1,65 @@
+/**
+ *
+ * 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.smackx.bob.provider;
+
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.jivesoftware.smackx.bob.BoBHash;
+import org.jivesoftware.smackx.bob.element.BoBExtension;
+import org.jivesoftware.smackx.xhtmlim.XHTMLText;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Bits of Binary extension provider class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+public class BoBExtensionProvider extends ExtensionElementProvider {
+
+ @Override
+ public BoBExtension parse(XmlPullParser parser, int initialDepth) throws Exception {
+ BoBHash bobHash = null;
+ String alt = null;
+
+ outerloop: while (true) {
+ int eventType = parser.next();
+
+ switch (eventType) {
+
+ case XmlPullParser.START_TAG:
+ if (parser.getName().equals(XHTMLText.IMG)) {
+ alt = parser.getAttributeValue("", "alt");
+
+ String src = parser.getAttributeValue("", "src");
+ bobHash = BoBHash.fromSrc(src);
+ }
+ break;
+
+ case XmlPullParser.END_TAG:
+ if (parser.getDepth() == initialDepth) {
+ break outerloop;
+ }
+ break;
+
+ }
+ }
+
+ return new BoBExtension(bobHash, alt, null);
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/BoBIQProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/BoBIQProvider.java
new file mode 100644
index 000000000..099defe7a
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/BoBIQProvider.java
@@ -0,0 +1,70 @@
+/**
+ *
+ * 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.smackx.bob.provider;
+
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.stringencoder.Base64;
+import org.jivesoftware.smackx.bob.BoBData;
+import org.jivesoftware.smackx.bob.BoBHash;
+import org.jivesoftware.smackx.bob.element.BoBIQ;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Bits of Binary IQ provider class.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+public class BoBIQProvider extends IQProvider {
+
+ @Override
+ public BoBIQ parse(XmlPullParser parser, int initialDepth) throws Exception {
+ BoBHash bobHash = null;
+ BoBData bobData = null;
+
+ parser.next();
+
+ if (parser.getName().equals(BoBIQ.ELEMENT)) {
+ String cid = parser.getAttributeValue("", "cid");
+ bobHash = BoBHash.fromCid(cid);
+
+ String dataType = parser.getAttributeValue("", "type");
+ String maxAgeString = parser.getAttributeValue("", "max-age");
+
+ long maxAge = 0;
+ if (maxAgeString != null) {
+ maxAge = Long.parseLong(maxAgeString);
+ }
+
+ String base64EncodedData = null;
+ try {
+ base64EncodedData = parser.nextText();
+ } catch (Exception e) {
+ }
+
+ if (base64EncodedData != null && dataType != null) {
+ byte[] base64EncodedDataBytes = base64EncodedData.getBytes();
+ bobData = new BoBData(maxAge, dataType, Base64.decode(base64EncodedDataBytes));
+ }
+
+ }
+
+ return new BoBIQ(bobHash, bobData);
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/package-info.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/package-info.java
new file mode 100644
index 000000000..31d133038
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bob/provider/package-info.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Bits of Binary providers.
+ *
+ * @author Fernando Ramirez
+ * @see XEP-0231: Bits of
+ * Binary
+ */
+package org.jivesoftware.smackx.bob.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 a8267f785..7fde63c7b 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
@@ -537,4 +537,16 @@
org.jivesoftware.smackx.blocking.provider.BlockedErrorExtensionProvider
+
+
+ html
+ http://jabber.org/protocol/xhtml-im
+ org.jivesoftware.smackx.bob.provider.BoBExtensionProvider
+
+
+ data
+ urn:xmpp:bob
+ org.jivesoftware.smackx.bob.provider.BoBIQProvider
+
+
diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bob/BoBExtensionTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bob/BoBExtensionTest.java
new file mode 100644
index 000000000..eedfb9703
--- /dev/null
+++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bob/BoBExtensionTest.java
@@ -0,0 +1,67 @@
+/**
+ *
+ * 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.smackx.bob;
+
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.Message.Type;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.bob.element.BoBExtension;
+import org.jivesoftware.smackx.bob.provider.BoBExtensionProvider;
+import org.junit.Assert;
+import org.junit.Test;
+import org.jxmpp.jid.impl.JidCreate;
+import org.xmlpull.v1.XmlPullParser;
+
+public class BoBExtensionTest {
+
+ String sampleMessageWithBoBExtension = ""
+ + "Yet here's a spot." + ""
+ + ""
+ + "" + ""
+ + "" + "";
+
+ String sampleBoBExtension = ""
+ + ""
+ + "" + ""
+ + "";
+
+ @Test
+ public void checkBoBMessageExtension() throws Exception {
+ Message message = (Message) PacketParserUtils.parseStanza(sampleMessageWithBoBExtension);
+
+ BoBHash bobHash = new BoBHash("8f35fef110ffc5df08d579a50083ff9308fb6242", "sha1");
+
+ Message createdMessage = new Message(JidCreate.from("macbeth@chat.shakespeare.lit"));
+ createdMessage.setStanzaId("sarasa");
+ createdMessage.setType(Type.groupchat);
+ createdMessage.setBody("Yet here's a spot.");
+ createdMessage.addExtension(new BoBExtension(bobHash, "A spot", null));
+
+ Assert.assertEquals(message.toXML().toString(), createdMessage.toXML().toString());
+ }
+
+ @Test
+ public void checkBoBExtensionProvider() throws Exception {
+ XmlPullParser parser = PacketParserUtils.getParserFor(sampleBoBExtension);
+ BoBExtension bobExtension = new BoBExtensionProvider().parse(parser);
+
+ Assert.assertEquals("A spot", bobExtension.getAlt());
+ Assert.assertEquals("sha1", bobExtension.getBoBHash().getHashType());
+ Assert.assertEquals("8f35fef110ffc5df08d579a50083ff9308fb6242", bobExtension.getBoBHash().getHash());
+ }
+
+}
diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bob/BoBIQTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bob/BoBIQTest.java
new file mode 100644
index 000000000..337018e03
--- /dev/null
+++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bob/BoBIQTest.java
@@ -0,0 +1,80 @@
+/**
+ *
+ * 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.smackx.bob;
+
+import org.jivesoftware.smack.packet.IQ.Type;
+import org.jivesoftware.smack.test.util.SmackTestSuite;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.bob.element.BoBIQ;
+import org.jivesoftware.smackx.bob.provider.BoBIQProvider;
+import org.junit.Assert;
+import org.junit.Test;
+import org.jxmpp.jid.impl.JidCreate;
+import org.xmlpull.v1.XmlPullParser;
+
+public class BoBIQTest extends SmackTestSuite {
+
+ String sampleBoBIQRequest = ""
+ + "" + "";
+
+ String sampleBoBIQResponse = ""
+ + "" + "c2FyYXNhZGUyMzU0ajI=" + "" + "";
+
+ @Test
+ public void checkBoBIQRequest() throws Exception {
+ XmlPullParser parser = PacketParserUtils.getParserFor(sampleBoBIQRequest);
+ BoBIQ bobIQ = new BoBIQProvider().parse(parser);
+ bobIQ.setStanzaId("sarasa");
+ bobIQ.setTo(JidCreate.from("ladymacbeth@shakespeare.lit/castle"));
+ bobIQ.setType(Type.get);
+
+ BoBHash bobHash = new BoBHash("8f35fef110ffc5df08d579a50083ff9308fb6242", "sha1");
+
+ BoBIQ createdBoBIQ = new BoBIQ(bobHash);
+ createdBoBIQ.setStanzaId("sarasa");
+ createdBoBIQ.setTo(JidCreate.from("ladymacbeth@shakespeare.lit/castle"));
+ createdBoBIQ.setType(Type.get);
+
+ Assert.assertEquals(sampleBoBIQRequest, createdBoBIQ.toXML().toString());
+ }
+
+ @Test
+ public void checkBoBIQResponse() throws Exception {
+ XmlPullParser parser = PacketParserUtils.getParserFor(sampleBoBIQResponse);
+ BoBIQ bobIQ = new BoBIQProvider().parse(parser);
+ bobIQ.setStanzaId("sarasa");
+ bobIQ.setTo(JidCreate.from("doctor@shakespeare.lit/pda"));
+ bobIQ.setType(Type.result);
+
+ BoBHash bobHash = new BoBHash("8f35fef110ffc5df08d579a50083ff9308fb6242", "sha1");
+ BoBData bobData = new BoBData(86400, "image/png", "sarasade2354j2".getBytes());
+
+ BoBIQ createdBoBIQ = new BoBIQ(bobHash, bobData);
+ createdBoBIQ.setStanzaId("sarasa");
+ createdBoBIQ.setTo(JidCreate.from("doctor@shakespeare.lit/pda"));
+ createdBoBIQ.setType(Type.result);
+
+ Assert.assertEquals(bobIQ.getBoBHash().getHash(), createdBoBIQ.getBoBHash().getHash());
+ Assert.assertEquals(bobIQ.getBoBHash().getHashType(), createdBoBIQ.getBoBHash().getHashType());
+ Assert.assertEquals(bobIQ.getBoBData().getMaxAge(), createdBoBIQ.getBoBData().getMaxAge());
+ Assert.assertEquals(bobIQ.getBoBData().getType(), createdBoBIQ.getBoBData().getType());
+ Assert.assertEquals(bobIQ.getBoBData().getBase64Encoded(), createdBoBIQ.getBoBData().getBase64Encoded());
+ Assert.assertEquals(bobIQ.toXML().toString(), createdBoBIQ.toXML().toString());
+ }
+
+}