diff options
author | Daniel Gultsch <daniel@gultsch.de> | 2021-03-18 18:18:40 +0300 |
---|---|---|
committer | Daniel Gultsch <daniel@gultsch.de> | 2021-03-18 18:18:40 +0300 |
commit | a09c32c4dd02c04ee9b2f7f349050f8e00feb5b6 (patch) | |
tree | 14e52938a51dc279226307387befae06e904568d | |
parent | b09a1432a347e9700ace8aa8fe729f53bfcfac80 (diff) |
WIP migrate HTTP Upload code to OkHttpokhttp
7 files changed, 244 insertions, 265 deletions
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java index 29d286a71..2fcfdc39c 100644 --- a/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java +++ b/src/main/java/eu/siacs/conversations/crypto/PgpDecryptionService.java @@ -210,7 +210,7 @@ public class PgpDecryptionService { } } URL url = message.getFileParams().url; - mXmppConnectionService.getFileBackend().updateFileParams(message, url); + mXmppConnectionService.getFileBackend().updateFileParams(message, url.toString()); message.setEncryption(Message.ENCRYPTION_DECRYPTED); mXmppConnectionService.updateMessage(message); if (!inputFile.delete()) { diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index e55de385a..32f9c58bd 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -524,7 +524,7 @@ public class HttpDownloadConnection implements Transferable { } else { url = mUrl; } - mXmppConnectionService.getFileBackend().updateFileParams(message, url); + mXmppConnectionService.getFileBackend().updateFileParams(message, url.toString()); mXmppConnectionService.updateMessage(message); } diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java index a6161701f..deaa80844 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java @@ -6,7 +6,6 @@ import android.util.Log; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; -import java.net.HttpURLConnection; import java.net.URL; import java.util.Arrays; import java.util.HashMap; @@ -26,225 +25,182 @@ import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.Checksum; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.WakeLockHelper; +import okhttp3.HttpUrl; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; import static eu.siacs.conversations.http.HttpConnectionManager.EXECUTOR; public class HttpUploadConnection implements Transferable { - static final List<String> WHITE_LISTED_HEADERS = Arrays.asList( - "Authorization", - "Cookie", - "Expires" - ); - - private final HttpConnectionManager mHttpConnectionManager; - private final XmppConnectionService mXmppConnectionService; - private final SlotRequester mSlotRequester; - private final Method method; - private final boolean mUseTor; - private boolean cancelled = false; - private boolean delayed = false; - private DownloadableFile file; - private final Message message; - private String mime; - private SlotRequester.Slot slot; - private byte[] key = null; - - private long transmitted = 0; - - public HttpUploadConnection(Message message, Method method, HttpConnectionManager httpConnectionManager) { - this.message = message; - this.method = method; - this.mHttpConnectionManager = httpConnectionManager; - this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); - this.mSlotRequester = new SlotRequester(this.mXmppConnectionService); - this.mUseTor = mXmppConnectionService.useTorToConnect(); - } - - @Override - public boolean start() { - return false; - } - - @Override - public int getStatus() { - return STATUS_UPLOADING; - } - - @Override - public long getFileSize() { - return file == null ? 0 : file.getExpectedSize(); - } - - @Override - public int getProgress() { - if (file == null) { - return 0; - } - return (int) ((((double) transmitted) / file.getExpectedSize()) * 100); - } - - @Override - public void cancel() { - this.cancelled = true; - } - - private void fail(String errorMessage) { - finish(); - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage); - } - - private void finish() { - mHttpConnectionManager.finishUploadConnection(this); - message.setTransferable(null); - } - - public void init(boolean delay) { - final Account account = message.getConversation().getAccount(); - this.file = mXmppConnectionService.getFileBackend().getFile(message, false); - if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - this.mime = "application/pgp-encrypted"; - } else { - this.mime = this.file.getMimeType(); - } - final long originalFileSize = file.getSize(); - this.delayed = delay; - if (Config.ENCRYPT_ON_HTTP_UPLOADED - || message.getEncryption() == Message.ENCRYPTION_AXOLOTL - || message.getEncryption() == Message.ENCRYPTION_OTR) { - this.key = new byte[44]; - mXmppConnectionService.getRNG().nextBytes(this.key); - this.file.setKeyAndIv(this.key); - } - - final String md5; - - if (method == Method.P1_S3) { - try { - md5 = Checksum.md5(AbstractConnectionManager.upgrade(file, new FileInputStream(file))); - } catch (Exception e) { - Log.d(Config.LOGTAG, account.getJid().asBareJid()+": unable to calculate md5()", e); - fail(e.getMessage()); - return; - } - } else { - md5 = null; - } - - this.file.setExpectedSize(originalFileSize + (file.getKey() != null ? 16 : 0)); - message.resetFileParams(); - this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() { - @Override - public void success(SlotRequester.Slot slot) { - if (!cancelled) { - HttpUploadConnection.this.slot = slot; - EXECUTOR.execute(HttpUploadConnection.this::upload); - } - } - - @Override - public void failure(String message) { - fail(message); - } - }); - message.setTransferable(this); - mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); - } - - private void upload() { - OutputStream os = null; - InputStream fileInputStream = null; - HttpURLConnection connection = null; - final PowerManager.WakeLock wakeLock = mHttpConnectionManager.createWakeLock(Thread.currentThread()); - try { - fileInputStream = new FileInputStream(file); - final String slotHostname = slot.getPutUrl().getHost(); - final boolean onionSlot = slotHostname != null && slotHostname.endsWith(".onion"); - final int expectedFileSize = (int) file.getExpectedSize(); - final int readTimeout = (expectedFileSize / 2048) + Config.SOCKET_TIMEOUT; //assuming a minimum transfer speed of 16kbit/s - wakeLock.acquire(readTimeout); - Log.d(Config.LOGTAG, "uploading to " + slot.getPutUrl().toString()+ " w/ read timeout of "+readTimeout+"s"); - - if (mUseTor || message.getConversation().getAccount().isOnion() || onionSlot) { - connection = (HttpURLConnection) slot.getPutUrl().openConnection(HttpConnectionManager.getProxy()); - } else { - connection = (HttpURLConnection) slot.getPutUrl().openConnection(); - } - if (connection instanceof HttpsURLConnection) { - mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); - } - connection.setUseCaches(false); - connection.setRequestMethod("PUT"); - connection.setFixedLengthStreamingMode(expectedFileSize); - connection.setRequestProperty("User-Agent",mXmppConnectionService.getIqGenerator().getUserAgent()); - if(slot.getHeaders() != null) { - for(HashMap.Entry<String,String> entry : slot.getHeaders().entrySet()) { - connection.setRequestProperty(entry.getKey(),entry.getValue()); - } - } - connection.setDoOutput(true); - connection.setDoInput(true); - connection.setConnectTimeout(Config.SOCKET_TIMEOUT * 1000); - connection.setReadTimeout(readTimeout * 1000); - connection.connect(); - final InputStream innerInputStream = AbstractConnectionManager.upgrade(file, fileInputStream); - os = connection.getOutputStream(); - transmitted = 0; - int count; - byte[] buffer = new byte[4096]; - while (((count = innerInputStream.read(buffer)) != -1) && !cancelled) { - transmitted += count; - os.write(buffer, 0, count); - mHttpConnectionManager.updateConversationUi(false); - } - os.flush(); - os.close(); - int code = connection.getResponseCode(); - InputStream is = connection.getErrorStream(); - if (is != null) { - try (Scanner scanner = new Scanner(is)) { - scanner.useDelimiter("\\Z"); - Log.d(Config.LOGTAG, "body: " + scanner.next()); - } - } - if (code == 200 || code == 201) { - Log.d(Config.LOGTAG, "finished uploading file"); - final URL get; - if (key != null) { - if (method == Method.P1_S3) { - get = new URL(slot.getGetUrl().toString()+"#"+CryptoHelper.bytesToHex(key)); - } else { - get = CryptoHelper.toAesGcmUrl(new URL(slot.getGetUrl().toString() + "#" + CryptoHelper.bytesToHex(key))); - } - } else { - get = slot.getGetUrl(); - } - mXmppConnectionService.getFileBackend().updateFileParams(message, get); - mXmppConnectionService.getFileBackend().updateMediaScanner(file); - finish(); - if (!message.isPrivateMessage()) { - message.setCounterpart(message.getConversation().getJid().asBareJid()); - } - mXmppConnectionService.resendMessage(message, delayed); - } else { - Log.d(Config.LOGTAG,"http upload failed because response code was "+code); - fail("http upload failed because response code was "+code); - } - } catch (Exception e) { - e.printStackTrace(); - Log.d(Config.LOGTAG,"http upload failed "+e.getMessage()); - fail(e.getMessage()); - } finally { - FileBackend.close(fileInputStream); - FileBackend.close(os); - if (connection != null) { - connection.disconnect(); - } - WakeLockHelper.release(wakeLock); - } - } - - public Message getMessage() { - return message; - } + static final List<String> WHITE_LISTED_HEADERS = Arrays.asList( + "Authorization", + "Cookie", + "Expires" + ); + + private final HttpConnectionManager mHttpConnectionManager; + private final XmppConnectionService mXmppConnectionService; + private final SlotRequester mSlotRequester; + private final Method method; + private final boolean mUseTor; + private boolean cancelled = false; + private boolean delayed = false; + private DownloadableFile file; + private final Message message; + private String mime; + private SlotRequester.Slot slot; + private byte[] key = null; + + private long transmitted = 0; + + public HttpUploadConnection(Message message, Method method, HttpConnectionManager httpConnectionManager) { + this.message = message; + this.method = method; + this.mHttpConnectionManager = httpConnectionManager; + this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); + this.mSlotRequester = new SlotRequester(this.mXmppConnectionService); + this.mUseTor = mXmppConnectionService.useTorToConnect(); + } + + @Override + public boolean start() { + return false; + } + + @Override + public int getStatus() { + return STATUS_UPLOADING; + } + + @Override + public long getFileSize() { + return file == null ? 0 : file.getExpectedSize(); + } + + @Override + public int getProgress() { + if (file == null) { + return 0; + } + return (int) ((((double) transmitted) / file.getExpectedSize()) * 100); + } + + @Override + public void cancel() { + this.cancelled = true; + } + + private void fail(String errorMessage) { + finish(); + mXmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED, cancelled ? Message.ERROR_MESSAGE_CANCELLED : errorMessage); + } + + private void finish() { + mHttpConnectionManager.finishUploadConnection(this); + message.setTransferable(null); + } + + public void init(boolean delay) { + final Account account = message.getConversation().getAccount(); + this.file = mXmppConnectionService.getFileBackend().getFile(message, false); + if (message.getEncryption() == Message.ENCRYPTION_PGP || message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + this.mime = "application/pgp-encrypted"; + } else { + this.mime = this.file.getMimeType(); + } + final long originalFileSize = file.getSize(); + this.delayed = delay; + if (Config.ENCRYPT_ON_HTTP_UPLOADED + || message.getEncryption() == Message.ENCRYPTION_AXOLOTL + || message.getEncryption() == Message.ENCRYPTION_OTR) { + this.key = new byte[44]; + mXmppConnectionService.getRNG().nextBytes(this.key); + this.file.setKeyAndIv(this.key); + } + + final String md5; + + if (method == Method.P1_S3) { + try { + md5 = Checksum.md5(AbstractConnectionManager.upgrade(file, new FileInputStream(file))); + } catch (Exception e) { + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to calculate md5()", e); + fail(e.getMessage()); + return; + } + } else { + md5 = null; + } + + this.file.setExpectedSize(originalFileSize + (file.getKey() != null ? 16 : 0)); + message.resetFileParams(); + this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() { + @Override + public void success(SlotRequester.Slot slot) { + if (!cancelled) { + HttpUploadConnection.this.slot = slot; + EXECUTOR.execute(HttpUploadConnection.this::upload); + } + } + + @Override + public void failure(String message) { + fail(message); + } + }); + message.setTransferable(this); + mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND); + } + + private void upload() { + final String slotHostname = slot.getPutUrl().host(); + final boolean onionSlot = slotHostname.endsWith(".onion"); + try { + final OkHttpClient client; + if (mUseTor || message.getConversation().getAccount().isOnion() || onionSlot) { + client = new OkHttpClient.Builder().proxy(HttpConnectionManager.getProxy()).build(); + } else { + client = new OkHttpClient(); + } + final RequestBody requestBody = AbstractConnectionManager.requestBody(file); + final Request request = new Request.Builder() + .url(slot.getPutUrl()) + .put(requestBody) + .headers(slot.getHeaders()) + .build(); + Log.d(Config.LOGTAG, "uploading file to " + slot.getPutUrl()); + final Response response = client.newCall(request).execute(); + final int code = response.code(); + if (code == 200 || code == 201) { + Log.d(Config.LOGTAG, "finished uploading file"); + final String get; + if (key != null) { + get = CryptoHelper.toAesGcmUrl(slot.getGetUrl().newBuilder().fragment(CryptoHelper.bytesToHex(key)).build()); + } else { + get = slot.getGetUrl().toString(); + } + mXmppConnectionService.getFileBackend().updateFileParams(message, get); + mXmppConnectionService.getFileBackend().updateMediaScanner(file); + finish(); + if (!message.isPrivateMessage()) { + message.setCounterpart(message.getConversation().getJid().asBareJid()); + } + mXmppConnectionService.resendMessage(message, delayed); + } else { + Log.d(Config.LOGTAG, "http upload failed because response code was " + code); + fail("http upload failed because response code was " + code); + } + } catch (final Exception e) { + Log.d(Config.LOGTAG, "http upload failed", e); + fail(e.getMessage()); + } + } + + public Message getMessage() { + return message; + } } diff --git a/src/main/java/eu/siacs/conversations/http/SlotRequester.java b/src/main/java/eu/siacs/conversations/http/SlotRequester.java index b52bab39a..249b52d43 100644 --- a/src/main/java/eu/siacs/conversations/http/SlotRequester.java +++ b/src/main/java/eu/siacs/conversations/http/SlotRequester.java @@ -31,9 +31,7 @@ package eu.siacs.conversations.http; import android.util.Log; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashMap; +import com.google.common.collect.ImmutableMap; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; @@ -44,6 +42,8 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.stanzas.IqPacket; +import okhttp3.Headers; +import okhttp3.HttpUrl; public class SlotRequester { @@ -75,14 +75,13 @@ public class SlotRequester { final String putUrl = slotElement.findChildContent("put"); final String getUrl = slotElement.findChildContent("get"); if (getUrl != null && putUrl != null) { - Slot slot = new Slot(new URL(putUrl)); - slot.getUrl = new URL(getUrl); - slot.headers = new HashMap<>(); - slot.headers.put("Content-Type", mime == null ? "application/octet-stream" : mime); + Slot slot = new Slot(HttpUrl.get(putUrl)); + slot.getUrl = HttpUrl.get(getUrl); + slot.headers = Headers.of("Content-Type", mime == null ? "application/octet-stream" : mime); callback.success(slot); return; } - } catch (MalformedURLException e) { + } catch (IllegalArgumentException e) { //fall through } } @@ -105,23 +104,24 @@ public class SlotRequester { final String putUrl = put == null ? null : put.getAttribute("url"); final String getUrl = get == null ? null : get.getAttribute("url"); if (getUrl != null && putUrl != null) { - Slot slot = new Slot(new URL(putUrl)); - slot.getUrl = new URL(getUrl); - slot.headers = new HashMap<>(); + Slot slot = new Slot(HttpUrl.get(putUrl)); + slot.getUrl = HttpUrl.get(getUrl); + final ImmutableMap.Builder<String,String> headers = new ImmutableMap.Builder<>(); for (Element child : put.getChildren()) { if ("header".equals(child.getName())) { final String name = child.getAttribute("name"); final String value = child.getContent(); if (HttpUploadConnection.WHITE_LISTED_HEADERS.contains(name) && value != null && !value.trim().contains("\n")) { - slot.headers.put(name, value.trim()); + headers.put(name, value.trim()); } } } - slot.headers.put("Content-Type", mime == null ? "application/octet-stream" : mime); + headers.put("Content-Type", mime == null ? "application/octet-stream" : mime); + slot.headers = Headers.of(headers.build()); callback.success(slot); return; } - } catch (MalformedURLException e) { + } catch (IllegalArgumentException e) { //fall through } } @@ -133,28 +133,7 @@ public class SlotRequester { } private void requestP1S3(final Account account, Jid host, String filename, String md5, OnSlotRequested callback) { - IqPacket request = service.getIqGenerator().requestP1S3Slot(host, md5); - service.sendIqPacket(account, request, (a, packet) -> { - if (packet.getType() == IqPacket.TYPE.RESULT) { - String putUrl = packet.query(Namespace.P1_S3_FILE_TRANSFER).getAttribute("upload"); - String id = packet.query().getAttribute("fileid"); - try { - if (putUrl != null && id != null) { - Slot slot = new Slot(new URL(putUrl)); - slot.getUrl = P1S3UrlStreamHandler.of(id, filename); - slot.headers = new HashMap<>(); - slot.headers.put("Content-MD5", md5); - slot.headers.put("Content-Type", " "); //required to force it to empty. otherwise library will set something - callback.success(slot); - return; - } - } catch (MalformedURLException e) { - //fall through; - } - } - callback.failure("unable to request slot"); - }); - Log.d(Config.LOGTAG, "requesting slot with p1. md5=" + md5); + callback.failure("unable to request slot"); } @@ -167,23 +146,23 @@ public class SlotRequester { } public static class Slot { - private final URL putUrl; - private URL getUrl; - private HashMap<String, String> headers; + private final HttpUrl putUrl; + private HttpUrl getUrl; + private Headers headers; - private Slot(URL putUrl) { + private Slot(HttpUrl putUrl) { this.putUrl = putUrl; } - public URL getPutUrl() { + public HttpUrl getPutUrl() { return putUrl; } - public URL getGetUrl() { + public HttpUrl getGetUrl() { return getUrl; } - public HashMap<String, String> getHeaders() { + public Headers getHeaders() { return headers; } } diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 690caaafd..1e5944a16 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -1305,7 +1305,7 @@ public class FileBackend { updateFileParams(message, null); } - public void updateFileParams(Message message, URL url) { + public void updateFileParams(Message message, String url) { DownloadableFile file = getFile(message); final String mime = file.getMimeType(); final boolean privateMessage = message.isPrivateMessage(); @@ -1315,7 +1315,7 @@ public class FileBackend { final boolean pdf = "application/pdf".equals(mime); final StringBuilder body = new StringBuilder(); if (url != null) { - body.append(url.toString()); + body.append(url); } body.append('|').append(file.getSize()); if (image || video || (pdf && Compatibility.runsTwentyOne())) { diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java index dcf8848bf..2db77412c 100644 --- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java @@ -13,8 +13,10 @@ import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.InvalidAlgorithmParameterException; @@ -23,12 +25,18 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.Nullable; import javax.crypto.NoSuchPaddingException; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.utils.Compatibility; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okio.BufferedSink; +import okio.Okio; +import okio.Source; import static eu.siacs.conversations.entities.Transferable.VALID_CRYPTO_EXTENSIONS; @@ -42,7 +50,7 @@ public class AbstractConnectionManager { this.mXmppConnectionService = service; } - public static InputStream upgrade(DownloadableFile file, InputStream is) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, NoSuchProviderException { + public static InputStream upgrade(DownloadableFile file, InputStream is) { if (file.getKey() != null && file.getIv() != null) { AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine()); cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv())); @@ -52,6 +60,33 @@ public class AbstractConnectionManager { } } + + //For progress tracking see: + //https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java + + public static RequestBody requestBody(final DownloadableFile file) { + return new RequestBody() { + + @Override + public long contentLength() { + return file.getSize() + (file.getKey() != null ? 16 : 0); + } + + @Nullable + @Override + public MediaType contentType() { + return MediaType.parse(file.getMimeType()); + } + + @Override + public void writeTo(final BufferedSink sink) throws IOException { + try (Source source = Okio.source(upgrade(file, new FileInputStream(file)))) { + sink.writeAll(source); + } + } + }; + } + public static OutputStream createOutputStream(DownloadableFile file, boolean append, boolean decrypt) { FileOutputStream os; try { diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index 3174c4198..711d1ef76 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -33,6 +33,7 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.http.AesGcmURLStreamHandler; import eu.siacs.conversations.xmpp.Jid; +import okhttp3.HttpUrl; public final class CryptoHelper { @@ -289,6 +290,14 @@ public final class CryptoHelper { } } + public static String toAesGcmUrl(HttpUrl url) { + if (url.isHttps()) { + return AesGcmURLStreamHandler.PROTOCOL_NAME + url.toString().substring(5); + } else { + return url.toString(); + } + } + public static URL toHttpsUrl(URL url) { if (!url.getProtocol().equalsIgnoreCase(AesGcmURLStreamHandler.PROTOCOL_NAME)) { return url; |