From 9276eff1db69a52a761721709cd5fd1617098747 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 28 Sep 2019 21:52:07 +0200 Subject: [PATCH 01/36] delete omemo keys when deleting account --- .../siacs/conversations/crypto/axolotl/AxolotlService.java | 7 +++++++ .../conversations/services/XmppConnectionService.java | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index b804f4222..59ff20fe5 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -839,6 +839,13 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { }); } + public void deleteOmemoIdentity() { + final String node = AxolotlService.PEP_BUNDLES + ":" + getOwnDeviceId(); + final IqPacket deleteBundleNode = mXmppConnectionService.getIqGenerator().deleteNode(node); + mXmppConnectionService.sendIqPacket(account, deleteBundleNode, null); + publishDeviceIdsAndRefineAccessModel(getOwnDeviceIds()); + } + public List getCryptoTargets(Conversation conversation) { final List jids; if (conversation.getMode() == Conversation.MODE_SINGLE) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 68db573a9..cacb1404e 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -2199,6 +2199,7 @@ public class XmppConnectionService extends Service { public void deleteAccount(final Account account) { synchronized (this.conversations) { + account.getAxolotlService().deleteOmemoIdentity(); for (final Conversation conversation : conversations) { if (conversation.getAccount() == account) { if (conversation.getMode() == Conversation.MODE_MULTI) { @@ -2209,7 +2210,7 @@ public class XmppConnectionService extends Service { } } if (account.getXmppConnection() != null) { - new Thread(() -> disconnect(account, true)).start(); + new Thread(() -> disconnect(account, false)).start(); } final Runnable runnable = () -> { if (!databaseBackend.deleteAccount(account)) { From 863ac7f2e5a9d94048c622d881a99edd27466e10 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 28 Sep 2019 23:56:02 +0200 Subject: [PATCH 02/36] show resource prompt when sending uncompressed video --- .../eu/siacs/conversations/persistance/FileBackend.java | 4 +++- .../services/AttachFileToConversationRunnable.java | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 509d4b193..fe6530b66 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -56,6 +56,7 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.services.AttachFileToConversationRunnable; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.RecordingActivity; import eu.siacs.conversations.ui.util.Attachment; @@ -111,6 +112,7 @@ public class FileBackend { } public static boolean allFilesUnderSize(Context context, List attachments, long max) { + final boolean compressVideo = !AttachFileToConversationRunnable.getVideoCompression(context).equals("uncompressed"); if (max <= 0) { Log.d(Config.LOGTAG, "server did not report max file size for http upload"); return true; //exception to be compatible with HTTP Upload < v0.2 @@ -120,7 +122,7 @@ public class FileBackend { continue; } String mime = attachment.getMime(); - if (mime != null && mime.startsWith("video/")) { + if (mime != null && mime.startsWith("video/") && compressVideo) { try { Dimensions dimensions = FileBackend.getVideoDimensions(context, attachment.getUri()); if (dimensions.getMin() > 720) { diff --git a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java index 21ab5fb00..eb3380396 100644 --- a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java +++ b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.services; +import android.content.Context; import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; @@ -177,7 +178,11 @@ public class AttachFileToConversationRunnable implements Runnable, MediaTranscod } private String getVideoCompression() { - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mXmppConnectionService); - return preferences.getString("video_compression", mXmppConnectionService.getResources().getString(R.string.video_compression)); + return getVideoCompression(mXmppConnectionService); + } + + public static String getVideoCompression(final Context context) { + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getString("video_compression", context.getResources().getString(R.string.video_compression)); } } From 7b160a358e01403357aca0ffe5ac6907815ed9e0 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 28 Sep 2019 23:56:29 +0200 Subject: [PATCH 03/36] do not add 'quote' when sharing own msgs --- src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java b/src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java index 89dbd0a25..459be645b 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java +++ b/src/main/java/eu/siacs/conversations/ui/util/ShareUtil.java @@ -53,11 +53,10 @@ public class ShareUtil { if (message.isGeoUri()) { shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody()); shareIntent.setType("text/plain"); - shareIntent.putExtra(ConversationsActivity.EXTRA_AS_QUOTE, true); } else if (!message.isFileOrImage()) { shareIntent.putExtra(Intent.EXTRA_TEXT, message.getMergedBody().toString()); shareIntent.setType("text/plain"); - shareIntent.putExtra(ConversationsActivity.EXTRA_AS_QUOTE, true); + shareIntent.putExtra(ConversationsActivity.EXTRA_AS_QUOTE, message.getStatus() == Message.STATUS_RECEIVED); } else { final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); try { From 618d892ae7a4b31eeb69bc2a6a5db3a866bf0c7f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 29 Sep 2019 01:40:40 +0200 Subject: [PATCH 04/36] account deletion: only attempt to delete omemo id when connected --- .../services/XmppConnectionService.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index cacb1404e..a5348e4e3 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -2198,19 +2198,24 @@ public class XmppConnectionService extends Service { } public void deleteAccount(final Account account) { + final boolean connected = account.getStatus() == Account.State.ONLINE; synchronized (this.conversations) { - account.getAxolotlService().deleteOmemoIdentity(); - for (final Conversation conversation : conversations) { - if (conversation.getAccount() == account) { - if (conversation.getMode() == Conversation.MODE_MULTI) { - leaveMuc(conversation); - } - conversations.remove(conversation); - mNotificationService.clear(conversation); - } - } + if (connected) { + account.getAxolotlService().deleteOmemoIdentity(); + } + for (final Conversation conversation : conversations) { + if (conversation.getAccount() == account) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + if (connected) { + leaveMuc(conversation); + } + } + conversations.remove(conversation); + mNotificationService.clear(conversation); + } + } if (account.getXmppConnection() != null) { - new Thread(() -> disconnect(account, false)).start(); + new Thread(() -> disconnect(account, !connected)).start(); } final Runnable runnable = () -> { if (!databaseBackend.deleteAccount(account)) { From aef394c9f0e6e82d522bb43809f6d60e5c739206 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 29 Sep 2019 02:00:06 +0200 Subject: [PATCH 05/36] ability to open files from media preview. fixes #3521 --- .../persistance/FileBackend.java | 8 ++++++++ .../ui/adapter/MediaPreviewAdapter.java | 20 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index fe6530b66..37168016a 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -193,6 +193,14 @@ public class FileBackend { return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/"; } + public static Uri getUriForUri(Context context, Uri uri) { + if ("file".equals(uri.getScheme())) { + return getUriForFile(context, new File(uri.getPath())); + } else { + return uri; + } + } + public static Uri getUriForFile(Context context, File file) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N || Config.ONLY_INTERNAL_STORAGE) { try { diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java index 405ddcb41..a2f423a04 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java @@ -1,17 +1,21 @@ package eu.siacs.conversations.ui.adapter; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; import android.databinding.DataBindingUtil; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.AsyncTask; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.Toast; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -20,6 +24,7 @@ import java.util.concurrent.RejectedExecutionException; import eu.siacs.conversations.R; import eu.siacs.conversations.databinding.MediaPreviewBinding; +import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.ui.ConversationFragment; import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.ui.util.Attachment; @@ -54,11 +59,24 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter { - int pos = mediaPreviews.indexOf(attachment); + final int pos = mediaPreviews.indexOf(attachment); mediaPreviews.remove(pos); notifyItemRemoved(pos); conversationFragment.toggleInputMethod(); }); + holder.binding.mediaPreview.setOnClickListener(v -> view(context, attachment)); + } + + private static void view(final Context context, Attachment attachment) { + final Intent view = new Intent(Intent.ACTION_VIEW); + final Uri uri = FileBackend.getUriForUri(context, attachment.getUri()); + view.setDataAndType(uri, attachment.getMime()); + view.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + try { + context.startActivity(view); + } catch (ActivityNotFoundException e) { + Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show(); + } } public void addMediaPreviews(List attachments) { From d2d9bbe3da9a948fa904094b94b89dc919840792 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 29 Sep 2019 13:32:45 +0200 Subject: [PATCH 06/36] improved jingle debugging --- .../eu/siacs/conversations/xmpp/jingle/JingleConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 919350ec8..5f03fc899 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -320,7 +320,7 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, "connection to our own proxy65 candidate failed"); + Log.d(Config.LOGTAG, String.format("connection to our own proxy65 candidate failed (%s:%d)", candidate.getHost(), candidate.getPort())); sendInitRequest(); } From f8bd4284a513e60a5f2da57b61c2023ba129582c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 1 Oct 2019 11:31:15 +0200 Subject: [PATCH 07/36] report not-acceptable on jingle errors --- .../xmpp/jingle/JingleConnection.java | 13 +- .../xmpp/jingle/JingleInbandTransport.java | 406 +++++++++--------- 2 files changed, 210 insertions(+), 209 deletions(-) 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 5f03fc899..6e8ac40c0 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -270,9 +270,10 @@ public class JingleConnection implements Transferable { IqPacket response; if (returnResult) { response = packet.generateResponse(IqPacket.TYPE.RESULT); - } else { response = packet.generateResponse(IqPacket.TYPE.ERROR); + final Element error = response.addChild("error").setAttribute("type", "cancel"); + error.addChild("not-acceptable", "urn:ietf:params:xml:ns:xmpp-stanzas"); } mXmppConnectionService.sendIqPacket(account, response, null); } @@ -933,8 +934,9 @@ public class JingleConnection implements Transferable { private boolean receiveTransportAccept(JinglePacket packet) { if (packet.getJingleContent().hasIbbTransport()) { - String receivedBlockSize = packet.getJingleContent().ibbTransport() - .getAttribute("block-size"); + final Element ibbTransport = packet.getJingleContent().ibbTransport(); + final String receivedBlockSize = ibbTransport.getAttribute("block-size"); + final String sid = ibbTransport.getAttribute("sid"); if (receivedBlockSize != null) { try { int bs = Integer.parseInt(receivedBlockSize); @@ -947,6 +949,10 @@ public class JingleConnection implements Transferable { } this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); + if (sid == null || !sid.equals(this.transportId)) { + Log.w(Config.LOGTAG,String.format("%s: sid in transport-accept (%s) did not match our sid (%s) ", account.getJid().asBareJid(), sid, transportId)); + } + //might be receive instead if we are not initiating if (initiating()) { this.transport.connect(onIbbTransportConnected); @@ -955,6 +961,7 @@ public class JingleConnection implements Transferable { } return true; } else { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received invalid transport-accept"); return false; } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index 276040979..ac22614f8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -23,223 +23,217 @@ import rocks.xmpp.addr.Jid; public class JingleInbandTransport extends JingleTransport { - private Account account; - private Jid counterpart; - private int blockSize; - private int seq = 0; - private String sessionId; + private Account account; + private Jid counterpart; + private int blockSize; + private int seq = 0; + private String sessionId; - private boolean established = false; + private boolean established = false; - private boolean connected = true; + private boolean connected = true; - private DownloadableFile file; - private JingleConnection connection; + private DownloadableFile file; + private JingleConnection connection; - private InputStream fileInputStream = null; - private InputStream innerInputStream = null; - private OutputStream fileOutputStream = null; - private long remainingSize = 0; - private long fileSize = 0; - private MessageDigest digest; + private InputStream fileInputStream = null; + private InputStream innerInputStream = null; + private OutputStream fileOutputStream = null; + private long remainingSize = 0; + private long fileSize = 0; + private MessageDigest digest; - private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged; + private OnFileTransmissionStatusChanged onFileTransmissionStatusChanged; - private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() { - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (connected && packet.getType() == IqPacket.TYPE.RESULT) { - if (remainingSize > 0) { - sendNextBlock(); - } - } - } - }; + private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (connected && packet.getType() == IqPacket.TYPE.RESULT) { + if (remainingSize > 0) { + sendNextBlock(); + } + } + } + }; - public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) { - this.connection = connection; - this.account = connection.getAccount(); - this.counterpart = connection.getCounterPart(); - this.blockSize = blocksize; - this.sessionId = sid; - } - - private void sendClose() { - IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - iq.setTo(this.counterpart); - Element close = iq.addChild("close", "http://jabber.org/protocol/ibb"); - close.setAttribute("sid", this.sessionId); - this.account.getXmppConnection().sendIqPacket(iq, null); - } - - public void connect(final OnTransportConnected callback) { - IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - iq.setTo(this.counterpart); - Element open = iq.addChild("open", "http://jabber.org/protocol/ibb"); - open.setAttribute("sid", this.sessionId); - open.setAttribute("stanza", "iq"); - open.setAttribute("block-size", Integer.toString(this.blockSize)); - this.connected = true; - this.account.getXmppConnection().sendIqPacket(iq, - new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, - IqPacket packet) { - if (packet.getType() != IqPacket.TYPE.RESULT) { - callback.failed(); - } else { - callback.established(); - } - } - }); - } - - @Override - public void receive(DownloadableFile file, OnFileTransmissionStatusChanged callback) { - this.onFileTransmissionStatusChanged = callback; - this.file = file; - try { - this.digest = MessageDigest.getInstance("SHA-1"); - digest.reset(); - this.fileOutputStream = connection.getFileOutputStream(); - if (this.fileOutputStream == null) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": could not create output stream"); - callback.onFileTransferAborted(); - return; - } - this.remainingSize = this.fileSize = file.getExpectedSize(); - } catch (final NoSuchAlgorithmException | IOException e) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+" "+e.getMessage()); - callback.onFileTransferAborted(); - } + public JingleInbandTransport(final JingleConnection connection, final String sid, final int blocksize) { + this.connection = connection; + this.account = connection.getAccount(); + this.counterpart = connection.getCounterPart(); + this.blockSize = blocksize; + this.sessionId = sid; } - @Override - public void send(DownloadableFile file, OnFileTransmissionStatusChanged callback) { - this.onFileTransmissionStatusChanged = callback; - this.file = file; - try { - this.remainingSize = this.file.getExpectedSize(); - this.fileSize = this.remainingSize; - this.digest = MessageDigest.getInstance("SHA-1"); - this.digest.reset(); - fileInputStream = connection.getFileInputStream(); - if (fileInputStream == null) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": could no create input stream"); - callback.onFileTransferAborted(); - return; - } - innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); - if (this.connected) { - this.sendNextBlock(); - } - } catch (Exception e) { - callback.onFileTransferAborted(); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": "+e.getMessage()); - } - } + private void sendClose() { + IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + iq.setTo(this.counterpart); + Element close = iq.addChild("close", "http://jabber.org/protocol/ibb"); + close.setAttribute("sid", this.sessionId); + this.account.getXmppConnection().sendIqPacket(iq, null); + } - @Override - public void disconnect() { - this.connected = false; - FileBackend.close(fileOutputStream); - FileBackend.close(fileInputStream); - } + public void connect(final OnTransportConnected callback) { + IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + iq.setTo(this.counterpart); + Element open = iq.addChild("open", "http://jabber.org/protocol/ibb"); + open.setAttribute("sid", this.sessionId); + open.setAttribute("stanza", "iq"); + open.setAttribute("block-size", Integer.toString(this.blockSize)); + this.connected = true; + this.account.getXmppConnection().sendIqPacket(iq, (account, packet) -> { + if (packet.getType() != IqPacket.TYPE.RESULT) { + callback.failed(); + } else { + callback.established(); + } + }); + } - private void sendNextBlock() { - byte[] buffer = new byte[this.blockSize]; - try { - int count = innerInputStream.read(buffer); - if (count == -1) { - sendClose(); - file.setSha1Sum(digest.digest()); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": sendNextBlock() count was -1"); - this.onFileTransmissionStatusChanged.onFileTransmitted(file); - fileInputStream.close(); - return; - } else if (count != buffer.length) { - int rem = innerInputStream.read(buffer,count,buffer.length-count); - if (rem > 0) { - count += rem; - } - } - this.remainingSize -= count; - this.digest.update(buffer,0,count); - String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP); - IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - iq.setTo(this.counterpart); - Element data = iq.addChild("data", "http://jabber.org/protocol/ibb"); - data.setAttribute("seq", Integer.toString(this.seq)); - data.setAttribute("block-size", Integer.toString(this.blockSize)); - data.setAttribute("sid", this.sessionId); - data.setContent(base64); - this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived); - this.account.getXmppConnection().r(); //don't fill up stanza queue too much - this.seq++; - connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); - if (this.remainingSize <= 0) { - sendClose(); - file.setSha1Sum(digest.digest()); - this.onFileTransmissionStatusChanged.onFileTransmitted(file); - fileInputStream.close(); - } - } catch (IOException e) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": io exception during sendNextBlock() "+e.getMessage()); - FileBackend.close(fileInputStream); - this.onFileTransmissionStatusChanged.onFileTransferAborted(); - } - } + @Override + public void receive(DownloadableFile file, OnFileTransmissionStatusChanged callback) { + this.onFileTransmissionStatusChanged = callback; + this.file = file; + try { + this.digest = MessageDigest.getInstance("SHA-1"); + digest.reset(); + this.fileOutputStream = connection.getFileOutputStream(); + if (this.fileOutputStream == null) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not create output stream"); + callback.onFileTransferAborted(); + return; + } + this.remainingSize = this.fileSize = file.getExpectedSize(); + } catch (final NoSuchAlgorithmException | IOException e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + " " + e.getMessage()); + callback.onFileTransferAborted(); + } + } - private void receiveNextBlock(String data) { - try { - byte[] buffer = Base64.decode(data, Base64.NO_WRAP); - if (this.remainingSize < buffer.length) { - buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize); - } - this.remainingSize -= buffer.length; - this.fileOutputStream.write(buffer); - this.digest.update(buffer); - if (this.remainingSize <= 0) { - file.setSha1Sum(digest.digest()); - fileOutputStream.flush(); - fileOutputStream.close(); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": receive next block nothing remaining"); - this.onFileTransmissionStatusChanged.onFileTransmitted(file); - } else { - connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); - } - } catch (Exception e) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": "+e.getMessage()); - FileBackend.close(fileOutputStream); - this.onFileTransmissionStatusChanged.onFileTransferAborted(); - } - } + @Override + public void send(DownloadableFile file, OnFileTransmissionStatusChanged callback) { + this.onFileTransmissionStatusChanged = callback; + this.file = file; + try { + this.remainingSize = this.file.getExpectedSize(); + this.fileSize = this.remainingSize; + this.digest = MessageDigest.getInstance("SHA-1"); + this.digest.reset(); + fileInputStream = connection.getFileInputStream(); + if (fileInputStream == null) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could no create input stream"); + callback.onFileTransferAborted(); + return; + } + innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); + if (this.connected) { + this.sendNextBlock(); + } + } catch (Exception e) { + callback.onFileTransferAborted(); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + e.getMessage()); + } + } - public void deliverPayload(IqPacket packet, Element payload) { - if (payload.getName().equals("open")) { - if (!established) { - established = true; - connected = true; - this.receiveNextBlock(""); - this.account.getXmppConnection().sendIqPacket( - packet.generateResponse(IqPacket.TYPE.RESULT), null); - } else { - this.account.getXmppConnection().sendIqPacket( - packet.generateResponse(IqPacket.TYPE.ERROR), null); - } - } else if (connected && payload.getName().equals("data")) { - this.receiveNextBlock(payload.getContent()); - this.account.getXmppConnection().sendIqPacket( - packet.generateResponse(IqPacket.TYPE.RESULT), null); - } else if (connected && payload.getName().equals("close")) { - this.connected = false; - this.account.getXmppConnection().sendIqPacket( - packet.generateResponse(IqPacket.TYPE.RESULT), null); - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": received ibb close"); - } else { - Log.d(Config.LOGTAG,payload.toString()); - // TODO some sort of exception - } - } + @Override + public void disconnect() { + this.connected = false; + FileBackend.close(fileOutputStream); + FileBackend.close(fileInputStream); + } + + private void sendNextBlock() { + byte[] buffer = new byte[this.blockSize]; + try { + int count = innerInputStream.read(buffer); + if (count == -1) { + sendClose(); + file.setSha1Sum(digest.digest()); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sendNextBlock() count was -1"); + this.onFileTransmissionStatusChanged.onFileTransmitted(file); + fileInputStream.close(); + return; + } else if (count != buffer.length) { + int rem = innerInputStream.read(buffer, count, buffer.length - count); + if (rem > 0) { + count += rem; + } + } + this.remainingSize -= count; + this.digest.update(buffer, 0, count); + String base64 = Base64.encodeToString(buffer, 0, count, Base64.NO_WRAP); + IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + iq.setTo(this.counterpart); + Element data = iq.addChild("data", "http://jabber.org/protocol/ibb"); + data.setAttribute("seq", Integer.toString(this.seq)); + data.setAttribute("block-size", Integer.toString(this.blockSize)); + data.setAttribute("sid", this.sessionId); + data.setContent(base64); + this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived); + this.account.getXmppConnection().r(); //don't fill up stanza queue too much + this.seq++; + connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); + if (this.remainingSize <= 0) { + sendClose(); + file.setSha1Sum(digest.digest()); + this.onFileTransmissionStatusChanged.onFileTransmitted(file); + fileInputStream.close(); + } + } catch (IOException e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": io exception during sendNextBlock() " + e.getMessage()); + FileBackend.close(fileInputStream); + this.onFileTransmissionStatusChanged.onFileTransferAborted(); + } + } + + private void receiveNextBlock(String data) { + try { + byte[] buffer = Base64.decode(data, Base64.NO_WRAP); + if (this.remainingSize < buffer.length) { + buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize); + } + this.remainingSize -= buffer.length; + this.fileOutputStream.write(buffer); + this.digest.update(buffer); + if (this.remainingSize <= 0) { + file.setSha1Sum(digest.digest()); + fileOutputStream.flush(); + fileOutputStream.close(); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": receive next block nothing remaining"); + this.onFileTransmissionStatusChanged.onFileTransmitted(file); + } else { + connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); + } + } catch (Exception e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + e.getMessage()); + FileBackend.close(fileOutputStream); + this.onFileTransmissionStatusChanged.onFileTransferAborted(); + } + } + + public void deliverPayload(IqPacket packet, Element payload) { + if (payload.getName().equals("open")) { + if (!established) { + established = true; + connected = true; + this.receiveNextBlock(""); + this.account.getXmppConnection().sendIqPacket( + packet.generateResponse(IqPacket.TYPE.RESULT), null); + } else { + this.account.getXmppConnection().sendIqPacket( + packet.generateResponse(IqPacket.TYPE.ERROR), null); + } + } else if (connected && payload.getName().equals("data")) { + this.receiveNextBlock(payload.getContent()); + this.account.getXmppConnection().sendIqPacket( + packet.generateResponse(IqPacket.TYPE.RESULT), null); + } else if (connected && payload.getName().equals("close")) { + this.connected = false; + this.account.getXmppConnection().sendIqPacket( + packet.generateResponse(IqPacket.TYPE.RESULT), null); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received ibb close"); + } else { + Log.d(Config.LOGTAG, payload.toString()); + // TODO some sort of exception + } + } } From 37b87e18eef256d12d1236f63309c77c8c0915bc Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 1 Oct 2019 12:24:57 +0200 Subject: [PATCH 08/36] fix NPE when using channel search and DOMAIN_LOCK closes #3458 --- .../conversations/ui/ChannelDiscoveryActivity.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java index 10bd4ced1..cef8d71a2 100644 --- a/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ChannelDiscoveryActivity.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding; import eu.siacs.conversations.entities.Account; @@ -217,18 +218,19 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O return false; } - public void joinChannelSearchResult(String accountJid, MuclumbusService.Room result) { - final boolean syncAutojoin = getBooleanPreference("autojoin", R.bool.autojoin); - Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid)); + public void joinChannelSearchResult(String selectedAccount, MuclumbusService.Room result) { + final Jid jid = Config.DOMAIN_LOCK == null ? Jid.of(selectedAccount) : Jid.of(selectedAccount, Config.DOMAIN_LOCK, null); + final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin); + final Account account = xmppConnectionService.findAccountByJid(jid); final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true); if (conversation.getBookmark() != null) { - if (!conversation.getBookmark().autojoin() && syncAutojoin) { + if (!conversation.getBookmark().autojoin() && syncAutoJoin) { conversation.getBookmark().setAutojoin(true); xmppConnectionService.pushBookmarks(account); } } else { final Bookmark bookmark = new Bookmark(account, conversation.getJid().asBareJid()); - bookmark.setAutojoin(syncAutojoin); + bookmark.setAutojoin(syncAutoJoin); account.getBookmarks().add(bookmark); xmppConnectionService.pushBookmarks(account); } From 322352ccbf69581e4d3111dd7778ebc639d948d4 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 1 Oct 2019 12:42:12 +0200 Subject: [PATCH 09/36] use new jabber.search.network endpoint --- src/main/java/eu/siacs/conversations/Config.java | 2 +- src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index ac637d76f..a842fc403 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -41,7 +41,7 @@ public final class Config { public static final String MAGIC_CREATE_DOMAIN = "conversations.im"; public static final String QUICKSY_DOMAIN = "quicksy.im"; - public static final String CHANNEL_DISCOVERY = "https://search.jabbercat.org"; + public static final String CHANNEL_DISCOVERY = "https://search.jabber.network"; public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index b818fad2e..11bbb6cf5 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -861,7 +861,7 @@ Discover channels Search channels Possible privacy violation! - search.jabbercat.org.

Using this feature will transmit your IP address and search terms to that service. See their Privacy Policy for more information.]]>
+ search.jabber.network.

Using this feature will transmit your IP address and search terms to that service. See their Privacy Policy for more information.]]>
I already have an account Add existing account Register new account From 7c631c493a7b55b95afd66d1a27083a36c61ea8b Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 1 Oct 2019 16:44:53 +0200 Subject: [PATCH 10/36] Use dark navigation bar in QR scanner activity (#3551) * Use dark navigation bar on dark theme This approach uses `tools:targetApi` instead of separate theme file and avoids lint errors. * Use dark navigation bar in QR scanner activity This is consistent with the black background that is already used in that activity. --- src/main/res/values-v21/themes.xml | 6 ------ src/main/res/values/themes.xml | 8 ++++---- 2 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 src/main/res/values-v21/themes.xml diff --git a/src/main/res/values-v21/themes.xml b/src/main/res/values-v21/themes.xml deleted file mode 100644 index d559dcbe7..000000000 --- a/src/main/res/values-v21/themes.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 8866e9931..b03c2476e 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -1,5 +1,5 @@ - + -