1package eu.siacs.conversations.xmpp.jingle;
2
3import java.io.FileInputStream;
4import java.io.FileNotFoundException;
5import java.io.FileOutputStream;
6import java.io.IOException;
7import java.security.MessageDigest;
8import java.security.NoSuchAlgorithmException;
9
10import android.util.Base64;
11import android.util.Log;
12import eu.siacs.conversations.entities.Account;
13import eu.siacs.conversations.utils.CryptoHelper;
14import eu.siacs.conversations.xml.Element;
15import eu.siacs.conversations.xmpp.OnIqPacketReceived;
16import eu.siacs.conversations.xmpp.PacketReceived;
17import eu.siacs.conversations.xmpp.stanzas.IqPacket;
18
19public class JingleInbandTransport extends JingleTransport {
20
21 private Account account;
22 private String counterpart;
23 private int blockSize;
24 private int bufferSize;
25 private int seq = 0;
26 private String sessionId;
27
28 private boolean established = false;
29
30 private JingleFile file;
31
32 private FileInputStream fileInputStream = null;
33 private FileOutputStream fileOutputStream;
34 private long remainingSize;
35 private MessageDigest digest;
36
37 private OnFileTransmitted onFileTransmitted;
38
39 private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
40 @Override
41 public void onIqPacketReceived(Account account, IqPacket packet) {
42 Log.d("xmppService", "on ack received");
43 if (packet.getType() == IqPacket.TYPE_RESULT) {
44 sendNextBlock();
45 }
46 }
47 };
48
49 public JingleInbandTransport(Account account, String counterpart,
50 String sid, int blocksize) {
51 this.account = account;
52 this.counterpart = counterpart;
53 this.blockSize = blocksize;
54 this.bufferSize = blocksize / 4;
55 this.sessionId = sid;
56 }
57
58 public void connect(final OnTransportConnected callback) {
59 IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
60 iq.setTo(this.counterpart);
61 Element open = iq.addChild("open", "http://jabber.org/protocol/ibb");
62 open.setAttribute("sid", this.sessionId);
63 open.setAttribute("stanza", "iq");
64 open.setAttribute("block-size", "" + this.blockSize);
65
66 this.account.getXmppConnection().sendIqPacket(iq,
67 new OnIqPacketReceived() {
68
69 @Override
70 public void onIqPacketReceived(Account account,
71 IqPacket packet) {
72 if (packet.getType() == IqPacket.TYPE_ERROR) {
73 callback.failed();
74 } else {
75 callback.established();
76 }
77 }
78 });
79 }
80
81 @Override
82 public void receive(JingleFile file, OnFileTransmitted callback) {
83 this.onFileTransmitted = callback;
84 this.file = file;
85 Log.d("xmppService", "receiving file over ibb");
86 try {
87 this.digest = MessageDigest.getInstance("SHA-1");
88 digest.reset();
89 file.getParentFile().mkdirs();
90 file.createNewFile();
91 this.fileOutputStream = new FileOutputStream(file);
92 this.remainingSize = file.getExpectedSize();
93 } catch (NoSuchAlgorithmException e) {
94 e.printStackTrace();
95 } catch (IOException e) {
96 e.printStackTrace();
97 }
98 }
99
100 @Override
101 public void send(JingleFile file, OnFileTransmitted callback) {
102 this.onFileTransmitted = callback;
103 this.file = file;
104 Log.d("xmppService", "sending file over ibb");
105 try {
106 this.digest = MessageDigest.getInstance("SHA-1");
107 this.digest.reset();
108 fileInputStream = new FileInputStream(file);
109 this.sendNextBlock();
110 } catch (FileNotFoundException e) {
111 e.printStackTrace();
112 } catch (NoSuchAlgorithmException e) {
113 e.printStackTrace();
114 }
115 }
116
117 private void sendNextBlock() {
118 byte[] buffer = new byte[this.bufferSize];
119 try {
120 int count = fileInputStream.read(buffer);
121 if (count==-1) {
122 file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
123 fileInputStream.close();
124 this.onFileTransmitted.onFileTransmitted(file);
125 } else {
126 this.digest.update(buffer);
127 String base64 = Base64.encodeToString(buffer, Base64.DEFAULT);
128 IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
129 iq.setTo(this.counterpart);
130 Element data = iq
131 .addChild("data", "http://jabber.org/protocol/ibb");
132 data.setAttribute("seq", "" + this.seq);
133 data.setAttribute("block-size", "" + this.blockSize);
134 data.setAttribute("sid", this.sessionId);
135 data.setContent(base64);
136 this.account.getXmppConnection().sendIqPacket(iq,
137 this.onAckReceived);
138 this.seq++;
139 }
140 } catch (IOException e) {
141 // TODO Auto-generated catch block
142 e.printStackTrace();
143 }
144 }
145
146 private void receiveNextBlock(String data) {
147 try {
148 byte[] buffer = Base64.decode(data, Base64.DEFAULT);
149 this.remainingSize -= buffer.length;
150
151 this.fileOutputStream.write(buffer);
152
153 this.digest.update(buffer);
154 Log.d("xmppService", "remaining file size:" + this.remainingSize);
155 if (this.remainingSize <= 0) {
156 file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
157 Log.d("xmppService","file name: "+file.getAbsolutePath());
158 fileOutputStream.flush();
159 fileOutputStream.close();
160 this.onFileTransmitted.onFileTransmitted(file);
161 }
162 } catch (IOException e) {
163 e.printStackTrace();
164 }
165 }
166
167 public void deliverPayload(IqPacket packet, Element payload) {
168 if (payload.getName().equals("open")) {
169 if (!established) {
170 established = true;
171 this.account.getXmppConnection().sendIqPacket(
172 packet.generateRespone(IqPacket.TYPE_RESULT), null);
173 } else {
174 this.account.getXmppConnection().sendIqPacket(
175 packet.generateRespone(IqPacket.TYPE_ERROR), null);
176 }
177 } else if (payload.getName().equals("data")) {
178 this.receiveNextBlock(payload.getContent());
179 this.account.getXmppConnection().sendIqPacket(
180 packet.generateRespone(IqPacket.TYPE_RESULT), null);
181 } else {
182 Log.d("xmppServic","couldnt deliver payload "+packet.toString());
183 }
184 }
185}