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					socket.setSoTimeout(30000);
141					file.getParentFile().mkdirs();
142					file.createNewFile();
143					OutputStream fileOutputStream = getOutputStream(file);
144					if (fileOutputStream==null) {
145						callback.onFileTransferAborted();
146						return;
147					}
148					long remainingSize = file.getExpectedSize();
149					byte[] buffer = new byte[8192];
150					int count = buffer.length;
151					while(remainingSize > 0) {
152						count = inputStream.read(buffer);
153						if (count==-1) {
154							callback.onFileTransferAborted();
155							return;
156						} else {
157							fileOutputStream.write(buffer, 0, count);
158							digest.update(buffer, 0, count);
159							remainingSize-=count;
160						}
161					}
162					fileOutputStream.flush();
163					fileOutputStream.close();
164					file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
165					callback.onFileTransmitted(file);
166				} catch (FileNotFoundException e) {
167					callback.onFileTransferAborted();
168				} catch (IOException e) {
169					callback.onFileTransferAborted();
170				} catch (NoSuchAlgorithmException e) {
171					callback.onFileTransferAborted();
172				}
173			}
174		}).start();
175	}
176
177	public boolean isProxy() {
178		return this.candidate.getType() == JingleCandidate.TYPE_PROXY;
179	}
180	
181	public boolean needsActivation() {
182		return (this.isProxy() && !this.activated);
183	}
184
185	public void disconnect() {
186		if (this.socket!=null) {
187			try {
188				this.socket.close();
189			} catch (IOException e) {
190				
191			}
192		}
193	}
194	
195	public boolean isEstablished() {
196		return this.isEstablished;
197	}
198	
199	public JingleCandidate getCandidate() {
200		return this.candidate;
201	}
202
203	public void setActivated(boolean activated) {
204		this.activated = activated;
205	}
206}