From 95cb2394154a287afcd295590d741096985ff69a Mon Sep 17 00:00:00 2001 From: steckbrief Date: Wed, 4 Jan 2017 19:41:20 +0100 Subject: Added columns to fileparams table: url, original file name, key and iv auto download of files moved from MessageParser to MessageAdapter download and open file representation cleaned up --- .../conversationsplus/entities/FileParams.java | 42 +++++++++++++ .../db/access/MessageDatabaseAccess.java | 36 +++++++++++ .../FileParamsBodyToDatabaseFieldsMigration.java | 5 ++ .../http/upload/HttpFileTransferEntity.java | 2 +- .../ui/dialogs/MessageDetailsDialog.java | 3 +- .../conversationsplus/utils/MessageUtil.java | 69 +++++++++++++++++++++- .../conversationsplus/utils/UrlUtil.java | 25 ++++++++ .../HttpUploadRequestSlotPacketGenerator.java | 2 +- 8 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 src/main/java/de/thedevstack/conversationsplus/utils/UrlUtil.java (limited to 'src/main/java/de/thedevstack') diff --git a/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java b/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java index 1eef078f..290b87b8 100644 --- a/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java +++ b/src/main/java/de/thedevstack/conversationsplus/entities/FileParams.java @@ -9,6 +9,7 @@ import eu.siacs.conversations.utils.MimeUtils; */ public class FileParams { private String name; + private String originalFilename; private String path; private String url; private String mimeType; @@ -16,6 +17,8 @@ public class FileParams { private int width = 0; private int height = 0; private FileStatus fileStatus; + private byte[] aeskey; + private byte[] iv; public FileParams() { fileStatus = FileStatus.UNDEFINED; @@ -89,6 +92,37 @@ public class FileParams { return path; } + public void setKeyAndIv(byte[] keyIvCombo) { + if (keyIvCombo.length == 48) { + this.aeskey = new byte[32]; + this.iv = new byte[16]; + System.arraycopy(keyIvCombo, 0, this.iv, 0, 16); + System.arraycopy(keyIvCombo, 16, this.aeskey, 0, 32); + } else if (keyIvCombo.length >= 32) { + this.aeskey = new byte[32]; + System.arraycopy(keyIvCombo, 0, aeskey, 0, 32); + } else if (keyIvCombo.length >= 16) { + this.aeskey = new byte[16]; + System.arraycopy(keyIvCombo, 0, this.aeskey, 0, 16); + } + } + + public void setKey(byte[] key) { + this.aeskey = key; + } + + public void setIv(byte[] iv) { + this.iv = iv; + } + + public byte[] getKey() { + return this.aeskey; + } + + public byte[] getIv() { + return this.iv; + } + /** * Sets the path to the file. * If no file name is stored yet here - this method tries to extract the file name from the path. @@ -127,4 +161,12 @@ public class FileParams { public FileStatus getFileStatus() { return this.fileStatus; } + + public void setOriginalFilename(String originalFilename) { + this.originalFilename = originalFilename; + } + + public String getOriginalFilename() { + return this.originalFilename; + } } diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java index 139dc419..0fb85fbf 100644 --- a/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/db/access/MessageDatabaseAccess.java @@ -10,6 +10,7 @@ import de.thedevstack.conversationsplus.enums.FileStatus; import de.thedevstack.conversationsplus.persistance.db.migrations.FileParamsBodyToDatabaseFieldsMigration; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.utils.CryptoHelper; /** * @@ -29,6 +30,9 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess { private static final String COLUMN_NAME_MSG_PARAMS_FILE_NAME = "file_name"; private static final String COLUMN_NAME_MSG_PARAMS_FILE_PATH = "file_path"; private static final String COLUMN_NAME_MSG_PARAMS_FILE_URL = "file_url"; + private static final String COLUMN_NAME_MSG_PARAMS_FILE_IV = "file_iv"; + private static final String COLUMN_NAME_MSG_PARAMS_FILE_KEY = "file_key"; + private static final String COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME = "original_file_name"; private static final String TABLE_ADDITIONAL_PARAMETERS_CREATE_V1 = "CREATE TABLE " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + " (" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + " TEXT, " @@ -46,8 +50,11 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess { + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_WIDTH + " INTEGER DEFAULT 0, " + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_STATUS + " TEXT DEFAULT 'UNDEFINED', " + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_NAME + " TEXT DEFAULT NULL, " + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME + " TEXT DEFAULT NULL, " + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_PATH + " TEXT DEFAULT NULL, " + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL + " TEXT DEFAULT NULL, " + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_IV + " TEXT DEFAULT NULL, " + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_KEY + " TEXT DEFAULT NULL, " + "FOREIGN KEY(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") REFERENCES " + Message.TABLENAME + "(" + Message.UUID + ") ON DELETE CASCADE)"; public static void updateMessageParameters(SQLiteDatabase db, Message message, String uuid) { @@ -66,8 +73,16 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess { additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_SIZE, fileParams.getSize()); additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT, fileParams.getHeight()); additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_WIDTH, fileParams.getWidth()); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_NAME, fileParams.getName()); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME, fileParams.getOriginalFilename()); additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_PATH, fileParams.getPath()); additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL, fileParams.getUrl()); + if (null != fileParams.getIv()) { + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_IV, CryptoHelper.bytesToHex(fileParams.getIv())); + } + if (null != fileParams.getKey()) { + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_KEY, CryptoHelper.bytesToHex(fileParams.getKey())); + } if (null != fileParams.getFileStatus()) { additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_STATUS, fileParams.getFileStatus().name()); } @@ -96,10 +111,20 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess { fileParams.setMimeType(fileType); String name = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_NAME); fileParams.setName(name); + String originalFilename = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME); + fileParams.setOriginalFilename(originalFilename); String path = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_PATH); fileParams.setPath(path); String url = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_URL); fileParams.setUrl(url); + String iv = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_IV); + if (null != iv && !iv.isEmpty()) { + fileParams.setIv(CryptoHelper.hexToBytes(iv)); + } + String key = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_FILE_KEY); + if (null != key && !key.isEmpty()) { + fileParams.setKey(CryptoHelper.hexToBytes(key)); + } long fileSize = CursorHelper.getLong(cursor, COLUMN_NAME_MSG_PARAMS_FILE_SIZE); fileParams.setSize(fileSize); int imageHeight = CursorHelper.getInt(cursor, COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT); @@ -147,7 +172,11 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess { MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_WIDTH + " INTEGER DEFAULT 0", MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_STATUS + " TEXT DEFAULT 'UNDEFINED'", MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_NAME + " TEXT DEFAULT NULL", + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_ORIGINAL_FILE_NAME + " TEXT DEFAULT NULL", MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_PATH + " TEXT DEFAULT NULL", + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL + " TEXT DEFAULT NULL", + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_IV + " TEXT DEFAULT NULL", + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_KEY + " TEXT DEFAULT NULL", }; addNewColumns(db, MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, columnDefinitions); @@ -167,6 +196,13 @@ public class MessageDatabaseAccess extends AbstractDatabaseAccess { parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_SIZE, fileParams.getSize()); parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_HEIGHT, fileParams.getHeight()); parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_WIDTH, fileParams.getWidth()); + parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_URL, fileParams.getUrl()); + if (null != fileParams.getIv()) { + parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_IV, CryptoHelper.bytesToHex(fileParams.getIv())); + } + if (null != fileParams.getKey()) { + parameterValues.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_FILE_KEY, CryptoHelper.bytesToHex(fileParams.getKey())); + } db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, parameterValues, MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", new String[] {uuid}); } diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java b/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java index c0aa63c0..8ad94ca0 100644 --- a/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/db/migrations/FileParamsBodyToDatabaseFieldsMigration.java @@ -4,6 +4,7 @@ import java.net.MalformedURLException; import java.net.URL; import de.thedevstack.conversationsplus.entities.FileParams; +import de.thedevstack.conversationsplus.utils.UrlUtil; /** * Created by steckbrief on 24.08.2016. @@ -28,6 +29,8 @@ public class FileParamsBodyToDatabaseFieldsMigration { try { URL url = new URL(parts[0]); params.setUrl(url.toString()); + byte[] ivAndKey = UrlUtil.getIvAndKeyFromURL(url); + params.setKeyAndIv(ivAndKey); } catch (MalformedURLException e1) { } } @@ -37,6 +40,8 @@ public class FileParamsBodyToDatabaseFieldsMigration { try { URL url = new URL(parts[0]); params.setUrl(url.toString()); + byte[] ivAndKey = UrlUtil.getIvAndKeyFromURL(url); + params.setKeyAndIv(ivAndKey); } catch (MalformedURLException e1) { } try { diff --git a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java index 9ec07679..a36db4cd 100644 --- a/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java +++ b/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/http/upload/HttpFileTransferEntity.java @@ -31,9 +31,9 @@ public class HttpFileTransferEntity extends FileTransferEntity { FileParams fileParams = this.getMessage().getFileParams(); if (null == fileParams) { fileParams = new FileParams(); + this.getMessage().setFileParams(fileParams); } fileParams.setFileStatus(FileStatus.NEEDS_UPLOAD); - this.getMessage().setFileParams(fileParams); if (Config.ENCRYPT_ON_HTTP_UPLOADED || message.getEncryption() == Message.ENCRYPTION_AXOLOTL || message.getEncryption() == Message.ENCRYPTION_OTR) { diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java index 1b44d09b..8a3de18d 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java @@ -62,8 +62,9 @@ public class MessageDetailsDialog extends AbstractAlertDialog { view.findViewById(R.id.dlgMsgDetFileTable).setVisibility(View.VISIBLE); if (null != message.getFileParams()) { FileParams params = message.getFileParams(); - TextViewUtil.setText(view, R.id.dlgMsgDetFileSize, UIHelper.getHumanReadableFileSize(params.getSize())); + TextViewUtil.setText(view, R.id.dlgMsgDetFileSize, UIHelper.getHumanReadableDetailedFileSize(params.getSize())); TextViewUtil.setText(view, R.id.dlgMsgDetFileMimeType, params.getMimeType()); + TextViewUtil.setText(view, R.id.dlgMsgDetFileOriginalName, params.getOriginalFilename()); } TextViewUtil.setText(view, R.id.dlgMsgDetFileHttpUploaded, message.isHttpUploaded() ? R.string.cplus_yes : R.string.cplus_no); diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java index 37e39285..ed144465 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java @@ -3,6 +3,7 @@ package de.thedevstack.conversationsplus.utils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import java.net.MalformedURLException; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -14,18 +15,80 @@ import de.thedevstack.conversationsplus.enums.FileStatus; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.utils.FileUtils; +import eu.siacs.conversations.utils.MimeUtils; /** * Utility class to work with messages. */ public final class MessageUtil { + public static void extractFileParamsFromBody(Message message) { + if (null == message) { + return; + } + String body = message.getBody(); + /** + * there are a few cases where spaces result in an unwanted behavior, e.g. + * "http://example.com/image.jpg" text that will not be shown /abc.png" + * or more than one image link in one message. + */ + if (null == body || body.isEmpty() || body.contains(" ")) { + return; + } + + FileParams fileParams = message.getFileParams(); + if (null == fileParams) { + fileParams = new FileParams(); + message.setFileParams(fileParams); + } + + try { + URL url = new URL(body); + if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) { + message.setTreatAsDownloadable(Message.Decision.NEVER); + return; + } + if (message.isHttpUploaded()) { + fileParams.setUrl(url.toString()); + message.setTreatAsDownloadable(Message.Decision.MUST); + return; + } + String extension = FileUtils.getRelevantExtension(url); + if (extension == null) { + message.setTreatAsDownloadable(Message.Decision.NEVER); + return; + } + byte[] ivAndKey = UrlUtil.getIvAndKeyFromURL(url); + + if (null != ivAndKey) { + if (MimeUtils.guessMimeTypeFromExtension(extension) != null) { + message.setTreatAsDownloadable(Message.Decision.MUST); + fileParams.setKeyAndIv(ivAndKey); + fileParams.setUrl(url.toString()); + } else { + message.setTreatAsDownloadable(Message.Decision.NEVER); + } + } else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension) + || Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) { + message.setTreatAsDownloadable(Message.Decision.SHOULD); + fileParams.setUrl(url.toString()); + } else { + message.setTreatAsDownloadable(Message.Decision.NEVER); + } + + } catch (MalformedURLException e) { + message.setTreatAsDownloadable(Message.Decision.NEVER); + } + } + public static boolean needsDownload(Message message) { FileStatus fileStatus = (null != message.getFileParams()) ? message.getFileParams().getFileStatus() : null; - return (null != fileStatus && (fileStatus == FileStatus.NEEDS_DOWNLOAD - || fileStatus == FileStatus.UNDEFINED)) + return (null == fileStatus + || (null != fileStatus && (fileStatus == FileStatus.NEEDS_DOWNLOAD || fileStatus == FileStatus.UNDEFINED))) && message.treatAsDownloadable() != Message.Decision.NEVER; } @@ -122,6 +185,7 @@ public final class MessageUtil { FileParams fileParams = message.getFileParams(); if (null == fileParams) { fileParams = new FileParams(); + message.setFileParams(fileParams); } fileParams.setSize(size); if (null != url) { @@ -137,7 +201,6 @@ public final class MessageUtil { if (null != relativeFilePathFromMessage && relativeFilePathFromMessage.startsWith("/")) { fileParams.setPath(relativeFilePathFromMessage); } - message.setFileParams(fileParams); } private MessageUtil() { diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UrlUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/UrlUtil.java new file mode 100644 index 00000000..50c42288 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/UrlUtil.java @@ -0,0 +1,25 @@ +package de.thedevstack.conversationsplus.utils; + +import java.net.URL; + +import eu.siacs.conversations.utils.CryptoHelper; + +/** + * This utility class provides helper methods to handle URLs. + */ + +public final class UrlUtil { + public static byte[] getIvAndKeyFromURL(URL url) { + if (null == url) { + return null; + } + String reference = url.getRef(); + boolean linkHasIvAndKey = reference != null && reference.matches("([A-Fa-f0-9]{2}){48}"); + + return linkHasIvAndKey ? CryptoHelper.hexToBytes(reference) : null; + } + + private UrlUtil() { + // Helper Class + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java index 915cf9a7..cdb957c4 100644 --- a/src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/filetransfer/http/upload/HttpUploadRequestSlotPacketGenerator.java @@ -33,7 +33,7 @@ public final class HttpUploadRequestSlotPacketGenerator { public static IqPacket generate(Jid host, String filename, long filesize, String mime) { SlotRequestPacket packet = new SlotRequestPacket(filename, filesize); packet.setTo(host); - packet.setMime((mime == null) ? HttpUpload.DEFAULT_MIME_TYPE : mime); + packet.setMime((mime == null || mime.isEmpty()) ? HttpUpload.DEFAULT_MIME_TYPE : mime); return packet; } -- cgit v1.2.3