diff options
Diffstat (limited to 'src/main/java/eu/siacs')
30 files changed, 347 insertions, 701 deletions
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java index 92eb158f..4ad01089 100644 --- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java @@ -14,10 +14,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.URL; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; -import de.thedevstack.conversationsplus.utils.MessageUtil; import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; @@ -65,12 +63,11 @@ public class PgpEngine { && message.getUuid().equals(uuid)) { message.setBody(os.toString()); message.setEncryption(Message.ENCRYPTION_DECRYPTED); - final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager(); if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && (message.isHttpUploaded() || ConversationsPlusPreferences.autoDownloadFileLink()) && ConversationsPlusPreferences.autoAcceptFileSize() > 0) { - manager.createNewDownloadConnection(message); + HttpConnectionManager.createNewDownloadConnection(message); } mXmppConnectionService.updateMessage(message); callback.success(message); @@ -107,8 +104,6 @@ public class PgpEngine { switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { case OpenPgpApi.RESULT_CODE_SUCCESS: - URL url = message.getFileParams().url; - MessageUtil.updateFileParams(message, url); message.setEncryption(Message.ENCRYPTION_DECRYPTED); PgpEngine.this.mXmppConnectionService .updateMessage(message); @@ -150,12 +145,7 @@ public class PgpEngine { if (!message.needsUploading()) { params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - String body; - if (message.hasFileOnRemoteHost()) { - body = message.getFileParams().url.toString(); - } else { - body = message.getBody(); - } + String body = message.getBody(); InputStream is = new ByteArrayInputStream(body.getBytes()); final OutputStream os = new ByteArrayOutputStream(); api.executeApiAsync(params, is, os, new IOpenPgpCallback() { diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceImpl.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceImpl.java index 3433a63b..f91dc066 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceImpl.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlServiceImpl.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Random; import java.util.Set; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.utils.MessageUtil; import eu.siacs.conversations.Config; @@ -463,7 +464,7 @@ public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, Axolo PrivateKey x509PrivateKey = KeyChain.getPrivateKey(mXmppConnectionService, account.getPrivateKeyAlias()); X509Certificate[] chain = KeyChain.getCertificateChain(mXmppConnectionService, account.getPrivateKeyAlias()); Signature verifier = Signature.getInstance("sha256WithRSA"); - verifier.initSign(x509PrivateKey,mXmppConnectionService.getRNG()); + verifier.initSign(x509PrivateKey, ConversationsPlusApplication.getSecureRandom()); verifier.update(axolotlPublicKey.serialize()); byte[] signature = verifier.sign(); IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId()); @@ -670,7 +671,7 @@ public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, Axolo verifier.update(identityKey.serialize()); if (verifier.verify(verification.second)) { try { - mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA"); + ConversationsPlusApplication.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA"); String fingerprint = session.getFingerprint(); Log.d(Config.LOGTAG, "verified session with x.509 signature. fingerprint was: "+fingerprint); setFingerprintTrust(fingerprint, XmppAxolotlSession.Trust.TRUSTED_X509); @@ -925,12 +926,7 @@ public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, Axolo XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation()); if (axolotlMessage != null) { - final String content; - if (message.hasFileOnRemoteHost()) { - content = message.getFileParams().url.toString(); - } else { - content = message.getBody(); - } + final String content = message.getBody(); try { axolotlMessage.encrypt(content); } catch (CryptoFailedException e) { diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 4c4a1916..9b4198c8 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -20,6 +20,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.utils.SimpleCryptoUtil; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.OtrService; @@ -172,6 +174,8 @@ public class Account extends AbstractEntity { private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>(); + public static final String PW_SEED = "sadjgdiahsdkhashp3zt98edAFSFIOKZUIUOz23ejj12ezhez2398iehz"; + public Account() { this.uuid = "0"; } @@ -210,9 +214,10 @@ public class Account extends AbstractEntity { cursor.getString(cursor.getColumnIndex(SERVER)), "mobile"); } catch (final InvalidJidException ignored) { } + String password = SimpleCryptoUtil.decrypt(PW_SEED, cursor.getString(cursor.getColumnIndex(PASSWORD))); return new Account(cursor.getString(cursor.getColumnIndex(UUID)), jid, - cursor.getString(cursor.getColumnIndex(PASSWORD)), + password, cursor.getInt(cursor.getColumnIndex(OPTIONS)), cursor.getString(cursor.getColumnIndex(ROSTERVERSION)), cursor.getString(cursor.getColumnIndex(KEYS)), @@ -342,7 +347,7 @@ public class Account extends AbstractEntity { values.put(UUID, uuid); values.put(USERNAME, jid.getLocalpart()); values.put(SERVER, jid.getDomainpart()); - values.put(PASSWORD, password); + values.put(PASSWORD, SimpleCryptoUtil.encrypt(PW_SEED, password)); values.put(OPTIONS, options); values.put(KEYS, this.keys.toString()); values.put(ROSTERVERSION, rosterVersion); diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 7878cecd..299c5f7d 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -748,12 +748,7 @@ public class Conversation extends AbstractEntity implements Blockable { for (int i = this.messages.size() - 1; i >= 0; --i) { Message message = this.messages.get(i); if (message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND) { - String otherBody; - if (message.hasFileOnRemoteHost()) { - otherBody = message.getFileParams().url.toString(); - } else { - otherBody = message.getBody(); - } + String otherBody = message.getBody(); if (otherBody != null && otherBody.equals(body)) { return message; } diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 6faebc65..5abba478 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -6,6 +6,9 @@ import android.database.Cursor; import java.net.MalformedURLException; import java.net.URL; +import de.thedevstack.conversationsplus.entities.FileParams; +import de.thedevstack.conversationsplus.enums.FileStatus; + import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.utils.MimeUtils; @@ -84,6 +87,7 @@ public class Message extends AbstractEntity { private Decision mTreatAsDownloadAble = Decision.NOT_DECIDED; private boolean httpUploaded; + private FileParams fileParams; private Message() { @@ -335,6 +339,9 @@ public class Message extends AbstractEntity { public void setType(int type) { this.type = type; + if (null != this.fileParams && (type == Message.TYPE_FILE || type == Message.TYPE_IMAGE)) { + this.setFileParams(new FileParams()); + } } public boolean isCarbon() { @@ -368,17 +375,8 @@ public class Message extends AbstractEntity { || message.getBody() == null || message.getCounterpart() == null) { return false; } else { - String body, otherBody; - if (this.hasFileOnRemoteHost()) { - body = this.getFileParams().url.toString(); - } else { - body = this.getBody(); - } - if (message.hasFileOnRemoteHost()) { - otherBody = message.getFileParams().url.toString(); - } else { - otherBody = message.getBody(); - } + String body = this.getBody(); + String otherBody = message.getBody(); if (message.getRemoteMsgId() != null && this.getRemoteMsgId() != null) { return (message.getRemoteMsgId().equals(this.getRemoteMsgId()) @@ -493,27 +491,10 @@ public class Message extends AbstractEntity { } private String extractRelevantExtension(String path) { - if (path == null || path.isEmpty()) { - return null; - } - - String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase(); - - final String lastPart = FileUtils.getLastExtension(filename); - - if (!lastPart.isEmpty()) { - // we want the real file extension, not the crypto one - final String secondToLastPart = FileUtils.getSecondToLastExtension(filename); - if (!secondToLastPart.isEmpty() && Transferable.VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { - return secondToLastPart; - } else { - return lastPart; - } - } - return null; + return FileUtils.getRelevantExtension(path); } - public String getMimeType() { + public String getMimeType() { // TODO: Move to fileparams if (relativeFilePath != null) { int start = relativeFilePath.lastIndexOf('.') + 1; if (start < relativeFilePath.length()) { @@ -592,103 +573,6 @@ public class Message extends AbstractEntity { } } - public FileParams getFileParams() { - FileParams params = getLegacyFileParams(); - if (params != null) { - return params; - } - params = new FileParams(); - if (this.transferable != null) { - params.size = this.transferable.getFileSize(); - } - if (this.getBody() == null) { - return params; - } - String parts[] = this.getBody().split("\\|"); - switch (parts.length) { - case 1: - try { - params.size = Long.parseLong(parts[0]); - } catch (NumberFormatException e) { - try { - params.url = new URL(parts[0]); - } catch (MalformedURLException e1) { - params.url = null; - } - } - break; - case 2: - case 4: - try { - params.url = new URL(parts[0]); - } catch (MalformedURLException e1) { - params.url = null; - } - try { - params.size = Long.parseLong(parts[1]); - } catch (NumberFormatException e) { - params.size = 0; - } - try { - params.width = Integer.parseInt(parts[2]); - } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { - params.width = 0; - } - try { - params.height = Integer.parseInt(parts[3]); - } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { - params.height = 0; - } - break; - case 3: - try { - params.size = Long.parseLong(parts[0]); - } catch (NumberFormatException e) { - params.size = 0; - } - try { - params.width = Integer.parseInt(parts[1]); - } catch (NumberFormatException e) { - params.width = 0; - } - try { - params.height = Integer.parseInt(parts[2]); - } catch (NumberFormatException e) { - params.height = 0; - } - break; - } - return params; - } - - public FileParams getLegacyFileParams() { - FileParams params = new FileParams(); - if (this.getBody() == null) { - return params; - } - String parts[] = this.getBody().split(","); - if (parts.length == 3) { - try { - params.size = Long.parseLong(parts[0]); - } catch (NumberFormatException e) { - return null; - } - try { - params.width = Integer.parseInt(parts[1]); - } catch (NumberFormatException e) { - return null; - } - try { - params.height = Integer.parseInt(parts[2]); - } catch (NumberFormatException e) { - return null; - } - return params; - } else { - return null; - } - } - public void untie() { this.mNextMessage = null; this.mPreviousMessage = null; @@ -698,19 +582,22 @@ public class Message extends AbstractEntity { return type == TYPE_FILE || type == TYPE_IMAGE; } - public boolean hasFileOnRemoteHost() { - return isFileOrImage() && getFileParams().url != null; - } + public boolean hasFileAttached() { + return isFileOrImage() || isHttpUploaded() || (null != fileParams && null != fileParams.getPath()); + } - public boolean needsUploading() { - return isFileOrImage() && getFileParams().url == null; + /* + @TODO better + */ + public boolean hasFileOnRemoteHost() { + return hasFileAttached() && null != getFileParams() && getFileParams().isRemoteAvailable(); } - public class FileParams { - public URL url; - public long size = 0; - public int width = 0; - public int height = 0; + /* + @TODO better + */ + public boolean needsUploading() { + return hasFileAttached() && getFileParams().getFileStatus() == FileStatus.NEEDS_UPLOAD; } public void setFingerprint(String fingerprint) { @@ -765,6 +652,14 @@ public class Message extends AbstractEntity { this.httpUploaded = httpUploaded; } + public FileParams getFileParams() { + return this.fileParams; + } + + public void setFileParams(FileParams params) { + this.fileParams = params; + } + private static int getCleanedEncryption(int encryption) { if (encryption == ENCRYPTION_DECRYPTED || encryption == ENCRYPTION_DECRYPTION_FAILED) { return ENCRYPTION_PGP; diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index eff9d9c0..012a5943 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -16,11 +16,11 @@ import java.util.List; import java.util.Set; import de.thedevstack.conversationsplus.ConversationsPlusApplication; + import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.xml.Element; @@ -244,18 +244,6 @@ public class IqGenerator extends AbstractGenerator { return packet; } - public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file, String mime) { - IqPacket packet = new IqPacket(IqPacket.TYPE.GET); - packet.setTo(host); - Element request = packet.addChild("request", Xmlns.HTTP_UPLOAD); - request.addChild("filename").setContent(file.getName()); - request.addChild("size").setContent(String.valueOf(file.getExpectedSize())); - if (mime != null) { - request.addChild("content-type").setContent(mime); - } - return packet; - } - public IqPacket generateCreateAccountWithCaptcha(Account account, String id, Data data) { final IqPacket register = new IqPacket(IqPacket.TYPE.SET); diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index c003da43..582d64e6 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -10,6 +10,7 @@ import java.util.Locale; import java.util.TimeZone; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.entities.FileParams; import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; @@ -71,13 +72,13 @@ public class MessageGenerator extends AbstractGenerator { return packet; } - public static void addXhtmlImImage(MessagePacket packet, Message.FileParams params) { + public static void addXhtmlImImage(MessagePacket packet, FileParams params) { Element html = packet.addChild("html", "http://jabber.org/protocol/xhtml-im"); Element body = html.addChild("body", "http://www.w3.org/1999/xhtml"); Element img = body.addChild("img"); - img.setAttribute("src", params.url.toString()); - img.setAttribute("height", params.height); - img.setAttribute("width", params.width); + img.setAttribute("src", params.getUrl()); + img.setAttribute("height", params.getHeight()); + img.setAttribute("width", params.getWidth()); } public static void addMessageHints(MessagePacket packet) { @@ -95,12 +96,7 @@ public class MessageGenerator extends AbstractGenerator { MessagePacket packet = preparePacket(message); addMessageHints(packet); try { - String content; - if (message.hasFileOnRemoteHost()) { - content = message.getFileParams().url.toString(); - } else { - content = message.getBody(); - } + String content = message.getBody(); packet.setBody(otrSession.transformSending(content)[0]); return packet; } catch (OtrException e) { @@ -112,14 +108,14 @@ public class MessageGenerator extends AbstractGenerator { MessagePacket packet = preparePacket(message); String content; if (message.hasFileOnRemoteHost()) { - Message.FileParams fileParams = message.getFileParams(); - content = fileParams.url.toString(); + FileParams fileParams = message.getFileParams(); + content = message.getBody(); if (message.isHttpUploaded()) { packet.addChild(new HttpUploadHint()); } packet.addChild("x","jabber:x:oob").addChild("url").setContent(content); - if (fileParams.width > 0 && fileParams.height > 0) { - addXhtmlImImage(packet,fileParams); + if (fileParams.getWidth() > 0 && fileParams.getHeight() > 0) { + addXhtmlImImage(packet, fileParams); } } else { content = message.getBody(); diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java index f105646f..a1ad3c01 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java @@ -17,6 +17,8 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; + import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; @@ -24,29 +26,22 @@ import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.SSLSocketHelper; public class HttpConnectionManager extends AbstractConnectionManager { + private static HttpConnectionManager INSTANCE; - public HttpConnectionManager(XmppConnectionService service) { - super(service); - } + public static void init() { + INSTANCE = new HttpConnectionManager(); + } private List<HttpDownloadConnection> downloadConnections = new CopyOnWriteArrayList<>(); - private List<HttpUploadConnection> uploadConnections = new CopyOnWriteArrayList<>(); - public HttpDownloadConnection createNewDownloadConnection(Message message) { - return this.createNewDownloadConnection(message, false); + public static HttpDownloadConnection createNewDownloadConnection(Message message) { + return createNewDownloadConnection(message, false); } - public HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) { - HttpDownloadConnection connection = new HttpDownloadConnection(this); + public static HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) { + HttpDownloadConnection connection = new HttpDownloadConnection(INSTANCE); connection.init(message,interactive); - this.downloadConnections.add(connection); - return connection; - } - - public HttpUploadConnection createNewUploadConnection(Message message, boolean delay) { - HttpUploadConnection connection = new HttpUploadConnection(this); - connection.init(message,delay); - this.uploadConnections.add(connection); + INSTANCE.downloadConnections.add(connection); return connection; } @@ -54,30 +49,24 @@ public class HttpConnectionManager extends AbstractConnectionManager { this.downloadConnections.remove(connection); } - public void finishUploadConnection(HttpUploadConnection httpUploadConnection) { - this.uploadConnections.remove(httpUploadConnection); - } - - public void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) { + public static void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) { final X509TrustManager trustManager; final HostnameVerifier hostnameVerifier; if (interactive) { - trustManager = mXmppConnectionService.getMemorizingTrustManager(); - hostnameVerifier = mXmppConnectionService - .getMemorizingTrustManager().wrapHostnameVerifier( + trustManager = ConversationsPlusApplication.getMemorizingTrustManager(); + hostnameVerifier = ConversationsPlusApplication.getMemorizingTrustManager().wrapHostnameVerifier( new StrictHostnameVerifier()); } else { - trustManager = mXmppConnectionService.getMemorizingTrustManager() + trustManager = ConversationsPlusApplication.getMemorizingTrustManager() .getNonInteractive(); - hostnameVerifier = mXmppConnectionService - .getMemorizingTrustManager() + hostnameVerifier = ConversationsPlusApplication.getMemorizingTrustManager() .wrapHostnameVerifierNonInteractive( new StrictHostnameVerifier()); } try { final SSLContext sc = SSLSocketHelper.getSSLContext(); sc.init(null, new X509TrustManager[]{trustManager}, - mXmppConnectionService.getRNG()); + ConversationsPlusApplication.getSecureRandom()); final SSLSocketFactory sf = sc.getSocketFactory(); final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites( diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index 66687c3a..8b905fd1 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -18,9 +18,12 @@ import javax.net.ssl.SSLHandshakeException; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.enums.FileStatus; import de.thedevstack.conversationsplus.exceptions.RemoteFileNotFoundException; import de.thedevstack.conversationsplus.utils.MessageUtil; import de.thedevstack.conversationsplus.utils.StreamUtil; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; @@ -48,7 +51,7 @@ public class HttpDownloadConnection implements Transferable { public HttpDownloadConnection(HttpConnectionManager manager) { this.mHttpConnectionManager = manager; - this.mXmppConnectionService = manager.getXmppConnectionService(); + this.mXmppConnectionService = XmppConnectionServiceAccessor.xmppConnectionService; } @Override @@ -73,11 +76,7 @@ public class HttpDownloadConnection implements Transferable { this.message = message; this.message.setTransferable(this); try { - if (message.hasFileOnRemoteHost()) { - mUrl = message.getFileParams().url; - } else { - mUrl = new URL(message.getBody()); - } + mUrl = new URL(message.getFileParams().getUrl()); final String sUrlFilename = mUrl.getPath().substring(mUrl.getPath().lastIndexOf('/')).toLowerCase(); final String lastPart = FileUtils.getLastExtension(sUrlFilename); @@ -130,6 +129,7 @@ public class HttpDownloadConnection implements Transferable { private void finish() { FileBackend.updateMediaScanner(file, mXmppConnectionService); message.setTransferable(null); + MessageUtil.setAndSaveFileStatus(this.message, FileStatus.DOWNLOADED); mHttpConnectionManager.finishConnection(this); if (message.getEncryption() == Message.ENCRYPTION_PGP) { message.getConversation().getAccount().getPgpDecryptionService().add(message); @@ -175,7 +175,7 @@ public class HttpDownloadConnection implements Transferable { HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); return; } catch (RemoteFileNotFoundException e) { - message.setNoDownloadable(); + message.setNoDownloadable(); // TODO Set remote file status to not-available cancel(); return; } catch (IOException e) { @@ -265,7 +265,7 @@ public class HttpDownloadConnection implements Transferable { private void download() throws SSLHandshakeException, IOException { InputStream is = null; - PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_download_"+message.getUuid()); + PowerManager.WakeLock wakeLock = ConversationsPlusApplication.createPartialWakeLock("http_download_"+message.getUuid()); try { wakeLock.acquire(); HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java deleted file mode 100644 index a7375b8a..00000000 --- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java +++ /dev/null @@ -1,242 +0,0 @@ -package eu.siacs.conversations.http; - -import android.app.PendingIntent; -import android.os.PowerManager; -import android.util.Pair; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Scanner; - -import javax.net.ssl.HttpsURLConnection; - -import de.thedevstack.android.logcat.Logging; -import de.thedevstack.conversationsplus.ConversationsPlusApplication; -import de.thedevstack.conversationsplus.utils.MessageUtil; -import de.thedevstack.conversationsplus.utils.StreamUtil; -import eu.siacs.conversations.Config; -import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.entities.DownloadableFile; -import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.entities.Transferable; -import eu.siacs.conversations.persistance.FileBackend; -import eu.siacs.conversations.services.AbstractConnectionManager; -import eu.siacs.conversations.services.XmppConnectionService; -import eu.siacs.conversations.ui.UiCallback; -import eu.siacs.conversations.utils.CryptoHelper; -import eu.siacs.conversations.utils.Xmlns; -import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.xmpp.OnIqPacketReceived; -import eu.siacs.conversations.xmpp.jid.Jid; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; - -public class HttpUploadConnection implements Transferable { - - private HttpConnectionManager mHttpConnectionManager; - private XmppConnectionService mXmppConnectionService; - - private boolean canceled = false; - private boolean delayed = false; - private Account account; - private DownloadableFile file; - private Message message; - private String mime; - private URL mGetUrl; - private URL mPutUrl; - - private byte[] key = null; - - private long transmitted = 0; - - private InputStream mFileInputStream; - - public HttpUploadConnection(HttpConnectionManager httpConnectionManager) { - this.mHttpConnectionManager = httpConnectionManager; - this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); - } - - @Override - public boolean start() { - return false; - } - - @Override - public int getStatus() { - return STATUS_UPLOADING; - } - - @Override - public long getFileSize() { - return file == null ? 0 : file.getExpectedSize(); - } - - @Override - public int getProgress() { - if (file == null) { - return 0; - } - return (int) ((((double) transmitted) / file.getExpectedSize()) * 100); - } - - @Override - public void cancel() { - this.canceled = true; - } - - private void fail() { - mHttpConnectionManager.finishUploadConnection(this); - message.setTransferable(null); - MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED); - StreamUtil.close(mFileInputStream); - } - - public void init(Message message, boolean delay) { - this.message = message; - this.message.setHttpUploaded(true); - this.message.setNoDownloadable(); - this.account = message.getConversation().getAccount(); - this.file = FileBackend.getFile(message, false); - this.mime = this.file.getMimeType(); - this.delayed = delay; - if (Config.ENCRYPT_ON_HTTP_UPLOADED - || message.getEncryption() == Message.ENCRYPTION_AXOLOTL - || message.getEncryption() == Message.ENCRYPTION_OTR) { - this.key = new byte[48]; - mXmppConnectionService.getRNG().nextBytes(this.key); - this.file.setKeyAndIv(this.key); - } - Pair<InputStream,Integer> pair; - try { - pair = AbstractConnectionManager.createInputStream(file, true); - } catch (FileNotFoundException e) { - fail(); - return; - } - this.file.setExpectedSize(pair.second); - this.mFileInputStream = pair.first; - Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD); - IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host,file,mime); - mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { - Element slot = packet.findChild("slot",Xmlns.HTTP_UPLOAD); - if (slot != null) { - try { - mGetUrl = new URL(slot.findChildContent("get")); - mPutUrl = new URL(slot.findChildContent("put")); - if (!canceled) { - new Thread(new FileUploader()).start(); - } - } catch (MalformedURLException e) { - fail(); - } - } else { - fail(); - } - } else { - fail(); - } - } - }); - message.setTransferable(this); - MessageUtil.markMessage(message, Message.STATUS_UNSEND); - } - - private class FileUploader implements Runnable { - - @Override - public void run() { - this.upload(); - } - - private void upload() { - OutputStream os = null; - InputStream errorStream = null; - HttpURLConnection connection = null; - PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock("http_upload_"+message.getUuid()); - try { - wakeLock.acquire(); - Logging.d(Config.LOGTAG, "uploading to " + mPutUrl.toString()); - connection = (HttpURLConnection) mPutUrl.openConnection(); - - if (connection instanceof HttpsURLConnection) { - mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); - } - connection.setRequestMethod("PUT"); - connection.setFixedLengthStreamingMode((int) file.getExpectedSize()); - connection.setRequestProperty("Content-Type", mime == null ? "application/octet-stream" : mime); - connection.setRequestProperty("User-Agent", ConversationsPlusApplication.getNameAndVersion()); - connection.setDoOutput(true); - connection.connect(); - os = connection.getOutputStream(); - transmitted = 0; - int count = -1; - byte[] buffer = new byte[4096]; - while (((count = mFileInputStream.read(buffer)) != -1) && !canceled) { - transmitted += count; - os.write(buffer, 0, count); - mXmppConnectionService.updateConversationUi(); - } - os.flush(); - os.close(); - mFileInputStream.close(); - int code = connection.getResponseCode(); - if (code == 200 || code == 201) { - Logging.d(Config.LOGTAG, "finished uploading file"); - if (key != null) { - mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key)); - } - MessageUtil.updateFileParams(message, mGetUrl); - FileBackend.updateMediaScanner(file, mXmppConnectionService); - message.setTransferable(null); - message.setCounterpart(message.getConversation().getJid().toBareJid()); - if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() { - @Override - public void success(Message message) { - mXmppConnectionService.resendMessage(message,delayed); - } - - @Override - public void error(int errorCode, Message object) { - fail(); - } - - @Override - public void userInputRequried(PendingIntent pi, Message object) { - fail(); - } - }); - } else { - mXmppConnectionService.resendMessage(message, delayed); - } - } else { - errorStream = connection.getErrorStream(); - Logging.e("httpupload", "file upload failed: http code (" + code + ") " + new Scanner(errorStream).useDelimiter("\\A").next()); - fail(); - } - } catch (IOException e) { - errorStream = (null != connection) ? connection.getErrorStream() : null; - String httpResponseMessage = null; - if (null != errorStream) { - httpResponseMessage = new Scanner(errorStream).useDelimiter("\\A").next(); - } - Logging.e("httpupload", ((null != httpResponseMessage) ? ("http response: " + httpResponseMessage + ", ") : "") + "exception message: " + e.getMessage()); - fail(); - } finally { - StreamUtil.close(os); - StreamUtil.close(errorStream); - if (connection != null) { - connection.disconnect(); - } - wakeLock.release(); - } - } - } -} diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index c79110c9..571a29b4 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -3,6 +3,8 @@ package eu.siacs.conversations.parser; import android.util.Log; import android.util.Pair; +import de.thedevstack.conversationsplus.entities.FileParams; +import de.thedevstack.conversationsplus.enums.FileStatus; import de.thedevstack.conversationsplus.utils.MessageUtil; import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint; import de.tzur.conversations.Settings; @@ -10,7 +12,6 @@ import de.tzur.conversations.Settings; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; -import java.net.URL; import java.util.ArrayList; import java.util.Set; @@ -473,7 +474,11 @@ public class MessageParser extends AbstractParser implements && message.treatAsDownloadable() != Message.Decision.NEVER && ConversationsPlusPreferences.autoAcceptFileSize() > 0 && (message.isHttpUploaded() || ConversationsPlusPreferences.autoDownloadFileLink())) { - this.mXmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message); + FileParams fileParams = new FileParams(); + fileParams.setFileStatus(FileStatus.NEEDS_DOWNLOAD); + fileParams.setUrl(message.getBody()); + message.setFileParams(fileParams); + HttpConnectionManager.createNewDownloadConnection(message); } else { if (query == null) { mXmppConnectionService.getNotificationService().push(message); diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index 76da5a31..3d933364 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -17,17 +17,14 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.Presence; -import eu.siacs.conversations.entities.Presences; -import eu.siacs.conversations.entities.ServiceDiscoveryResult; import eu.siacs.conversations.generator.PresenceGenerator; +import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.pep.Avatar; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public class PresenceParser extends AbstractParser implements @@ -186,14 +183,19 @@ public class PresenceParser extends AbstractParser implements final String resource = from.isBareJid() ? "" : from.getResourcepart(); contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick")); Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); - if (avatar != null && !contact.isSelf()) { + if (avatar != null && (!contact.isSelf() || null == account.getAvatar())) { avatar.owner = from.toBareJid(); if (AvatarUtil.isAvatarCached(avatar)) { - if (contact.setAvatar(avatar)) { + if (avatar.owner.equals(account.getJid().toBareJid())) { + account.setAvatar(avatar.getFilename()); + DatabaseBackend.getInstance().updateAccount(account); + AvatarService.getInstance().clear(account); + UiUpdateHelper.updateAccountUi(); + } else if (contact.setAvatar(avatar)) { AvatarService.getInstance().clear(contact); - UiUpdateHelper.updateConversationUi(); UiUpdateHelper.updateRosterUi(); } + UiUpdateHelper.updateConversationUi(); } else { AvatarService.getInstance().fetchAvatar(account, avatar); } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index bd20e694..793b050f 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -34,7 +34,11 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.json.JSONException; import de.thedevstack.android.logcat.Logging; -import de.thedevstack.conversationsplus.persistance.MessageDatabaseAccess; + +import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import de.thedevstack.conversationsplus.persistance.db.access.CursorHelper; +import de.thedevstack.conversationsplus.persistance.db.access.MessageDatabaseAccess; +import de.thedevstack.conversationsplus.utils.SimpleCryptoUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlServiceImpl; @@ -56,7 +60,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static final String DATABASE_NAME = "history"; private static final int DATABASE_VERSION = 25; private static final int C_TO_CPLUS_VERSION_OFFSET = 1000; - private static final int CPLUS_DATABASE_VERSION = 1; + private static final int CPLUS_DATABASE_VERSION = 3; private static final int CPLUS_DATABASE_VERSION_MULTIPLIER = 100; private static final int PHYSICAL_DATABASE_VERSION = DATABASE_VERSION + C_TO_CPLUS_VERSION_OFFSET + (CPLUS_DATABASE_VERSION * CPLUS_DATABASE_VERSION_MULTIPLIER); @@ -192,18 +196,31 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT); db.execSQL(CREATE_IDENTITIES_STATEMENT); - // Create Conversations+ related tables - db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0); + MessageDatabaseAccess.create(db); } protected void onUpgradeCPlusDatabase(SQLiteDatabase db, int oldVersion, int newVersion) { Logging.d("db.upgrade.cplus", "Updating Conversations+ database from version '" + oldVersion + "' to '" + newVersion + "'"); if (oldVersion < newVersion) { - if (oldVersion == 0 && newVersion == 1) { - Logging.d("db.upgrade.cplus", "Creating additional parameters table for messages."); - db.execSQL(MessageDatabaseAccess.TABLE_ADDITIONAL_PARAMETERS_CREATE_V0); - db.execSQL("INSERT INTO " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + "(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") " - + " SELECT " + Message.UUID + " FROM " + Message.TABLENAME); + if (oldVersion < 1 && newVersion >= 1) { + MessageDatabaseAccess.upgrade(db, oldVersion, newVersion); + } + if (oldVersion < 2 && newVersion >= 2) { + Logging.d("db.upgrade.cplus", "Encrypt all passwords for the first time"); + Cursor cursor = db.rawQuery("SELECT " + Account.UUID + ", " + Account.PASSWORD + " FROM " + Account.TABLENAME, new String[0]); + while (cursor.moveToNext()) { + String uuid = CursorHelper.getString(cursor, Account.UUID); + String password = CursorHelper.getString(cursor, Account.PASSWORD); + String encryptedPassword = SimpleCryptoUtil.encrypt(Account.PW_SEED, password); + ContentValues values = new ContentValues(); + values.put(Account.PASSWORD, encryptedPassword); + db.update(Account.TABLENAME, values, Account.UUID + "=?", new String[] {uuid}); + } + cursor.close(); + } + + if (oldVersion < 3 && newVersion >= 3) { + MessageDatabaseAccess.upgrade(db, oldVersion, newVersion); } } } @@ -422,6 +439,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { } } + public static synchronized DatabaseBackend getInstance() { + return getInstance(ConversationsPlusApplication.getAppContext()); + } + public static synchronized DatabaseBackend getInstance(Context context) { if (instance == null) { instance = new DatabaseBackend(context); @@ -645,15 +666,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { } public void updateMessage(Message message) { - Logging.d("db.msg.update", "Updating message with uuid '" + message.getUuid() + "', isRead: " + message.isRead()); - SQLiteDatabase db = this.getWritableDatabase(); - String[] args = {message.getUuid()}; - db.beginTransaction(); - db.update(Message.TABLENAME, message.getContentValues(), Message.UUID - + "=?", args); - db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args); - db.setTransactionSuccessful(); - db.endTransaction(); + this.updateMessage(message, message.getUuid()); } public void updateMessage(Message message, String uuid) { @@ -664,7 +677,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.beginTransaction(); db.update(Message.TABLENAME, message.getContentValues(), Message.UUID + "=?", args); - db.update(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, MessageDatabaseAccess.getAdditionalParametersContentValues(message), MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", args); + MessageDatabaseAccess.updateMessageParameters(db, message, uuid); db.setTransactionSuccessful(); db.endTransaction(); } diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 8abbb0cb..6f99ef55 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -20,9 +20,12 @@ import java.util.Locale; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.entities.FileParams; import de.thedevstack.conversationsplus.exceptions.FileCopyException; import de.thedevstack.conversationsplus.persistance.observers.FileDeletionObserver; import de.thedevstack.conversationsplus.utils.StreamUtil; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; @@ -90,10 +93,10 @@ public class FileBackend { } } - public static boolean deleteFile(Message message, XmppConnectionService xmppConnectionService) { + public static boolean deleteFile(Message message) { File file = getFile(message); if (file.delete()) { - updateMediaScanner(file, xmppConnectionService); + updateMediaScanner(file, XmppConnectionServiceAccessor.xmppConnectionService); return true; } else { return false; @@ -105,31 +108,55 @@ public class FileBackend { } public static DownloadableFile getFile(Message message, boolean decrypted) { - final boolean encrypted = !decrypted - && (message.getEncryption() == Message.ENCRYPTION_PGP - || message.getEncryption() == Message.ENCRYPTION_DECRYPTED); - final DownloadableFile file; - String path = message.getRelativeFilePath(); - if (path == null) { - path = message.getUuid(); - } - if (path.startsWith("/")) { - file = new DownloadableFile(path); - } else { - String mime = message.getMimeType(); - if (mime != null && mime.startsWith("image")) { - file = new DownloadableFile(getConversationsImageDirectory() + path); - } else { - file = new DownloadableFile(getConversationsFileDirectory() + path); - } - } - if (encrypted) { - return new DownloadableFile(getConversationsFileDirectory() + file.getName() + ".pgp"); - } else { - return file; - } + return new DownloadableFile(getFilePath(message, decrypted)); } + protected static String getFilePath(Message message, String extension) { + String path = FileBackend.getFilePath(message, true); + if (!path.endsWith(extension)) { + path += "." + extension; + } + + return path; + } + + protected static String getFilePath(Message message, Uri uri) { + String mime = ConversationsPlusApplication.getInstance().getContentResolver().getType(uri); + String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime); + + return getFilePath(message, extension); + } + + protected static String getFilePath(Message message, boolean decrypted) { + final boolean encrypted = !decrypted + && (message.getEncryption() == Message.ENCRYPTION_PGP + || message.getEncryption() == Message.ENCRYPTION_DECRYPTED); + FileParams fileParams = message.getFileParams(); + if (null == fileParams) { + fileParams = new FileParams(); + message.setFileParams(fileParams); + } + String path = fileParams.getPath(); + + if (null == path) { // File does not yet exist + path = message.getUuid(); + String mime = message.getMimeType(); + if (mime != null && mime.startsWith("image")) { // TODO: Check if this can be determined in a better way + path = getConversationsImageDirectory() + path; + } else { + path = getConversationsFileDirectory() + path; + } + String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime); + path += "." + extension; + + fileParams.setPath(path); + } + if (encrypted) { + path += ".pgp"; + } + return path; + } + public static String getConversationsFileDirectory() { return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ConversationsPlusPreferences.fileTransferFolder() + File.separator; } @@ -174,14 +201,16 @@ public class FileBackend { public static void copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException { Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage"); - String mime = ConversationsPlusApplication.getInstance().getContentResolver().getType(uri); - String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime); - message.setRelativeFilePath(message.getUuid() + "." + extension); + String path = getFilePath(message, uri); + message.getFileParams().setPath(path); + message.setRelativeFilePath(path); // TODO: Remove copyFileToPrivateStorage(getFile(message), uri); } public static DownloadableFile compressImageAndCopyToPrivateStorage(Message message, Bitmap scaledBitmap) throws FileCopyException { - message.setRelativeFilePath(FileBackend.getPrivateImageDirectoryPath() + message.getUuid() + ".jpg"); + String path = getFilePath(message, "jpg"); + message.getFileParams().setPath(path); + message.setRelativeFilePath(path); // TODO: Remove DownloadableFile file = getFile(message); file.getParentFile().mkdirs(); OutputStream os = null; diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java index 7728c38a..dbee8720 100644 --- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java @@ -30,23 +30,16 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; + import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.DownloadableFile; public class AbstractConnectionManager { - protected XmppConnectionService mXmppConnectionService; - - public AbstractConnectionManager(XmppConnectionService service) { - this.mXmppConnectionService = service; - } - - public XmppConnectionService getXmppConnectionService() { - return this.mXmppConnectionService; - } public boolean hasStoragePermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - return mXmppConnectionService.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + return ConversationsPlusApplication.getAppContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } else { return true; } @@ -125,9 +118,4 @@ public class AbstractConnectionManager { return null; } } - - public PowerManager.WakeLock createWakeLock(String name) { - PowerManager powerManager = (PowerManager) mXmppConnectionService.getSystemService(Context.POWER_SERVICE); - return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,name); - } } diff --git a/src/main/java/eu/siacs/conversations/services/ExportLogsService.java b/src/main/java/eu/siacs/conversations/services/ExportLogsService.java index 53d0caaf..38c8e1d4 100644 --- a/src/main/java/eu/siacs/conversations/services/ExportLogsService.java +++ b/src/main/java/eu/siacs/conversations/services/ExportLogsService.java @@ -103,7 +103,7 @@ public class ExportLogsService extends Service { break; } if (jid != null) { - String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody(); + String body = message.hasFileOnRemoteHost() ? message.getFileParams().getUrl() : message.getBody(); bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid, body.replace("\\\n", "\\ \n").replace("\n", "\\ \n"))); } diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java index 613d7150..e76eb16b 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java @@ -9,6 +9,8 @@ import java.util.Iterator; import java.util.List; import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; @@ -290,7 +292,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { this.account = account; this.start = start; this.end = end; - this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32); + this.queryId = new BigInteger(50, ConversationsPlusApplication.getSecureRandom()).toString(32); } private Query page(String reference) { diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 12c11dd0..d11661fa 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -356,7 +356,8 @@ public class NotificationService { if (message.getType() != Message.TYPE_TEXT && message.getTransferable() == null && message.getEncryption() != Message.ENCRYPTION_PGP - && message.getFileParams().height > 0) { + && message.getFileParams() != null + && message.getFileParams().getHeight() > 0) { // TODO Use FileParams.getMimeType() return message; } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 5f7a9a9e..90f9027a 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -12,7 +12,6 @@ import android.graphics.Bitmap; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -58,13 +57,14 @@ import de.duenndns.ssl.MemorizingTrustManager; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; -import de.thedevstack.conversationsplus.exceptions.FileCopyException; +import de.thedevstack.conversationsplus.services.filetransfer.FileTransferManager; import de.thedevstack.conversationsplus.utils.ImageUtil; import de.thedevstack.conversationsplus.utils.MessageUtil; import de.thedevstack.conversationsplus.utils.UiUpdateHelper; import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; import de.thedevstack.conversationsplus.utils.XmppSendUtil; import de.tzur.conversations.Settings; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; @@ -97,7 +97,6 @@ import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.FileUtils; import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener; -import eu.siacs.conversations.utils.PRNGFixes; import eu.siacs.conversations.utils.PhoneHelper; import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.xml.Element; @@ -122,6 +121,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; + import me.leolin.shortcutbadger.ShortcutBadger; public class XmppConnectionService extends Service implements OnPhoneContactsLoadedListener { @@ -148,7 +148,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa startService(intent); } }; - private MemorizingTrustManager mMemorizingTrustManager; private NotificationService mNotificationService = new NotificationService( this); private OnMessagePacketReceived mMessageParser = new MessageParser(this); @@ -169,8 +168,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private MessageGenerator mMessageGenerator = new MessageGenerator(); private PresenceGenerator mPresenceGenerator = new PresenceGenerator(); private List<Account> accounts; - private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( - this); + private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(); public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() { @Override @@ -197,8 +195,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } }; - private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager( - this); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private PushManagementService mPushManagementService = new PushManagementService(this); private OnConversationUpdate mOnConversationUpdate = null; @@ -239,7 +235,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private int mucRosterChangedListenerCount = 0; private OnKeyStatusUpdated mOnKeyStatusUpdated = null; private int keyStatusUpdatedListenerCount = 0; - private SecureRandom mRandom; private LruCache<Pair<String,String>,ServiceDiscoveryResult> discoCache = new LruCache<>(20); private final OnBindListener mOnBindListener = new OnBindListener() { @@ -311,7 +306,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa && checkListeners(); Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": push mode "+Boolean.toString(pushMode)); if (!disabled && !pushMode) { - int timeToReconnect = mRandom.nextInt(20) + 10; + int timeToReconnect = ConversationsPlusApplication.getSecureRandom().nextInt(20) + 10; scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode()); } } else if (account.getStatus() == Account.State.REGISTRATION_SUCCESSFUL) { @@ -334,8 +329,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private OpenPgpServiceConnection pgpServiceConnection; private PgpEngine mPgpEngine = null; private WakeLock wakeLock; - private PowerManager pm; - private LruCache<String, Bitmap> mBitmapCache; private Thread mPhoneContactMergerThread; private EventReceiver mEventReceiver = new EventReceiver(); @@ -577,17 +570,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onCreate() { ExceptionHelper.init(getApplicationContext()); - PRNGFixes.apply(); - this.mRandom = new SecureRandom(); - updateMemorizingTrustmanager(); final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize = maxMemory / 8; - this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) { - @Override - protected int sizeOf(final String key, final Bitmap bitmap) { - return bitmap.getByteCount() / 1024; - } - }; this.databaseBackend = DatabaseBackend.getInstance(getApplicationContext()); this.accounts = databaseBackend.getAccounts(); @@ -614,8 +598,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.pgpServiceConnection.bindToService(); } - this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "XmppConnectionService"); + this.wakeLock = ConversationsPlusApplication.createPartialWakeLock("XmppConnectionService"); toggleForegroundService(); updateUnreadCountBadge(); UiUpdateHelper.initXmppConnectionService(this); @@ -628,7 +611,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa super.onTrimMemory(level); if (level >= TRIM_MEMORY_COMPLETE) { Log.d(Config.LOGTAG, "clear cache due to low memory"); - getBitmapCache().evictAll(); + ImageUtil.evictBitmapCache(); } } @@ -739,16 +722,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } - private void sendFileMessage(final Message message, final boolean delay) { - Logging.d(Config.LOGTAG, "send file message"); - final Account account = message.getConversation().getAccount(); - if (account.httpUploadAvailable(FileBackend.getFile(message,false).getSize())) { - mHttpConnectionManager.createNewUploadConnection(message, delay); - } else { - mJingleConnectionManager.createNewConnection(message); - } - } - public void sendMessage(final Message message) { sendMessage(message, false, false); } @@ -776,28 +749,23 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } if (account.isOnlineAndConnected()) { + FileTransferManager fileTransferManager = FileTransferManager.getInstance(); switch (message.getEncryption()) { case Message.ENCRYPTION_NONE: - if (message.needsUploading()) { - if (account.httpUploadAvailable(FileBackend.getFile(message,false).getSize()) - || message.fixCounterpart()) { - this.sendFileMessage(message, delay); - } else { - break; - } + if (fileTransferManager.accept(message)) { + if (!fileTransferManager.transferFile(message, delay)) { + break; + } } else { packet = mMessageGenerator.generateChat(message); } break; case Message.ENCRYPTION_PGP: case Message.ENCRYPTION_DECRYPTED: - if (message.needsUploading()) { - if (account.httpUploadAvailable(FileBackend.getFile(message,false).getSize()) - || message.fixCounterpart()) { - this.sendFileMessage(message, delay); - } else { - break; - } + if (fileTransferManager.accept(message)) { + if (!fileTransferManager.transferFile(message, delay)) { + break; + } } else { packet = mMessageGenerator.generatePgpChat(message); } @@ -810,7 +778,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } catch (InvalidJidException e) { break; } - if (message.needsUploading()) { + if (message.needsUploading()) { //TODO: Use FileTransferManager with a preselection of filetransfer method mJingleConnectionManager.createNewConnection(message); } else { packet = mMessageGenerator.generateOtrChat(message); @@ -828,13 +796,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa break; case Message.ENCRYPTION_AXOLOTL: message.setFingerprint(account.getAxolotlService().getOwnFingerprint()); - if (message.needsUploading()) { - if (account.httpUploadAvailable(FileBackend.getFile(message,false).getSize()) - || message.fixCounterpart()) { - this.sendFileMessage(message, delay); - } else { - break; - } + if (fileTransferManager.accept(message)) { + if (!fileTransferManager.transferFile(message, delay)) { + break; + } } else { XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message); if (axolotlMessage == null) { @@ -1331,7 +1296,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.onAccountCreated(account); if (Config.X509_VERIFICATION) { try { - getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); + ConversationsPlusApplication.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); } catch (CertificateException e) { callback.informUser(R.string.certificate_chain_is_not_trusted); } @@ -1359,7 +1324,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa databaseBackend.updateAccount(account); if (Config.X509_VERIFICATION) { try { - getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); + ConversationsPlusApplication.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA"); } catch (CertificateException e) { showErrorToastInUi(R.string.certificate_chain_is_not_trusted); } @@ -1901,7 +1866,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } return; } - String name = new BigInteger(75, getRNG()).toString(32); + String name = new BigInteger(75, ConversationsPlusApplication.getSecureRandom()).toString(32); Jid jid = Jid.fromParts(name, server, null); final Conversation conversation = findOrCreateConversation(account, jid, true); joinMuc(conversation, new OnConferenceJoined() { @@ -2170,7 +2135,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public boolean renewSymmetricKey(Conversation conversation) { Account account = conversation.getAccount(); byte[] symmetricKey = new byte[32]; - this.mRandom.nextBytes(symmetricKey); + ConversationsPlusApplication.getSecureRandom().nextBytes(symmetricKey); Session otrSession = conversation.getOtrSession(); if (otrSession != null) { MessagePacket packet = new MessagePacket(); @@ -2440,36 +2405,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } - public SecureRandom getRNG() { - return this.mRandom; - } - - public MemorizingTrustManager getMemorizingTrustManager() { - return this.mMemorizingTrustManager; - } - - public void setMemorizingTrustManager(MemorizingTrustManager trustManager) { - this.mMemorizingTrustManager = trustManager; - } - - public void updateMemorizingTrustmanager() { - final MemorizingTrustManager tm; - if (ConversationsPlusPreferences.dontTrustSystemCAs()) { - tm = new MemorizingTrustManager(getApplicationContext(), null); - } else { - tm = new MemorizingTrustManager(getApplicationContext()); - } - setMemorizingTrustManager(tm); - } - - public PowerManager getPowerManager() { - return this.pm; - } - - public LruCache<String, Bitmap> getBitmapCache() { - return this.mBitmapCache; - } - public void syncRosterToDisk(final Account account) { Runnable runnable = new Runnable() { @@ -2599,10 +2534,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return this.mNotificationService; } - public HttpConnectionManager getHttpConnectionManager() { - return this.mHttpConnectionManager; - } - public void resendFailedMessages(final Message message) { if (message.getStatus() == Message.STATUS_SEND_FAILED) { message.setTime(System.currentTimeMillis()); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index f7e53112..2a7432dd 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -49,6 +49,7 @@ import de.thedevstack.conversationsplus.ui.dialogs.UserDecisionDialog; import de.thedevstack.conversationsplus.ui.listeners.ResizePictureUserDecisionListener; import de.thedevstack.conversationsplus.utils.ConversationUtil; import de.timroes.android.listview.EnhancedListView; + import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; @@ -60,6 +61,7 @@ import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Transferable; +import eu.siacs.conversations.http.HttpConnectionManager; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; @@ -620,7 +622,7 @@ public class ConversationActivity extends XmppActivity Toast.makeText(this, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show(); } } else if (message.treatAsDownloadable() != Message.Decision.NEVER) { - xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message, true); + HttpConnectionManager.createNewDownloadConnection(message, true); } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 6d170787..5068af8d 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -8,7 +8,6 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.support.annotation.Nullable; @@ -24,8 +23,6 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.ImageButton; @@ -49,7 +46,12 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.UUID; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; +import de.thedevstack.conversationsplus.services.filetransfer.http.delete.DeleteRemoteFileService; +import de.thedevstack.conversationsplus.ui.dialogs.SimpleConfirmDialog; import de.thedevstack.conversationsplus.ui.dialogs.MessageDetailsDialog; +import de.thedevstack.conversationsplus.ui.listeners.DeleteFileCallback; +import de.thedevstack.conversationsplus.ui.listeners.SimpleUserDecisionCallback; +import de.thedevstack.conversationsplus.ui.listeners.UserDecisionListener; import de.thedevstack.conversationsplus.utils.MessageUtil; import eu.siacs.conversations.Config; @@ -64,6 +66,7 @@ import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.Presence; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.TransferablePlaceholder; +import eu.siacs.conversations.http.HttpConnectionManager; import eu.siacs.conversations.http.HttpDownloadConnection; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.XmppConnectionService; @@ -580,12 +583,19 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa deleteFile.setVisible(true); deleteFile.setTitle(activity.getString(R.string.delete_x_file,UIHelper.getFileDescriptionString(activity, m))); } + if (m.isHttpUploaded() && MessageUtil.isMessageSent(m)) { + MenuItem deleteRemoteFile = menu.findItem(R.id.msg_ctx_menu_delete_remote_file); + deleteRemoteFile.setVisible(true); + } } } @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.msg_ctx_menu_delete_remote_file: + new SimpleConfirmDialog(getActivity(), R.string.cplus_are_you_sure, new DeleteRemoteFileService(selectedMessage)).show(); + return true; case R.id.msg_ctx_mnu_details: new MessageDetailsDialog(getActivity(), selectedMessage).show(); return true; @@ -611,7 +621,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa retryDecryption(selectedMessage); return true; case R.id.delete_file: - deleteFile(selectedMessage); + new SimpleConfirmDialog(getActivity(), R.string.cplus_are_you_sure, new DeleteFileCallback(selectedMessage)).show(); return true; default: return super.onContextItemSelected(item); @@ -650,13 +660,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } } - private void deleteFile(Message message) { - if (FileBackend.deleteFile(message, activity.xmppConnectionService)) { - message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); - activity.xmppConnectionService.updateConversationUi(); - } - } - private void resendMessage(Message message) { if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) { DownloadableFile file = FileBackend.getFile(message); @@ -678,7 +681,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa url = message.getBody(); } else if (message.hasFileOnRemoteHost()) { resId = R.string.file_url; - url = message.getFileParams().url.toString(); + url = message.getFileParams().getUrl(); } else { url = message.getBody(); resId = R.string.file_url; @@ -690,8 +693,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } private void downloadFile(Message message) { - activity.xmppConnectionService.getHttpConnectionManager() - .createNewDownloadConnection(message,true); + HttpConnectionManager.createNewDownloadConnection(message,true); } private void cancelTransmission(Message message) { diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index 30f71229..6dd8a84f 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Locale; import de.duenndns.ssl.MemorizingTrustManager; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.tzur.conversations.Settings; import eu.siacs.conversations.R; @@ -73,7 +74,7 @@ public class SettingsActivity extends XmppActivity implements removeCertsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - final MemorizingTrustManager mtm = xmppConnectionService.getMemorizingTrustManager(); + final MemorizingTrustManager mtm = ConversationsPlusApplication.getMemorizingTrustManager(); final ArrayList<String> aliases = Collections.list(mtm.getCertificates()); if (aliases.size() == 0) { displayToast(getString(R.string.toast_no_trusted_certs)); @@ -180,7 +181,7 @@ public class SettingsActivity extends XmppActivity implements xmppConnectionService.refreshAllPresences(); } } else if (name.equals("dont_trust_system_cas")) { - xmppConnectionService.updateMemorizingTrustmanager(); + ConversationsPlusApplication.updateMemorizingTrustmanager(); reconnectAccounts(); } else if ("parse_emoticons".equals(name)) { EmojiconHandler.setParseEmoticons(Settings.PARSE_EMOTICONS); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java index 0567de06..4ea7d7cf 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java @@ -91,7 +91,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { convName.setTypeface(null, Typeface.NORMAL); } - if (message.getFileParams().width > 0 + if ((null != message.getFileParams() && message.getFileParams().getWidth() > 0) // TODO: Use FileParams.getMimeType() && (message.getTransferable() == null || message.getTransferable().getStatus() != Transferable.STATUS_DELETED)) { mLastMessage.setVisibility(View.GONE); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 9d2917d5..509e3b9b 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -20,6 +20,7 @@ import android.text.style.StyleSpan; import android.text.util.Linkify; import android.util.DisplayMetrics; import android.util.Patterns; +import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -41,16 +42,19 @@ import java.util.regex.Pattern; import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.ConversationsPlusColors; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; -import eu.siacs.conversations.providers.ConversationsPlusFileProvider; +import de.thedevstack.conversationsplus.entities.FileParams; +import de.thedevstack.conversationsplus.enums.FileStatus; +import de.thedevstack.conversationsplus.utils.MessageUtil; + import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.entities.Message.FileParams; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.providers.ConversationsPlusFileProvider; import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.utils.CryptoHelper; @@ -138,13 +142,17 @@ public class MessageAdapter extends ArrayAdapter<Message> { boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI && message.getStatus() <= Message.STATUS_RECEIVED; - if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) { + if (message.hasFileAttached() || message.getTransferable() != null) { FileParams params = message.getFileParams(); - if (params.size > (1.5 * 1024 * 1024)) { - filesize = params.size / (1024 * 1024)+ " MiB"; - } else if (params.size > 0) { - filesize = params.size / 1024 + " KiB"; - } + if (null != params) { + long size = params.getSize(); + if (size > (1.5 * 1024 * 1024)) { + filesize = size / (1024 * 1024)+ " MiB"; + } else if (size > 0) { + filesize = size / 1024 + " KiB"; + } + } + if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) { error = true; } @@ -247,6 +255,25 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.time.setText(formatedTime); } } + + if (message.hasFileAttached() && null != message.getFileParams() && null != viewHolder.remoteFileStatus) { + FileStatus fileStatus = message.getFileParams().getFileStatus(); + if (fileStatus == FileStatus.DELETE_FAILED || fileStatus == FileStatus.DELETED || fileStatus == FileStatus.DELETING) { + viewHolder.remoteFileStatus.setVisibility(View.VISIBLE); + viewHolder.remoteFileStatus.setTypeface(null, Typeface.ITALIC); + switch (fileStatus) { + case DELETE_FAILED: + viewHolder.remoteFileStatus.setText(R.string.remote_filestatus_delete_failed); + break; + case DELETED: + viewHolder.remoteFileStatus.setText(R.string.remote_filestatus_delete_success); + break; + case DELETING: + viewHolder.remoteFileStatus.setText(R.string.remote_filestatus_delete_inprogress); + break; + } + } + } } private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) { @@ -434,9 +461,10 @@ public class MessageAdapter extends ArrayAdapter<Message> { } viewHolder.messageBody.setVisibility(View.GONE); viewHolder.image.setVisibility(View.VISIBLE); - FileParams params = message.getFileParams(); //TODO: Check what value add the following lines have (compared with setting height/width in XmppActivity.loadBitmap from thumbnail after thumbnail is created) - /*double target = metrics.density * 288; + /* + FileParams params = message.getFileParams(); + double target = metrics.density * 288; int scalledW; int scalledH; if (params.width <= params.height) { @@ -491,6 +519,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { .findViewById(R.id.message_time); viewHolder.indicatorReceived = (ImageView) view .findViewById(R.id.indicator_received); + viewHolder.remoteFileStatus = (TextView) view.findViewById(R.id.remote_file_status); break; case RECEIVED: view = activity.getLayoutInflater().inflate( @@ -585,7 +614,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { } else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) { displayImageMessage(viewHolder, message); } else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) { - if (message.getFileParams().width > 0) { + if (message.getFileParams() != null && message.getFileParams().getWidth() > 0) { // TODO Use FileParams.getMimeType() displayImageMessage(viewHolder,message); } else { displayOpenableMessage(viewHolder, message); @@ -612,12 +641,20 @@ public class MessageAdapter extends ArrayAdapter<Message> { } } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { displayDecryptionFailed(viewHolder,darkBackground); - } else { + } else if (message.hasFileAttached()) { // TODO: Move to a single block with Message.TYPE_FILE OR Message.TYPE_IMAGE + FileParams fileParams = message.getFileParams(); + String mimeType = (null != fileParams) ? fileParams.getMimeType() : null; + if (null != mimeType && mimeType.startsWith("image/")) { + displayImageMessage(viewHolder, message); + } else { + displayOpenableMessage(viewHolder, message); + } + } else { if (GeoHelper.isGeoUri(message.getBody())) { displayLocationMessage(viewHolder,message); - } else if (message.treatAsDownloadable() == Message.Decision.MUST) { + } else if (MessageUtil.needsDownload(message)) { try { - URL url = new URL(message.getBody()); + URL url = new URL(message.getFileParams().getUrl()); displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize_on_host, @@ -724,7 +761,8 @@ public class MessageAdapter extends ArrayAdapter<Message> { protected ImageView contact_picture; protected TextView status_message; protected TextView encryption; - } + public TextView remoteFileStatus; + } class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index 1568eb8c..9caa53f7 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -135,15 +135,20 @@ public class DNSHelper { Logging.d("dns", "using dns server: " + dnsServerHostAddress + " to look up " + qname); try { DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServerHostAddress); - Record[] rrset = message.getAnswers(); - for (Record rr : rrset) { - Data d = rr.getPayload(); - if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) { - SRV srv = (SRV) d; - SrvRecord srvRecord = new SrvRecord(srv.getPriority(), srv.getName(), srv.getPort(), tlsSrvRecord); - result.add(srvRecord); + if (null != message) { + Record[] rrset = message.getAnswers(); + for (Record rr : rrset) { + Data d = rr.getPayload(); + if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) { + SRV srv = (SRV) d; + SrvRecord srvRecord = new SrvRecord(srv.getPriority(), srv.getName(), srv.getPort(), tlsSrvRecord); + result.add(srvRecord); + } } + } else { + Logging.e("dns", "No valid DNS message retrieved."); } + } catch (IOException e) { Logging.d("dns", "Error while retrieving SRV record '" + qname + "' for '" + host + "' from DNS '" + dnsServerHostAddress + "': " + e.getMessage()); } diff --git a/src/main/java/eu/siacs/conversations/utils/FileUtils.java b/src/main/java/eu/siacs/conversations/utils/FileUtils.java index 1f2a71ca..18014894 100644 --- a/src/main/java/eu/siacs/conversations/utils/FileUtils.java +++ b/src/main/java/eu/siacs/conversations/utils/FileUtils.java @@ -17,6 +17,8 @@ import java.util.List; import de.thedevstack.conversationsplus.ConversationsPlusApplication; +import eu.siacs.conversations.entities.Transferable; + public final class FileUtils { /** @@ -160,6 +162,27 @@ public final class FileUtils { return "com.android.providers.media.documents".equals(uri.getAuthority()); } + public static String getRelevantExtension(String path) { + if (path == null || path.isEmpty()) { + return null; + } + + String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase(); + + final String lastPart = FileUtils.getLastExtension(filename); + + if (!lastPart.isEmpty()) { + // we want the real file extension, not the crypto one + final String secondToLastPart = FileUtils.getSecondToLastExtension(filename); + if (!secondToLastPart.isEmpty() && Transferable.VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { + return secondToLastPart; + } else { + return lastPart; + } + } + return null; + } + /** * @param filename The filename to extract extension from * @return last extension or empty string diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index a45cce01..a5d0321d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -2,9 +2,6 @@ package eu.siacs.conversations.xmpp; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.os.Bundle; -import android.os.Parcelable; -import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; import android.security.KeyChain; @@ -53,6 +50,7 @@ import javax.net.ssl.X509TrustManager; import de.duenndns.ssl.MemorizingTrustManager; import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import de.thedevstack.conversationsplus.dto.SrvRecord; import eu.siacs.conversations.Config; @@ -203,8 +201,7 @@ public class XmppConnection implements Runnable { public XmppConnection(final Account account, final XmppConnectionService service) { this.account = account; - this.wakeLock = service.getPowerManager().newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString()); + this.wakeLock = ConversationsPlusApplication.createPartialWakeLock(account.getJid().toBareJid().toString()); tagWriter = new TagWriter(); mXmppConnectionService = service; } @@ -386,14 +383,14 @@ public class XmppConnection implements Runnable { private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException { final SSLContext sc = SSLSocketHelper.getSSLContext(); - MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager(); + MemorizingTrustManager trustManager = ConversationsPlusApplication.getMemorizingTrustManager(); KeyManager[] keyManager; if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) { keyManager = new KeyManager[]{mKeyManager}; } else { keyManager = null; } - sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, mXmppConnectionService.getRNG()); + sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, ConversationsPlusApplication.getSecureRandom()); final SSLSocketFactory factory = sc.getSocketFactory(); final HostnameVerifier verifier; if (mInteractive) { @@ -741,13 +738,13 @@ public class XmppConnection implements Runnable { final Element auth = new Element("auth"); auth.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) { - saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG()); + saslMechanism = new External(tagWriter, account, ConversationsPlusApplication.getSecureRandom()); } else if (mechanisms.contains("SCRAM-SHA-1")) { - saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG()); + saslMechanism = new ScramSha1(tagWriter, account, ConversationsPlusApplication.getSecureRandom()); } else if (mechanisms.contains("PLAIN")) { saslMechanism = new Plain(tagWriter, account); } else if (mechanisms.contains("DIGEST-MD5")) { - saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG()); + saslMechanism = new DigestMd5(tagWriter, account, ConversationsPlusApplication.getSecureRandom()); } if (saslMechanism != null) { final JSONObject keys = account.getKeys(); @@ -1175,7 +1172,7 @@ public class XmppConnection implements Runnable { } private String nextRandomId() { - return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32); + return new BigInteger(50, ConversationsPlusApplication.getSecureRandom()).toString(32); } public String sendIqPacket(final IqPacket packet, final OnIqPacketReceived callback) { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index f0431869..fca0597f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -1,14 +1,11 @@ package eu.siacs.conversations.xmpp.jingle; -import android.content.Intent; -import android.net.Uri; import android.util.Pair; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -19,6 +16,8 @@ import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import de.thedevstack.conversationsplus.utils.MessageUtil; import de.thedevstack.conversationsplus.utils.StreamUtil; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; + import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.OnMessageCreatedCallback; @@ -157,8 +156,7 @@ public class JingleConnection implements Transferable { public JingleConnection(JingleConnectionManager mJingleConnectionManager) { this.mJingleConnectionManager = mJingleConnectionManager; - this.mXmppConnectionService = mJingleConnectionManager - .getXmppConnectionService(); + this.mXmppConnectionService = XmppConnectionServiceAccessor.xmppConnectionService; } public String getSessionId() { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index c7865292..19ac354d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -33,10 +33,6 @@ public class JingleConnectionManager extends AbstractConnectionManager { @SuppressLint("TrulyRandom") private SecureRandom random = new SecureRandom(); - public JingleConnectionManager(XmppConnectionService service) { - super(service); - } - public void deliverPacket(Account account, JinglePacket packet) { if (packet.isAction("session-initiate")) { JingleConnection connection = new JingleConnection(this); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index 76cd0c87..318a35f7 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -14,6 +14,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.utils.StreamUtil; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.DownloadableFile; @@ -84,7 +85,7 @@ public class JingleSocks5Transport extends JingleTransport { @Override public void run() { InputStream fileInputStream = null; - final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_"+connection.getSessionId()); + final PowerManager.WakeLock wakeLock = ConversationsPlusApplication.createPartialWakeLock("jingle_send_"+connection.getSessionId()); try { wakeLock.acquire(); MessageDigest digest = MessageDigest.getInstance("SHA-1"); @@ -134,7 +135,7 @@ public class JingleSocks5Transport extends JingleTransport { @Override public void run() { OutputStream fileOutputStream = null; - final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_"+connection.getSessionId()); + final PowerManager.WakeLock wakeLock = ConversationsPlusApplication.createPartialWakeLock("jingle_receive_"+connection.getSessionId()); try { wakeLock.acquire(); MessageDigest digest = MessageDigest.getInstance("SHA-1"); |