package de.thedevstack.conversationsplus.services.filetransfer.http.upload; import android.os.PowerManager; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.UnknownHostException; import java.util.Scanner; import javax.net.ssl.HttpsURLConnection; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.entities.DownloadableFile; import de.thedevstack.conversationsplus.entities.Message; import de.thedevstack.conversationsplus.http.HttpConnectionManager; import de.thedevstack.conversationsplus.persistance.FileBackend; import de.thedevstack.conversationsplus.services.filetransfer.FileTransferFailureReason; import de.thedevstack.conversationsplus.utils.StreamUtil; import de.thedevstack.conversationsplus.utils.UiUpdateHelper; import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; import de.thedevstack.conversationsplus.xmpp.filetransfer.http.upload.HttpUpload; public class HttpFileUploader implements Runnable { private static final String HTTP_METHOD = "PUT"; private static final String MIME_REQUEST_PROPERTY_NAME = "Content-Type"; private static final String USER_AGENT_REQUEST_PROPERTY_NAME = "User-Agent"; private static final int BUFFER_LENGTH = 4096; private final HttpFileTransferEntity entity; public HttpFileUploader(HttpFileTransferEntity entity) { this.entity = entity; } @Override public void run() { this.upload(); } private void upload() { OutputStream os = null; HttpURLConnection connection = null; InputStream fileInputStream = null; DownloadableFile file = this.entity.getFile(); PowerManager.WakeLock wakeLock = ConversationsPlusApplication.createPartialWakeLock("http_upload_" + entity.getMessage().getUuid()); try { wakeLock.acquire(); Logging.d("httpupload", "uploading file to " + this.entity.getPutUrl()); URL putUrl = new URL(this.entity.getPutUrl()); fileInputStream = this.entity.getFileInputStream(); connection = (HttpURLConnection) putUrl.openConnection(); if (connection instanceof HttpsURLConnection) { HttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); } connection.setRequestMethod(HTTP_METHOD); connection.setFixedLengthStreamingMode((int) file.getExpectedSize()); String mime = file.getMimeType(); connection.setRequestProperty(MIME_REQUEST_PROPERTY_NAME, mime == null ? HttpUpload.DEFAULT_MIME_TYPE : mime); connection.setRequestProperty(USER_AGENT_REQUEST_PROPERTY_NAME, ConversationsPlusApplication.getNameAndVersion()); connection.setDoOutput(true); connection.connect(); os = connection.getOutputStream(); int count = -1; byte[] buffer = new byte[BUFFER_LENGTH]; while (((count = fileInputStream.read(buffer)) != -1) && !this.entity.isCanceled()) { this.entity.updateProgress(count); os.write(buffer, 0, count); UiUpdateHelper.updateConversationUi(); } os.flush(); os.close(); fileInputStream.close(); int code = connection.getResponseCode(); if (code == 200 || code == 201) { Logging.d("httpupload", "finished uploading file"); this.entity.transferred(); FileBackend.updateMediaScanner(file, XmppConnectionServiceAccessor.xmppConnectionService); // Why??? // Send the URL to the counterpart Message message = this.entity.getMessage(); message.setCounterpart(message.getConversation().getJid().toBareJid()); if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { XmppConnectionServiceAccessor.xmppConnectionService.getPgpEngine().encrypt(message, new HttpUploadedFileEncryptionUiCallback(this.entity)); } else { XmppConnectionServiceAccessor.xmppConnectionService.resendMessage(message, this.entity.isDelayed()); } } else { String httpResponseMessage = this.getErrorStreamContent(connection); String errorMessage = "file upload failed: http code (" + code + ") " + httpResponseMessage; Logging.e("httpupload", errorMessage); FileTransferFailureReason failureReason = null; switch (code) { case 403: failureReason = FileTransferFailureReason.createLimitedRecoverableFailureReason(errorMessage); break; case 404: failureReason = FileTransferFailureReason.createNonRecoverableFailureReason("Upload URL not found"); break; case 500: failureReason = FileTransferFailureReason.createNonRecoverableFailureReason("Internal Server Error"); break; default: failureReason = FileTransferFailureReason.createRecoverableFailureReason(errorMessage); } this.entity.fail(failureReason); } } catch (UnknownHostException e) { Logging.e("httpupload", "File upload failed due to unknown host. " + e.getMessage()); //if (!HAS_INTERNET_CONNECTION) { this.entity.fail(FileTransferFailureReason.createRecoverableFailureReason(e, "Missing internet connection")); //} } catch (IOException e) { String httpResponseMessage = this.getErrorStreamContent(connection); Logging.e("httpupload", ((null != httpResponseMessage) ? ("http response: " + httpResponseMessage + ", ") : "") + "exception message: " + e.getMessage()); // TODO: Differentiate IOException while internet connection wasn't available and others this.entity.fail(FileTransferFailureReason.createRecoverableFailureReason(e, httpResponseMessage)); } finally { StreamUtil.close(os); StreamUtil.close(fileInputStream); if (connection != null) { connection.disconnect(); } wakeLock.release(); } } private String getErrorStreamContent(HttpURLConnection connection) { InputStream errorStream = null; String httpResponseMessage = null; try { errorStream = connection.getErrorStream(); if (null != errorStream) { Scanner scanner = new Scanner(errorStream).useDelimiter("\\A"); if (scanner.hasNext()) { httpResponseMessage = scanner.next(); } } } finally { StreamUtil.close(errorStream); } return httpResponseMessage; } }