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 android.util.Log;
16import eu.siacs.conversations.utils.CryptoHelper;
17
18public class JingleSocks5Transport extends JingleTransport {
19 private JingleCandidate candidate;
20 private String destination;
21 private OutputStream outputStream;
22 private InputStream inputStream;
23 private boolean isEstablished = false;
24 private boolean activated = false;
25 protected Socket socket;
26
27 public JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) {
28 this.candidate = candidate;
29 try {
30 MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
31 StringBuilder destBuilder = new StringBuilder();
32 destBuilder.append(jingleConnection.getSessionId());
33 if (candidate.isOurs()) {
34 destBuilder.append(jingleConnection.getAccountJid());
35 destBuilder.append(jingleConnection.getCounterPart());
36 } else {
37 destBuilder.append(jingleConnection.getCounterPart());
38 destBuilder.append(jingleConnection.getAccountJid());
39 }
40 mDigest.reset();
41 this.destination = CryptoHelper.bytesToHex(mDigest
42 .digest(destBuilder.toString().getBytes()));
43 } catch (NoSuchAlgorithmException e) {
44
45 }
46 }
47
48 public void connect(final OnTransportConnected callback) {
49 new Thread(new Runnable() {
50
51 @Override
52 public void run() {
53 try {
54 socket = new Socket(candidate.getHost(), 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 if (Arrays.equals(reply, expectedReply)) {
63 String connect = "" + '\u0005' + '\u0001' + '\u0000' + '\u0003'
64 + '\u0028' + destination + '\u0000' + '\u0000';
65 outputStream.write(connect.getBytes());
66 byte[] result = new byte[2];
67 inputStream.read(result);
68 int status = result[1];
69 if (status == 0) {
70 isEstablished = true;
71 callback.established();
72 } else {
73 callback.failed();
74 }
75 } else {
76 socket.close();
77 callback.failed();
78 }
79 } catch (UnknownHostException e) {
80 callback.failed();
81 } catch (IOException e) {
82 callback.failed();
83 }
84 }
85 }).start();
86
87 }
88
89 public void send(final JingleFile file, final OnFileTransmitted callback) {
90 new Thread(new Runnable() {
91
92 @Override
93 public void run() {
94 InputStream fileInputStream = null;
95 try {
96 MessageDigest digest = MessageDigest.getInstance("SHA-1");
97 digest.reset();
98 fileInputStream = getInputStream(file);
99 int count;
100 long txbytes = 0;
101 byte[] buffer = new byte[8192];
102 while ((count = fileInputStream.read(buffer)) != -1) {
103 txbytes += count;
104 outputStream.write(buffer, 0, count);
105 digest.update(buffer, 0, count);
106 Log.d("xmppService","tx bytes: "+txbytes);
107 }
108 outputStream.flush();
109 file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
110 //outputStream.close();
111 if (callback!=null) {
112 callback.onFileTransmitted(file);
113 }
114 } catch (FileNotFoundException e) {
115 // TODO Auto-generated catch block
116 e.printStackTrace();
117 } catch (IOException e) {
118 Log.d("xmppService","io exception: "+e.getMessage());
119 } catch (NoSuchAlgorithmException e) {
120 // TODO Auto-generated catch block
121 e.printStackTrace();
122 } finally {
123 try {
124 if (fileInputStream != null) {
125 fileInputStream.close();
126 }
127 } catch (IOException e) {
128 // TODO Auto-generated catch block
129 e.printStackTrace();
130 }
131 }
132 }
133 }).start();
134
135 }
136
137 public void receive(final JingleFile file, final OnFileTransmitted 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 file.getParentFile().mkdirs();
147 file.createNewFile();
148 OutputStream fileOutputStream = getOutputStream(file);
149 long remainingSize = file.getExpectedSize();
150 byte[] buffer = new byte[8192];
151 int count = buffer.length;
152 //while(remainingSize > 0) {
153 while((count = inputStream.read(buffer)) > 0) {
154 Log.d("xmppService","remaining size: "+remainingSize+" reading "+count+" bytes");
155 count = inputStream.read(buffer);
156 if (count!=-1) {
157 fileOutputStream.write(buffer, 0, count);
158 digest.update(buffer, 0, count);
159 }
160 remainingSize-=count;
161 }
162 fileOutputStream.flush();
163 fileOutputStream.close();
164 file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
165 callback.onFileTransmitted(file);
166 } catch (FileNotFoundException e) {
167 Log.d("xmppService","file not found exception");
168 } catch (IOException e) {
169 Log.d("xmppService","io exception: "+e.getMessage());
170 } catch (NoSuchAlgorithmException e) {
171 Log.d("xmppService","no such algo"+e.getMessage());
172 }
173 }
174 }).start();
175 }
176
177 public boolean isProxy() {
178 return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
179 }
180
181 public boolean needsActivation() {
182 return (this.isProxy() && !this.activated);
183 }
184
185 public void disconnect() {
186 if (this.socket!=null) {
187 try {
188 this.socket.close();
189 } catch (IOException e) {
190
191 }
192 }
193 }
194
195 public boolean isEstablished() {
196 return this.isEstablished;
197 }
198
199 public JingleCandidate getCandidate() {
200 return this.candidate;
201 }
202
203 public void setActivated(boolean activated) {
204 this.activated = activated;
205 }
206}