From ed8793f354cf052bed65e90b49222b40642489ee Mon Sep 17 00:00:00 2001 From: Kevin R Date: Fri, 3 Jun 2022 00:08:20 +0200 Subject: [PATCH] Added base64 encoded attachment support --- .../manager/helper/AttachmentHelper.java | 2 +- .../signal/manager/util/AttachmentUtils.java | 15 ++++++--- .../org/asamk/signal/manager/util/Utils.java | 33 +++++++++++++++++++ .../asamk/signal/commands/SendCommand.java | 4 ++- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java index 8793ce93..0d560e14 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/AttachmentHelper.java @@ -51,7 +51,7 @@ public class AttachmentHelper { } public SignalServiceAttachmentPointer uploadAttachment(String attachment) throws IOException, AttachmentInvalidException { - var attachmentStream = AttachmentUtils.createAttachmentStream(new File(attachment)); + var attachmentStream = AttachmentUtils.createAttachmentStream(attachment); return uploadAttachment(attachmentStream); } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java index a94d7673..171d266c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java @@ -6,6 +6,7 @@ import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -19,17 +20,21 @@ public class AttachmentUtils { } final var signalServiceAttachments = new ArrayList(attachments.size()); for (var attachment : attachments) { - signalServiceAttachments.add(createAttachmentStream(new File(attachment))); + signalServiceAttachments.add(createAttachmentStream(attachment)); } return signalServiceAttachments; } - public static SignalServiceAttachmentStream createAttachmentStream(File attachmentFile) throws AttachmentInvalidException { + public static SignalServiceAttachmentStream createAttachmentStream(String attachment) throws AttachmentInvalidException { try { - final var streamDetails = Utils.createStreamDetailsFromFile(attachmentFile); - return createAttachmentStream(streamDetails, Optional.of(attachmentFile.getName())); + final var streamDetails = Utils.createStreamDetails(attachment); + final var name = streamDetails.getStream() instanceof FileInputStream + ? new File(attachment).getName() + : null; + + return createAttachmentStream(streamDetails, Optional.ofNullable(name)); } catch (IOException e) { - throw new AttachmentInvalidException(attachmentFile.toString(), e); + throw new AttachmentInvalidException(attachment, e); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java index 3fc69801..a4a5bc3e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -9,6 +9,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.StreamDetails; import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -17,6 +18,7 @@ import java.net.URLConnection; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.Base64; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -44,6 +46,29 @@ public class Utils { return mime; } + private static boolean isBase64DataString(String[] parts) { + return parts.length == 2 + && parts[0].startsWith("data:") + && parts[0].contains("/") + && parts[1].startsWith("base64,"); + } + + public static boolean isBase64DataString(String value) { + return isBase64DataString(value.split(";", 2)); + } + + public static StreamDetails createStreamDetailsFromBase64(String base64) { + final String[] parts = base64.split(";", 2); + if (!isBase64DataString(parts)) { + throw new IllegalArgumentException("The given argument is not a valid base64 string."); + } + + parts[0] = parts[0].substring(5); + byte[] bytes = Base64.getDecoder().decode(parts[1].substring(7).getBytes(StandardCharsets.UTF_8)); + + return new StreamDetails(new ByteArrayInputStream(bytes), parts[0], bytes.length); + } + public static StreamDetails createStreamDetailsFromFile(File file) throws IOException { InputStream stream = new FileInputStream(file); final var size = file.length(); @@ -51,6 +76,14 @@ public class Utils { return new StreamDetails(stream, mime, size); } + public static StreamDetails createStreamDetails(String value) throws IOException { + if (isBase64DataString(value)) { + return createStreamDetailsFromBase64(value); + } + + return createStreamDetailsFromFile(new File(value)); + } + public static Fingerprint computeSafetyNumber( boolean isUuidCapable, SignalServiceAddress ownAddress, diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index ae324c84..7ffd9fc7 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -55,7 +55,9 @@ public class SendCommand implements JsonRpcLocalCommand { mut.addArgument("--message-from-stdin") .action(Arguments.storeTrue()) .help("Read the message from standard input."); - subparser.addArgument("-a", "--attachment").nargs("*").help("Add file as attachment"); + subparser.addArgument("-a", "--attachment").nargs("*").help("Add file as attachment." + + "Base64 encoded attachments can be added and must follow the format " + + "data:;base64,."); subparser.addArgument("-e", "--end-session", "--endsession") .help("Clear session state and send end session message.") .action(Arguments.storeTrue());