JingleInbandTransport.java

  1package eu.siacs.conversations.xmpp.jingle;
  2
  3import java.io.FileInputStream;
  4import java.io.FileNotFoundException;
  5import java.io.FileOutputStream;
  6import java.io.IOException;
  7import java.security.MessageDigest;
  8import java.security.NoSuchAlgorithmException;
  9
 10import android.util.Base64;
 11import eu.siacs.conversations.entities.Account;
 12import eu.siacs.conversations.utils.CryptoHelper;
 13import eu.siacs.conversations.xml.Element;
 14import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 15import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 16
 17public class JingleInbandTransport extends JingleTransport {
 18
 19	private Account account;
 20	private String counterpart;
 21	private int blockSize;
 22	private int bufferSize;
 23	private int seq = 0;
 24	private String sessionId;
 25
 26	private boolean established = false;
 27
 28	private JingleFile file;
 29	
 30	private FileInputStream fileInputStream = null;
 31	private FileOutputStream fileOutputStream;
 32	private long remainingSize;
 33	private MessageDigest digest;
 34
 35	private OnFileTransmitted onFileTransmitted;
 36	
 37	private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
 38		@Override
 39		public void onIqPacketReceived(Account account, IqPacket packet) {
 40			if (packet.getType() == IqPacket.TYPE_RESULT) {
 41				sendNextBlock();
 42			}
 43		}
 44	};
 45
 46	public JingleInbandTransport(Account account, String counterpart,
 47			String sid, int blocksize) {
 48		this.account = account;
 49		this.counterpart = counterpart;
 50		this.blockSize = blocksize;
 51		this.bufferSize = blocksize / 4;
 52		this.sessionId = sid;
 53	}
 54
 55	public void connect(final OnTransportConnected callback) {
 56		IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
 57		iq.setTo(this.counterpart);
 58		Element open = iq.addChild("open", "http://jabber.org/protocol/ibb");
 59		open.setAttribute("sid", this.sessionId);
 60		open.setAttribute("stanza", "iq");
 61		open.setAttribute("block-size", "" + this.blockSize);
 62
 63		this.account.getXmppConnection().sendIqPacket(iq,
 64				new OnIqPacketReceived() {
 65
 66					@Override
 67					public void onIqPacketReceived(Account account,
 68							IqPacket packet) {
 69						if (packet.getType() == IqPacket.TYPE_ERROR) {
 70							callback.failed();
 71						} else {
 72							callback.established();
 73						}
 74					}
 75				});
 76	}
 77
 78	@Override
 79	public void receive(JingleFile file, OnFileTransmitted callback) {
 80		this.onFileTransmitted = callback;
 81		this.file = file;
 82		try {
 83			this.digest = MessageDigest.getInstance("SHA-1");
 84			digest.reset();
 85			file.getParentFile().mkdirs();
 86			file.createNewFile();
 87			this.fileOutputStream = new FileOutputStream(file);
 88			this.remainingSize = file.getExpectedSize();
 89		} catch (NoSuchAlgorithmException e) {
 90			e.printStackTrace();
 91		} catch (IOException e) {
 92			e.printStackTrace();
 93		}
 94	}
 95
 96	@Override
 97	public void send(JingleFile file, OnFileTransmitted callback) {
 98		this.onFileTransmitted = callback;
 99		this.file = file;
100		try {
101			this.digest = MessageDigest.getInstance("SHA-1");
102			this.digest.reset();
103			fileInputStream = new FileInputStream(file);
104			this.sendNextBlock();
105		} catch (FileNotFoundException e) {
106			e.printStackTrace();
107		} catch (NoSuchAlgorithmException e) {
108			e.printStackTrace();
109		}
110	}
111
112	private void sendNextBlock() {
113		byte[] buffer = new byte[this.bufferSize];
114		try {
115			int count = fileInputStream.read(buffer);
116			if (count==-1) {
117				file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
118				fileInputStream.close();
119				this.onFileTransmitted.onFileTransmitted(file);
120			} else {
121				this.digest.update(buffer);
122				String base64 = Base64.encodeToString(buffer, Base64.DEFAULT);
123				IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
124				iq.setTo(this.counterpart);
125				Element data = iq
126						.addChild("data", "http://jabber.org/protocol/ibb");
127				data.setAttribute("seq", "" + this.seq);
128				data.setAttribute("block-size", "" + this.blockSize);
129				data.setAttribute("sid", this.sessionId);
130				data.setContent(base64);
131				this.account.getXmppConnection().sendIqPacket(iq,
132						this.onAckReceived);
133				this.seq++;
134			}
135		} catch (IOException e) {
136			// TODO Auto-generated catch block
137			e.printStackTrace();
138		}
139	}
140
141	private void receiveNextBlock(String data) {
142		try {
143			byte[] buffer = Base64.decode(data, Base64.DEFAULT);
144			this.remainingSize -= buffer.length;
145
146			this.fileOutputStream.write(buffer);
147
148			this.digest.update(buffer);
149			if (this.remainingSize <= 0) {
150				file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest()));
151				fileOutputStream.flush();
152				fileOutputStream.close();
153				this.onFileTransmitted.onFileTransmitted(file);
154			}
155		} catch (IOException e) {
156			e.printStackTrace();
157		}
158	}
159
160	public void deliverPayload(IqPacket packet, Element payload) {
161		if (payload.getName().equals("open")) {
162			if (!established) {
163				established = true;
164				this.account.getXmppConnection().sendIqPacket(
165						packet.generateRespone(IqPacket.TYPE_RESULT), null);
166			} else {
167				this.account.getXmppConnection().sendIqPacket(
168						packet.generateRespone(IqPacket.TYPE_ERROR), null);
169			}
170		} else if (payload.getName().equals("data")) {
171			this.receiveNextBlock(payload.getContent());
172			this.account.getXmppConnection().sendIqPacket(
173					packet.generateRespone(IqPacket.TYPE_RESULT), null);
174		} else {
175			//TODO some sort of exception
176		}
177	}
178}