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