Reply to requests for BOB data

Stephen Paul Weber created

Change summary

src/cheogram/java/com/cheogram/android/BobTransfer.java         |  5 
src/main/java/eu/siacs/conversations/generator/IqGenerator.java | 43 ++
src/main/java/eu/siacs/conversations/parser/IqParser.java       |  7 
3 files changed, 52 insertions(+), 3 deletions(-)

Detailed changes

src/cheogram/java/com/cheogram/android/BobTransfer.java 🔗

@@ -37,7 +37,10 @@ public class BobTransfer implements Transferable {
 
 	public static Cid cid(URI uri) {
 		if (!uri.getScheme().equals("cid")) return null;
-		String bobCid = uri.getSchemeSpecificPart();
+		return cid(uri.getSchemeSpecificPart());
+	}
+
+	public static Cid cid(String bobCid) {
 		if (!bobCid.contains("@") || !bobCid.contains("+")) return null;
 		String[] cidParts = bobCid.split("@")[0].split("\\+");
 		try {

src/main/java/eu/siacs/conversations/generator/IqGenerator.java 🔗

@@ -1,15 +1,22 @@
 package eu.siacs.conversations.generator;
 
-
 import android.os.Bundle;
 import android.util.Base64;
+import android.util.Base64OutputStream;
 import android.util.Log;
 
+import com.cheogram.android.BobTransfer;
+
+import com.google.common.io.ByteStreams;
+
 import org.whispersystems.libsignal.IdentityKey;
 import org.whispersystems.libsignal.ecc.ECPublicKey;
 import org.whispersystems.libsignal.state.PreKeyRecord;
 import org.whispersystems.libsignal.state.SignedPreKeyRecord;
 
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
@@ -20,6 +27,8 @@ import java.util.Set;
 import java.util.TimeZone;
 import java.util.UUID;
 
+import io.ipfs.cid.Cid;
+
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@@ -569,4 +578,36 @@ public class IqGenerator extends AbstractGenerator {
         packet.addChild("query",Namespace.DISCO_INFO);
         return packet;
     }
+
+    public IqPacket bobResponse(IqPacket request) {
+        try {
+            String bobCid = request.findChild("data", "urn:xmpp:bob").getAttribute("cid");
+            Cid cid = BobTransfer.cid(bobCid);
+            DownloadableFile f = mXmppConnectionService.getFileForCid(cid);
+            if (f == null || !f.canRead()) {
+                throw new IOException("No such file");
+            } else if (f.getSize() > 129000) {
+                final IqPacket response = request.generateResponse(IqPacket.TYPE.ERROR);
+                final Element error = response.addChild("error");
+                error.setAttribute("type", "cancel");
+                error.addChild("policy-violation", "urn:ietf:params:xml:ns:xmpp-stanzas");
+                return response;
+            } else {
+                final IqPacket response = request.generateResponse(IqPacket.TYPE.RESULT);
+                final Element data = response.addChild("data", "urn:xmpp:bob");
+                data.setAttribute("cid", bobCid);
+                data.setAttribute("type", f.getMimeType());
+                ByteArrayOutputStream b64 = new ByteArrayOutputStream((int) f.getSize() * 2);
+                ByteStreams.copy(new FileInputStream(f), new Base64OutputStream(b64, Base64.NO_WRAP));
+                data.setContent(b64.toString("utf-8"));
+                return response;
+            }
+        } catch (final IOException | IllegalStateException e) {
+            final IqPacket response = request.generateResponse(IqPacket.TYPE.ERROR);
+            final Element error = response.addChild("error");
+            error.setAttribute("type", "cancel");
+            error.addChild("item-not-found", "urn:ietf:params:xml:ns:xmpp-stanzas");
+            return response;
+        }
+    }
 }

src/main/java/eu/siacs/conversations/parser/IqParser.java 🔗

@@ -31,6 +31,7 @@ import eu.siacs.conversations.Config;
 import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Room;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.xml.Element;
@@ -453,7 +454,11 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
             }
             mXmppConnectionService.sendIqPacket(account, response, null);
         } else {
-            if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
+            final Contact contact = account.getRoster().getContact(packet.getFrom());
+            final Conversation conversation = mXmppConnectionService.find(account, packet.getFrom());
+            if (packet.hasChild("data", "urn:xmpp:bob") && isGet && (conversation == null ? contact != null && contact.canInferPresence() : conversation.canInferPresence())) {
+                mXmppConnectionService.sendIqPacket(account, mXmppConnectionService.getIqGenerator().bobResponse(packet), null);
+            } else if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
                 final IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
                 final Element error = response.addChild("error");
                 error.setAttribute("type", "cancel");