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;
16import eu.siacs.conversations.xml.Element;
17
18import android.util.Log;
19import android.widget.Button;
20
21public class JingleSocks5Transport extends JingleTransport {
22 private JingleCandidate candidate;
23 private String destination;
24 private OutputStream outputStream;
25 private InputStream inputStream;
26 private boolean isEstablished = false;
27 private boolean activated = false;
28 protected Socket socket;
29
30 public JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) {
31 this.candidate = candidate;
32 try {
33 MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
34 StringBuilder destBuilder = new StringBuilder();
35 destBuilder.append(jingleConnection.getSessionId());
36 if (candidate.isOurs()) {
37 destBuilder.append(jingleConnection.getAccountJid());
38 destBuilder.append(jingleConnection.getCounterPart());
39 } else {
40 destBuilder.append(jingleConnection.getCounterPart());
41 destBuilder.append(jingleConnection.getAccountJid());
42 }
43 mDigest.reset();
44 this.destination = CryptoHelper.bytesToHex(mDigest
45 .digest(destBuilder.toString().getBytes()));
46 } catch (NoSuchAlgorithmException e) {
47
48 }
49 }
50
51 public void connect(final OnTransportConnected callback) {
52 new Thread(new Runnable() {
53
54 @Override
55 public void run() {
56 try {
57 socket = new Socket(candidate.getHost(), candidate.getPort());
58 inputStream = socket.getInputStream();
59 outputStream = socket.getOutputStream();
60 byte[] login = { 0x05, 0x01, 0x00 };
61 byte[] expectedReply = { 0x05, 0x00 };
62 byte[] reply = new byte[2];
63 outputStream.write(login);
64 inputStream.read(reply);
65 if (Arrays.equals(reply, expectedReply)) {
66 String connect = "" + '\u0005' + '\u0001' + '\u0000' + '\u0003'
67 + '\u0028' + destination + '\u0000' + '\u0000';
68 outputStream.write(connect.getBytes());
69 byte[] result = new byte[2];
70 inputStream.read(result);
71 int status = result[1];
72 if (status == 0) {
73 isEstablished = true;
74 callback.established();
75 } else {
76 callback.failed();
77 }
78 } else {
79 socket.close();
80 callback.failed();
81 }
82 } catch (UnknownHostException e) {
83 callback.failed();
84 } catch (IOException e) {
85 callback.failed();
86 }
87 }
88 }).start();
89
90 }
91
92 public void send(final JingleFile file, final OnFileTransmitted callback) {
93 new Thread(new Runnable() {
94
95 @Override
96 public void run() {
97 FileInputStream fileInputStream = null;
98 try {
99 MessageDigest digest = MessageDigest.getInstance("SHA-1");
100 digest.reset();
101 fileInputStream = new FileInputStream(file);
102 int count;
103 byte[] buffer = new byte[8192];
104 while ((count = fileInputStream.read(buffer)) > 0) {
105 outputStream.write(buffer, 0, count);
106 digest.update(buffer, 0, count);
107 }
108 outputStream.flush();
109 file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
110 if (callback!=null) {
111 callback.onFileTransmitted(file);
112 }
113 } catch (FileNotFoundException e) {
114 // TODO Auto-generated catch block
115 e.printStackTrace();
116 } catch (IOException e) {
117 // TODO Auto-generated catch block
118 e.printStackTrace();
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 FileOutputStream fileOutputStream = new FileOutputStream(file);
149 long remainingSize = file.getExpectedSize();
150 byte[] buffer = new byte[8192];
151 int count = buffer.length;
152 while(remainingSize > 0) {
153 Log.d("xmppService","remaning size:"+remainingSize);
154 if (remainingSize<=count) {
155 count = (int) remainingSize;
156 }
157 count = inputStream.read(buffer, 0, count);
158 if (count==-1) {
159 Log.d("xmppService","end of stream");
160 } else {
161 fileOutputStream.write(buffer, 0, count);
162 digest.update(buffer, 0, count);
163 remainingSize-=count;
164 }
165 }
166 fileOutputStream.flush();
167 fileOutputStream.close();
168 file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
169 Log.d("xmppService","transmitted filename was: "+file.getAbsolutePath());
170 callback.onFileTransmitted(file);
171 } catch (FileNotFoundException e) {
172 // TODO Auto-generated catch block
173 e.printStackTrace();
174 } catch (IOException e) {
175 // TODO Auto-generated catch block
176 e.printStackTrace();
177 } catch (NoSuchAlgorithmException e) {
178 // TODO Auto-generated catch block
179 e.printStackTrace();
180 }
181 }
182 }).start();
183 }
184
185 public boolean isProxy() {
186 return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
187 }
188
189 public boolean needsActivation() {
190 return (this.isProxy() && !this.activated);
191 }
192
193 public void disconnect() {
194 if (this.socket!=null) {
195 try {
196 this.socket.close();
197 Log.d("xmppService","cloesd socket with "+candidate.getHost()+":"+candidate.getPort());
198 } catch (IOException e) {
199 Log.d("xmppService","error closing socket with "+candidate.getHost()+":"+candidate.getPort());
200 }
201 }
202 }
203
204 public boolean isEstablished() {
205 return this.isEstablished;
206 }
207
208 public JingleCandidate getCandidate() {
209 return this.candidate;
210 }
211
212 public void setActivated(boolean activated) {
213 this.activated = activated;
214 }
215}