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.utils.CryptoHelper;
 14
 15public class JingleSocks5Transport extends JingleTransport {
 16	private JingleCandidate candidate;
 17	private String destination;
 18	private OutputStream outputStream;
 19	private InputStream inputStream;
 20	private boolean isEstablished = false;
 21	private boolean activated = false;
 22	protected Socket socket;
 23
 24	public JingleSocks5Transport(JingleConnection jingleConnection,
 25			JingleCandidate candidate) {
 26		this.candidate = candidate;
 27		try {
 28			MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
 29			StringBuilder destBuilder = new StringBuilder();
 30			destBuilder.append(jingleConnection.getSessionId());
 31			if (candidate.isOurs()) {
 32				destBuilder.append(jingleConnection.getAccountJid());
 33				destBuilder.append(jingleConnection.getCounterPart());
 34			} else {
 35				destBuilder.append(jingleConnection.getCounterPart());
 36				destBuilder.append(jingleConnection.getAccountJid());
 37			}
 38			mDigest.reset();
 39			this.destination = CryptoHelper.bytesToHex(mDigest
 40					.digest(destBuilder.toString().getBytes()));
 41		} catch (NoSuchAlgorithmException e) {
 42
 43		}
 44	}
 45
 46	public void connect(final OnTransportConnected callback) {
 47		new Thread(new Runnable() {
 48
 49			@Override
 50			public void run() {
 51				try {
 52					socket = new Socket(candidate.getHost(),
 53							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					final String connect = Character.toString('\u0005')
 62							+ '\u0001' + '\u0000' + '\u0003' + '\u0028'
 63							+ destination + '\u0000' + '\u0000';
 64					if (Arrays.equals(reply, expectedReply)) {
 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,
 90			final OnFileTransmissionStatusChanged callback) {
 91		new Thread(new Runnable() {
 92
 93			@Override
 94			public void run() {
 95				InputStream fileInputStream = null;
 96				try {
 97					MessageDigest digest = MessageDigest.getInstance("SHA-1");
 98					digest.reset();
 99					fileInputStream = getInputStream(file);
100					if (fileInputStream == null) {
101						callback.onFileTransferAborted();
102						return;
103					}
104					int count;
105					byte[] buffer = new byte[8192];
106					while ((count = fileInputStream.read(buffer)) > 0) {
107						outputStream.write(buffer, 0, count);
108						digest.update(buffer, 0, count);
109					}
110					outputStream.flush();
111					file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
112					if (callback != null) {
113						callback.onFileTransmitted(file);
114					}
115				} catch (FileNotFoundException e) {
116					callback.onFileTransferAborted();
117				} catch (IOException e) {
118					callback.onFileTransferAborted();
119				} catch (NoSuchAlgorithmException e) {
120					callback.onFileTransferAborted();
121				} finally {
122					try {
123						if (fileInputStream != null) {
124							fileInputStream.close();
125						}
126					} catch (IOException e) {
127						callback.onFileTransferAborted();
128					}
129				}
130			}
131		}).start();
132
133	}
134
135	public void receive(final JingleFile file,
136			final OnFileTransmissionStatusChanged callback) {
137		new Thread(new Runnable() {
138
139			@Override
140			public void run() {
141				try {
142					MessageDigest digest = MessageDigest.getInstance("SHA-1");
143					digest.reset();
144					inputStream.skip(45);
145					socket.setSoTimeout(30000);
146					file.getParentFile().mkdirs();
147					file.createNewFile();
148					OutputStream fileOutputStream = getOutputStream(file);
149					if (fileOutputStream == null) {
150						callback.onFileTransferAborted();
151						return;
152					}
153					long remainingSize = file.getExpectedSize();
154					byte[] buffer = new byte[8192];
155					int count = buffer.length;
156					while (remainingSize > 0) {
157						count = inputStream.read(buffer);
158						if (count == -1) {
159							callback.onFileTransferAborted();
160							return;
161						} else {
162							fileOutputStream.write(buffer, 0, count);
163							digest.update(buffer, 0, count);
164							remainingSize -= count;
165						}
166					}
167					fileOutputStream.flush();
168					fileOutputStream.close();
169					file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
170					callback.onFileTransmitted(file);
171				} catch (FileNotFoundException e) {
172					callback.onFileTransferAborted();
173				} catch (IOException e) {
174					callback.onFileTransferAborted();
175				} catch (NoSuchAlgorithmException e) {
176					callback.onFileTransferAborted();
177				}
178			}
179		}).start();
180	}
181
182	public boolean isProxy() {
183		return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
184	}
185
186	public boolean needsActivation() {
187		return (this.isProxy() && !this.activated);
188	}
189
190	public void disconnect() {
191		if (this.socket != null) {
192			try {
193				this.socket.close();
194			} catch (IOException e) {
195
196			}
197		}
198	}
199
200	public boolean isEstablished() {
201		return this.isEstablished;
202	}
203
204	public JingleCandidate getCandidate() {
205		return this.candidate;
206	}
207
208	public void setActivated(boolean activated) {
209		this.activated = activated;
210	}
211}