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