aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java')
-rw-r--r--src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java236
1 files changed, 236 insertions, 0 deletions
diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
new file mode 100644
index 00000000..e236cdc0
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java
@@ -0,0 +1,236 @@
+package eu.siacs.conversations.http;
+
+import android.app.PendingIntent;
+import android.os.PowerManager;
+import android.util.Pair;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Scanner;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import de.thedevstack.android.logcat.Logging;
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.utils.MessageUtil;
+import de.thedevstack.conversationsplus.utils.StreamUtil;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.DownloadableFile;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.entities.Transferable;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.services.AbstractConnectionManager;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.UiCallback;
+import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.utils.Xmlns;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.jid.Jid;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+public class HttpUploadConnection implements Transferable {
+
+ private HttpConnectionManager mHttpConnectionManager;
+ private XmppConnectionService mXmppConnectionService;
+
+ private boolean canceled = false;
+ private boolean delayed = false;
+ private Account account;
+ private DownloadableFile file;
+ private Message message;
+ private String mime;
+ private URL mGetUrl;
+ private URL mPutUrl;
+
+ private byte[] key = null;
+
+ private long transmitted = 0;
+
+ private InputStream mFileInputStream;
+
+ public HttpUploadConnection(HttpConnectionManager httpConnectionManager) {
+ this.mHttpConnectionManager = httpConnectionManager;
+ this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService();
+ }
+
+ @Override
+ public boolean start() {
+ return false;
+ }
+
+ @Override
+ public int getStatus() {
+ return STATUS_UPLOADING;
+ }
+
+ @Override
+ public long getFileSize() {
+ return file == null ? 0 : file.getExpectedSize();
+ }
+
+ @Override
+ public int getProgress() {
+ if (file == null) {
+ return 0;
+ }
+ return (int) ((((double) transmitted) / file.getExpectedSize()) * 100);
+ }
+
+ @Override
+ public void cancel() {
+ this.canceled = true;
+ }
+
+ private void fail() {
+ mHttpConnectionManager.finishUploadConnection(this);
+ message.setTransferable(null);
+ mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED);
+ StreamUtil.close(mFileInputStream);
+ }
+
+ public void init(Message message, boolean delay) {
+ this.message = message;
+ this.account = message.getConversation().getAccount();
+ this.file = FileBackend.getFile(message, false);
+ this.mime = this.file.getMimeType();
+ this.delayed = delay;
+ if (Config.ENCRYPT_ON_HTTP_UPLOADED
+ || message.getEncryption() == Message.ENCRYPTION_AXOLOTL
+ || message.getEncryption() == Message.ENCRYPTION_OTR) {
+ this.key = new byte[48];
+ mXmppConnectionService.getRNG().nextBytes(this.key);
+ this.file.setKeyAndIv(this.key);
+ }
+ Pair<InputStream,Integer> pair;
+ try {
+ pair = AbstractConnectionManager.createInputStream(file, true);
+ } catch (FileNotFoundException e) {
+ fail();
+ return;
+ }
+ this.file.setExpectedSize(pair.second);
+ this.mFileInputStream = pair.first;
+ Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD);
+ IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host,file,mime);
+ mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ Element slot = packet.findChild("slot",Xmlns.HTTP_UPLOAD);
+ if (slot != null) {
+ try {
+ mGetUrl = new URL(slot.findChildContent("get"));
+ mPutUrl = new URL(slot.findChildContent("put"));
+ if (!canceled) {
+ new Thread(new FileUploader()).start();
+ }
+ } catch (MalformedURLException e) {
+ fail();
+ }
+ } else {
+ fail();
+ }
+ } else {
+ fail();
+ }
+ }
+ });
+ message.setTransferable(this);
+ mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
+ }
+
+ private class FileUploader implements Runnable {
+
+ @Override
+ public void run() {
+ this.upload();
+ }
+
+ private void upload() {
+ OutputStream os = null;
+ InputStream errorStream = null;
+ HttpURLConnection connection = null;
+ PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid());
+ try {
+ wakeLock.acquire();
+ Logging.d(Config.LOGTAG, "uploading to " + mPutUrl.toString());
+ connection = (HttpURLConnection) mPutUrl.openConnection();
+
+ if (connection instanceof HttpsURLConnection) {
+ mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true);
+ }
+ connection.setRequestMethod("PUT");
+ connection.setFixedLengthStreamingMode((int) file.getExpectedSize());
+ connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime);
+ connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion());
+ connection.setDoOutput(true);
+ connection.connect();
+ os = connection.getOutputStream();
+ transmitted = 0;
+ int count = -1;
+ byte[] buffer = new byte[4096];
+ while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) {
+ transmitted += count;
+ os.write(buffer, 0, count);
+ mXmppConnectionService.updateConversationUi();
+ }
+ os.flush();
+ os.close();
+ mFileInputStream.close();
+ int code = connection.getResponseCode();
+ if (code == 200 || code == 201) {
+ Logging.d(Config.LOGTAG, "finished uploading file");
+ if (key != null) {
+ mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key));
+ }
+ MessageUtil.updateFileParams(message, mGetUrl);
+ FileBackend.updateMediaScanner(file, mXmppConnectionService);
+ message.setTransferable(null);
+ message.setCounterpart(message.getConversation().getJid().toBareJid());
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() {
+ @Override
+ public void success(Message message) {
+ mXmppConnectionService.resendMessage(message,delayed);
+ }
+
+ @Override
+ public void error(int errorCode, Message object) {
+ fail();
+ }
+
+ @Override
+ public void userInputRequried(PendingIntent pi, Message object) {
+ fail();
+ }
+ });
+ } else {
+ mXmppConnectionService.resendMessage(message, delayed);
+ }
+ } else {
+ errorStream = connection.getErrorStream();
+ Logging.e("httpupload", "file upload failed: http code (" + code + ") " + new Scanner(errorStream).useDelimiter("\\A").next());
+ fail();
+ }
+ } catch (IOException e) {
+ errorStream = connection.getErrorStream();
+ Logging.e("httpupload", "http response: " + new Scanner(errorStream).useDelimiter("\\A").next() + ", exception message: " + e.getMessage());
+ fail();
+ } finally {
+ StreamUtil.close(os);
+ StreamUtil.close(errorStream);
+ if (connection != null) {
+ connection.disconnect();
+ }
+ wakeLock.release();
+ }
+ }
+ }
+}