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