1package eu.siacs.conversations.xmpp.jingle;
2
3import java.io.FileNotFoundException;
4import java.io.IOException;
5import java.io.InputStream;
6import java.io.OutputStream;
7import java.net.Socket;
8import java.net.UnknownHostException;
9import java.security.MessageDigest;
10import java.security.NoSuchAlgorithmException;
11import java.util.Arrays;
12
13import eu.siacs.conversations.entities.DownloadableFile;
14import eu.siacs.conversations.utils.CryptoHelper;
15
16public class JingleSocks5Transport extends JingleTransport {
17 private JingleCandidate candidate;
18 private String destination;
19 private OutputStream outputStream;
20 private InputStream inputStream;
21 private boolean isEstablished = false;
22 private boolean activated = false;
23 protected Socket socket;
24
25 public JingleSocks5Transport(JingleConnection jingleConnection,
26 JingleCandidate candidate) {
27 this.candidate = candidate;
28 try {
29 MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
30 StringBuilder destBuilder = new StringBuilder();
31 destBuilder.append(jingleConnection.getSessionId());
32 if (candidate.isOurs()) {
33 destBuilder.append(jingleConnection.getAccount().getJid());
34 destBuilder.append(jingleConnection.getCounterPart());
35 } else {
36 destBuilder.append(jingleConnection.getCounterPart());
37 destBuilder.append(jingleConnection.getAccount().getJid());
38 }
39 mDigest.reset();
40 this.destination = CryptoHelper.bytesToHex(mDigest
41 .digest(destBuilder.toString().getBytes()));
42 } catch (NoSuchAlgorithmException e) {
43
44 }
45 }
46
47 public void connect(final OnTransportConnected callback) {
48 new Thread(new Runnable() {
49
50 @Override
51 public void run() {
52 try {
53 socket = new Socket(candidate.getHost(),
54 candidate.getPort());
55 inputStream = socket.getInputStream();
56 outputStream = socket.getOutputStream();
57 byte[] login = { 0x05, 0x01, 0x00 };
58 byte[] expectedReply = { 0x05, 0x00 };
59 byte[] reply = new byte[2];
60 outputStream.write(login);
61 inputStream.read(reply);
62 final String connect = Character.toString('\u0005')
63 + '\u0001' + '\u0000' + '\u0003' + '\u0028'
64 + destination + '\u0000' + '\u0000';
65 if (Arrays.equals(reply, expectedReply)) {
66 outputStream.write(connect.getBytes());
67 byte[] result = new byte[2];
68 inputStream.read(result);
69 int status = result[1];
70 if (status == 0) {
71 isEstablished = true;
72 callback.established();
73 } else {
74 callback.failed();
75 }
76 } else {
77 socket.close();
78 callback.failed();
79 }
80 } catch (UnknownHostException e) {
81 callback.failed();
82 } catch (IOException e) {
83 callback.failed();
84 }
85 }
86 }).start();
87
88 }
89
90 public void send(final DownloadableFile file,
91 final OnFileTransmissionStatusChanged callback) {
92 new Thread(new Runnable() {
93
94 @Override
95 public void run() {
96 InputStream fileInputStream = null;
97 try {
98 MessageDigest digest = MessageDigest.getInstance("SHA-1");
99 digest.reset();
100 fileInputStream = file.createInputStream();
101 if (fileInputStream == null) {
102 callback.onFileTransferAborted();
103 return;
104 }
105 int count;
106 byte[] buffer = new byte[8192];
107 while ((count = fileInputStream.read(buffer)) > 0) {
108 outputStream.write(buffer, 0, count);
109 digest.update(buffer, 0, count);
110 }
111 outputStream.flush();
112 file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
113 if (callback != null) {
114 callback.onFileTransmitted(file);
115 }
116 } catch (FileNotFoundException e) {
117 callback.onFileTransferAborted();
118 } catch (IOException e) {
119 callback.onFileTransferAborted();
120 } catch (NoSuchAlgorithmException e) {
121 callback.onFileTransferAborted();
122 } finally {
123 try {
124 if (fileInputStream != null) {
125 fileInputStream.close();
126 }
127 } catch (IOException e) {
128 callback.onFileTransferAborted();
129 }
130 }
131 }
132 }).start();
133
134 }
135
136 public void receive(final DownloadableFile file,
137 final OnFileTransmissionStatusChanged callback) {
138 new Thread(new Runnable() {
139
140 @Override
141 public void run() {
142 try {
143 MessageDigest digest = MessageDigest.getInstance("SHA-1");
144 digest.reset();
145 inputStream.skip(45);
146 socket.setSoTimeout(30000);
147 file.getParentFile().mkdirs();
148 file.createNewFile();
149 OutputStream fileOutputStream = file.createOutputStream();
150 if (fileOutputStream == null) {
151 callback.onFileTransferAborted();
152 return;
153 }
154 long remainingSize = file.getExpectedSize();
155 byte[] buffer = new byte[8192];
156 int count = buffer.length;
157 while (remainingSize > 0) {
158 count = inputStream.read(buffer);
159 if (count == -1) {
160 callback.onFileTransferAborted();
161 return;
162 } else {
163 fileOutputStream.write(buffer, 0, count);
164 digest.update(buffer, 0, count);
165 remainingSize -= count;
166 }
167 }
168 fileOutputStream.flush();
169 fileOutputStream.close();
170 file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
171 callback.onFileTransmitted(file);
172 } catch (FileNotFoundException e) {
173 callback.onFileTransferAborted();
174 } catch (IOException e) {
175 callback.onFileTransferAborted();
176 } catch (NoSuchAlgorithmException e) {
177 callback.onFileTransferAborted();
178 }
179 }
180 }).start();
181 }
182
183 public boolean isProxy() {
184 return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
185 }
186
187 public boolean needsActivation() {
188 return (this.isProxy() && !this.activated);
189 }
190
191 public void disconnect() {
192 if (this.socket != null) {
193 try {
194 this.socket.close();
195 } catch (IOException e) {
196
197 }
198 }
199 }
200
201 public boolean isEstablished() {
202 return this.isEstablished;
203 }
204
205 public JingleCandidate getCandidate() {
206 return this.candidate;
207 }
208
209 public void setActivated(boolean activated) {
210 this.activated = activated;
211 }
212}