HttpConnection.java

  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,
 96			boolean interactive) {
 97		X509TrustManager trustManager;
 98		if (interactive) {
 99			trustManager = mXmppConnectionService.getMemorizingTrustManager();
100		} else {
101			trustManager = mXmppConnectionService.getMemorizingTrustManager()
102					.getNonInteractive();
103		}
104		try {
105			SSLContext sc = SSLContext.getInstance("TLS");
106			sc.init(null, new X509TrustManager[] { trustManager },
107					mXmppConnectionService.getRNG());
108			connection.setSSLSocketFactory(sc.getSocketFactory());
109		} catch (KeyManagementException e) {
110			return;
111		} catch (NoSuchAlgorithmException e) {
112			return;
113		}
114	}
115
116	private class FileSizeChecker implements Runnable {
117
118		private boolean interactive = false;
119
120		public FileSizeChecker(boolean interactive) {
121			this.interactive = interactive;
122		}
123
124		@Override
125		public void run() {
126			long size;
127			try {
128				size = retrieveFileSize();
129			} catch (SSLHandshakeException e) {
130				changeStatus(STATUS_OFFER_CHECK_FILESIZE);
131				return;
132			} catch (IOException e) {
133				cancel();
134				return;
135			}
136			file.setExpectedSize(size);
137			if (size <= mHttpConnectionManager.getAutoAcceptFileSize()
138					&& mAutostart) {
139				start();
140			} else {
141				changeStatus(STATUS_OFFER);
142			}
143		}
144
145		private long retrieveFileSize() throws IOException,
146				SSLHandshakeException {
147			HttpURLConnection connection = (HttpURLConnection) mUrl
148					.openConnection();
149			connection.setRequestMethod("HEAD");
150			if (connection instanceof HttpsURLConnection) {
151				setupTrustManager((HttpsURLConnection) connection, interactive);
152			}
153			connection.connect();
154			String contentLength = connection.getHeaderField("Content-Length");
155			if (contentLength == null) {
156				throw new IOException();
157			}
158			try {
159				return Long.parseLong(contentLength, 10);
160			} catch (NumberFormatException e) {
161				throw new IOException();
162			}
163		}
164
165	}
166
167	private class FileDownloader implements Runnable {
168
169		@Override
170		public void run() {
171			try {
172				download();
173				updateImageBounds();
174				finish();
175			} catch (IOException e) {
176				cancel();
177			}
178		}
179
180		private void download() throws IOException {
181			HttpURLConnection connection = (HttpURLConnection) mUrl
182					.openConnection();
183			if (connection instanceof HttpsURLConnection) {
184				setupTrustManager((HttpsURLConnection) connection, true);
185			}
186			BufferedInputStream is = new BufferedInputStream(
187					connection.getInputStream());
188			OutputStream os = file.createOutputStream();
189			int count = -1;
190			byte[] buffer = new byte[1024];
191			while ((count = is.read(buffer)) != -1) {
192				os.write(buffer, 0, count);
193			}
194			os.flush();
195			os.close();
196			is.close();
197		}
198
199		private void updateImageBounds() {
200			BitmapFactory.Options options = new BitmapFactory.Options();
201			options.inJustDecodeBounds = true;
202			BitmapFactory.decodeFile(file.getAbsolutePath(), options);
203			int imageHeight = options.outHeight;
204			int imageWidth = options.outWidth;
205			message.setBody(mUrl.toString() + "," + file.getSize() + ','
206					+ imageWidth + ',' + imageHeight);
207			message.setType(Message.TYPE_IMAGE);
208			mXmppConnectionService.updateMessage(message);
209		}
210
211	}
212
213	@Override
214	public int getStatus() {
215		return this.mStatus;
216	}
217
218	@Override
219	public long getFileSize() {
220		if (this.file != null) {
221			return this.file.getExpectedSize();
222		} else {
223			return 0;
224		}
225	}
226}