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