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