1package eu.siacs.conversations.http;
2
3import java.io.BufferedInputStream;
4import java.io.IOException;
5import java.io.OutputStream;
6import java.net.HttpURLConnection;
7import java.net.MalformedURLException;
8import java.net.URL;
9import java.security.KeyManagementException;
10import java.security.NoSuchAlgorithmException;
11
12import javax.net.ssl.HttpsURLConnection;
13import javax.net.ssl.SSLContext;
14import javax.net.ssl.SSLHandshakeException;
15import javax.net.ssl.X509TrustManager;
16
17import android.content.Intent;
18import android.graphics.BitmapFactory;
19import android.net.Uri;
20
21import eu.siacs.conversations.entities.Downloadable;
22import eu.siacs.conversations.entities.DownloadableFile;
23import eu.siacs.conversations.entities.Message;
24import eu.siacs.conversations.services.XmppConnectionService;
25
26public class HttpConnection implements Downloadable {
27
28 private HttpConnectionManager mHttpConnectionManager;
29 private XmppConnectionService mXmppConnectionService;
30
31 private URL mUrl;
32 private Message message;
33 private DownloadableFile file;
34 private int mStatus = Downloadable.STATUS_UNKNOWN;
35 private boolean mAutostart = true;
36
37 public HttpConnection(HttpConnectionManager manager) {
38 this.mHttpConnectionManager = manager;
39 this.mXmppConnectionService = manager.getXmppConnectionService();
40 }
41
42 @Override
43 public boolean start() {
44 if (mXmppConnectionService.hasInternetConnection()) {
45 if (this.mStatus == STATUS_OFFER_CHECK_FILESIZE) {
46 checkFileSize(true);
47 } else {
48 changeStatus(STATUS_DOWNLOADING);
49 new Thread(new FileDownloader()).start();
50 }
51 return true;
52 } else {
53 return false;
54 }
55 }
56
57 public void init(Message message) {
58 this.message = message;
59 this.message.setDownloadable(this);
60 try {
61 mUrl = new URL(message.getBody());
62 this.file = mXmppConnectionService.getFileBackend().getFile(
63 message, false);
64 this.mAutostart = true;
65 checkFileSize(false);
66 } catch (MalformedURLException e) {
67 this.cancel();
68 }
69 }
70
71 private void checkFileSize(boolean interactive) {
72 changeStatus(STATUS_CHECKING);
73 new Thread(new FileSizeChecker(interactive)).start();
74 }
75
76 public void cancel() {
77 mHttpConnectionManager.finishConnection(this);
78 message.setDownloadable(null);
79 mXmppConnectionService.updateConversationUi();
80 }
81
82 private void finish() {
83 Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
84 intent.setData(Uri.fromFile(file));
85 mXmppConnectionService.sendBroadcast(intent);
86 message.setDownloadable(null);
87 mHttpConnectionManager.finishConnection(this);
88 }
89
90 private void changeStatus(int status) {
91 this.mStatus = status;
92 mXmppConnectionService.updateConversationUi();
93 }
94
95 private void setupTrustManager(HttpsURLConnection connection, boolean interactive) {
96 X509TrustManager trustManager;
97 if (interactive) {
98 trustManager = mXmppConnectionService.getMemorizingTrustManager();
99 } else {
100 trustManager = mXmppConnectionService.getMemorizingTrustManager().getNonInteractive();
101 }
102 try {
103 SSLContext sc = SSLContext.getInstance("TLS");
104 sc.init(null,new X509TrustManager[] { trustManager },mXmppConnectionService.getRNG());
105 connection.setSSLSocketFactory(sc.getSocketFactory());
106 } catch (KeyManagementException e) {
107 return;
108 } catch (NoSuchAlgorithmException e) {
109 return;
110 }
111 }
112
113 private class FileSizeChecker implements Runnable {
114
115 private boolean interactive = false;
116
117 public FileSizeChecker(boolean interactive) {
118 this.interactive = interactive;
119 }
120
121 @Override
122 public void run() {
123 long size;
124 try {
125 size = retrieveFileSize();
126 } catch (SSLHandshakeException e) {
127 changeStatus(STATUS_OFFER_CHECK_FILESIZE);
128 return;
129 } catch (IOException e) {
130 cancel();
131 return;
132 }
133 file.setExpectedSize(size);
134 if (size <= mHttpConnectionManager.getAutoAcceptFileSize()
135 && mAutostart) {
136 start();
137 } else {
138 changeStatus(STATUS_OFFER);
139 }
140 }
141
142 private long retrieveFileSize() throws IOException, SSLHandshakeException {
143 HttpURLConnection connection = (HttpURLConnection) mUrl
144 .openConnection();
145 connection.setRequestMethod("HEAD");
146 if (connection instanceof HttpsURLConnection) {
147 setupTrustManager((HttpsURLConnection) connection, interactive);
148 }
149 connection.connect();
150 String contentLength = connection.getHeaderField("Content-Length");
151 if (contentLength == null) {
152 throw new IOException();
153 }
154 try {
155 return Long.parseLong(contentLength, 10);
156 } catch (NumberFormatException e) {
157 throw new IOException();
158 }
159 }
160
161 }
162
163 private class FileDownloader implements Runnable {
164
165 @Override
166 public void run() {
167 try {
168 download();
169 updateImageBounds();
170 finish();
171 } catch (IOException e) {
172 cancel();
173 }
174 }
175
176 private void download() throws IOException {
177 HttpURLConnection connection = (HttpURLConnection) mUrl
178 .openConnection();
179 if (connection instanceof HttpsURLConnection) {
180 setupTrustManager((HttpsURLConnection) connection, true);
181 }
182 BufferedInputStream is = new BufferedInputStream(
183 connection.getInputStream());
184 OutputStream os = file.createOutputStream();
185 int count = -1;
186 byte[] buffer = new byte[1024];
187 while ((count = is.read(buffer)) != -1) {
188 os.write(buffer, 0, count);
189 }
190 os.flush();
191 os.close();
192 is.close();
193 }
194
195 private void updateImageBounds() {
196 BitmapFactory.Options options = new BitmapFactory.Options();
197 options.inJustDecodeBounds = true;
198 BitmapFactory.decodeFile(file.getAbsolutePath(), options);
199 int imageHeight = options.outHeight;
200 int imageWidth = options.outWidth;
201 message.setBody(mUrl.toString() + "," + file.getSize() + ','
202 + imageWidth + ',' + imageHeight);
203 message.setType(Message.TYPE_IMAGE);
204 mXmppConnectionService.updateMessage(message);
205 }
206
207 }
208
209 @Override
210 public int getStatus() {
211 return this.mStatus;
212 }
213
214 @Override
215 public long getFileSize() {
216 if (this.file != null) {
217 return this.file.getExpectedSize();
218 } else {
219 return 0;
220 }
221 }
222}