HttpUploadConnection.java

  1package eu.siacs.conversations.http;
  2
  3import android.app.PendingIntent;
  4import android.util.Log;
  5
  6import java.io.IOException;
  7import java.io.InputStream;
  8import java.io.OutputStream;
  9import java.net.HttpURLConnection;
 10import java.net.MalformedURLException;
 11import java.net.URL;
 12
 13import javax.net.ssl.HttpsURLConnection;
 14
 15import eu.siacs.conversations.Config;
 16import eu.siacs.conversations.entities.Account;
 17import eu.siacs.conversations.entities.Transferable;
 18import eu.siacs.conversations.entities.DownloadableFile;
 19import eu.siacs.conversations.entities.Message;
 20import eu.siacs.conversations.persistance.FileBackend;
 21import eu.siacs.conversations.services.XmppConnectionService;
 22import eu.siacs.conversations.ui.UiCallback;
 23import eu.siacs.conversations.utils.CryptoHelper;
 24import eu.siacs.conversations.utils.Xmlns;
 25import eu.siacs.conversations.xml.Element;
 26import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 27import eu.siacs.conversations.xmpp.jid.Jid;
 28import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 29
 30public class HttpUploadConnection implements Transferable {
 31
 32	private HttpConnectionManager mHttpConnectionManager;
 33	private XmppConnectionService mXmppConnectionService;
 34
 35	private boolean canceled = false;
 36	private Account account;
 37	private DownloadableFile file;
 38	private Message message;
 39	private URL mGetUrl;
 40	private URL mPutUrl;
 41
 42	private byte[] key = null;
 43
 44	private long transmitted = 0;
 45	private long expected = 1;
 46
 47	public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
 48		this.mHttpConnectionManager = httpConnectionManager;
 49		this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService();
 50	}
 51
 52	@Override
 53	public boolean start() {
 54		return false;
 55	}
 56
 57	@Override
 58	public int getStatus() {
 59		return STATUS_UPLOADING;
 60	}
 61
 62	@Override
 63	public long getFileSize() {
 64		return this.file.getExpectedSize();
 65	}
 66
 67	@Override
 68	public int getProgress() {
 69		return (int) ((((double) transmitted) / expected) * 100);
 70	}
 71
 72	@Override
 73	public void cancel() {
 74		this.canceled = true;
 75	}
 76
 77	private void fail() {
 78		mHttpConnectionManager.finishUploadConnection(this);
 79		message.setTransferable(null);
 80		mXmppConnectionService.markMessage(message,Message.STATUS_SEND_FAILED);
 81	}
 82
 83	public void init(Message message) {
 84		this.message = message;
 85		message.setTransferable(this);
 86		mXmppConnectionService.markMessage(message,Message.STATUS_UNSEND);
 87		this.account = message.getConversation().getAccount();
 88		this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
 89		this.file.setExpectedSize(this.file.getSize());
 90
 91		if (Config.ENCRYPT_ON_HTTP_UPLOADED
 92				|| message.getEncryption() == Message.ENCRYPTION_AXOLOTL
 93				|| message.getEncryption() == Message.ENCRYPTION_OTR) {
 94			this.key = new byte[48];
 95			mXmppConnectionService.getRNG().nextBytes(this.key);
 96			this.file.setKey(this.key);
 97		}
 98
 99		Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD);
100		IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host,file);
101		mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() {
102			@Override
103			public void onIqPacketReceived(Account account, IqPacket packet) {
104				if (packet.getType() == IqPacket.TYPE.RESULT) {
105					Element slot = packet.findChild("slot",Xmlns.HTTP_UPLOAD);
106					if (slot != null) {
107						try {
108							mGetUrl = new URL(slot.findChildContent("get"));
109							mPutUrl = new URL(slot.findChildContent("put"));
110							if (!canceled) {
111								new Thread(new FileUploader()).start();
112							}
113						} catch (MalformedURLException e) {
114							fail();
115						}
116					} else {
117						fail();
118					}
119				} else {
120					fail();
121				}
122			}
123		});
124	}
125
126	private class FileUploader implements Runnable {
127
128		@Override
129		public void run() {
130			this.upload();
131		}
132
133		private void upload() {
134			OutputStream os = null;
135			InputStream is = null;
136			HttpURLConnection connection = null;
137			try {
138				Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
139				connection = (HttpURLConnection) mPutUrl.openConnection();
140				if (connection instanceof HttpsURLConnection) {
141					mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true);
142				}
143				connection.setRequestMethod("PUT");
144				connection.setFixedLengthStreamingMode((int) file.getExpectedSize());
145				connection.setDoOutput(true);
146				connection.connect();
147				os = connection.getOutputStream();
148				is = file.createInputStream();
149				transmitted = 0;
150				expected = file.getExpectedSize();
151				int count = -1;
152				byte[] buffer = new byte[4096];
153				while (((count = is.read(buffer)) != -1) && !canceled) {
154					transmitted += count;
155					os.write(buffer, 0, count);
156					mXmppConnectionService.updateConversationUi();
157				}
158				os.flush();
159				os.close();
160				is.close();
161				int code = connection.getResponseCode();
162				if (code == 200 || code == 201) {
163					Log.d(Config.LOGTAG, "finished uploading file");
164					Message.FileParams params = message.getFileParams();
165					if (key != null) {
166						mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key));
167					}
168					mXmppConnectionService.getFileBackend().updateFileParams(message, mGetUrl);
169					message.setTransferable(null);
170					message.setCounterpart(message.getConversation().getJid().toBareJid());
171					if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
172						mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() {
173							@Override
174							public void success(Message message) {
175								mXmppConnectionService.resendMessage(message);
176							}
177
178							@Override
179							public void error(int errorCode, Message object) {
180								fail();
181							}
182
183							@Override
184							public void userInputRequried(PendingIntent pi, Message object) {
185								fail();
186							}
187						});
188					} else {
189						mXmppConnectionService.resendMessage(message);
190					}
191				} else {
192					fail();
193				}
194			} catch (IOException e) {
195				Log.d(Config.LOGTAG, e.getMessage());
196				fail();
197			} finally {
198				FileBackend.close(is);
199				FileBackend.close(os);
200				if (connection != null) {
201					connection.disconnect();
202				}
203			}
204		}
205	}
206}