diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index 755de63ea..5ebe20bb6 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -123,9 +123,16 @@ public class MessageGenerator extends AbstractGenerator { public MessagePacket generatePgpChat(Message message) { MessagePacket packet = preparePacket(message); if (message.hasFileOnRemoteHost()) { - final String url = message.getFileParams().url.toString(); - packet.setBody(url); - packet.addChild("x", Namespace.OOB).addChild("url").setContent(url); + Message.FileParams fileParams = message.getFileParams(); + final URL url = fileParams.url; + if (P1S3UrlStreamHandler.PROTOCOL_NAME.equals(url.getProtocol())) { + Element x = packet.addChild("x", Namespace.P1_S3_FILE_TRANSFER); + x.setAttribute("name", url.getFile()); + x.setAttribute("fileid", url.getHost()); + } else { + packet.setBody(url.toString()); + packet.addChild("x", Namespace.OOB).addChild("url").setContent(url.toString()); + } } else { if (Config.supportUnencrypted()) { packet.setBody(PGP_FALLBACK_MESSAGE); diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java index f1a9d6a4d..345f36692 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java @@ -1,18 +1,14 @@ package eu.siacs.conversations.http; import android.os.PowerManager; -import android.renderscript.ScriptGroup; import android.util.Log; import android.util.Pair; -import org.bouncycastle.jce.exception.ExtIOException; - import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; -import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.HashMap; @@ -26,17 +22,12 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Transferable; -import eu.siacs.conversations.parser.IqParser; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.AbstractConnectionManager; 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 eu.siacs.conversations.xml.Namespace; -import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; -import rocks.xmpp.addr.Jid; public class HttpUploadConnection implements Transferable { @@ -50,15 +41,13 @@ public class HttpUploadConnection implements Transferable { private final XmppConnectionService mXmppConnectionService; private final SlotRequester mSlotRequester; private final Method method; - + private final boolean mUseTor; private boolean canceled = false; private boolean delayed = false; private DownloadableFile file; private Message message; private String mime; private SlotRequester.Slot slot; - private final boolean mUseTor; - private byte[] key = null; private long transmitted = 0; @@ -156,7 +145,6 @@ public class HttpUploadConnection implements Transferable { public void success(SlotRequester.Slot slot) { if (!canceled) { HttpUploadConnection.this.slot = slot; - Log.d(Config.LOGTAG,"not starting upload to "+slot.getPutUrl()); new Thread(HttpUploadConnection.this::upload).start(); } } @@ -225,7 +213,11 @@ public class HttpUploadConnection implements Transferable { Log.d(Config.LOGTAG, "finished uploading file"); final URL get; if (key != null) { - get = CryptoHelper.toAesGcmUrl(new URL(slot.getGetUrl().toString() + "#" + CryptoHelper.bytesToHex(key))); + 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(); } diff --git a/src/main/java/eu/siacs/conversations/http/P1S3UrlStreamHandler.java b/src/main/java/eu/siacs/conversations/http/P1S3UrlStreamHandler.java index 07550d883..2ba92180c 100644 --- a/src/main/java/eu/siacs/conversations/http/P1S3UrlStreamHandler.java +++ b/src/main/java/eu/siacs/conversations/http/P1S3UrlStreamHandler.java @@ -29,12 +29,13 @@ package eu.siacs.conversations.http; -import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; +import eu.siacs.conversations.xml.Element; + public class P1S3UrlStreamHandler extends URLStreamHandler { public static final String PROTOCOL_NAME = "p1s3"; @@ -45,6 +46,17 @@ public class P1S3UrlStreamHandler extends URLStreamHandler { } public static URL of(String fileId, String filename) throws MalformedURLException { + if (fileId == null || filename == null) { + throw new MalformedURLException("Paramaters must not be null"); + } return new URL(PROTOCOL_NAME+"://" + fileId + "/" + filename); } + + public static URL of(Element x) { + try { + return of(x.getAttribute("fileid"),x.getAttribute("name")); + } catch (MalformedURLException e) { + return null; + } + } } diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 3114f0d14..78844f740 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -3,6 +3,7 @@ package eu.siacs.conversations.parser; import android.util.Log; import android.util.Pair; +import java.net.URL; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -27,6 +28,7 @@ import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.ReadByMarker; import eu.siacs.conversations.entities.ReceiptRequest; import eu.siacs.conversations.http.HttpConnectionManager; +import eu.siacs.conversations.http.P1S3UrlStreamHandler; import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; @@ -277,6 +279,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted"); final Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0"); final Element oob = packet.findChild("x", Namespace.OOB); + final Element xP1S3 = packet.findChild("x", Namespace.P1_S3_FILE_TRANSFER); + final URL xP1S3url = xP1S3 == null ? null : P1S3UrlStreamHandler.of(xP1S3); final String oobUrl = oob != null ? oob.findChildContent("url") : null; final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id"); final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX); @@ -324,7 +328,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece return; } - if ((body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || oobUrl != null) && !isMucStatusMessage) { + if ((body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || oobUrl != null || xP1S3 != null) && !isMucStatusMessage) { final boolean conversationIsProbablyMuc = isTypeGroupChat || mucUserElement != null || account.getXmppConnection().getMucServersWithholdAccount().contains(counterpart.getDomain()); final Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), conversationIsProbablyMuc, false, query, false); final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI; @@ -362,7 +366,13 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } } final Message message; - if (pgpEncrypted != null && Config.supportOpenPgp()) { + if (xP1S3url != null) { + message = new Message(conversation, xP1S3url.toString(), Message.ENCRYPTION_NONE, status); + message.setOob(true); + if (CryptoHelper.isPgpEncryptedUrl(xP1S3url.toString())) { + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + } + } else if (pgpEncrypted != null && Config.supportOpenPgp()) { message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status); } else if (axolotlEncrypted != null && Config.supportOmemo()) { Jid origin; diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index 426e63b86..2df9c27b3 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -276,6 +276,6 @@ public final class CryptoHelper { return false; } final String u = url.toLowerCase(); - return !u.contains(" ") && (u.startsWith("https://") || u.startsWith("http://")) && u.endsWith(".pgp"); + return !u.contains(" ") && (u.startsWith("https://") || u.startsWith("http://") || u.startsWith("p1s3://")) && u.endsWith(".pgp"); } } diff --git a/src/main/java/eu/siacs/conversations/utils/MessageUtils.java b/src/main/java/eu/siacs/conversations/utils/MessageUtils.java index b6399ede1..ca3416579 100644 --- a/src/main/java/eu/siacs/conversations/utils/MessageUtils.java +++ b/src/main/java/eu/siacs/conversations/utils/MessageUtils.java @@ -35,6 +35,7 @@ import java.util.regex.Pattern; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.http.AesGcmURLStreamHandler; +import eu.siacs.conversations.http.P1S3UrlStreamHandler; public class MessageUtils { @@ -79,7 +80,8 @@ public class MessageUtils { final boolean encrypted = ref != null && AesGcmURLStreamHandler.IV_KEY.matcher(ref).matches(); final boolean followedByDataUri = lines.length == 2 && lines[1].startsWith("data:"); final boolean validAesGcm = AesGcmURLStreamHandler.PROTOCOL_NAME.equalsIgnoreCase(protocol) && encrypted && (lines.length == 1 || followedByDataUri); - final boolean validOob = ("http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol)) && (oob || encrypted) && lines.length == 1; + final boolean validProtocol = "http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol) || P1S3UrlStreamHandler.PROTOCOL_NAME.equalsIgnoreCase(protocol); + final boolean validOob = validProtocol && (oob || encrypted) && lines.length == 1; return validAesGcm || validOob; } catch (MalformedURLException e) { return false;