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