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.SocketTimeoutException;
  9import java.net.UnknownHostException;
 10import java.security.MessageDigest;
 11import java.security.NoSuchAlgorithmException;
 12import java.util.Arrays;
 13
 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, 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(), candidate.getPort());
 53					inputStream = socket.getInputStream();
 54					outputStream = socket.getOutputStream();
 55					byte[] login = { 0x05, 0x01, 0x00 };
 56					byte[] expectedReply = { 0x05, 0x00 };
 57					byte[] reply = new byte[2];
 58					outputStream.write(login);
 59					inputStream.read(reply);
 60					if (Arrays.equals(reply, expectedReply)) {
 61						String connect = "" + '\u0005' + '\u0001' + '\u0000' + '\u0003'
 62								+ '\u0028' + destination + '\u0000' + '\u0000';
 63						outputStream.write(connect.getBytes());
 64						byte[] result = new byte[2];
 65						inputStream.read(result);
 66						int status = result[1];
 67						if (status == 0) {
 68							isEstablished = true;
 69							callback.established();
 70						} else {
 71							callback.failed();
 72						}
 73					} else {
 74						socket.close();
 75						callback.failed();
 76					}
 77				} catch (UnknownHostException e) {
 78					callback.failed();
 79				} catch (IOException e) {
 80					callback.failed();
 81				}
 82			}
 83		}).start();
 84		
 85	}
 86
 87	public void send(final JingleFile file, final OnFileTransmissionStatusChanged callback) {
 88		new Thread(new Runnable() {
 89			
 90			@Override
 91			public void run() {
 92				InputStream fileInputStream = null;
 93				try {
 94					MessageDigest digest = MessageDigest.getInstance("SHA-1");
 95					digest.reset();
 96					fileInputStream = getInputStream(file);
 97					if (fileInputStream==null) {
 98						callback.onFileTransferAborted();
 99						return;
100					}
101					int count;
102					byte[] buffer = new byte[8192];
103					while ((count = fileInputStream.read(buffer)) > 0) {
104						outputStream.write(buffer, 0, count);
105						digest.update(buffer, 0, count);
106					}
107					outputStream.flush();
108					file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
109					if (callback!=null) {
110						callback.onFileTransmitted(file);
111					}
112				} catch (FileNotFoundException e) {
113					callback.onFileTransferAborted();
114				} catch (IOException e) {
115					callback.onFileTransferAborted();
116				} catch (NoSuchAlgorithmException e) {
117					callback.onFileTransferAborted();
118				} finally {
119					try {
120						if (fileInputStream != null) {
121							fileInputStream.close();
122						}
123					} catch (IOException e) {
124						callback.onFileTransferAborted();
125					}
126				}
127			}
128		}).start();
129		
130	}
131	
132	public void receive(final JingleFile file, final OnFileTransmissionStatusChanged callback) {
133		new Thread(new Runnable() {
134			
135			@Override
136			public void run() {
137				try {
138					MessageDigest digest = MessageDigest.getInstance("SHA-1");
139					digest.reset();
140					inputStream.skip(45);
141					socket.setSoTimeout(30000);
142					file.getParentFile().mkdirs();
143					file.createNewFile();
144					OutputStream fileOutputStream = getOutputStream(file);
145					if (fileOutputStream==null) {
146						callback.onFileTransferAborted();
147						return;
148					}
149					long remainingSize = file.getExpectedSize();
150					byte[] buffer = new byte[8192];
151					int count = buffer.length;
152					while(remainingSize > 0) {
153						count = inputStream.read(buffer);
154						if (count==-1) {
155							callback.onFileTransferAborted();
156							return;
157						} else {
158							fileOutputStream.write(buffer, 0, count);
159							digest.update(buffer, 0, count);
160							remainingSize-=count;
161						}
162					}
163					fileOutputStream.flush();
164					fileOutputStream.close();
165					file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
166					callback.onFileTransmitted(file);
167				} catch (FileNotFoundException e) {
168					callback.onFileTransferAborted();
169				} catch (IOException e) {
170					callback.onFileTransferAborted();
171				} catch (NoSuchAlgorithmException e) {
172					callback.onFileTransferAborted();
173				}
174			}
175		}).start();
176	}
177
178	public boolean isProxy() {
179		return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
180	}
181	
182	public boolean needsActivation() {
183		return (this.isProxy() && !this.activated);
184	}
185
186	public void disconnect() {
187		if (this.socket!=null) {
188			try {
189				this.socket.close();
190			} catch (IOException e) {
191				
192			}
193		}
194	}
195	
196	public boolean isEstablished() {
197		return this.isEstablished;
198	}
199	
200	public JingleCandidate getCandidate() {
201		return this.candidate;
202	}
203
204	public void setActivated(boolean activated) {
205		this.activated = activated;
206	}
207}