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