aboutsummaryrefslogtreecommitdiffstats
path: root/src/eu/siacs
diff options
context:
space:
mode:
authorDaniel Gultsch <daniel.gultsch@rwth-aachen.de>2014-04-22 13:11:53 +0200
committerDaniel Gultsch <daniel.gultsch@rwth-aachen.de>2014-04-22 13:11:53 +0200
commitaa5f775fc79ca7b1912c9d7fba759e7efb09fa2b (patch)
treeec0da5fa392616f06d4e8dabb4034b02980581a2 /src/eu/siacs
parentd151ad90311f74df8bcd49ad3c9b1e0bc71423ec (diff)
added support for ibb
Diffstat (limited to 'src/eu/siacs')
-rw-r--r--src/eu/siacs/conversations/services/XmppConnectionService.java2
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java113
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java27
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java183
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java (renamed from src/eu/siacs/conversations/xmpp/jingle/SocksConnection.java)6
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java3
-rw-r--r--src/eu/siacs/conversations/xmpp/jingle/OnTransportConnected.java (renamed from src/eu/siacs/conversations/xmpp/jingle/OnSocksConnection.java)2
7 files changed, 313 insertions, 23 deletions
diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java
index 49cb6288..7c241f3d 100644
--- a/src/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/eu/siacs/conversations/services/XmppConnectionService.java
@@ -368,6 +368,8 @@ public class XmppConnectionService extends Service {
processRosterItems(account, query);
mergePhoneContactsWithRoster(null);
}
+ } else if (packet.hasChild("open","http://jabber.org/protocol/ibb")||packet.hasChild("data","http://jabber.org/protocol/ibb")) {
+ XmppConnectionService.this.mJingleConnectionManager.deliverIbbPacket(account,packet);
} else {
Log.d(LOGTAG,"iq packet arrived "+packet.toString());
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
index 75cc94e1..81c17d4c 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java
@@ -32,6 +32,8 @@ public class JingleConnection {
public static final int STATUS_TRANSMITTING = 5;
public static final int STATUS_FAILED = 99;
+ private int ibbBlockSize = 4096;
+
private int status = -1;
private Message message;
private String sessionId;
@@ -39,7 +41,7 @@ public class JingleConnection {
private String initiator;
private String responder;
private List<JingleCandidate> candidates = new ArrayList<JingleCandidate>();
- private HashMap<String, SocksConnection> connections = new HashMap<String, SocksConnection>();
+ private HashMap<String, JingleSocks5Transport> connections = new HashMap<String, JingleSocks5Transport>();
private String transportId;
private Element fileOffer;
@@ -126,7 +128,15 @@ public class JingleConnection {
} else if (packet.isAction("session-accept")) {
accept(packet);
} else if (packet.isAction("transport-info")) {
- transportInfo(packet);
+ receiveTransportInfo(packet);
+ } else if (packet.isAction("transport-replace")) {
+ if (packet.getJingleContent().hasIbbTransport()) {
+ this.receiveFallbackToIbb(packet);
+ } else {
+ Log.d("xmppService","trying to fallback to something unknown"+packet.toString());
+ }
+ } else if (packet.isAction("transport-accept")) {
+ this.receiveTransportAccept(packet);
} else {
Log.d("xmppService","packet arrived in connection. action was "+packet.getAction());
}
@@ -146,9 +156,9 @@ public class JingleConnection {
@Override
public void onPrimaryCandidateFound(boolean success, final JingleCandidate candidate) {
if (success) {
- final SocksConnection socksConnection = new SocksConnection(JingleConnection.this, candidate);
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(JingleConnection.this, candidate);
connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnSocksConnection() {
+ socksConnection.connect(new OnTransportConnected() {
@Override
public void failed() {
@@ -248,9 +258,9 @@ public class JingleConnection {
content.setFileOffer(fileOffer);
content.setTransportId(transportId);
if ((success)&&(!equalCandidateExists(candidate))) {
- final SocksConnection socksConnection = new SocksConnection(JingleConnection.this, candidate);
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(JingleConnection.this, candidate);
connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnSocksConnection() {
+ socksConnection.connect(new OnTransportConnected() {
@Override
public void failed() {
@@ -307,7 +317,7 @@ public class JingleConnection {
account.getXmppConnection().sendIqPacket(response, null);
}
- private void transportInfo(JinglePacket packet) {
+ private void receiveTransportInfo(JinglePacket packet) {
Content content = packet.getJingleContent();
if (content.hasSocks5Transport()) {
if (content.socks5transport().hasChild("activated")) {
@@ -345,13 +355,14 @@ public class JingleConnection {
}
private void connect() {
- final SocksConnection connection = chooseConnection();
+ final JingleSocks5Transport connection = chooseConnection();
this.transport = connection;
if (connection==null) {
Log.d("xmppService","could not find suitable candidate");
this.disconnect();
- this.status = STATUS_FAILED;
- this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED);
+ if (this.initiator.equals(account.getFullJid())) {
+ this.sendFallbackToIbb();
+ }
} else {
this.status = STATUS_TRANSMITTING;
if (connection.isProxy()) {
@@ -386,12 +397,12 @@ public class JingleConnection {
}
}
- private SocksConnection chooseConnection() {
- SocksConnection connection = null;
- Iterator<Entry<String, SocksConnection>> it = this.connections.entrySet().iterator();
+ private JingleSocks5Transport chooseConnection() {
+ JingleSocks5Transport connection = null;
+ Iterator<Entry<String, JingleSocks5Transport>> it = this.connections.entrySet().iterator();
while (it.hasNext()) {
- Entry<String, SocksConnection> pairs = it.next();
- SocksConnection currentConnection = pairs.getValue();
+ Entry<String, JingleSocks5Transport> pairs = it.next();
+ JingleSocks5Transport currentConnection = pairs.getValue();
//Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString());
if (currentConnection.isEstablished()&&(currentConnection.getCandidate().isUsedByCounterpart()||(!currentConnection.getCandidate().isOurs()))) {
//Log.d("xmppService","is usable");
@@ -430,6 +441,62 @@ public class JingleConnection {
this.mXmppConnectionService.markMessage(this.message, Message.STATUS_RECIEVED);
}
+ private void sendFallbackToIbb() {
+ JinglePacket packet = this.bootstrapPacket("transport-replace");
+ Content content = new Content("initiator","a-file-offer");
+ this.transportId = this.mJingleConnectionManager.nextRandomId();
+ content.setTransportId(this.transportId);
+ content.ibbTransport().setAttribute("block-size",""+this.ibbBlockSize);
+ packet.setContent(content);
+ this.sendJinglePacket(packet);
+ }
+
+ private void receiveFallbackToIbb(JinglePacket packet) {
+ String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
+ if (receivedBlockSize!=null) {
+ int bs = Integer.parseInt(receivedBlockSize);
+ if (bs>this.ibbBlockSize) {
+ this.ibbBlockSize = bs;
+ }
+ }
+ this.transportId = packet.getJingleContent().getTransportId();
+ this.transport = new JingleInbandTransport(this.account,this.responder,this.transportId,this.ibbBlockSize);
+ this.transport.receive(file, onFileTransmitted);
+ JinglePacket answer = bootstrapPacket("transport-accept");
+ Content content = new Content("initiator", "a-file-offer");
+ content.setTransportId(this.transportId);
+ content.ibbTransport().setAttribute("block-size", ""+this.ibbBlockSize);
+ answer.setContent(content);
+ this.sendJinglePacket(answer);
+ }
+
+ private void receiveTransportAccept(JinglePacket packet) {
+ if (packet.getJingleContent().hasIbbTransport()) {
+ String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
+ if (receivedBlockSize!=null) {
+ int bs = Integer.parseInt(receivedBlockSize);
+ if (bs>this.ibbBlockSize) {
+ this.ibbBlockSize = bs;
+ }
+ }
+ this.transport = new JingleInbandTransport(this.account,this.responder,this.transportId,this.ibbBlockSize);
+ this.transport.connect(new OnTransportConnected() {
+
+ @Override
+ public void failed() {
+ Log.d("xmppService","ibb open failed");
+ }
+
+ @Override
+ public void established() {
+ JingleConnection.this.transport.send(file, onFileTransmitted);
+ }
+ });
+ } else {
+ Log.d("xmppService","invalid transport accept");
+ }
+ }
+
private void finish() {
this.status = STATUS_FINISHED;
this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND);
@@ -453,9 +520,9 @@ public class JingleConnection {
}
private void connectWithCandidate(final JingleCandidate candidate) {
- final SocksConnection socksConnection = new SocksConnection(this,candidate);
+ final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this,candidate);
connections.put(candidate.getCid(), socksConnection);
- socksConnection.connect(new OnSocksConnection() {
+ socksConnection.connect(new OnTransportConnected() {
@Override
public void failed() {
@@ -472,9 +539,9 @@ public class JingleConnection {
}
private void disconnect() {
- Iterator<Entry<String, SocksConnection>> it = this.connections.entrySet().iterator();
+ Iterator<Entry<String, JingleSocks5Transport>> it = this.connections.entrySet().iterator();
while (it.hasNext()) {
- Entry<String, SocksConnection> pairs = it.next();
+ Entry<String, JingleSocks5Transport> pairs = it.next();
pairs.getValue().disconnect();
it.remove();
}
@@ -564,4 +631,12 @@ public class JingleConnection {
public void success();
public void failed();
}
+
+ public boolean hasTransportId(String sid) {
+ return sid.equals(this.transportId);
+ }
+
+ public JingleTransport getTransport() {
+ return this.transport;
+ }
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index b5b92554..0a805afd 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -124,4 +124,31 @@ public class JingleConnectionManager {
return 524288;
}
}
+
+ public void deliverIbbPacket(Account account, IqPacket packet) {
+ String sid = null;
+ Element payload = null;
+ if (packet.hasChild("open","http://jabber.org/protocol/ibb")) {
+ payload = packet.findChild("open","http://jabber.org/protocol/ibb");
+ sid = payload.getAttribute("sid");
+ } else if (packet.hasChild("data","http://jabber.org/protocol/ibb")) {
+ payload = packet.findChild("data","http://jabber.org/protocol/ibb");
+ sid = payload.getAttribute("sid");
+ }
+ if (sid!=null) {
+ for (JingleConnection connection : connections) {
+ if (connection.hasTransportId(sid)) {
+ JingleTransport transport = connection.getTransport();
+ if (transport instanceof JingleInbandTransport) {
+ JingleInbandTransport inbandTransport = (JingleInbandTransport) transport;
+ inbandTransport.deliverPayload(packet,payload);
+ return;
+ }
+ }
+ }
+ Log.d("xmppService","couldnt deliver payload: "+payload.toString());
+ } else {
+ Log.d("xmppService","no sid found in incomming ibb packet");
+ }
+ }
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
new file mode 100644
index 00000000..c963136d
--- /dev/null
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java
@@ -0,0 +1,183 @@
+package eu.siacs.conversations.xmpp.jingle;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import android.util.Base64;
+import android.util.Log;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.PacketReceived;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+public class JingleInbandTransport extends JingleTransport {
+
+ private Account account;
+ private String counterpart;
+ private int blockSize;
+ private int bufferSize;
+ private int seq = 0;
+ private String sessionId;
+
+ private boolean established = false;
+
+ private JingleFile file;
+
+ private FileInputStream fileInputStream = null;
+ private FileOutputStream fileOutputStream;
+ private long remainingSize;
+ private MessageDigest digest;
+
+ private OnFileTransmitted onFileTransmitted;
+
+ private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Log.d("xmppService", "on ack received");
+ if (packet.getType() == IqPacket.TYPE_RESULT) {
+ sendNextBlock();
+ }
+ }
+ };
+
+ public JingleInbandTransport(Account account, String counterpart,
+ String sid, int blocksize) {
+ this.account = account;
+ this.counterpart = counterpart;
+ this.blockSize = blocksize;
+ this.bufferSize = blocksize / 4;
+ this.sessionId = sid;
+ }
+
+ public void connect(final OnTransportConnected callback) {
+ IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+ iq.setTo(this.counterpart);
+ Element open = iq.addChild("open", "http://jabber.org/protocol/ibb");
+ open.setAttribute("sid", this.sessionId);
+ open.setAttribute("stanza", "iq");
+ open.setAttribute("block-size", "" + this.blockSize);
+
+ this.account.getXmppConnection().sendIqPacket(iq,
+ new OnIqPacketReceived() {
+
+ @Override
+ public void onIqPacketReceived(Account account,
+ IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_ERROR) {
+ callback.failed();
+ } else {
+ callback.established();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void receive(JingleFile file, OnFileTransmitted callback) {
+ this.onFileTransmitted = callback;
+ this.file = file;
+ Log.d("xmppService", "receiving file over ibb");
+ try {
+ this.digest = MessageDigest.getInstance("SHA-1");
+ digest.reset();
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ this.fileOutputStream = new FileOutputStream(file);
+ this.remainingSize = file.getExpectedSize();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void send(JingleFile file, OnFileTransmitted callback) {
+ this.onFileTransmitted = callback;
+ this.file = file;
+ Log.d("xmppService", "sending file over ibb");
+ try {
+ this.digest = MessageDigest.getInstance("SHA-1");
+ this.digest.reset();
+ fileInputStream = new FileInputStream(file);
+ this.sendNextBlock();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void sendNextBlock() {
+ byte[] buffer = new byte[this.bufferSize];
+ try {
+ int count = fileInputStream.read(buffer);
+ if (count==-1) {
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ this.onFileTransmitted.onFileTransmitted(file);
+ } else {
+ this.digest.update(buffer);
+ String base64 = Base64.encodeToString(buffer, Base64.DEFAULT);
+ IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
+ iq.setTo(this.counterpart);
+ Element data = iq
+ .addChild("data", "http://jabber.org/protocol/ibb");
+ data.setAttribute("seq", "" + this.seq);
+ data.setAttribute("block-size", "" + this.blockSize);
+ data.setAttribute("sid", this.sessionId);
+ data.setContent(base64);
+ this.account.getXmppConnection().sendIqPacket(iq,
+ this.onAckReceived);
+ this.seq++;
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void receiveNextBlock(String data) {
+ try {
+ byte[] buffer = Base64.decode(data, Base64.DEFAULT);
+ this.remainingSize -= buffer.length;
+
+ this.fileOutputStream.write(buffer);
+
+ this.digest.update(buffer);
+ Log.d("xmppService", "remaining file size:" + this.remainingSize);
+ if (this.remainingSize <= 0) {
+ file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
+ Log.d("xmppService","file name: "+file.getAbsolutePath());
+ fileOutputStream.flush();
+ this.onFileTransmitted.onFileTransmitted(file);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void deliverPayload(IqPacket packet, Element payload) {
+ if (payload.getName().equals("open")) {
+ if (!established) {
+ established = true;
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateRespone(IqPacket.TYPE_RESULT), null);
+ } else {
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateRespone(IqPacket.TYPE_ERROR), null);
+ }
+ } else if (payload.getName().equals("data")) {
+ this.receiveNextBlock(payload.getContent());
+ this.account.getXmppConnection().sendIqPacket(
+ packet.generateRespone(IqPacket.TYPE_RESULT), null);
+ } else {
+ Log.d("xmppServic","couldnt deliver payload "+packet.toString());
+ }
+ }
+}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/SocksConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
index 1a945a65..f37d5a71 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/SocksConnection.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java
@@ -18,7 +18,7 @@ import eu.siacs.conversations.xml.Element;
import android.util.Log;
import android.widget.Button;
-public class SocksConnection extends JingleTransport {
+public class JingleSocks5Transport extends JingleTransport {
private JingleCandidate candidate;
private String destination;
private OutputStream outputStream;
@@ -26,7 +26,7 @@ public class SocksConnection extends JingleTransport {
private boolean isEstablished = false;
protected Socket socket;
- public SocksConnection(JingleConnection jingleConnection, JingleCandidate candidate) {
+ public JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) {
this.candidate = candidate;
try {
MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
@@ -47,7 +47,7 @@ public class SocksConnection extends JingleTransport {
}
}
- public void connect(final OnSocksConnection callback) {
+ public void connect(final OnTransportConnected callback) {
new Thread(new Runnable() {
@Override
diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
index 347824f1..6e9482a9 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/JingleTransport.java
@@ -1,6 +1,9 @@
package eu.siacs.conversations.xmpp.jingle;
+import eu.siacs.conversations.xml.Element;
+
public abstract class JingleTransport {
+ public abstract void connect(final OnTransportConnected callback);
public abstract void receive(final JingleFile file, final OnFileTransmitted callback);
public abstract void send(final JingleFile file, final OnFileTransmitted callback);
}
diff --git a/src/eu/siacs/conversations/xmpp/jingle/OnSocksConnection.java b/src/eu/siacs/conversations/xmpp/jingle/OnTransportConnected.java
index 88771997..7d9a084a 100644
--- a/src/eu/siacs/conversations/xmpp/jingle/OnSocksConnection.java
+++ b/src/eu/siacs/conversations/xmpp/jingle/OnTransportConnected.java
@@ -1,6 +1,6 @@
package eu.siacs.conversations.xmpp.jingle;
-public interface OnSocksConnection {
+public interface OnTransportConnected {
public void failed();
public void established();
}