From 783ed53d3a97276173fd752e5b7a19ac492189bc Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 1 Sep 2019 11:34:36 +0200 Subject: [PATCH 01/13] order canditates by priority before attempting to connect --- .../xmpp/jingle/JingleConnection.java | 12 +- .../xmpp/jingle/JingleSocks5Transport.java | 322 +++++++++--------- 2 files changed, 168 insertions(+), 166 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 d06c2469f..e229c11ed 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -11,6 +11,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; @@ -303,7 +304,7 @@ public class JingleConnection implements Transferable { if (this.initialTransport == Transport.IBB) { this.sendInitRequest(); } else if (this.candidates.size() > 0) { - this.sendInitRequest(); + this.sendInitRequest(); //TODO we will never get here? Can probably be removed } else { this.mJingleConnectionManager.getPrimaryCandidate(account, (success, candidate) -> { if (success) { @@ -635,7 +636,7 @@ public class JingleConnection implements Transferable { private boolean receiveAccept(JinglePacket packet) { if (this.mJingleStatus != JINGLE_STATUS_INITIATED) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": received out of order session-accept"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received out of order session-accept"); return false; } this.mJingleStatus = JINGLE_STATUS_ACCEPTED; @@ -654,7 +655,7 @@ public class JingleConnection implements Transferable { this.ibbBlockSize = bs; } } catch (Exception e) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": unable to parse block size in session-accept"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to parse block size in session-accept"); } } this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); @@ -850,7 +851,7 @@ public class JingleConnection implements Transferable { this.ibbBlockSize = bs; } } catch (NumberFormatException e) { - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": unable to parse block size in transport-replace"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to parse block size in transport-replace"); } } this.transportId = packet.getJingleContent().getTransportId(); @@ -889,7 +890,7 @@ public class JingleConnection implements Transferable { this.ibbBlockSize = bs; } } catch (NumberFormatException e) { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": unable to parse block size in transport-accept"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to parse block size in transport-accept"); } } this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize); @@ -1087,6 +1088,7 @@ public class JingleConnection implements Transferable { } private void mergeCandidates(List candidates) { + Collections.sort(candidates, (a, b) -> Integer.compare(b.getPriority(), a.getPriority())); for (JingleCandidate c : candidates) { mergeCandidate(c); } 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 99c12ba3a..4181335f6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -22,177 +22,177 @@ import eu.siacs.conversations.utils.WakeLockHelper; import eu.siacs.conversations.xmpp.jingle.stanzas.Content; public class JingleSocks5Transport extends JingleTransport { - private JingleCandidate candidate; - private JingleConnection connection; - private String destination; - private OutputStream outputStream; - private InputStream inputStream; - private boolean isEstablished = false; - private boolean activated = false; - private Socket socket; + private final JingleCandidate candidate; + private final JingleConnection connection; + private final String destination; + private OutputStream outputStream; + private InputStream inputStream; + private boolean isEstablished = false; + private boolean activated = false; + private Socket socket; - JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) { - this.candidate = candidate; - this.connection = jingleConnection; - try { - MessageDigest mDigest = MessageDigest.getInstance("SHA-1"); - StringBuilder destBuilder = new StringBuilder(); - if (jingleConnection.getFtVersion() == Content.Version.FT_3) { - Log.d(Config.LOGTAG, this.connection.getAccount().getJid().asBareJid() + ": using session Id instead of transport Id for proxy destination"); - destBuilder.append(jingleConnection.getSessionId()); - } else { - destBuilder.append(jingleConnection.getTransportId()); - } - if (candidate.isOurs()) { - destBuilder.append(jingleConnection.getAccount().getJid()); - destBuilder.append(jingleConnection.getCounterPart()); - } else { - destBuilder.append(jingleConnection.getCounterPart()); - destBuilder.append(jingleConnection.getAccount().getJid()); - } - mDigest.reset(); - this.destination = CryptoHelper.bytesToHex(mDigest - .digest(destBuilder.toString().getBytes())); - } catch (NoSuchAlgorithmException e) { + JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) { + final MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + this.candidate = candidate; + this.connection = jingleConnection; + final StringBuilder destBuilder = new StringBuilder(); + if (jingleConnection.getFtVersion() == Content.Version.FT_3) { + Log.d(Config.LOGTAG, this.connection.getAccount().getJid().asBareJid() + ": using session Id instead of transport Id for proxy destination"); + destBuilder.append(jingleConnection.getSessionId()); + } else { + destBuilder.append(jingleConnection.getTransportId()); + } + if (candidate.isOurs()) { + destBuilder.append(jingleConnection.getAccount().getJid()); + destBuilder.append(jingleConnection.getCounterPart()); + } else { + destBuilder.append(jingleConnection.getCounterPart()); + destBuilder.append(jingleConnection.getAccount().getJid()); + } + messageDigest.reset(); + this.destination = CryptoHelper.bytesToHex(messageDigest.digest(destBuilder.toString().getBytes())); + } - } - } + public void connect(final OnTransportConnected callback) { + new Thread(() -> { + try { + final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect(); + if (useTor) { + socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(), candidate.getPort()); + } else { + socket = new Socket(); + SocketAddress address = new InetSocketAddress(candidate.getHost(), candidate.getPort()); + socket.connect(address, Config.SOCKET_TIMEOUT * 1000); + } + inputStream = socket.getInputStream(); + outputStream = socket.getOutputStream(); + SocksSocketFactory.createSocksConnection(socket, destination, 0); + isEstablished = true; + callback.established(); + } catch (IOException e) { + callback.failed(); + } + }).start(); - public void connect(final OnTransportConnected callback) { - new Thread(() -> { - try { - final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect(); - if (useTor) { - socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(), candidate.getPort()); - } else { - socket = new Socket(); - SocketAddress address = new InetSocketAddress(candidate.getHost(), candidate.getPort()); - socket.connect(address, Config.SOCKET_TIMEOUT * 1000); - } - inputStream = socket.getInputStream(); - outputStream = socket.getOutputStream(); - SocksSocketFactory.createSocksConnection(socket, destination, 0); - isEstablished = true; - callback.established(); - } catch (IOException e) { - callback.failed(); - } - }).start(); + } - } + public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { + new Thread(() -> { + InputStream fileInputStream = null; + final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId()); + try { + wakeLock.acquire(); + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + digest.reset(); + fileInputStream = connection.getFileInputStream(); + if (fileInputStream == null) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create input stream"); + callback.onFileTransferAborted(); + return; + } + final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); + long size = file.getExpectedSize(); + long transmitted = 0; + int count; + byte[] buffer = new byte[8192]; + while ((count = innerInputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, count); + digest.update(buffer, 0, count); + transmitted += count; + connection.updateProgress((int) ((((double) transmitted) / size) * 100)); + } + outputStream.flush(); + file.setSha1Sum(digest.digest()); + if (callback != null) { + callback.onFileTransmitted(file); + } + } catch (Exception e) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage()); + callback.onFileTransferAborted(); + } finally { + FileBackend.close(fileInputStream); + WakeLockHelper.release(wakeLock); + } + }).start(); - public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { - new Thread(() -> { - InputStream fileInputStream = null; - final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId()); - try { - wakeLock.acquire(); - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - digest.reset(); - fileInputStream = connection.getFileInputStream(); - if (fileInputStream == null) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create input stream"); - callback.onFileTransferAborted(); - return; - } - final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); - long size = file.getExpectedSize(); - long transmitted = 0; - int count; - byte[] buffer = new byte[8192]; - while ((count = innerInputStream.read(buffer)) > 0) { - outputStream.write(buffer, 0, count); - digest.update(buffer, 0, count); - transmitted += count; - connection.updateProgress((int) ((((double) transmitted) / size) * 100)); - } - outputStream.flush(); - file.setSha1Sum(digest.digest()); - if (callback != null) { - callback.onFileTransmitted(file); - } - } catch (Exception e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage()); - callback.onFileTransferAborted(); - } finally { - FileBackend.close(fileInputStream); - WakeLockHelper.release(wakeLock); - } - }).start(); + } - } + public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { + new Thread(() -> { + OutputStream fileOutputStream = null; + final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_" + connection.getSessionId()); + try { + wakeLock.acquire(); + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + digest.reset(); + //inputStream.skip(45); + socket.setSoTimeout(30000); + fileOutputStream = connection.getFileOutputStream(); + if (fileOutputStream == null) { + callback.onFileTransferAborted(); + Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create output stream"); + return; + } + double size = file.getExpectedSize(); + long remainingSize = file.getExpectedSize(); + byte[] buffer = new byte[8192]; + int count; + while (remainingSize > 0) { + count = inputStream.read(buffer); + if (count == -1) { + callback.onFileTransferAborted(); + Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": file ended prematurely with " + remainingSize + " bytes remaining"); + return; + } else { + fileOutputStream.write(buffer, 0, count); + digest.update(buffer, 0, count); + remainingSize -= count; + } + connection.updateProgress((int) (((size - remainingSize) / size) * 100)); + } + fileOutputStream.flush(); + fileOutputStream.close(); + file.setSha1Sum(digest.digest()); + callback.onFileTransmitted(file); + } catch (Exception e) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage()); + callback.onFileTransferAborted(); + } finally { + WakeLockHelper.release(wakeLock); + FileBackend.close(fileOutputStream); + FileBackend.close(inputStream); + } + }).start(); + } - public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { - new Thread(() -> { - OutputStream fileOutputStream = null; - final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_" + connection.getSessionId()); - try { - wakeLock.acquire(); - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - digest.reset(); - //inputStream.skip(45); - socket.setSoTimeout(30000); - fileOutputStream = connection.getFileOutputStream(); - if (fileOutputStream == null) { - callback.onFileTransferAborted(); - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create output stream"); - return; - } - double size = file.getExpectedSize(); - long remainingSize = file.getExpectedSize(); - byte[] buffer = new byte[8192]; - int count; - while (remainingSize > 0) { - count = inputStream.read(buffer); - if (count == -1) { - callback.onFileTransferAborted(); - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": file ended prematurely with " + remainingSize + " bytes remaining"); - return; - } else { - fileOutputStream.write(buffer, 0, count); - digest.update(buffer, 0, count); - remainingSize -= count; - } - connection.updateProgress((int) (((size - remainingSize) / size) * 100)); - } - fileOutputStream.flush(); - fileOutputStream.close(); - file.setSha1Sum(digest.digest()); - callback.onFileTransmitted(file); - } catch (Exception e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage()); - callback.onFileTransferAborted(); - } finally { - WakeLockHelper.release(wakeLock); - FileBackend.close(fileOutputStream); - FileBackend.close(inputStream); - } - }).start(); - } + public boolean isProxy() { + return this.candidate.getType() == JingleCandidate.TYPE_PROXY; + } - public boolean isProxy() { - return this.candidate.getType() == JingleCandidate.TYPE_PROXY; - } + public boolean needsActivation() { + return (this.isProxy() && !this.activated); + } - public boolean needsActivation() { - return (this.isProxy() && !this.activated); - } + public void disconnect() { + FileBackend.close(inputStream); + FileBackend.close(outputStream); + FileBackend.close(socket); + } - public void disconnect() { - FileBackend.close(inputStream); - FileBackend.close(outputStream); - FileBackend.close(socket); - } + public boolean isEstablished() { + return this.isEstablished; + } - public boolean isEstablished() { - return this.isEstablished; - } + public JingleCandidate getCandidate() { + return this.candidate; + } - public JingleCandidate getCandidate() { - return this.candidate; - } - - public void setActivated(boolean activated) { - this.activated = activated; - } + public void setActivated(boolean activated) { + this.activated = activated; + } } From 1c413edf0686f0347cd113116f5e1485016672b7 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 1 Sep 2019 15:06:59 +0200 Subject: [PATCH 02/13] bare minimum direct connections --- .../persistance/FileBackend.java | 10 +++ .../utils/SocksSocketFactory.java | 12 +++ .../xmpp/jingle/DirectConnectionUtils.java | 52 +++++++++++ .../xmpp/jingle/JingleCandidate.java | 4 +- .../xmpp/jingle/JingleConnection.java | 18 ++-- .../xmpp/jingle/JingleSocks5Transport.java | 89 +++++++++++++++++++ 6 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 8c77bdad4..509d4b193 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -39,6 +39,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.ServerSocket; import java.net.Socket; import java.net.URL; import java.security.DigestOutputStream; @@ -359,6 +360,15 @@ public class FileBackend { } } + public static void close(final ServerSocket socket) { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + } + } + } + public static boolean weOwnFile(Context context, Uri uri) { if (uri == null || !ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { return false; diff --git a/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java b/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java index 81f936538..eab9acfcb 100644 --- a/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java +++ b/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java @@ -20,6 +20,9 @@ public class SocksSocketFactory { proxyOs.write(new byte[]{0x05, 0x01, 0x00}); byte[] response = new byte[2]; proxyIs.read(response); + if (response[0] != 0x05 || response[1] != 0x00) { + throw new SocksConnectionException(); + } byte[] dest = destination.getBytes(); ByteBuffer request = ByteBuffer.allocate(7 + dest.length); request.put(new byte[]{0x05, 0x01, 0x00, 0x03}); @@ -34,6 +37,15 @@ public class SocksSocketFactory { } } + public static boolean contains(byte needle, byte[] haystack) { + for(byte hay : haystack) { + if (hay == needle) { + return true; + } + } + return false; + } + public static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException { Socket socket = new Socket(); try { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java new file mode 100644 index 000000000..41a9455b7 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java @@ -0,0 +1,52 @@ +package eu.siacs.conversations.xmpp.jingle; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.UUID; + +import rocks.xmpp.addr.Jid; + +public class DirectConnectionUtils { + + private static List getLocalAddresses() { + final List addresses = new ArrayList<>(); + final Enumeration interfaces; + try { + interfaces = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + return addresses; + } + while (interfaces.hasMoreElements()) { + NetworkInterface networkInterface = interfaces.nextElement(); + final Enumeration inetAddressEnumeration = networkInterface.getInetAddresses(); + while (inetAddressEnumeration.hasMoreElements()) { + final InetAddress inetAddress = inetAddressEnumeration.nextElement(); + if (!inetAddress.isLoopbackAddress()) { + addresses.add(inetAddress); + } + } + } + return addresses; + } + + public static List getLocalCandidates(Jid jid) { + SecureRandom random = new SecureRandom(); + ArrayList candidates = new ArrayList<>(); + for (InetAddress inetAddress : getLocalAddresses()) { + final JingleCandidate candidate = new JingleCandidate(UUID.randomUUID().toString(), true); + candidate.setHost(inetAddress.getHostAddress()); + candidate.setPort(random.nextInt(60000) + 1024); + candidate.setType(JingleCandidate.TYPE_DIRECT); + candidate.setJid(jid); + candidate.setPriority(8257536 + candidates.size()); + candidates.add(candidate); + } + return candidates; + } + +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java index d48804691..3f69980aa 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java @@ -127,7 +127,9 @@ public class JingleCandidate { element.setAttribute("cid", this.getCid()); element.setAttribute("host", this.getHost()); element.setAttribute("port", Integer.toString(this.getPort())); - element.setAttribute("jid", this.getJid().toString()); + if (jid != null) { + element.setAttribute("jid", jid.toEscapedString()); + } element.setAttribute("priority", Integer.toString(this.getPriority())); if (this.getType() == TYPE_DIRECT) { element.setAttribute("type", "direct"); 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 e229c11ed..a9ac6e7df 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -11,7 +11,6 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; @@ -303,9 +302,15 @@ public class JingleConnection implements Transferable { this.transportId = this.mJingleConnectionManager.nextRandomId(); if (this.initialTransport == Transport.IBB) { this.sendInitRequest(); - } else if (this.candidates.size() > 0) { - this.sendInitRequest(); //TODO we will never get here? Can probably be removed } else { + + final List directCandidates = DirectConnectionUtils.getLocalCandidates(account.getJid()); + for (JingleCandidate directCandidate : directCandidates) { + final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, directCandidate); + connections.put(directCandidate.getCid(), socksConnection); + candidates.add(directCandidate); + } + this.mJingleConnectionManager.getPrimaryCandidate(account, (success, candidate) -> { if (success) { final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); @@ -690,7 +695,7 @@ public class JingleConnection implements Transferable { onProxyActivated.failed(); return true; } else if (content.socks5transport().hasChild("candidate-error")) { - Log.d(Config.LOGTAG, "received candidate error"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received candidate error"); this.receivedCandidate = true; if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) { this.connect(); @@ -728,7 +733,7 @@ public class JingleConnection implements Transferable { final JingleSocks5Transport connection = chooseConnection(); this.transport = connection; if (connection == null) { - Log.d(Config.LOGTAG, "could not find suitable candidate"); + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": could not find suitable candidate"); this.disconnectSocks5Connections(); if (initiating()) { this.sendFallbackToIbb(); @@ -755,6 +760,7 @@ public class JingleConnection implements Transferable { .setContent(this.getCounterPart().toString()); mXmppConnectionService.sendIqPacket(account, activation, (account, response) -> { if (response.getType() != IqPacket.TYPE.RESULT) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + response.toString()); onProxyActivated.failed(); } else { onProxyActivated.success(); @@ -1052,7 +1058,7 @@ public class JingleConnection implements Transferable { } private void sendCandidateError() { - Log.d(Config.LOGTAG, "sending candidate error"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending candidate error"); JinglePacket packet = bootstrapPacket("transport-info"); Content content = new Content(this.contentCreator, this.contentName); content.setTransportId(this.transportId); 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 4181335f6..2db161271 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -6,9 +6,12 @@ import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -29,6 +32,7 @@ public class JingleSocks5Transport extends JingleTransport { private InputStream inputStream; private boolean isEstablished = false; private boolean activated = false; + private ServerSocket serverSocket; private Socket socket; JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) { @@ -56,6 +60,88 @@ public class JingleSocks5Transport extends JingleTransport { } messageDigest.reset(); this.destination = CryptoHelper.bytesToHex(messageDigest.digest(destBuilder.toString().getBytes())); + if (candidate.isOurs() && candidate.getType() == JingleCandidate.TYPE_DIRECT) { + createServerSocket(); + } + } + + private void createServerSocket() { + try { + serverSocket = new ServerSocket(); + serverSocket.bind(new InetSocketAddress(InetAddress.getByName(candidate.getHost()), candidate.getPort())); + new Thread(() -> { + try { + final Socket socket = serverSocket.accept(); + new Thread(() -> { + try { + acceptIncomingSocketConnection(socket); + } catch (IOException e) { + Log.d(Config.LOGTAG,"unable to read from socket",e); + + } + }).start(); + } catch (IOException e) { + if (!serverSocket.isClosed()) { + Log.d(Config.LOGTAG, "unable to accept socket", e); + } + } + }).start(); + } catch (IOException e) { + Log.d(Config.LOGTAG,"unable to bind server socket ",e); + } + } + + private void acceptIncomingSocketConnection(Socket socket) throws IOException { + Log.d(Config.LOGTAG, "accepted connection from " + socket.getInetAddress().getHostAddress()); + byte[] authBegin = new byte[2]; + InputStream inputStream = socket.getInputStream(); + OutputStream outputStream = socket.getOutputStream(); + inputStream.read(authBegin); + if (authBegin[0] != 0x5) { + socket.close(); + } + short methodCount = authBegin[1]; + byte[] methods = new byte[methodCount]; + inputStream.read(methods); + if (SocksSocketFactory.contains((byte) 0x00, methods)) { + outputStream.write(new byte[]{0x05,0x00}); + } else { + outputStream.write(new byte[]{0x05,(byte) 0xff}); + } + byte[] connectCommand = new byte[4]; + inputStream.read(connectCommand); + if (connectCommand[0] == 0x05 && connectCommand[1] == 0x01 && connectCommand[3] == 0x03) { + int destinationCount = inputStream.read(); + byte[] destination = new byte[destinationCount]; + inputStream.read(destination); + int port = inputStream.read(); + final String receivedDestination = new String(destination); + Log.d(Config.LOGTAG, "received destination " + receivedDestination + ":" + port + " - expected " + this.destination); + final ByteBuffer response = ByteBuffer.allocate(7 + destination.length); + final byte[] responseHeader; + final boolean success; + if (receivedDestination.equals(this.destination)) { + responseHeader = new byte[]{0x05, 0x00, 0x00, 0x03}; + success = true; + } else { + responseHeader = new byte[]{0x05, 0x04, 0x00, 0x03}; + success = false; + } + response.put(responseHeader); + response.put((byte) destination.length); + response.put(destination); + response.putShort((short) port); + outputStream.write(response.array()); + outputStream.flush(); + if (success) { + this.socket = socket; + this.inputStream = inputStream; + this.outputStream = outputStream; + this.isEstablished = true; + } + } else { + socket.close(); + } } public void connect(final OnTransportConnected callback) { @@ -71,7 +157,9 @@ public class JingleSocks5Transport extends JingleTransport { } inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); + socket.setSoTimeout(5000); SocksSocketFactory.createSocksConnection(socket, destination, 0); + socket.setSoTimeout(0); isEstablished = true; callback.established(); } catch (IOException e) { @@ -182,6 +270,7 @@ public class JingleSocks5Transport extends JingleTransport { FileBackend.close(inputStream); FileBackend.close(outputStream); FileBackend.close(socket); + FileBackend.close(serverSocket); } public boolean isEstablished() { From 10b13652647ba10ce7fd9ae201f36f229526923e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 1 Sep 2019 20:42:07 +0200 Subject: [PATCH 03/13] also reply with direct connections on response --- .../java/eu/siacs/conversations/Config.java | 1 + .../xmpp/jingle/JingleConnection.java | 34 +++++++++++++------ .../xmpp/jingle/JingleSocks5Transport.java | 29 ++++++++-------- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 89883a61e..a5b3ab720 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -101,6 +101,7 @@ public final class Config { public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb + public static final boolean USE_DIRECT_JINGLE_CANDIDATES = false; public static final boolean DISABLE_HTTP_UPLOAD = false; public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts public static final boolean BACKGROUND_STANZA_LOGGING = false; //log all stanzas that were received while the app is in background 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 a9ac6e7df..7e48ad666 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -171,7 +171,8 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, "proxy activation failed"); + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": proxy activation failed"); + //TODO: when initiating send fallback to ibb } }; @@ -303,14 +304,7 @@ public class JingleConnection implements Transferable { if (this.initialTransport == Transport.IBB) { this.sendInitRequest(); } else { - - final List directCandidates = DirectConnectionUtils.getLocalCandidates(account.getJid()); - for (JingleCandidate directCandidate : directCandidates) { - final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, directCandidate); - connections.put(directCandidate.getCid(), socksConnection); - candidates.add(directCandidate); - } - + gatherAndConnectDirectCandidates(); this.mJingleConnectionManager.getPrimaryCandidate(account, (success, candidate) -> { if (success) { final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); @@ -342,6 +336,24 @@ public class JingleConnection implements Transferable { } + private void gatherAndConnectDirectCandidates() { + final List directCandidates; + if (Config.USE_DIRECT_JINGLE_CANDIDATES) { + if (account.isOnion() || mXmppConnectionService.useTorToConnect()) { + directCandidates = Collections.emptyList(); + } else { + directCandidates = DirectConnectionUtils.getLocalCandidates(account.getJid()); + } + } else { + directCandidates = Collections.emptyList(); + } + for (JingleCandidate directCandidate : directCandidates) { + final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, directCandidate); + connections.put(directCandidate.getCid(), socksConnection); + candidates.add(directCandidate); + } + } + private void upgradeNamespace() { List features = getRemoteFeatures(); if (features.contains(Content.Version.FT_5.getNamespace())) { @@ -570,6 +582,7 @@ public class JingleConnection implements Transferable { } private void sendAcceptSocks() { + gatherAndConnectDirectCandidates(); this.mJingleConnectionManager.getPrimaryCandidate(this.account, (success, candidate) -> { final JinglePacket packet = bootstrapPacket("session-accept"); final Content content = new Content(contentCreator, contentName); @@ -600,7 +613,7 @@ public class JingleConnection implements Transferable { } }); } else { - Log.d(Config.LOGTAG, "did not find a primary candidate for ourself"); + Log.d(Config.LOGTAG, "did not find a primary candidate for ourselves"); content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); sendJinglePacket(packet); @@ -762,6 +775,7 @@ public class JingleConnection implements Transferable { if (response.getType() != IqPacket.TYPE.RESULT) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + response.toString()); onProxyActivated.failed(); + //TODO send proxy-error } else { onProxyActivated.success(); sendProxyActivated(connection.getCandidate().getCid()); 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 2db161271..f0ee5c12e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -76,7 +76,7 @@ public class JingleSocks5Transport extends JingleTransport { try { acceptIncomingSocketConnection(socket); } catch (IOException e) { - Log.d(Config.LOGTAG,"unable to read from socket",e); + Log.d(Config.LOGTAG, "unable to read from socket", e); } }).start(); @@ -87,43 +87,43 @@ public class JingleSocks5Transport extends JingleTransport { } }).start(); } catch (IOException e) { - Log.d(Config.LOGTAG,"unable to bind server socket ",e); + Log.d(Config.LOGTAG, "unable to bind server socket ", e); } } private void acceptIncomingSocketConnection(Socket socket) throws IOException { Log.d(Config.LOGTAG, "accepted connection from " + socket.getInetAddress().getHostAddress()); - byte[] authBegin = new byte[2]; - InputStream inputStream = socket.getInputStream(); - OutputStream outputStream = socket.getOutputStream(); + final byte[] authBegin = new byte[2]; + final InputStream inputStream = socket.getInputStream(); + final OutputStream outputStream = socket.getOutputStream(); inputStream.read(authBegin); if (authBegin[0] != 0x5) { socket.close(); } - short methodCount = authBegin[1]; - byte[] methods = new byte[methodCount]; + final short methodCount = authBegin[1]; + final byte[] methods = new byte[methodCount]; inputStream.read(methods); if (SocksSocketFactory.contains((byte) 0x00, methods)) { - outputStream.write(new byte[]{0x05,0x00}); + outputStream.write(new byte[]{0x05, 0x00}); } else { - outputStream.write(new byte[]{0x05,(byte) 0xff}); + outputStream.write(new byte[]{0x05, (byte) 0xff}); } byte[] connectCommand = new byte[4]; inputStream.read(connectCommand); if (connectCommand[0] == 0x05 && connectCommand[1] == 0x01 && connectCommand[3] == 0x03) { int destinationCount = inputStream.read(); - byte[] destination = new byte[destinationCount]; + final byte[] destination = new byte[destinationCount]; inputStream.read(destination); - int port = inputStream.read(); + final int port = inputStream.read(); final String receivedDestination = new String(destination); - Log.d(Config.LOGTAG, "received destination " + receivedDestination + ":" + port + " - expected " + this.destination); final ByteBuffer response = ByteBuffer.allocate(7 + destination.length); final byte[] responseHeader; final boolean success; - if (receivedDestination.equals(this.destination)) { + if (receivedDestination.equals(this.destination) && this.socket == null) { responseHeader = new byte[]{0x05, 0x00, 0x00, 0x03}; success = true; } else { + Log.d(Config.LOGTAG,connection.getAccount().getJid().asBareJid()+": destination mismatch. received "+receivedDestination+" (expected "+this.destination+")"); responseHeader = new byte[]{0x05, 0x04, 0x00, 0x03}; success = false; } @@ -138,6 +138,7 @@ public class JingleSocks5Transport extends JingleTransport { this.inputStream = inputStream; this.outputStream = outputStream; this.isEstablished = true; + FileBackend.close(serverSocket); } } else { socket.close(); @@ -153,7 +154,7 @@ public class JingleSocks5Transport extends JingleTransport { } else { socket = new Socket(); SocketAddress address = new InetSocketAddress(candidate.getHost(), candidate.getPort()); - socket.connect(address, Config.SOCKET_TIMEOUT * 1000); + socket.connect(address, 5000); } inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); From 90e669313ef12a5109228913eda0948b92b4df8e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 1 Sep 2019 21:41:28 +0200 Subject: [PATCH 04/13] send fallback to ibb after proxy activation failed --- .../xmpp/jingle/JingleConnection.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 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 7e48ad666..a470ce3af 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -172,7 +172,9 @@ public class JingleConnection implements Transferable { @Override public void failed() { Log.d(Config.LOGTAG, account.getJid().asBareJid()+": proxy activation failed"); - //TODO: when initiating send fallback to ibb + if (initiating()) { + sendFallbackToIbb(); + } } }; @@ -774,11 +776,11 @@ public class JingleConnection implements Transferable { mXmppConnectionService.sendIqPacket(account, activation, (account, response) -> { if (response.getType() != IqPacket.TYPE.RESULT) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + response.toString()); + sendProxyError(); onProxyActivated.failed(); - //TODO send proxy-error } else { - onProxyActivated.success(); sendProxyActivated(connection.getCandidate().getCid()); + onProxyActivated.success(); } }); } else { @@ -862,7 +864,7 @@ public class JingleConnection implements Transferable { private boolean receiveFallbackToIbb(JinglePacket packet) { - Log.d(Config.LOGTAG, "receiving fallback to ibb"); + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": receiving fallback to ibb"); final String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size"); if (receivedBlockSize != null) { try { @@ -1050,14 +1052,23 @@ public class JingleConnection implements Transferable { } private void sendProxyActivated(String cid) { - JinglePacket packet = bootstrapPacket("transport-info"); - Content content = new Content(this.contentCreator, this.contentName); + final JinglePacket packet = bootstrapPacket("transport-info"); + final Content content = new Content(this.contentCreator, this.contentName); content.setTransportId(this.transportId); content.socks5transport().addChild("activated").setAttribute("cid", cid); packet.setContent(content); this.sendJinglePacket(packet); } + private void sendProxyError() { + final JinglePacket packet = bootstrapPacket("transport-info"); + final Content content = new Content(this.contentCreator, this.contentName); + content.setTransportId(this.transportId); + content.socks5transport().addChild("proxy-error"); + packet.setContent(content); + this.sendJinglePacket(packet); + } + private void sendCandidateUsed(final String cid) { JinglePacket packet = bootstrapPacket("transport-info"); Content content = new Content(this.contentCreator, this.contentName); From ff4d127b6f398927fd7e36696f7395aff2ddd6c3 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 2 Sep 2019 08:51:50 +0200 Subject: [PATCH 05/13] use higher priority for proxy on receiving end --- .../eu/siacs/conversations/xmpp/jingle/JingleConnection.java | 4 ++-- .../conversations/xmpp/jingle/JingleConnectionManager.java | 5 ++--- 2 files changed, 4 insertions(+), 5 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 a470ce3af..b112799d5 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -307,7 +307,7 @@ public class JingleConnection implements Transferable { this.sendInitRequest(); } else { gatherAndConnectDirectCandidates(); - this.mJingleConnectionManager.getPrimaryCandidate(account, (success, candidate) -> { + this.mJingleConnectionManager.getPrimaryCandidate(account, initiating(), (success, candidate) -> { if (success) { final JingleSocks5Transport socksConnection = new JingleSocks5Transport(this, candidate); connections.put(candidate.getCid(), socksConnection); @@ -585,7 +585,7 @@ public class JingleConnection implements Transferable { private void sendAcceptSocks() { gatherAndConnectDirectCandidates(); - this.mJingleConnectionManager.getPrimaryCandidate(this.account, (success, candidate) -> { + this.mJingleConnectionManager.getPrimaryCandidate(this.account, initiating(), (success, candidate) -> { final JinglePacket packet = bootstrapPacket("session-accept"); final Content content = new Content(contentCreator, contentName); content.setFileOffer(fileOffer, ftVersion); 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 237fd1229..30057f532 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -81,8 +81,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { this.connections.remove(connection); } - public void getPrimaryCandidate(Account account, - final OnPrimaryCandidateFound listener) { + public void getPrimaryCandidate(final Account account, final boolean initiator, final OnPrimaryCandidateFound listener) { if (Config.DISABLE_PROXY_LOOKUP) { listener.onPrimaryCandidateFound(false, null); return; @@ -107,7 +106,7 @@ public class JingleConnectionManager extends AbstractConnectionManager { candidate.setPort(Integer.parseInt(port)); candidate.setType(JingleCandidate.TYPE_PROXY); candidate.setJid(proxy); - candidate.setPriority(655360 + 65535); + candidate.setPriority(655360 + (initiator ? 10 : 20)); primaryCandidates.put(account.getJid().asBareJid(),candidate); listener.onPrimaryCandidateFound(true,candidate); } catch (final NumberFormatException e) { From 7ec1b443abe48f89e4e98bffa06bd6d814ba780e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 4 Sep 2019 16:14:01 +0200 Subject: [PATCH 06/13] implemented support for for jingle encrypted transports (XEP-0396) --- .../java/eu/siacs/conversations/Config.java | 4 +- .../generator/AbstractGenerator.java | 2 + .../eu/siacs/conversations/xml/Namespace.java | 2 + .../xmpp/jingle/JingleConnection.java | 38 +++++++++++++++---- .../xmpp/jingle/JingleSocks5Transport.java | 9 ++++- src/main/res/values-hu/strings.xml | 1 + 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index a5b3ab720..75104500e 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -101,8 +101,8 @@ public final class Config { public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb - public static final boolean USE_DIRECT_JINGLE_CANDIDATES = false; - public static final boolean DISABLE_HTTP_UPLOAD = false; + public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true; + public static final boolean DISABLE_HTTP_UPLOAD = true; public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts public static final boolean BACKGROUND_STANZA_LOGGING = false; //log all stanzas that were received while the app is in background public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index 1270c1019..5f93400e2 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -29,6 +29,8 @@ public abstract class AbstractGenerator { Content.Version.FT_5.getNamespace(), Namespace.JINGLE_TRANSPORTS_S5B, Namespace.JINGLE_TRANSPORTS_IBB, + Namespace.JINGLE_ENCRYPTED_TRANSPORT, + Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO, "http://jabber.org/protocol/muc", "jabber:x:conference", Namespace.OOB, diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index dbab039d7..55d54853e 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -30,4 +30,6 @@ public final class Namespace { public static final String PING = "urn:xmpp:ping"; public static final String PUSH = "urn:xmpp:push:0"; public static final String COMMANDS = "http://jabber.org/protocol/commands"; + public static final String JINGLE_ENCRYPTED_TRANSPORT = "urn:xmpp:jingle:jet:0"; + public static final String JINGLE_ENCRYPTED_TRANSPORT_OMEMO = "urn:xmpp:jingle:jet-omemo:0"; } 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 b112799d5..828d4a021 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -43,6 +43,8 @@ import rocks.xmpp.addr.Jid; public class JingleConnection implements Transferable { + private static final String JET_OMEMO_CIPHER = "urn:xmpp:ciphers:aes-128-gcm-nopadding"; + private static final int JINGLE_STATUS_INITIATED = 0; private static final int JINGLE_STATUS_ACCEPTED = 1; private static final int JINGLE_STATUS_FINISHED = 4; @@ -72,6 +74,7 @@ public class JingleConnection implements Transferable { private String contentName; private String contentCreator; private Transport initialTransport; + private boolean remoteSupportsOmemoJet; private int mProgress = 0; @@ -295,8 +298,10 @@ public class JingleConnection implements Transferable { this.contentName = this.mJingleConnectionManager.nextRandomId(); this.message = message; this.account = message.getConversation().getAccount(); - upgradeNamespace(); - this.initialTransport = getRemoteFeatures().contains(Namespace.JINGLE_TRANSPORTS_S5B) ? Transport.SOCKS : Transport.IBB; + final List remoteFeatures = getRemoteFeatures(); + upgradeNamespace(remoteFeatures); + this.initialTransport = remoteFeatures.contains(Namespace.JINGLE_TRANSPORTS_S5B) ? Transport.SOCKS : Transport.IBB; + this.remoteSupportsOmemoJet = remoteFeatures.contains(Namespace.JINGLE_ENCRYPTED_TRANSPORT_OMEMO); this.message.setTransferable(this); this.mStatus = Transferable.STATUS_UPLOADING; this.initiator = this.account.getJid(); @@ -356,11 +361,10 @@ public class JingleConnection implements Transferable { } } - private void upgradeNamespace() { - List features = getRemoteFeatures(); - if (features.contains(Content.Version.FT_5.getNamespace())) { + private void upgradeNamespace(List remoteFeatures) { + if (remoteFeatures.contains(Content.Version.FT_5.getNamespace())) { this.ftVersion = Content.Version.FT_5; - } else if (features.contains(Content.Version.FT_4.getNamespace())) { + } else if (remoteFeatures.contains(Content.Version.FT_4.getNamespace())) { this.ftVersion = Content.Version.FT_4; } } @@ -430,6 +434,13 @@ public class JingleConnection implements Transferable { if (fileOffer != null) { Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX); + if (encrypted == null) { + final Element security = content.findChild("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); + if (security != null && AxolotlService.PEP_PREFIX.equals(security.getAttribute("type"))) { + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": received jingle file offer with JET"); + encrypted = security.findChild("encrypted", AxolotlService.PEP_PREFIX); + } + } if (encrypted != null) { this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().asBareJid()); } @@ -520,7 +531,18 @@ public class JingleConnection implements Transferable { this.file.setKey(mXmppAxolotlMessage.getInnerKey()); this.file.setIv(mXmppAxolotlMessage.getIV()); this.file.setExpectedSize(file.getSize() + 16); - content.setFileOffer(this.file, false, this.ftVersion).addChild(mXmppAxolotlMessage.toElement()); + final Element file = content.setFileOffer(this.file, false, this.ftVersion); + if (remoteSupportsOmemoJet) { + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": remote announced support for JET"); + final Element security = new Element("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); + security.setAttribute("name", this.contentName); + security.setAttribute("cipher", JET_OMEMO_CIPHER); + security.setAttribute("type", AxolotlService.PEP_PREFIX); + security.addChild(mXmppAxolotlMessage.toElement()); + content.addChild(security); + } else { + file.addChild(mXmppAxolotlMessage.toElement()); + } } else { this.file.setExpectedSize(file.getSize()); content.setFileOffer(this.file, false, this.ftVersion); @@ -754,6 +776,8 @@ public class JingleConnection implements Transferable { this.sendFallbackToIbb(); } } else { + final JingleCandidate candidate = connection.getCandidate(); + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": elected candidate "+candidate.getHost()+":"+candidate.getPort()); this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; if (connection.needsActivation()) { if (connection.getCandidate().isOurs()) { 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 f0ee5c12e..41aa75ad9 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -16,6 +16,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.AbstractConnectionManager; @@ -134,11 +135,14 @@ public class JingleSocks5Transport extends JingleTransport { outputStream.write(response.array()); outputStream.flush(); if (success) { + Log.d(Config.LOGTAG,connection.getAccount().getJid().asBareJid()+": successfully processed connection to candidate "+candidate.getHost()+":"+candidate.getPort()); this.socket = socket; this.inputStream = inputStream; this.outputStream = outputStream; this.isEstablished = true; FileBackend.close(serverSocket); + } else { + this.socket.close(); } } else { socket.close(); @@ -174,6 +178,7 @@ public class JingleSocks5Transport extends JingleTransport { new Thread(() -> { InputStream fileInputStream = null; final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId()); + long transmitted = 0; try { wakeLock.acquire(); MessageDigest digest = MessageDigest.getInstance("SHA-1"); @@ -186,7 +191,6 @@ public class JingleSocks5Transport extends JingleTransport { } final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); long size = file.getExpectedSize(); - long transmitted = 0; int count; byte[] buffer = new byte[8192]; while ((count = innerInputStream.read(buffer)) > 0) { @@ -201,7 +205,8 @@ public class JingleSocks5Transport extends JingleTransport { callback.onFileTransmitted(file); } } catch (Exception e) { - Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage()); + final Account account = connection.getAccount(); + Log.d(Config.LOGTAG, account.getJid().asBareJid()+": failed sending file after "+transmitted+"/"+file.getExpectedSize()+" ("+ socket.getInetAddress()+":"+socket.getPort()+")", e); callback.onFileTransferAborted(); } finally { FileBackend.close(fileInputStream); diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index 8ad9d1a45..d880a6707 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -873,4 +873,5 @@ A kiválasztott fájl nem a Conversations biztonsági mentése Ez a fiók már be lett állítva Kérem, adja meg a fiókhoz tartozó jelszót + Nem sikerült ezt a cselekvést elvégezni From 571c29f92a073fb19f4e0ab4633dfb367b67d33e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 5 Sep 2019 12:08:58 +0200 Subject: [PATCH 07/13] make Tor connections work with direct TLS --- .../siacs/conversations/utils/Resolver.java | 6 +- .../utils/SocksSocketFactory.java | 22 +++- .../conversations/xmpp/XmppConnection.java | 111 ++++++++---------- 3 files changed, 74 insertions(+), 65 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/utils/Resolver.java b/src/main/java/eu/siacs/conversations/utils/Resolver.java index 96ce63c90..8ba1c4cd7 100644 --- a/src/main/java/eu/siacs/conversations/utils/Resolver.java +++ b/src/main/java/eu/siacs/conversations/utils/Resolver.java @@ -76,12 +76,16 @@ public class Resolver { Result result = new Result(); result.hostname = DNSName.from(hostname); result.port = port; - result.directTls = port == 443 || port == 5223; + result.directTls = useDirectTls(port); result.authenticated = true; return Collections.singletonList(result); } + public static boolean useDirectTls(final int port) { + return port == 443 || port == 5223; + } + public static List resolve(String domain) { final List ipResults = fromIpAddress(domain); if (ipResults.size() > 0) { diff --git a/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java b/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java index eab9acfcb..48e0c1d55 100644 --- a/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java +++ b/src/main/java/eu/siacs/conversations/utils/SocksSocketFactory.java @@ -21,7 +21,7 @@ public class SocksSocketFactory { byte[] response = new byte[2]; proxyIs.read(response); if (response[0] != 0x05 || response[1] != 0x00) { - throw new SocksConnectionException(); + throw new SocksConnectionException("Socks 5 handshake failed"); } byte[] dest = destination.getBytes(); ByteBuffer request = ByteBuffer.allocate(7 + dest.length); @@ -33,7 +33,13 @@ public class SocksSocketFactory { response = new byte[7 + dest.length]; proxyIs.read(response); if (response[1] != 0x00) { - throw new SocksConnectionException(); + if (response[1] == 0x04) { + throw new HostNotFoundException("Host unreachable"); + } + if (response[1] == 0x05) { + throw new HostNotFoundException("Connection refused"); + } + throw new SocksConnectionException("Unable to connect to destination "+(int) (response[1])); } } @@ -61,11 +67,19 @@ public class SocksSocketFactory { return createSocket(new InetSocketAddress(InetAddress.getByAddress(LOCALHOST), 9050), destination, port); } - static class SocksConnectionException extends IOException { - + private static class SocksConnectionException extends IOException { + SocksConnectionException(String message) { + super(message); + } } public static class SocksProxyNotFoundException extends IOException { } + + public static class HostNotFoundException extends SocksConnectionException { + HostNotFoundException(String message) { + super(message); + } + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 049a8fe68..fe384ebf2 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -267,8 +267,18 @@ public class XmppConnection implements Runnable { destination = account.getHostname(); this.verifiedHostname = destination; } - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": connect to " + destination + " via Tor"); - localSocket = SocksSocketFactory.createSocketOverTor(destination, account.getPort()); + + final int port = account.getPort(); + final boolean directTls = Resolver.useDirectTls(port); + + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": connect to " + destination + " via Tor. directTls="+directTls); + localSocket = SocksSocketFactory.createSocketOverTor(destination, port); + + if (directTls) { + localSocket = upgradeSocketToTls(localSocket); + features.encryptionEnabled = true; + } + try { startXmpp(localSocket); } catch (InterruptedException e) { @@ -328,29 +338,13 @@ public class XmppConnection implements Runnable { + result.getHostname().toString() + ":" + result.getPort() + " tls: " + features.encryptionEnabled); } - if (!features.encryptionEnabled) { - localSocket = new Socket(); - localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000); - } else { - final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier(); - localSocket = tlsFactoryVerifier.factory.createSocket(); + localSocket = new Socket(); + localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000); - if (localSocket == null) { - throw new IOException("could not initialize ssl socket"); - } - - SSLSocketHelper.setSecurity((SSLSocket) localSocket); - SSLSocketHelper.setHostname((SSLSocket) localSocket, account.getServer()); - SSLSocketHelper.setApplicationProtocol((SSLSocket) localSocket, "xmpp-client"); - - localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000); - - if (!tlsFactoryVerifier.verifier.verify(account.getServer(), verifiedHostname, ((SSLSocket) localSocket).getSession())) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed"); - FileBackend.close(localSocket); - throw new StateChangingException(Account.State.TLS_ERROR); - } + if (features.encryptionEnabled) { + localSocket = upgradeSocketToTls(localSocket); } + localSocket.setSoTimeout(Config.SOCKET_TIMEOUT * 1000); if (startXmpp(localSocket)) { localSocket.setSoTimeout(0); //reset to 0; once the connection is established we don’t want this @@ -384,6 +378,8 @@ public class XmppConnection implements Runnable { this.changeStatus(e.state); } catch (final UnknownHostException | ConnectException e) { this.changeStatus(Account.State.SERVER_NOT_FOUND); + } catch (final SocksSocketFactory.HostNotFoundException e) { + this.changeStatus(Account.State.SERVER_NOT_FOUND); } catch (final SocksSocketFactory.SocksProxyNotFoundException e) { this.changeStatus(Account.State.TOR_NOT_AVAILABLE); } catch (final IOException | XmlPullParserException e) { @@ -796,46 +792,41 @@ public class XmppConnection implements Runnable { private void switchOverToTls() throws XmlPullParserException, IOException { tagReader.readTag(); + final Socket socket = this.socket; + final SSLSocket sslSocket = upgradeSocketToTls(socket); + tagReader.setInputStream(sslSocket.getInputStream()); + tagWriter.setOutputStream(sslSocket.getOutputStream()); + sendStartStream(); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS connection established"); + features.encryptionEnabled = true; + final Tag tag = tagReader.readTag(); + if (tag != null && tag.isStart("stream")) { + SSLSocketHelper.log(account, sslSocket); + processStream(); + } else { + throw new StateChangingException(Account.State.STREAM_OPENING_ERROR); + } + sslSocket.close(); + } + + private SSLSocket upgradeSocketToTls(final Socket socket) throws IOException { + final TlsFactoryVerifier tlsFactoryVerifier; try { - final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier(); - final InetAddress address = socket == null ? null : socket.getInetAddress(); - - if (address == null) { - throw new IOException("could not setup ssl"); - } - - final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true); - - - if (sslSocket == null) { - throw new IOException("could not initialize ssl socket"); - } - - SSLSocketHelper.setSecurity(sslSocket); - SSLSocketHelper.setHostname(sslSocket, account.getServer()); - SSLSocketHelper.setApplicationProtocol(sslSocket, "xmpp-client"); - - if (!tlsFactoryVerifier.verifier.verify(account.getServer(), this.verifiedHostname, sslSocket.getSession())) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed"); - throw new StateChangingException(Account.State.TLS_ERROR); - } - tagReader.setInputStream(sslSocket.getInputStream()); - tagWriter.setOutputStream(sslSocket.getOutputStream()); - sendStartStream(); - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS connection established"); - features.encryptionEnabled = true; - final Tag tag = tagReader.readTag(); - if (tag != null && tag.isStart("stream")) { - SSLSocketHelper.log(account, sslSocket); - processStream(); - } else { - throw new StateChangingException(Account.State.STREAM_OPENING_ERROR); - } - sslSocket.close(); - } catch (final NoSuchAlgorithmException | KeyManagementException e1) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed"); + tlsFactoryVerifier = getTlsFactoryVerifier(); + } catch (final NoSuchAlgorithmException | KeyManagementException e) { throw new StateChangingException(Account.State.TLS_ERROR); } + final InetAddress address = socket.getInetAddress(); + final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true); + SSLSocketHelper.setSecurity(sslSocket); + SSLSocketHelper.setHostname(sslSocket, account.getServer()); + SSLSocketHelper.setApplicationProtocol(sslSocket, "xmpp-client"); + if (!tlsFactoryVerifier.verifier.verify(account.getServer(), this.verifiedHostname, sslSocket.getSession())) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": TLS certificate verification failed"); + FileBackend.close(sslSocket); + throw new StateChangingException(Account.State.TLS_ERROR); + } + return sslSocket; } private void processStreamFeatures(final Tag currentTag) throws XmlPullParserException, IOException { From 0e1c26c569ee909aca027bbe21e966c68fee5d7c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 5 Sep 2019 13:17:45 +0200 Subject: [PATCH 08/13] do not include link local in direct candidates --- .../services/AbstractConnectionManager.java | 1 + .../xmpp/jingle/DirectConnectionUtils.java | 5 ++-- .../xmpp/jingle/JingleCandidate.java | 9 +++---- .../xmpp/jingle/JingleConnection.java | 26 +++++++++---------- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java index 432c70390..43c28b854 100644 --- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java @@ -26,6 +26,7 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.utils.Compatibility; +import eu.siacs.conversations.utils.CryptoHelper; public class AbstractConnectionManager { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java index 41a9455b7..d418e4e93 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java @@ -26,9 +26,10 @@ public class DirectConnectionUtils { final Enumeration inetAddressEnumeration = networkInterface.getInetAddresses(); while (inetAddressEnumeration.hasMoreElements()) { final InetAddress inetAddress = inetAddressEnumeration.nextElement(); - if (!inetAddress.isLoopbackAddress()) { - addresses.add(inetAddress); + if (inetAddress.isLoopbackAddress() || inetAddress.isLinkLocalAddress()) { + continue; } + addresses.add(inetAddress); } } return addresses; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java index 3f69980aa..7415c32aa 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleCandidate.java @@ -110,15 +110,12 @@ public class JingleCandidate { } public static JingleCandidate parse(Element candidate) { - JingleCandidate parsedCandidate = new JingleCandidate( - candidate.getAttribute("cid"), false); + JingleCandidate parsedCandidate = new JingleCandidate(candidate.getAttribute("cid"), false); parsedCandidate.setHost(candidate.getAttribute("host")); parsedCandidate.setJid(InvalidJid.getNullForInvalid(candidate.getAttributeAsJid("jid"))); parsedCandidate.setType(candidate.getAttribute("type")); - parsedCandidate.setPriority(Integer.parseInt(candidate - .getAttribute("priority"))); - parsedCandidate - .setPort(Integer.parseInt(candidate.getAttribute("port"))); + parsedCandidate.setPriority(Integer.parseInt(candidate.getAttribute("priority"))); + parsedCandidate.setPort(Integer.parseInt(candidate.getAttribute("port"))); return parsedCandidate; } 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 828d4a021..45539f8f5 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -174,7 +174,7 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": proxy activation failed"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": proxy activation failed"); if (initiating()) { sendFallbackToIbb(); } @@ -320,22 +320,20 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, - "connection to our own primary candidete failed"); + Log.d(Config.LOGTAG, "connection to our own proxy65 candidate failed"); sendInitRequest(); } @Override public void established() { - Log.d(Config.LOGTAG, - "successfully connected to our own primary candidate"); + Log.d(Config.LOGTAG, "successfully connected to our own proxy65 candidate"); mergeCandidate(candidate); sendInitRequest(); } }); mergeCandidate(candidate); } else { - Log.d(Config.LOGTAG, "no primary candidate of our own was found"); + Log.d(Config.LOGTAG, "no proxy65 candidate of our own was found"); sendInitRequest(); } }); @@ -437,7 +435,7 @@ public class JingleConnection implements Transferable { if (encrypted == null) { final Element security = content.findChild("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); if (security != null && AxolotlService.PEP_PREFIX.equals(security.getAttribute("type"))) { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": received jingle file offer with JET"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received jingle file offer with JET"); encrypted = security.findChild("encrypted", AxolotlService.PEP_PREFIX); } } @@ -533,7 +531,7 @@ public class JingleConnection implements Transferable { this.file.setExpectedSize(file.getSize() + 16); final Element file = content.setFileOffer(this.file, false, this.ftVersion); if (remoteSupportsOmemoJet) { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": remote announced support for JET"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": remote announced support for JET"); final Element security = new Element("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); security.setAttribute("name", this.contentName); security.setAttribute("cipher", JET_OMEMO_CIPHER); @@ -619,7 +617,7 @@ public class JingleConnection implements Transferable { @Override public void failed() { - Log.d(Config.LOGTAG, "connection to our own primary candidate failed"); + Log.d(Config.LOGTAG, "connection to our own proxy65 candidate failed"); content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); sendJinglePacket(packet); @@ -628,7 +626,7 @@ public class JingleConnection implements Transferable { @Override public void established() { - Log.d(Config.LOGTAG, "connected to primary candidate"); + Log.d(Config.LOGTAG, "connected to proxy65 candidate"); mergeCandidate(candidate); content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); @@ -637,7 +635,7 @@ public class JingleConnection implements Transferable { } }); } else { - Log.d(Config.LOGTAG, "did not find a primary candidate for ourselves"); + Log.d(Config.LOGTAG, "did not find a proxy65 candidate for ourselves"); content.socks5transport().setChildren(getCandidatesAsElements()); packet.setContent(content); sendJinglePacket(packet); @@ -770,14 +768,14 @@ public class JingleConnection implements Transferable { final JingleSocks5Transport connection = chooseConnection(); this.transport = connection; if (connection == null) { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": could not find suitable candidate"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not find suitable candidate"); this.disconnectSocks5Connections(); if (initiating()) { this.sendFallbackToIbb(); } } else { final JingleCandidate candidate = connection.getCandidate(); - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": elected candidate "+candidate.getHost()+":"+candidate.getPort()); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": elected candidate " + candidate.getHost() + ":" + candidate.getPort()); this.mJingleStatus = JINGLE_STATUS_TRANSMITTING; if (connection.needsActivation()) { if (connection.getCandidate().isOurs()) { @@ -888,7 +886,7 @@ public class JingleConnection implements Transferable { private boolean receiveFallbackToIbb(JinglePacket packet) { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": receiving fallback to ibb"); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": receiving fallback to ibb"); final String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size"); if (receivedBlockSize != null) { try { From 9129c9acded99d60b99ab8a86421a0c581889072 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 6 Sep 2019 13:11:37 +0200 Subject: [PATCH 09/13] JET uses plain text file size --- src/main/java/eu/siacs/conversations/Config.java | 2 +- .../conversations/xmpp/jingle/JingleConnection.java | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 75104500e..0b43675ad 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -102,7 +102,7 @@ public final class Config { public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true; - public static final boolean DISABLE_HTTP_UPLOAD = true; + public static final boolean DISABLE_HTTP_UPLOAD = false; public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts public static final boolean BACKGROUND_STANZA_LOGGING = false; //log all stanzas that were received while the app is in background public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption 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 45539f8f5..919350ec8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -430,13 +430,16 @@ public class JingleConnection implements Transferable { } this.fileOffer = content.getFileOffer(this.ftVersion); + if (fileOffer != null) { + boolean remoteIsUsingJet = false; Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX); if (encrypted == null) { final Element security = content.findChild("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT); if (security != null && AxolotlService.PEP_PREFIX.equals(security.getAttribute("type"))) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received jingle file offer with JET"); encrypted = security.findChild("encrypted", AxolotlService.PEP_PREFIX); + remoteIsUsingJet = true; } } if (encrypted != null) { @@ -479,7 +482,10 @@ public class JingleConnection implements Transferable { } } message.resetFileParams(); - this.file.setExpectedSize(size); + //legacy OMEMO encrypted file transfers reported the file size after encryption + //JET reports the plain text size. however lower levels of our receiving code still + //expect the cipher text size. so we just + 16 bytes (auth tag size) here + this.file.setExpectedSize(size + (remoteIsUsingJet ? 16 : 0)); if (mJingleConnectionManager.hasStoragePermission() && size < this.mJingleConnectionManager.getAutoAcceptFileSize() && mXmppConnectionService.isDataSaverDisabled()) { @@ -528,7 +534,9 @@ public class JingleConnection implements Transferable { if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { this.file.setKey(mXmppAxolotlMessage.getInnerKey()); this.file.setIv(mXmppAxolotlMessage.getIV()); - this.file.setExpectedSize(file.getSize() + 16); + //legacy OMEMO encrypted file transfer reported file size of the encrypted file + //JET uses the file size of the plain text file. The difference is only 16 bytes (auth tag) + this.file.setExpectedSize(file.getSize() + (this.remoteSupportsOmemoJet ? 0 : 16)); final Element file = content.setFileOffer(this.file, false, this.ftVersion); if (remoteSupportsOmemoJet) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": remote announced support for JET"); From dd9777a6b71b4b1a0cea8e46c26fa4fab1de24b8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 8 Sep 2019 17:44:26 +0200 Subject: [PATCH 10/13] do not include scope in ipv6 annoucment --- .../xmpp/jingle/DirectConnectionUtils.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java index d418e4e93..8d9818ed9 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/DirectConnectionUtils.java @@ -1,8 +1,10 @@ package eu.siacs.conversations.xmpp.jingle; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; +import java.net.UnknownHostException; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Enumeration; @@ -29,7 +31,16 @@ public class DirectConnectionUtils { if (inetAddress.isLoopbackAddress() || inetAddress.isLinkLocalAddress()) { continue; } - addresses.add(inetAddress); + if (inetAddress instanceof Inet6Address) { + //let's get rid of scope + try { + addresses.add(Inet6Address.getByAddress(inetAddress.getAddress())); + } catch (UnknownHostException e) { + //ignored + } + } else { + addresses.add(inetAddress); + } } } return addresses; From 8b69f8ae50790eefd44f8ec4042c62addc1a8dc0 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 8 Sep 2019 17:58:15 +0200 Subject: [PATCH 11/13] include ticker information in notification. fixes #3532 --- .../services/NotificationService.java | 29 +++++++++---------- src/main/res/values/strings.xml | 2 +- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 13827d98d..01bf371af 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -11,7 +11,6 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Typeface; -import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.media.RingtoneManager; import android.net.Uri; @@ -23,7 +22,6 @@ import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.BigPictureStyle; import android.support.v4.app.NotificationCompat.Builder; import android.support.v4.app.NotificationManagerCompat; -import android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation; import android.support.v4.app.Person; import android.support.v4.app.RemoteInput; import android.support.v4.content.ContextCompat; @@ -32,7 +30,6 @@ import android.text.SpannableString; import android.text.style.StyleSpan; import android.util.DisplayMetrics; import android.util.Log; -import android.util.Pair; import java.io.File; import java.io.IOException; @@ -64,7 +61,6 @@ import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.XmppConnection; -import rocks.xmpp.addr.Jid; public class NotificationService { @@ -469,10 +465,7 @@ public class NotificationService { private Builder buildMultipleConversation(final boolean notify, final boolean quietHours) { final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, quietHours ? "quiet_hours" : (notify ? "messages" : "silent_messages")); final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); - style.setBigContentTitle(notifications.size() - + " " - + mXmppConnectionService - .getString(R.string.unread_conversations)); + style.setBigContentTitle(mXmppConnectionService.getString(R.string.x_unread_conversations,notifications.size())); final StringBuilder names = new StringBuilder(); Conversation conversation = null; for (final ArrayList messages : notifications.values()) { @@ -497,10 +490,8 @@ public class NotificationService { if (names.length() >= 2) { names.delete(names.length() - 2, names.length()); } - mBuilder.setContentTitle(notifications.size() - + " " - + mXmppConnectionService - .getString(R.string.unread_conversations)); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.x_unread_conversations, notifications.size())); + mBuilder.setTicker(mXmppConnectionService.getString(R.string.x_unread_conversations, notifications.size())); mBuilder.setContentText(names.toString()); mBuilder.setStyle(style); if (conversation != null) { @@ -627,8 +618,11 @@ public class NotificationService { CharSequence text = getMergedBodies(tmp); bigPictureStyle.setSummaryText(text); builder.setContentText(text); + builder.setTicker(text); } else { - builder.setContentText(UIHelper.getFileDescriptionString(mXmppConnectionService, message)); + final String description = UIHelper.getFileDescriptionString(mXmppConnectionService, message); + builder.setContentText(description); + builder.setTicker(description); } builder.setStyle(bigPictureStyle); } catch (final IOException e) { @@ -685,7 +679,9 @@ public class NotificationService { } else { if (messages.get(0).getConversation().getMode() == Conversation.MODE_SINGLE) { builder.setStyle(new NotificationCompat.BigTextStyle().bigText(getMergedBodies(messages))); - builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, messages.get(messages.size()-1)).first); + final CharSequence preview = UIHelper.getMessagePreview(mXmppConnectionService, messages.get(messages.size()-1)).first; + builder.setContentText(preview); + builder.setTicker(preview); builder.setNumber(messages.size()); } else { final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); @@ -703,8 +699,11 @@ public class NotificationService { styledString = new SpannableString(name + ": " + messages.get(0).getBody()); styledString.setSpan(new StyleSpan(Typeface.BOLD), 0, name.length(), 0); builder.setContentText(styledString); + builder.setTicker(styledString); } else { - builder.setContentText(mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages, count, count)); + final String text = mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages, count, count); + builder.setContentText(text); + builder.setTicker(text); } } } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index a387e9ec5..cf949b85b 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -30,7 +30,7 @@ just now 1 min ago %d mins ago - unread Conversations + %d unread conversations sending… Decrypting message. Please wait… OpenPGP encrypted message From 24d1fc2bb5859769fd9ceb3c893be2b4169f3eac Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 9 Sep 2019 09:43:10 +0200 Subject: [PATCH 12/13] pulled translations from transifex --- src/main/res/values-ar/strings.xml | 1 - src/main/res/values-bg/strings.xml | 1 - src/main/res/values-ca/strings.xml | 1 - src/main/res/values-cs/strings.xml | 1 - src/main/res/values-de/strings.xml | 2 +- src/main/res/values-el/strings.xml | 1 - src/main/res/values-es/strings.xml | 1 - src/main/res/values-eu/strings.xml | 1 - src/main/res/values-fa-rIR/strings.xml | 1 - src/main/res/values-fr/strings.xml | 1 - src/main/res/values-gl/strings.xml | 2 +- src/main/res/values-hu/strings.xml | 1 - src/main/res/values-id/strings.xml | 1 - src/main/res/values-it/strings.xml | 1 - src/main/res/values-iw/strings.xml | 1 - src/main/res/values-ja/strings.xml | 1 - src/main/res/values-ko/strings.xml | 1 - src/main/res/values-nb-rNO/strings.xml | 1 - src/main/res/values-nl/strings.xml | 1 - src/main/res/values-pl/strings.xml | 1 - src/main/res/values-pt-rBR/strings.xml | 12 +++++++++++- src/main/res/values-pt/strings.xml | 1 - src/main/res/values-ro-rRO/strings.xml | 1 - src/main/res/values-ru/strings.xml | 1 - src/main/res/values-sk/strings.xml | 1 - src/main/res/values-sr/strings.xml | 1 - src/main/res/values-sv/strings.xml | 1 - src/main/res/values-tr-rTR/strings.xml | 1 - src/main/res/values-uk/strings.xml | 1 - src/main/res/values-vi/strings.xml | 1 - src/main/res/values-zh-rCN/strings.xml | 1 - src/main/res/values-zh-rTW/strings.xml | 1 - src/quicksy/res/values-pt-rBR/strings.xml | 6 +++++- 33 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/main/res/values-ar/strings.xml b/src/main/res/values-ar/strings.xml index f1098f805..22967c71e 100644 --- a/src/main/res/values-ar/strings.xml +++ b/src/main/res/values-ar/strings.xml @@ -27,7 +27,6 @@ الآن منذ 1 دقيقة دقائق %d منذ - محادثات غير مقروءة ارسال حل شيفرة الرسالة. الرجاء الإنتظار ... رسالة مشفرة عبر OpenPGP diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 04effedc3..a6324dde4 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -28,7 +28,6 @@ току-що преди 1 минута преди %d минути - непрочетени разговори изпращане… Дешифроване на съобщението. Моля, изчакайте… Съобщение, шифр. чрез OpenPGP diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 3276bf677..aea48b16e 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -27,7 +27,6 @@ Ara fa 1 min fa %d mins - Converses sense llegir o no llegides enviant… Desxifrant el missatge. Espereu… Missatge xifrat amb OpenPGP diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 770587f00..1a25c64ad 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -21,7 +21,6 @@ právě teď před minutou před %d minutami - nepřečtené konverzace odesílám… Dešifrování zprávy. Chvíli strpení... OpenPGP šifrovaná zpráva diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 42a9ab49c..c99c015b9 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -30,7 +30,7 @@ gerade vor einer Minute vor %d Minuten - ungelesene Unterhaltungen + %d ungelesene Unterhaltungen senden… Nachricht wird entschlüsselt. Bitte warten … OpenPGP-verschlüsselte Nachricht diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index 6b7eef228..2dc0f6b0a 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -30,7 +30,6 @@ μόλις τώρα πριν από 1 λεπτό πριν από %d λεπτά - μη αναγνωσμένες Συζητήσεις αποστολή... Αποκρυπτογράφηση μηνύματος. Παρακαλώ περιμένετε... OpenPGP κρυπτογραφημένο μήνυμα diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index fb4d63c9b..813be5abc 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -30,7 +30,6 @@ ahora hace 1 min hace %d min - conversaciones por leer enviando… Descifrando mensaje. Por favor, espera... Mensaje cifrado con OpenPGP diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index cf5279270..57c737611 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -30,7 +30,6 @@ orain min 1 lehenago %d min lehenago - irakurri gabeko elkarrizketak bidaltzen… Mezua desenkriptatzen. Mesedez itxaron… OpenPGPz enkriptatutako mezua diff --git a/src/main/res/values-fa-rIR/strings.xml b/src/main/res/values-fa-rIR/strings.xml index e7ff22809..d70439f87 100644 --- a/src/main/res/values-fa-rIR/strings.xml +++ b/src/main/res/values-fa-rIR/strings.xml @@ -25,7 +25,6 @@ هم اکنون 1 دقیقه قبل %d دقیقه قبل - گفتگو های خوانده نشده در حال ارسال... در حال رمزگشایی پیام. لطفا صبور باشید... پیام رمز شده به وسیله OpenPGP diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index 654e92708..f34a6a0ac 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -28,7 +28,6 @@ À l\'instant Il y a 1 minute Il y a %d minutes - Conversations non lues Envoi… Déchiffrement du message. Veuillez patienter... Message chiffré avec OpenPGP diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index 35e58dad6..34e13fb4f 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -30,7 +30,7 @@ agora Hai 1 min hai %d minutos - conversas sen ler + %d conversas non lidas enviando… Descifrando a mensaxe. Por favor agarde... Mensaxe cifrado con OpenPGP diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index d880a6707..885617701 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -30,7 +30,6 @@ Éppen most 1 perce %d perce - olvasatlan beszélgetés küldés... Üzenet dekódolása. Kérem várjon... OpenPGP kódolású üzenet diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index d2cad894b..f045f3968 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -21,7 +21,6 @@ sekarang 1 min lalu %d min lalu - Percakapan belum dibaca mengirim... Mendekripsi pesan. Mohon tunggu… Pesan terenkripsi OpenPGP diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index f7f0cbc19..2e0f1447d 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -30,7 +30,6 @@ adesso 1 min fa %d min fa - Conversazioni non lette invio… Decifrazione messaggio. Attendere prego... Messaggio cifrato con OpenPGP diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index 93f55608d..3a5ad007e 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -20,7 +20,6 @@ ממש עכשיו לפני דקה לפני %d דקות - שיחות שלא נקראו שולח... כעת מפענח צופן הודעה. אנא המתן… הודעה מוצפנת OpenPGP diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index bcc03cb83..a276feb31 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -28,7 +28,6 @@ ちょうど今 1 分前 %d 分前 - 未読の会話 送信中… メッセージを復号化しています。しばらくお待ちください… OpenPGP 暗号化メッセージ diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index 773111fdf..81704165f 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -21,7 +21,6 @@ 방금 1분 전 %d 분 전 - 읽지 않은 대화 보내는중... 메세지 복호화중입니다. 기다리세요... OpenPGP로 암호화된 메세지 diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index 8bef5221d..851856a4a 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -24,7 +24,6 @@ akkurat nå 1 minutt siden %d minutter siden - uleste samtaler sender... Dekrypterer melding mens du venter. OpenPGP-kryptert melding diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 9d65de57a..5416974d3 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -30,7 +30,6 @@ zojuist 1 min. geleden %d min. geleden - ongelezen gesprekken versturen… Bericht aan het ontsleutelen. Even geduld… OpenPGP-versleuteld bericht diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 1c7014337..ce4517a8e 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -30,7 +30,6 @@ przed chwilą minutę temu %d minut temu - nieprzeczytanych konwersacji wysyłanie... Odszyfrowywanie wiadomości. To zajmie tylko chwilę... Wiadomość zaszyfrowana OpenPGP diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index ed79ad02c..535677f3c 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -17,6 +17,8 @@ Desbloquear contato Bloquear domínio Desbloquear domínio + Bloquear participante + Desbloquear participante Gerenciar contas Configurações Compartilhar com a conversa @@ -28,7 +30,7 @@ agora 1 minuto atrás %d minutos atrás - conversas não lidas + %d conversas não lidas enviando... Descriptografando a mensagem. Por favor, aguarde... Mensagem criptografada via OpenPGP @@ -864,4 +866,12 @@ Isso se parece com um endereço de domínio Adicionar mesmo assim Isso se parece com um endereço de canal + Compartilhar arquivos de backup + Backup do Conversations + Evento + Abrir backup + O arquivo que você selecionou não é um backup do Conversations + Esta conta já foi configurada + Por favor, digite a senha para esta conta + Não foi possível executar essa ação diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml index 59a9c7772..cc10ff888 100644 --- a/src/main/res/values-pt/strings.xml +++ b/src/main/res/values-pt/strings.xml @@ -23,7 +23,6 @@ agora há pouco 1 minuto atrás %d minutos atrás - Conversas não lidas enviando... Decifrando a mensagem. Por favor aguarde... Mensagem cifrada OpenPGP diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index 668b2e60b..6492eff3a 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -30,7 +30,6 @@ în acest moment acum un minut acum %d minute - conversații necitite trimitere... Decriptez mesaj. Te rog așteaptă... Mesaj criptat cu OpenPGP diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index c0c6e5604..f9ee2c43c 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -27,7 +27,6 @@ только что 1 минуту назад %d мин. назад - сообщен. не прочитано отправка… Расшифровка сообщения. Подождите… OpenPGP зашифр. сообщение diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index 27def940e..6862167ed 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -20,7 +20,6 @@ práve teraz pred 1 minútou pred %d minútami - neprečítané konverzácie posielam... Prezývka už existuje Administrátor diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index 44b27c868..9402ba053 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -25,7 +25,6 @@ управо сад пре минут пре %d минута - непрочитане преписке шаљем… Дешифрујем поруку, сачекајте… ОпенПГП шифрована порука diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 66035d74f..f95042863 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -21,7 +21,6 @@ just nu 1 min sedan %d min sedan - olästa konversationer skickar… Avkrypterar meddelande. Vänta… OpenPGP-krypterat meddelande diff --git a/src/main/res/values-tr-rTR/strings.xml b/src/main/res/values-tr-rTR/strings.xml index 46733dce0..0506db725 100644 --- a/src/main/res/values-tr-rTR/strings.xml +++ b/src/main/res/values-tr-rTR/strings.xml @@ -21,7 +21,6 @@ şimdi 1 dakika önce %d dakika önc - okunmamış konuşmalar gönderiyor… İleti deşifre ediliyor. Lütfen bekleyin… OpenPGP şifreli ileti diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml index 13e6d17cb..8a5c64fe6 100644 --- a/src/main/res/values-uk/strings.xml +++ b/src/main/res/values-uk/strings.xml @@ -28,7 +28,6 @@ щойно 1 хвилину тому %d хвилин тому - не переглянуті розмови відправляю… Розшифровую повідомлення. Зачекайте, будь ласка… Повідомлення, зашифроване OpenPGP diff --git a/src/main/res/values-vi/strings.xml b/src/main/res/values-vi/strings.xml index c2536a70b..153b8d706 100644 --- a/src/main/res/values-vi/strings.xml +++ b/src/main/res/values-vi/strings.xml @@ -21,7 +21,6 @@ mới đây 1 phút trước %d phút trước - Các hội thoại chưa đọc đang gửi... Đang giải mã tin nhắn. Xin chờ... Tin nhắn mã hoá bằng OpenPGP diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 110db92b7..889088d43 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -28,7 +28,6 @@ 刚刚 1分钟前 %d分钟前 - 未读会话 正在发送… 解密信息中. 请稍候… OpenPGP 加密的信息 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index d10a65650..88451f872 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -21,7 +21,6 @@ 剛剛 1 分鐘前 %d分鐘前 - 未讀會話 正在發送… 訊息解密中,請稍候… OpenPGP 加密的信息 diff --git a/src/quicksy/res/values-pt-rBR/strings.xml b/src/quicksy/res/values-pt-rBR/strings.xml index 6e78e69fd..54502a36e 100644 --- a/src/quicksy/res/values-pt-rBR/strings.xml +++ b/src/quicksy/res/values-pt-rBR/strings.xml @@ -19,4 +19,8 @@ O Quicksy necessita de acesso ao microfone Essa categoria de notificação é utilizada para exibir uma notificação permanente indicando que o Quicksy está em execução. Imagem de perfil do Quicksy - + Quicksy agora está disponível no seu país. + Não foi possível verificar a identidade do servidor. + Erro de segurança desconhecido. + Tempo esgotado ao tentar conectar ao servidor. + From dfa6cebe99499e785dd44c7562678669f18178d1 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 10 Sep 2019 13:08:09 +0200 Subject: [PATCH 13/13] version bump to 2.5.8 + changelog --- CHANGELOG.md | 5 +++++ build.gradle | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56d2bf476..73f0f42f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Version 2.5.8 +* fixed connection issues over Tor +* P2P file transfer (Jingle) now offers direct candidates +* Support XEP-0396: Jingle Encrypted Transports - OMEMO + ### Version 2.5.7 * fixed crash when scanning QR codes on Android 6 and lower * when sharing a message from and to Conversations insert it as quote diff --git a/build.gradle b/build.gradle index 1863c43f7..0a3f2297a 100644 --- a/build.gradle +++ b/build.gradle @@ -83,8 +83,8 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 28 - versionCode 337 - versionName "2.5.7" + versionCode 338 + versionName "2.5.8" archivesBaseName += "-$versionName" applicationId "eu.siacs.conversations" resValue "string", "applicationId", applicationId