JingleSocks5Transport.java

  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 JingleConnection connection;
 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,
 27			JingleCandidate candidate) {
 28		this.candidate = candidate;
 29		this.connection = jingleConnection;
 30		try {
 31			MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
 32			StringBuilder destBuilder = new StringBuilder();
 33			destBuilder.append(jingleConnection.getSessionId());
 34			if (candidate.isOurs()) {
 35				destBuilder.append(jingleConnection.getAccount().getJid());
 36				destBuilder.append(jingleConnection.getCounterPart());
 37			} else {
 38				destBuilder.append(jingleConnection.getCounterPart());
 39				destBuilder.append(jingleConnection.getAccount().getJid());
 40			}
 41			mDigest.reset();
 42			this.destination = CryptoHelper.bytesToHex(mDigest
 43					.digest(destBuilder.toString().getBytes()));
 44		} catch (NoSuchAlgorithmException e) {
 45
 46		}
 47	}
 48
 49	public void connect(final OnTransportConnected callback) {
 50		new Thread(new Runnable() {
 51
 52			@Override
 53			public void run() {
 54				try {
 55					socket = new Socket(candidate.getHost(),
 56							candidate.getPort());
 57					inputStream = socket.getInputStream();
 58					outputStream = socket.getOutputStream();
 59					byte[] login = { 0x05, 0x01, 0x00 };
 60					byte[] expectedReply = { 0x05, 0x00 };
 61					byte[] reply = new byte[2];
 62					outputStream.write(login);
 63					inputStream.read(reply);
 64					final String connect = Character.toString('\u0005')
 65							+ '\u0001' + '\u0000' + '\u0003' + '\u0028'
 66							+ destination + '\u0000' + '\u0000';
 67					if (Arrays.equals(reply, expectedReply)) {
 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 DownloadableFile file,
 93			final OnFileTransmissionStatusChanged callback) {
 94		new Thread(new Runnable() {
 95
 96			@Override
 97			public void run() {
 98				InputStream fileInputStream = null;
 99				try {
100					MessageDigest digest = MessageDigest.getInstance("SHA-1");
101					digest.reset();
102					fileInputStream = file.createInputStream();
103					if (fileInputStream == null) {
104						callback.onFileTransferAborted();
105						return;
106					}
107					long size = file.getSize();
108					long transmitted = 0;
109					int count;
110					byte[] buffer = new byte[8192];
111					while ((count = fileInputStream.read(buffer)) > 0) {
112						outputStream.write(buffer, 0, count);
113						digest.update(buffer, 0, count);
114						transmitted += count;
115						connection.updateProgress((int) ((((double) transmitted) / size) * 100));
116					}
117					outputStream.flush();
118					file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
119					if (callback != null) {
120						callback.onFileTransmitted(file);
121					}
122				} catch (FileNotFoundException e) {
123					callback.onFileTransferAborted();
124				} catch (IOException e) {
125					callback.onFileTransferAborted();
126				} catch (NoSuchAlgorithmException e) {
127					callback.onFileTransferAborted();
128				} finally {
129					try {
130						if (fileInputStream != null) {
131							fileInputStream.close();
132						}
133					} catch (IOException e) {
134						callback.onFileTransferAborted();
135					}
136				}
137			}
138		}).start();
139
140	}
141
142	public void receive(final DownloadableFile file,
143			final OnFileTransmissionStatusChanged callback) {
144		new Thread(new Runnable() {
145
146			@Override
147			public void run() {
148				try {
149					MessageDigest digest = MessageDigest.getInstance("SHA-1");
150					digest.reset();
151					inputStream.skip(45);
152					socket.setSoTimeout(30000);
153					file.getParentFile().mkdirs();
154					file.createNewFile();
155					OutputStream fileOutputStream = file.createOutputStream();
156					if (fileOutputStream == null) {
157						callback.onFileTransferAborted();
158						return;
159					}
160					double size = file.getExpectedSize();
161					long remainingSize = file.getExpectedSize();
162					byte[] buffer = new byte[8192];
163					int count = buffer.length;
164					while (remainingSize > 0) {
165						count = inputStream.read(buffer);
166						if (count == -1) {
167							callback.onFileTransferAborted();
168							return;
169						} else {
170							fileOutputStream.write(buffer, 0, count);
171							digest.update(buffer, 0, count);
172							remainingSize -= count;
173						}
174						connection.updateProgress((int) (((size - remainingSize) / size) * 100));
175					}
176					fileOutputStream.flush();
177					fileOutputStream.close();
178					file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
179					callback.onFileTransmitted(file);
180				} catch (FileNotFoundException e) {
181					callback.onFileTransferAborted();
182				} catch (IOException e) {
183					callback.onFileTransferAborted();
184				} catch (NoSuchAlgorithmException e) {
185					callback.onFileTransferAborted();
186				}
187			}
188		}).start();
189	}
190
191	public boolean isProxy() {
192		return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
193	}
194
195	public boolean needsActivation() {
196		return (this.isProxy() && !this.activated);
197	}
198
199	public void disconnect() {
200		if (this.outputStream != null) {
201			try {
202				this.outputStream.close();
203			} catch (IOException e) {
204
205			}
206		}
207		if (this.inputStream != null) {
208			try {
209				this.inputStream.close();
210			} catch (IOException e) {
211
212			}
213		}
214		if (this.socket != null) {
215			try {
216				this.socket.close();
217			} catch (IOException e) {
218
219			}
220		}
221	}
222
223	public boolean isEstablished() {
224		return this.isEstablished;
225	}
226
227	public JingleCandidate getCandidate() {
228		return this.candidate;
229	}
230
231	public void setActivated(boolean activated) {
232		this.activated = activated;
233	}
234}