HttpUploadConnection.java

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