From 9e6a3534275d5bac8454792e05280f13fa5ef13c Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 29 Dec 2020 22:09:06 +0100 Subject: [PATCH 001/124] Move group classes to separate package --- .../asamk/signal/JsonDbusReceiveMessageHandler.java | 2 +- .../java/org/asamk/signal/ReceiveMessageHandler.java | 4 ++-- .../java/org/asamk/signal/commands/BlockCommand.java | 6 +++--- .../org/asamk/signal/commands/JoinGroupCommand.java | 4 ++-- .../org/asamk/signal/commands/ListGroupsCommand.java | 2 +- .../org/asamk/signal/commands/QuitGroupCommand.java | 8 ++++---- .../java/org/asamk/signal/commands/SendCommand.java | 2 +- .../org/asamk/signal/commands/SendReactionCommand.java | 8 ++++---- .../java/org/asamk/signal/commands/UnblockCommand.java | 6 +++--- .../org/asamk/signal/commands/UpdateGroupCommand.java | 2 +- .../java/org/asamk/signal/dbus/DbusSignalImpl.java | 6 +++--- src/main/java/org/asamk/signal/json/JsonGroupInfo.java | 2 +- .../java/org/asamk/signal/manager/HandleAction.java | 1 + src/main/java/org/asamk/signal/manager/KeyUtils.java | 4 ++-- src/main/java/org/asamk/signal/manager/Manager.java | 7 +++++++ .../org/asamk/signal/manager/{ => groups}/GroupId.java | 2 +- .../manager/{ => groups}/GroupIdFormatException.java | 2 +- .../asamk/signal/manager/{ => groups}/GroupIdV1.java | 2 +- .../asamk/signal/manager/{ => groups}/GroupIdV2.java | 2 +- .../manager/{ => groups}/GroupInviteLinkUrl.java | 2 +- .../signal/manager/{ => groups}/GroupLinkPassword.java | 4 +++- .../manager/{ => groups}/GroupNotFoundException.java | 2 +- .../asamk/signal/manager/{ => groups}/GroupUtils.java | 2 +- .../manager/{ => groups}/NotAGroupMemberException.java | 2 +- .../org/asamk/signal/manager/helper/GroupHelper.java | 6 +++--- .../java/org/asamk/signal/storage/SignalAccount.java | 2 +- .../org/asamk/signal/storage/groups/GroupInfo.java | 4 ++-- .../org/asamk/signal/storage/groups/GroupInfoV1.java | 10 +++++----- .../org/asamk/signal/storage/groups/GroupInfoV2.java | 4 ++-- .../asamk/signal/storage/groups/JsonGroupStore.java | 8 ++++---- src/main/java/org/asamk/signal/util/ErrorUtils.java | 6 +++--- src/main/java/org/asamk/signal/util/Util.java | 4 ++-- 32 files changed, 69 insertions(+), 59 deletions(-) rename src/main/java/org/asamk/signal/manager/{ => groups}/GroupId.java (97%) rename src/main/java/org/asamk/signal/manager/{ => groups}/GroupIdFormatException.java (85%) rename src/main/java/org/asamk/signal/manager/{ => groups}/GroupIdV1.java (87%) rename src/main/java/org/asamk/signal/manager/{ => groups}/GroupIdV2.java (86%) rename src/main/java/org/asamk/signal/manager/{ => groups}/GroupInviteLinkUrl.java (99%) rename src/main/java/org/asamk/signal/manager/{ => groups}/GroupLinkPassword.java (90%) rename src/main/java/org/asamk/signal/manager/{ => groups}/GroupNotFoundException.java (81%) rename src/main/java/org/asamk/signal/manager/{ => groups}/GroupUtils.java (98%) rename src/main/java/org/asamk/signal/manager/{ => groups}/NotAGroupMemberException.java (85%) diff --git a/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java index 50eb9f9b..0cffd7b1 100644 --- a/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java @@ -1,8 +1,8 @@ package org.asamk.signal; import org.asamk.Signal; -import org.asamk.signal.manager.GroupUtils; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupUtils; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 99010e13..8dc38e4f 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -1,8 +1,8 @@ package org.asamk.signal; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupUtils; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.storage.contacts.ContactInfo; import org.asamk.signal.storage.groups.GroupInfo; import org.asamk.signal.util.DateUtils; diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index 2a9bc4e9..627be5a9 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -3,10 +3,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupIdFormatException; -import org.asamk.signal.manager.GroupNotFoundException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.util.Util; import org.whispersystems.signalservice.api.util.InvalidNumberException; diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index 8438e1fa..305bf55b 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -4,9 +4,9 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupInviteLinkUrl; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 4d1032a2..66ff3a00 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -4,8 +4,8 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; -import org.asamk.signal.manager.GroupInviteLinkUrl; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.storage.groups.GroupInfo; import org.whispersystems.signalservice.api.push.SignalServiceAddress; diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index efc63f8f..c1f6bfbf 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -3,11 +3,11 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupIdFormatException; -import org.asamk.signal.manager.GroupNotFoundException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.NotAGroupMemberException; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.asamk.signal.manager.groups.GroupNotFoundException; +import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.util.Util; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.messages.SendMessageResult; diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 04b06434..ee51dd91 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -5,7 +5,7 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; -import org.asamk.signal.manager.GroupIdFormatException; +import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; import org.freedesktop.dbus.exceptions.DBusExecutionException; diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index 345c9180..c680bfd7 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -4,11 +4,11 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupIdFormatException; -import org.asamk.signal.manager.GroupNotFoundException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.NotAGroupMemberException; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.asamk.signal.manager.groups.GroupNotFoundException; +import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.util.Util; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.messages.SendMessageResult; diff --git a/src/main/java/org/asamk/signal/commands/UnblockCommand.java b/src/main/java/org/asamk/signal/commands/UnblockCommand.java index 73e578ac..14ea2996 100644 --- a/src/main/java/org/asamk/signal/commands/UnblockCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnblockCommand.java @@ -3,10 +3,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupIdFormatException; -import org.asamk.signal.manager.GroupNotFoundException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.util.Util; import org.whispersystems.signalservice.api.util.InvalidNumberException; diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index dae06b86..a6f40ef2 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -4,7 +4,7 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; -import org.asamk.signal.manager.GroupIdFormatException; +import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.Util; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.whispersystems.util.Base64; diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index cbb72835..df3f12f2 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -2,10 +2,10 @@ package org.asamk.signal.dbus; import org.asamk.Signal; import org.asamk.signal.manager.AttachmentInvalidException; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupNotFoundException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.NotAGroupMemberException; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupNotFoundException; +import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.storage.groups.GroupInfo; import org.asamk.signal.util.ErrorUtils; import org.freedesktop.dbus.exceptions.DBusExecutionException; diff --git a/src/main/java/org/asamk/signal/json/JsonGroupInfo.java b/src/main/java/org/asamk/signal/json/JsonGroupInfo.java index 9709be20..79967955 100644 --- a/src/main/java/org/asamk/signal/json/JsonGroupInfo.java +++ b/src/main/java/org/asamk/signal/json/JsonGroupInfo.java @@ -1,6 +1,6 @@ package org.asamk.signal.json; -import org.asamk.signal.manager.GroupUtils; +import org.asamk.signal.manager.groups.GroupUtils; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import org.whispersystems.signalservice.api.push.SignalServiceAddress; diff --git a/src/main/java/org/asamk/signal/manager/HandleAction.java b/src/main/java/org/asamk/signal/manager/HandleAction.java index aa25d8c5..0dd151a9 100644 --- a/src/main/java/org/asamk/signal/manager/HandleAction.java +++ b/src/main/java/org/asamk/signal/manager/HandleAction.java @@ -1,5 +1,6 @@ package org.asamk.signal.manager; +import org.asamk.signal.manager.groups.GroupIdV1; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.Objects; diff --git a/src/main/java/org/asamk/signal/manager/KeyUtils.java b/src/main/java/org/asamk/signal/manager/KeyUtils.java index 21f6037f..6ac093db 100644 --- a/src/main/java/org/asamk/signal/manager/KeyUtils.java +++ b/src/main/java/org/asamk/signal/manager/KeyUtils.java @@ -5,7 +5,7 @@ import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.util.Base64; -class KeyUtils { +public class KeyUtils { private KeyUtils() { } @@ -35,7 +35,7 @@ class KeyUtils { return Base64.encodeBytes(secret); } - static byte[] getSecretBytes(int size) { + public static byte[] getSecretBytes(int size) { byte[] secret = new byte[size]; RandomUtils.getSecureRandom().nextBytes(secret); return secret; diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 761905df..c958e0a4 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -18,6 +18,13 @@ package org.asamk.signal.manager; import com.fasterxml.jackson.databind.ObjectMapper; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupIdV1; +import org.asamk.signal.manager.groups.GroupIdV2; +import org.asamk.signal.manager.groups.GroupInviteLinkUrl; +import org.asamk.signal.manager.groups.GroupNotFoundException; +import org.asamk.signal.manager.groups.GroupUtils; +import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.helper.GroupHelper; import org.asamk.signal.manager.helper.ProfileHelper; import org.asamk.signal.manager.helper.UnidentifiedAccessHelper; diff --git a/src/main/java/org/asamk/signal/manager/GroupId.java b/src/main/java/org/asamk/signal/manager/groups/GroupId.java similarity index 97% rename from src/main/java/org/asamk/signal/manager/GroupId.java rename to src/main/java/org/asamk/signal/manager/groups/GroupId.java index 34e18e8e..9a15de65 100644 --- a/src/main/java/org/asamk/signal/manager/GroupId.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupId.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; import org.whispersystems.util.Base64; diff --git a/src/main/java/org/asamk/signal/manager/GroupIdFormatException.java b/src/main/java/org/asamk/signal/manager/groups/GroupIdFormatException.java similarity index 85% rename from src/main/java/org/asamk/signal/manager/GroupIdFormatException.java rename to src/main/java/org/asamk/signal/manager/groups/GroupIdFormatException.java index 83afd15b..8050da22 100644 --- a/src/main/java/org/asamk/signal/manager/GroupIdFormatException.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupIdFormatException.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; public class GroupIdFormatException extends Exception { diff --git a/src/main/java/org/asamk/signal/manager/GroupIdV1.java b/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java similarity index 87% rename from src/main/java/org/asamk/signal/manager/GroupIdV1.java rename to src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java index 40862f07..d865356e 100644 --- a/src/main/java/org/asamk/signal/manager/GroupIdV1.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; import static org.asamk.signal.manager.KeyUtils.getSecretBytes; diff --git a/src/main/java/org/asamk/signal/manager/GroupIdV2.java b/src/main/java/org/asamk/signal/manager/groups/GroupIdV2.java similarity index 86% rename from src/main/java/org/asamk/signal/manager/GroupIdV2.java rename to src/main/java/org/asamk/signal/manager/groups/GroupIdV2.java index b329be1d..913a9e93 100644 --- a/src/main/java/org/asamk/signal/manager/GroupIdV2.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupIdV2.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; import java.util.Base64; diff --git a/src/main/java/org/asamk/signal/manager/GroupInviteLinkUrl.java b/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java similarity index 99% rename from src/main/java/org/asamk/signal/manager/GroupInviteLinkUrl.java rename to src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java index 67ce7892..bf9e0e55 100644 --- a/src/main/java/org/asamk/signal/manager/GroupInviteLinkUrl.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; import com.google.protobuf.ByteString; diff --git a/src/main/java/org/asamk/signal/manager/GroupLinkPassword.java b/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java similarity index 90% rename from src/main/java/org/asamk/signal/manager/GroupLinkPassword.java rename to src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java index 38e2aaf4..41be672a 100644 --- a/src/main/java/org/asamk/signal/manager/GroupLinkPassword.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java @@ -1,4 +1,6 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; + +import org.asamk.signal.manager.KeyUtils; import java.util.Arrays; diff --git a/src/main/java/org/asamk/signal/manager/GroupNotFoundException.java b/src/main/java/org/asamk/signal/manager/groups/GroupNotFoundException.java similarity index 81% rename from src/main/java/org/asamk/signal/manager/GroupNotFoundException.java rename to src/main/java/org/asamk/signal/manager/groups/GroupNotFoundException.java index d7efa923..0fc0c444 100644 --- a/src/main/java/org/asamk/signal/manager/GroupNotFoundException.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupNotFoundException.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; public class GroupNotFoundException extends Exception { diff --git a/src/main/java/org/asamk/signal/manager/GroupUtils.java b/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java similarity index 98% rename from src/main/java/org/asamk/signal/manager/GroupUtils.java rename to src/main/java/org/asamk/signal/manager/groups/GroupUtils.java index d86dfbe9..c5f727e1 100644 --- a/src/main/java/org/asamk/signal/manager/GroupUtils.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; import org.asamk.signal.storage.groups.GroupInfo; import org.asamk.signal.storage.groups.GroupInfoV1; diff --git a/src/main/java/org/asamk/signal/manager/NotAGroupMemberException.java b/src/main/java/org/asamk/signal/manager/groups/NotAGroupMemberException.java similarity index 85% rename from src/main/java/org/asamk/signal/manager/NotAGroupMemberException.java rename to src/main/java/org/asamk/signal/manager/groups/NotAGroupMemberException.java index 2c9b3f33..08cbcacd 100644 --- a/src/main/java/org/asamk/signal/manager/NotAGroupMemberException.java +++ b/src/main/java/org/asamk/signal/manager/groups/NotAGroupMemberException.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.groups; public class NotAGroupMemberException extends Exception { diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 5a88bc66..6a52e00e 100644 --- a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -2,9 +2,9 @@ package org.asamk.signal.manager.helper; import com.google.protobuf.InvalidProtocolBufferException; -import org.asamk.signal.manager.GroupIdV2; -import org.asamk.signal.manager.GroupLinkPassword; -import org.asamk.signal.manager.GroupUtils; +import org.asamk.signal.manager.groups.GroupIdV2; +import org.asamk.signal.manager.groups.GroupLinkPassword; +import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.storage.groups.GroupInfoV2; import org.asamk.signal.storage.profiles.SignalProfile; import org.asamk.signal.util.IOUtils; diff --git a/src/main/java/org/asamk/signal/storage/SignalAccount.java b/src/main/java/org/asamk/signal/storage/SignalAccount.java index 3af52708..393d0449 100644 --- a/src/main/java/org/asamk/signal/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/storage/SignalAccount.java @@ -10,7 +10,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.asamk.signal.manager.GroupId; +import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.storage.contacts.ContactInfo; import org.asamk.signal.storage.contacts.JsonContactsStore; import org.asamk.signal.storage.groups.GroupInfo; diff --git a/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java b/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java index 40b8c884..fe725141 100644 --- a/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java +++ b/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java @@ -2,8 +2,8 @@ package org.asamk.signal.storage.groups; import com.fasterxml.jackson.annotation.JsonIgnore; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupInviteLinkUrl; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.Set; diff --git a/src/main/java/org/asamk/signal/storage/groups/GroupInfoV1.java b/src/main/java/org/asamk/signal/storage/groups/GroupInfoV1.java index 90b26b81..e48fe297 100644 --- a/src/main/java/org/asamk/signal/storage/groups/GroupInfoV1.java +++ b/src/main/java/org/asamk/signal/storage/groups/GroupInfoV1.java @@ -13,11 +13,11 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupIdV1; -import org.asamk.signal.manager.GroupIdV2; -import org.asamk.signal.manager.GroupInviteLinkUrl; -import org.asamk.signal.manager.GroupUtils; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupIdV1; +import org.asamk.signal.manager.groups.GroupIdV2; +import org.asamk.signal.manager.groups.GroupInviteLinkUrl; +import org.asamk.signal.manager.groups.GroupUtils; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.IOException; diff --git a/src/main/java/org/asamk/signal/storage/groups/GroupInfoV2.java b/src/main/java/org/asamk/signal/storage/groups/GroupInfoV2.java index 1b00caaa..0139f879 100644 --- a/src/main/java/org/asamk/signal/storage/groups/GroupInfoV2.java +++ b/src/main/java/org/asamk/signal/storage/groups/GroupInfoV2.java @@ -1,7 +1,7 @@ package org.asamk.signal.storage.groups; -import org.asamk.signal.manager.GroupIdV2; -import org.asamk.signal.manager.GroupInviteLinkUrl; +import org.asamk.signal.manager.groups.GroupIdV2; +import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.zkgroup.groups.GroupMasterKey; diff --git a/src/main/java/org/asamk/signal/storage/groups/JsonGroupStore.java b/src/main/java/org/asamk/signal/storage/groups/JsonGroupStore.java index 18bf5ed0..1aae49f7 100644 --- a/src/main/java/org/asamk/signal/storage/groups/JsonGroupStore.java +++ b/src/main/java/org/asamk/signal/storage/groups/JsonGroupStore.java @@ -12,10 +12,10 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupIdV1; -import org.asamk.signal.manager.GroupIdV2; -import org.asamk.signal.manager.GroupUtils; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupIdV1; +import org.asamk.signal.manager.groups.GroupIdV2; +import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.util.Hex; import org.asamk.signal.util.IOUtils; import org.signal.storageservice.protos.groups.local.DecryptedGroup; diff --git a/src/main/java/org/asamk/signal/util/ErrorUtils.java b/src/main/java/org/asamk/signal/util/ErrorUtils.java index 44d505be..e9553f98 100644 --- a/src/main/java/org/asamk/signal/util/ErrorUtils.java +++ b/src/main/java/org/asamk/signal/util/ErrorUtils.java @@ -1,8 +1,8 @@ package org.asamk.signal.util; -import org.asamk.signal.manager.GroupIdFormatException; -import org.asamk.signal.manager.GroupNotFoundException; -import org.asamk.signal.manager.NotAGroupMemberException; +import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.asamk.signal.manager.groups.GroupNotFoundException; +import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.util.InvalidNumberException; diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index 3cd5619a..79a6587b 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -2,8 +2,8 @@ package org.asamk.signal.util; import com.fasterxml.jackson.databind.JsonNode; -import org.asamk.signal.manager.GroupId; -import org.asamk.signal.manager.GroupIdFormatException; +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupIdFormatException; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; From b738f5740c94fe7a5df9e322e1345a99ef0c5ce5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 29 Dec 2020 22:15:38 +0100 Subject: [PATCH 002/124] Move storage package to manager --- .../asamk/signal/ReceiveMessageHandler.java | 4 +- .../signal/commands/ListContactsCommand.java | 2 +- .../signal/commands/ListGroupsCommand.java | 2 +- .../commands/ListIdentitiesCommand.java | 10 +-- .../org/asamk/signal/dbus/DbusSignalImpl.java | 2 +- .../org/asamk/signal/manager/Manager.java | 37 +++++---- .../signal/manager/ProvisioningManager.java | 2 +- .../signal/manager/groups/GroupUtils.java | 6 +- .../signal/manager/helper/GroupHelper.java | 4 +- .../manager/helper/ProfileProvider.java | 2 +- .../helper/UnidentifiedAccessHelper.java | 2 +- .../{ => manager}/storage/SignalAccount.java | 32 ++++---- .../storage/contacts/ContactInfo.java | 2 +- .../storage/contacts/JsonContactsStore.java | 2 +- .../storage/groups/GroupInfo.java | 2 +- .../storage/groups/GroupInfoV1.java | 2 +- .../storage/groups/GroupInfoV2.java | 2 +- .../storage/groups/JsonGroupStore.java | 2 +- .../storage/profiles/ProfileStore.java | 2 +- .../storage/profiles/SignalProfile.java | 2 +- .../storage/profiles/SignalProfileEntry.java | 2 +- .../storage/protocol/IdentityInfo.java | 57 +++++++++++++ .../protocol/JsonIdentityKeyStore.java | 81 ++++--------------- .../storage/protocol/JsonPreKeyStore.java | 2 +- .../storage/protocol/JsonSessionStore.java | 2 +- .../protocol/JsonSignalProtocolStore.java | 8 +- .../protocol/JsonSignedPreKeyStore.java | 2 +- .../storage/protocol/RecipientStore.java | 2 +- .../storage/protocol/SessionInfo.java | 2 +- .../SignalServiceAddressResolver.java | 2 +- .../storage/stickers/Sticker.java | 2 +- .../storage/stickers/StickerStore.java | 2 +- .../threads/LegacyJsonThreadStore.java | 2 +- .../storage/threads/ThreadInfo.java | 2 +- 34 files changed, 148 insertions(+), 141 deletions(-) rename src/main/java/org/asamk/signal/{ => manager}/storage/SignalAccount.java (94%) rename src/main/java/org/asamk/signal/{ => manager}/storage/contacts/ContactInfo.java (95%) rename src/main/java/org/asamk/signal/{ => manager}/storage/contacts/JsonContactsStore.java (96%) rename src/main/java/org/asamk/signal/{ => manager}/storage/groups/GroupInfo.java (97%) rename src/main/java/org/asamk/signal/{ => manager}/storage/groups/GroupInfoV1.java (99%) rename src/main/java/org/asamk/signal/{ => manager}/storage/groups/GroupInfoV2.java (98%) rename src/main/java/org/asamk/signal/{ => manager}/storage/groups/JsonGroupStore.java (99%) rename src/main/java/org/asamk/signal/{ => manager}/storage/profiles/ProfileStore.java (99%) rename src/main/java/org/asamk/signal/{ => manager}/storage/profiles/SignalProfile.java (98%) rename src/main/java/org/asamk/signal/{ => manager}/storage/profiles/SignalProfileEntry.java (96%) create mode 100644 src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java rename src/main/java/org/asamk/signal/{ => manager}/storage/protocol/JsonIdentityKeyStore.java (80%) rename src/main/java/org/asamk/signal/{ => manager}/storage/protocol/JsonPreKeyStore.java (98%) rename src/main/java/org/asamk/signal/{ => manager}/storage/protocol/JsonSessionStore.java (99%) rename src/main/java/org/asamk/signal/{ => manager}/storage/protocol/JsonSignalProtocolStore.java (95%) rename src/main/java/org/asamk/signal/{ => manager}/storage/protocol/JsonSignedPreKeyStore.java (98%) rename src/main/java/org/asamk/signal/{ => manager}/storage/protocol/RecipientStore.java (98%) rename src/main/java/org/asamk/signal/{ => manager}/storage/protocol/SessionInfo.java (89%) rename src/main/java/org/asamk/signal/{ => manager}/storage/protocol/SignalServiceAddressResolver.java (88%) rename src/main/java/org/asamk/signal/{ => manager}/storage/stickers/Sticker.java (93%) rename src/main/java/org/asamk/signal/{ => manager}/storage/stickers/StickerStore.java (98%) rename src/main/java/org/asamk/signal/{ => manager}/storage/threads/LegacyJsonThreadStore.java (97%) rename src/main/java/org/asamk/signal/{ => manager}/storage/threads/ThreadInfo.java (78%) diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 8dc38e4f..db78f454 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -3,8 +3,8 @@ package org.asamk.signal; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupUtils; -import org.asamk.signal.storage.contacts.ContactInfo; -import org.asamk.signal.storage.groups.GroupInfo; +import org.asamk.signal.manager.storage.contacts.ContactInfo; +import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.Util; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 24d6898c..2c98ec6b 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -4,7 +4,7 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; -import org.asamk.signal.storage.contacts.ContactInfo; +import org.asamk.signal.manager.storage.contacts.ContactInfo; import java.util.List; diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 66ff3a00..b4be4ad0 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -6,7 +6,7 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; -import org.asamk.signal.storage.groups.GroupInfo; +import org.asamk.signal.manager.storage.groups.GroupInfo; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.List; diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index a75e4328..3f422cbd 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -4,7 +4,7 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; -import org.asamk.signal.storage.protocol.JsonIdentityKeyStore; +import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.util.Hex; import org.asamk.signal.util.Util; import org.whispersystems.signalservice.api.util.InvalidNumberException; @@ -13,7 +13,7 @@ import java.util.List; public class ListIdentitiesCommand implements LocalCommand { - private static void printIdentityFingerprint(Manager m, JsonIdentityKeyStore.Identity theirId) { + private static void printIdentityFingerprint(Manager m, IdentityInfo theirId) { String digits = Util.formatSafetyNumber(m.computeSafetyNumber(theirId.getAddress(), theirId.getIdentityKey())); System.out.println(String.format("%s: %s Added: %s Fingerprint: %s Safety Number: %s", theirId.getAddress().getNumber().orNull(), @@ -35,14 +35,14 @@ public class ListIdentitiesCommand implements LocalCommand { return 1; } if (ns.get("number") == null) { - for (JsonIdentityKeyStore.Identity identity : m.getIdentities()) { + for (IdentityInfo identity : m.getIdentities()) { printIdentityFingerprint(m, identity); } } else { String number = ns.getString("number"); try { - List identities = m.getIdentities(number); - for (JsonIdentityKeyStore.Identity id : identities) { + List identities = m.getIdentities(number); + for (IdentityInfo id : identities) { printIdentityFingerprint(m, id); } } catch (InvalidNumberException e) { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index df3f12f2..d19116a4 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -6,7 +6,7 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.storage.groups.GroupInfo; +import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.util.ErrorUtils; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.whispersystems.libsignal.util.Pair; diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index c958e0a4..e00f44ac 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -28,15 +28,15 @@ import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.helper.GroupHelper; import org.asamk.signal.manager.helper.ProfileHelper; import org.asamk.signal.manager.helper.UnidentifiedAccessHelper; -import org.asamk.signal.storage.SignalAccount; -import org.asamk.signal.storage.contacts.ContactInfo; -import org.asamk.signal.storage.groups.GroupInfo; -import org.asamk.signal.storage.groups.GroupInfoV1; -import org.asamk.signal.storage.groups.GroupInfoV2; -import org.asamk.signal.storage.profiles.SignalProfile; -import org.asamk.signal.storage.profiles.SignalProfileEntry; -import org.asamk.signal.storage.protocol.JsonIdentityKeyStore; -import org.asamk.signal.storage.stickers.Sticker; +import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.storage.contacts.ContactInfo; +import org.asamk.signal.manager.storage.groups.GroupInfo; +import org.asamk.signal.manager.storage.groups.GroupInfoV1; +import org.asamk.signal.manager.storage.groups.GroupInfoV2; +import org.asamk.signal.manager.storage.profiles.SignalProfile; +import org.asamk.signal.manager.storage.profiles.SignalProfileEntry; +import org.asamk.signal.manager.storage.protocol.IdentityInfo; +import org.asamk.signal.manager.storage.stickers.Sticker; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; import org.signal.libsignal.metadata.InvalidMetadataMessageException; @@ -2422,8 +2422,7 @@ public class Manager implements Closeable { DeviceContactsOutputStream out = new DeviceContactsOutputStream(fos); for (ContactInfo record : account.getContactStore().getContacts()) { VerifiedMessage verifiedMessage = null; - JsonIdentityKeyStore.Identity currentIdentity = account.getSignalProtocolStore() - .getIdentity(record.getAddress()); + IdentityInfo currentIdentity = account.getSignalProtocolStore().getIdentity(record.getAddress()); if (currentIdentity != null) { verifiedMessage = new VerifiedMessage(record.getAddress(), currentIdentity.getIdentityKey(), @@ -2517,11 +2516,11 @@ public class Manager implements Closeable { return account.getGroupStore().getGroup(groupId); } - public List getIdentities() { + public List getIdentities() { return account.getSignalProtocolStore().getIdentities(); } - public List getIdentities(String number) throws InvalidNumberException { + public List getIdentities(String number) throws InvalidNumberException { return account.getSignalProtocolStore().getIdentities(canonicalizeAndResolveSignalServiceAddress(number)); } @@ -2533,11 +2532,11 @@ public class Manager implements Closeable { */ public boolean trustIdentityVerified(String name, byte[] fingerprint) throws InvalidNumberException { SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); + List ids = account.getSignalProtocolStore().getIdentities(address); if (ids == null) { return false; } - for (JsonIdentityKeyStore.Identity id : ids) { + for (IdentityInfo id : ids) { if (!Arrays.equals(id.getIdentityKey().serialize(), fingerprint)) { continue; } @@ -2563,11 +2562,11 @@ public class Manager implements Closeable { */ public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) throws InvalidNumberException { SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); + List ids = account.getSignalProtocolStore().getIdentities(address); if (ids == null) { return false; } - for (JsonIdentityKeyStore.Identity id : ids) { + for (IdentityInfo id : ids) { if (!safetyNumber.equals(computeSafetyNumber(address, id.getIdentityKey()))) { continue; } @@ -2592,11 +2591,11 @@ public class Manager implements Closeable { */ public boolean trustIdentityAllKeys(String name) { SignalServiceAddress address = resolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); + List ids = account.getSignalProtocolStore().getIdentities(address); if (ids == null) { return false; } - for (JsonIdentityKeyStore.Identity id : ids) { + for (IdentityInfo id : ids) { if (id.getTrustLevel() == TrustLevel.UNTRUSTED) { account.getSignalProtocolStore() .setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED); diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index f81cfa49..95e92c7a 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -16,7 +16,7 @@ */ package org.asamk.signal.manager; -import org.asamk.signal.storage.SignalAccount; +import org.asamk.signal.manager.storage.SignalAccount; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.IdentityKeyPair; diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java b/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java index c5f727e1..f56639e3 100644 --- a/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java @@ -1,8 +1,8 @@ package org.asamk.signal.manager.groups; -import org.asamk.signal.storage.groups.GroupInfo; -import org.asamk.signal.storage.groups.GroupInfoV1; -import org.asamk.signal.storage.groups.GroupInfoV2; +import org.asamk.signal.manager.storage.groups.GroupInfo; +import org.asamk.signal.manager.storage.groups.GroupInfoV1; +import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupSecretParams; diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 6a52e00e..394eba57 100644 --- a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -5,8 +5,8 @@ import com.google.protobuf.InvalidProtocolBufferException; import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupLinkPassword; import org.asamk.signal.manager.groups.GroupUtils; -import org.asamk.signal.storage.groups.GroupInfoV2; -import org.asamk.signal.storage.profiles.SignalProfile; +import org.asamk.signal.manager.storage.groups.GroupInfoV2; +import org.asamk.signal.manager.storage.profiles.SignalProfile; import org.asamk.signal.util.IOUtils; import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.protos.groups.GroupChange; diff --git a/src/main/java/org/asamk/signal/manager/helper/ProfileProvider.java b/src/main/java/org/asamk/signal/manager/helper/ProfileProvider.java index 1ff4cb05..c16b5e0d 100644 --- a/src/main/java/org/asamk/signal/manager/helper/ProfileProvider.java +++ b/src/main/java/org/asamk/signal/manager/helper/ProfileProvider.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.helper; -import org.asamk.signal.storage.profiles.SignalProfile; +import org.asamk.signal.manager.storage.profiles.SignalProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; public interface ProfileProvider { diff --git a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java b/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java index 97331cf3..a994c40a 100644 --- a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.helper; -import org.asamk.signal.storage.profiles.SignalProfile; +import org.asamk.signal.manager.storage.profiles.SignalProfile; import org.signal.libsignal.metadata.certificate.InvalidCertificateException; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.util.guava.Optional; diff --git a/src/main/java/org/asamk/signal/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java similarity index 94% rename from src/main/java/org/asamk/signal/storage/SignalAccount.java rename to src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 393d0449..c3573209 100644 --- a/src/main/java/org/asamk/signal/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage; +package org.asamk.signal.manager.storage; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; @@ -11,20 +11,20 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; import org.asamk.signal.manager.groups.GroupId; -import org.asamk.signal.storage.contacts.ContactInfo; -import org.asamk.signal.storage.contacts.JsonContactsStore; -import org.asamk.signal.storage.groups.GroupInfo; -import org.asamk.signal.storage.groups.GroupInfoV1; -import org.asamk.signal.storage.groups.JsonGroupStore; -import org.asamk.signal.storage.profiles.ProfileStore; -import org.asamk.signal.storage.protocol.JsonIdentityKeyStore; -import org.asamk.signal.storage.protocol.JsonSignalProtocolStore; -import org.asamk.signal.storage.protocol.RecipientStore; -import org.asamk.signal.storage.protocol.SessionInfo; -import org.asamk.signal.storage.protocol.SignalServiceAddressResolver; -import org.asamk.signal.storage.stickers.StickerStore; -import org.asamk.signal.storage.threads.LegacyJsonThreadStore; -import org.asamk.signal.storage.threads.ThreadInfo; +import org.asamk.signal.manager.storage.contacts.ContactInfo; +import org.asamk.signal.manager.storage.contacts.JsonContactsStore; +import org.asamk.signal.manager.storage.groups.GroupInfo; +import org.asamk.signal.manager.storage.groups.GroupInfoV1; +import org.asamk.signal.manager.storage.groups.JsonGroupStore; +import org.asamk.signal.manager.storage.profiles.ProfileStore; +import org.asamk.signal.manager.storage.protocol.IdentityInfo; +import org.asamk.signal.manager.storage.protocol.JsonSignalProtocolStore; +import org.asamk.signal.manager.storage.protocol.RecipientStore; +import org.asamk.signal.manager.storage.protocol.SessionInfo; +import org.asamk.signal.manager.storage.protocol.SignalServiceAddressResolver; +import org.asamk.signal.manager.storage.stickers.StickerStore; +import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore; +import org.asamk.signal.manager.storage.threads.ThreadInfo; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; import org.signal.zkgroup.InvalidInputException; @@ -286,7 +286,7 @@ public class SignalAccount implements Closeable { session.address = recipientStore.resolveServiceAddress(session.address); } - for (JsonIdentityKeyStore.Identity identity : signalProtocolStore.getIdentities()) { + for (IdentityInfo identity : signalProtocolStore.getIdentities()) { identity.setAddress(recipientStore.resolveServiceAddress(identity.getAddress())); } } diff --git a/src/main/java/org/asamk/signal/storage/contacts/ContactInfo.java b/src/main/java/org/asamk/signal/manager/storage/contacts/ContactInfo.java similarity index 95% rename from src/main/java/org/asamk/signal/storage/contacts/ContactInfo.java rename to src/main/java/org/asamk/signal/manager/storage/contacts/ContactInfo.java index 3b155210..4dd132f7 100644 --- a/src/main/java/org/asamk/signal/storage/contacts/ContactInfo.java +++ b/src/main/java/org/asamk/signal/manager/storage/contacts/ContactInfo.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.contacts; +package org.asamk.signal.manager.storage.contacts; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/org/asamk/signal/storage/contacts/JsonContactsStore.java b/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java similarity index 96% rename from src/main/java/org/asamk/signal/storage/contacts/JsonContactsStore.java rename to src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java index bb81b0c9..d2859f3f 100644 --- a/src/main/java/org/asamk/signal/storage/contacts/JsonContactsStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.contacts; +package org.asamk.signal.manager.storage.contacts; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java similarity index 97% rename from src/main/java/org/asamk/signal/storage/groups/GroupInfo.java rename to src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java index fe725141..a644b620 100644 --- a/src/main/java/org/asamk/signal/storage/groups/GroupInfo.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.groups; +package org.asamk.signal.manager.storage.groups; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/src/main/java/org/asamk/signal/storage/groups/GroupInfoV1.java b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java similarity index 99% rename from src/main/java/org/asamk/signal/storage/groups/GroupInfoV1.java rename to src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java index e48fe297..39591647 100644 --- a/src/main/java/org/asamk/signal/storage/groups/GroupInfoV1.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.groups; +package org.asamk.signal.manager.storage.groups; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/org/asamk/signal/storage/groups/GroupInfoV2.java b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java similarity index 98% rename from src/main/java/org/asamk/signal/storage/groups/GroupInfoV2.java rename to src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java index 0139f879..17c23925 100644 --- a/src/main/java/org/asamk/signal/storage/groups/GroupInfoV2.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.groups; +package org.asamk.signal.manager.storage.groups; import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; diff --git a/src/main/java/org/asamk/signal/storage/groups/JsonGroupStore.java b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java similarity index 99% rename from src/main/java/org/asamk/signal/storage/groups/JsonGroupStore.java rename to src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java index 1aae49f7..2b4dbcf5 100644 --- a/src/main/java/org/asamk/signal/storage/groups/JsonGroupStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.groups; +package org.asamk.signal.manager.storage.groups; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonGenerator; diff --git a/src/main/java/org/asamk/signal/storage/profiles/ProfileStore.java b/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java similarity index 99% rename from src/main/java/org/asamk/signal/storage/profiles/ProfileStore.java rename to src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java index 3b3d3f9f..bff2f17e 100644 --- a/src/main/java/org/asamk/signal/storage/profiles/ProfileStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.profiles; +package org.asamk.signal.manager.storage.profiles; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonGenerator; diff --git a/src/main/java/org/asamk/signal/storage/profiles/SignalProfile.java b/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java similarity index 98% rename from src/main/java/org/asamk/signal/storage/profiles/SignalProfile.java rename to src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java index 023458ed..48a38578 100644 --- a/src/main/java/org/asamk/signal/storage/profiles/SignalProfile.java +++ b/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.profiles; +package org.asamk.signal.manager.storage.profiles; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/org/asamk/signal/storage/profiles/SignalProfileEntry.java b/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfileEntry.java similarity index 96% rename from src/main/java/org/asamk/signal/storage/profiles/SignalProfileEntry.java rename to src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfileEntry.java index e6acf30d..a81fbcb5 100644 --- a/src/main/java/org/asamk/signal/storage/profiles/SignalProfileEntry.java +++ b/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfileEntry.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.profiles; +package org.asamk.signal.manager.storage.profiles; import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKeyCredential; diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java b/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java new file mode 100644 index 00000000..d4af11f2 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java @@ -0,0 +1,57 @@ +package org.asamk.signal.manager.storage.protocol; + +import org.asamk.signal.manager.TrustLevel; +import org.whispersystems.libsignal.IdentityKey; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; + +import java.util.Date; + +public class IdentityInfo { + + SignalServiceAddress address; + IdentityKey identityKey; + TrustLevel trustLevel; + Date added; + + public IdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel) { + this.address = address; + this.identityKey = identityKey; + this.trustLevel = trustLevel; + this.added = new Date(); + } + + IdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) { + this.address = address; + this.identityKey = identityKey; + this.trustLevel = trustLevel; + this.added = added; + } + + public SignalServiceAddress getAddress() { + return address; + } + + public void setAddress(final SignalServiceAddress address) { + this.address = address; + } + + boolean isTrusted() { + return trustLevel == TrustLevel.TRUSTED_UNVERIFIED || trustLevel == TrustLevel.TRUSTED_VERIFIED; + } + + public IdentityKey getIdentityKey() { + return this.identityKey; + } + + public TrustLevel getTrustLevel() { + return this.trustLevel; + } + + public Date getDateAdded() { + return this.added; + } + + public byte[] getFingerprint() { + return identityKey.getPublicKey().serialize(); + } +} diff --git a/src/main/java/org/asamk/signal/storage/protocol/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java similarity index 80% rename from src/main/java/org/asamk/signal/storage/protocol/JsonIdentityKeyStore.java rename to src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index 29160cf1..517b384e 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.protocol; +package org.asamk.signal.manager.storage.protocol; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; @@ -31,7 +31,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { final static Logger logger = LoggerFactory.getLogger(JsonIdentityKeyStore.class); - private final List identities = new ArrayList<>(); + private final List identities = new ArrayList<>(); private final IdentityKeyPair identityKeyPair; private final int localRegistrationId; @@ -85,7 +85,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { public boolean saveIdentity( SignalServiceAddress serviceAddress, IdentityKey identityKey, TrustLevel trustLevel, Date added ) { - for (Identity id : identities) { + for (IdentityInfo id : identities) { if (!id.address.matches(serviceAddress) || !id.identityKey.equals(identityKey)) { continue; } @@ -97,7 +97,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { return true; } - identities.add(new Identity(serviceAddress, identityKey, trustLevel, added != null ? added : new Date())); + identities.add(new IdentityInfo(serviceAddress, identityKey, trustLevel, added != null ? added : new Date())); return false; } @@ -111,7 +111,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { public void setIdentityTrustLevel( SignalServiceAddress serviceAddress, IdentityKey identityKey, TrustLevel trustLevel ) { - for (Identity id : identities) { + for (IdentityInfo id : identities) { if (!id.address.matches(serviceAddress) || !id.identityKey.equals(identityKey)) { continue; } @@ -123,7 +123,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { return; } - identities.add(new Identity(serviceAddress, identityKey, trustLevel, new Date())); + identities.add(new IdentityInfo(serviceAddress, identityKey, trustLevel, new Date())); } @Override @@ -132,7 +132,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName()); boolean trustOnFirstUse = true; - for (Identity id : identities) { + for (IdentityInfo id : identities) { if (!id.address.matches(serviceAddress)) { continue; } @@ -150,14 +150,14 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { @Override public IdentityKey getIdentity(SignalProtocolAddress address) { SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName()); - Identity identity = getIdentity(serviceAddress); + IdentityInfo identity = getIdentity(serviceAddress); return identity == null ? null : identity.getIdentityKey(); } - public Identity getIdentity(SignalServiceAddress serviceAddress) { + public IdentityInfo getIdentity(SignalServiceAddress serviceAddress) { long maxDate = 0; - Identity maxIdentity = null; - for (Identity id : this.identities) { + IdentityInfo maxIdentity = null; + for (IdentityInfo id : this.identities) { if (!id.address.matches(serviceAddress)) { continue; } @@ -171,14 +171,14 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { return maxIdentity; } - public List getIdentities() { + public List getIdentities() { // TODO deep copy return identities; } - public List getIdentities(SignalServiceAddress serviceAddress) { - List identities = new ArrayList<>(); - for (Identity identity : this.identities) { + public List getIdentities(SignalServiceAddress serviceAddress) { + List identities = new ArrayList<>(); + for (IdentityInfo identity : this.identities) { if (identity.address.matches(serviceAddress)) { identities.add(identity); } @@ -246,7 +246,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { json.writeStringField("identityKey", Base64.encodeBytes(jsonIdentityKeyStore.getIdentityKeyPair().serialize())); json.writeArrayFieldStart("trustedKeys"); - for (Identity trustedKey : jsonIdentityKeyStore.identities) { + for (IdentityInfo trustedKey : jsonIdentityKeyStore.identities) { json.writeStartObject(); if (trustedKey.getAddress().getNumber().isPresent()) { json.writeStringField("name", trustedKey.getAddress().getNumber().get()); @@ -264,53 +264,4 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { } } - public static class Identity { - - SignalServiceAddress address; - IdentityKey identityKey; - TrustLevel trustLevel; - Date added; - - public Identity(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel) { - this.address = address; - this.identityKey = identityKey; - this.trustLevel = trustLevel; - this.added = new Date(); - } - - Identity(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) { - this.address = address; - this.identityKey = identityKey; - this.trustLevel = trustLevel; - this.added = added; - } - - public SignalServiceAddress getAddress() { - return address; - } - - public void setAddress(final SignalServiceAddress address) { - this.address = address; - } - - boolean isTrusted() { - return trustLevel == TrustLevel.TRUSTED_UNVERIFIED || trustLevel == TrustLevel.TRUSTED_VERIFIED; - } - - public IdentityKey getIdentityKey() { - return this.identityKey; - } - - public TrustLevel getTrustLevel() { - return this.trustLevel; - } - - public Date getDateAdded() { - return this.added; - } - - public byte[] getFingerprint() { - return identityKey.getPublicKey().serialize(); - } - } } diff --git a/src/main/java/org/asamk/signal/storage/protocol/JsonPreKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java similarity index 98% rename from src/main/java/org/asamk/signal/storage/protocol/JsonPreKeyStore.java rename to src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java index 523809c1..4d884c3e 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/JsonPreKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.protocol; +package org.asamk.signal.manager.storage.protocol; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; diff --git a/src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java similarity index 99% rename from src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java rename to src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java index 24e4594e..f55aff14 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.protocol; +package org.asamk.signal.manager.storage.protocol; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; diff --git a/src/main/java/org/asamk/signal/storage/protocol/JsonSignalProtocolStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java similarity index 95% rename from src/main/java/org/asamk/signal/storage/protocol/JsonSignalProtocolStore.java rename to src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java index 5939749d..41a63013 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/JsonSignalProtocolStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.protocol; +package org.asamk.signal.manager.storage.protocol; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -91,11 +91,11 @@ public class JsonSignalProtocolStore implements SignalProtocolStore { identityKeyStore.setIdentityTrustLevel(serviceAddress, identityKey, trustLevel); } - public List getIdentities() { + public List getIdentities() { return identityKeyStore.getIdentities(); } - public List getIdentities(SignalServiceAddress serviceAddress) { + public List getIdentities(SignalServiceAddress serviceAddress) { return identityKeyStore.getIdentities(serviceAddress); } @@ -109,7 +109,7 @@ public class JsonSignalProtocolStore implements SignalProtocolStore { return identityKeyStore.getIdentity(address); } - public JsonIdentityKeyStore.Identity getIdentity(SignalServiceAddress serviceAddress) { + public IdentityInfo getIdentity(SignalServiceAddress serviceAddress) { return identityKeyStore.getIdentity(serviceAddress); } diff --git a/src/main/java/org/asamk/signal/storage/protocol/JsonSignedPreKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java similarity index 98% rename from src/main/java/org/asamk/signal/storage/protocol/JsonSignedPreKeyStore.java rename to src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java index 7accf5a1..5eae4500 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/JsonSignedPreKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.protocol; +package org.asamk.signal.manager.storage.protocol; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; diff --git a/src/main/java/org/asamk/signal/storage/protocol/RecipientStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java similarity index 98% rename from src/main/java/org/asamk/signal/storage/protocol/RecipientStore.java rename to src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java index 701eca34..60634ae5 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/RecipientStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.protocol; +package org.asamk.signal.manager.storage.protocol; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonGenerator; diff --git a/src/main/java/org/asamk/signal/storage/protocol/SessionInfo.java b/src/main/java/org/asamk/signal/manager/storage/protocol/SessionInfo.java similarity index 89% rename from src/main/java/org/asamk/signal/storage/protocol/SessionInfo.java rename to src/main/java/org/asamk/signal/manager/storage/protocol/SessionInfo.java index 00221233..802b896b 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/SessionInfo.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/SessionInfo.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.protocol; +package org.asamk.signal.manager.storage.protocol; import org.whispersystems.signalservice.api.push.SignalServiceAddress; diff --git a/src/main/java/org/asamk/signal/storage/protocol/SignalServiceAddressResolver.java b/src/main/java/org/asamk/signal/manager/storage/protocol/SignalServiceAddressResolver.java similarity index 88% rename from src/main/java/org/asamk/signal/storage/protocol/SignalServiceAddressResolver.java rename to src/main/java/org/asamk/signal/manager/storage/protocol/SignalServiceAddressResolver.java index b1c5fb38..86eea05e 100644 --- a/src/main/java/org/asamk/signal/storage/protocol/SignalServiceAddressResolver.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/SignalServiceAddressResolver.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.protocol; +package org.asamk.signal.manager.storage.protocol; import org.whispersystems.signalservice.api.push.SignalServiceAddress; diff --git a/src/main/java/org/asamk/signal/storage/stickers/Sticker.java b/src/main/java/org/asamk/signal/manager/storage/stickers/Sticker.java similarity index 93% rename from src/main/java/org/asamk/signal/storage/stickers/Sticker.java rename to src/main/java/org/asamk/signal/manager/storage/stickers/Sticker.java index 386924c4..54e95d0a 100644 --- a/src/main/java/org/asamk/signal/storage/stickers/Sticker.java +++ b/src/main/java/org/asamk/signal/manager/storage/stickers/Sticker.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.stickers; +package org.asamk.signal.manager.storage.stickers; public class Sticker { diff --git a/src/main/java/org/asamk/signal/storage/stickers/StickerStore.java b/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java similarity index 98% rename from src/main/java/org/asamk/signal/storage/stickers/StickerStore.java rename to src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java index e5d817d2..10cd2e99 100644 --- a/src/main/java/org/asamk/signal/storage/stickers/StickerStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.stickers; +package org.asamk.signal.manager.storage.stickers; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; diff --git a/src/main/java/org/asamk/signal/storage/threads/LegacyJsonThreadStore.java b/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java similarity index 97% rename from src/main/java/org/asamk/signal/storage/threads/LegacyJsonThreadStore.java rename to src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java index 24463933..f37360a2 100644 --- a/src/main/java/org/asamk/signal/storage/threads/LegacyJsonThreadStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.threads; +package org.asamk.signal.manager.storage.threads; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonGenerator; diff --git a/src/main/java/org/asamk/signal/storage/threads/ThreadInfo.java b/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java similarity index 78% rename from src/main/java/org/asamk/signal/storage/threads/ThreadInfo.java rename to src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java index 67e6b474..b81a0051 100644 --- a/src/main/java/org/asamk/signal/storage/threads/ThreadInfo.java +++ b/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java @@ -1,4 +1,4 @@ -package org.asamk.signal.storage.threads; +package org.asamk.signal.manager.storage.threads; import com.fasterxml.jackson.annotation.JsonProperty; From bbdd6a89102f200f284a01a41ac2809c0759ae50 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 29 Dec 2020 22:48:39 +0100 Subject: [PATCH 003/124] Cleanup utils --- .../asamk/signal/manager/DeviceLinkInfo.java | 60 ++++ .../org/asamk/signal/manager/Manager.java | 43 +-- .../signal/manager/ProvisioningManager.java | 4 +- .../asamk/signal/manager/ServiceConfig.java | 11 + .../java/org/asamk/signal/manager/Utils.java | 304 ------------------ .../signal/manager/groups/GroupIdV1.java | 2 +- .../manager/groups/GroupLinkPassword.java | 2 +- .../signal/manager/helper/GroupHelper.java | 2 +- .../signal/manager/storage/SignalAccount.java | 22 +- .../storage/groups/JsonGroupStore.java | 2 +- .../protocol/JsonIdentityKeyStore.java | 6 +- .../storage/protocol/JsonSessionStore.java | 6 +- .../signal/manager/util/AttachmentUtils.java | 79 +++++ .../asamk/signal/manager/util/IOUtils.java | 72 +++++ .../signal/manager/{ => util}/KeyUtils.java | 10 +- .../manager/util/MessageCacheUtils.java | 105 ++++++ .../org/asamk/signal/manager/util/Utils.java | 97 ++++++ .../java/org/asamk/signal/util/IOUtils.java | 64 ---- src/main/java/org/asamk/signal/util/Util.java | 42 --- 19 files changed, 477 insertions(+), 456 deletions(-) create mode 100644 src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java delete mode 100644 src/main/java/org/asamk/signal/manager/Utils.java create mode 100644 src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java create mode 100644 src/main/java/org/asamk/signal/manager/util/IOUtils.java rename src/main/java/org/asamk/signal/manager/{ => util}/KeyUtils.java (79%) create mode 100644 src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java create mode 100644 src/main/java/org/asamk/signal/manager/util/Utils.java diff --git a/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java b/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java new file mode 100644 index 00000000..5b9fbe28 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java @@ -0,0 +1,60 @@ +package org.asamk.signal.manager; + +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.util.Base64; + +import java.io.IOException; +import java.net.URI; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import static org.whispersystems.signalservice.internal.util.Util.isEmpty; + +public class DeviceLinkInfo { + + final String deviceIdentifier; + final ECPublicKey deviceKey; + + public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws IOException, InvalidKeyException { + Map query = getQueryMap(linkUri.getRawQuery()); + String deviceIdentifier = query.get("uuid"); + String publicKeyEncoded = query.get("pub_key"); + + if (isEmpty(deviceIdentifier) || isEmpty(publicKeyEncoded)) { + throw new RuntimeException("Invalid device link uri"); + } + + ECPublicKey deviceKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); + + return new DeviceLinkInfo(deviceIdentifier, deviceKey); + } + + private static Map getQueryMap(String query) { + String[] params = query.split("&"); + Map map = new HashMap<>(); + for (String param : params) { + final String[] paramParts = param.split("="); + String name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8); + String value = URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8); + map.put(name, value); + } + return map; + } + + public DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) { + this.deviceIdentifier = deviceIdentifier; + this.deviceKey = deviceKey; + } + + public String createDeviceLinkUri() { + return "tsdevice:/?uuid=" + + URLEncoder.encode(deviceIdentifier, StandardCharsets.UTF_8) + + "&pub_key=" + + URLEncoder.encode(Base64.encodeBytesWithoutPadding(deviceKey.serialize()), StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index e00f44ac..5ed8fc4e 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -37,8 +37,11 @@ import org.asamk.signal.manager.storage.profiles.SignalProfile; import org.asamk.signal.manager.storage.profiles.SignalProfileEntry; import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.manager.storage.stickers.Sticker; -import org.asamk.signal.util.IOUtils; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.util.AttachmentUtils; +import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.KeyUtils; +import org.asamk.signal.manager.util.MessageCacheUtils; +import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.metadata.InvalidMetadataMessageException; import org.signal.libsignal.metadata.InvalidMetadataVersionException; import org.signal.libsignal.metadata.ProtocolDuplicateMessageException; @@ -50,6 +53,7 @@ import org.signal.libsignal.metadata.ProtocolLegacyMessageException; import org.signal.libsignal.metadata.ProtocolNoSessionException; import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.signal.libsignal.metadata.SelfSendException; +import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; @@ -125,6 +129,7 @@ import org.whispersystems.signalservice.api.push.ContactTokenDetails; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; import org.whispersystems.signalservice.api.util.InvalidNumberException; +import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; @@ -185,6 +190,7 @@ public class Manager implements Closeable { final static Logger logger = LoggerFactory.getLogger(Manager.class); private final SleepTimer timer = new UptimeSleepTimer(); + private final CertificateValidator certificateValidator = new CertificateValidator(ServiceConfig.getUnidentifiedSenderTrustRoot()); private final SignalServiceConfiguration serviceConfiguration; private final String userAgent; @@ -419,7 +425,7 @@ public class Manager implements Closeable { } public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException { - Utils.DeviceLinkInfo info = Utils.parseDeviceLinkUri(linkUri); + DeviceLinkInfo info = DeviceLinkInfo.parseDeviceLinkUri(linkUri); addDevice(info.deviceIdentifier, info.deviceKey); } @@ -696,7 +702,7 @@ public class Manager implements Closeable { return Optional.absent(); } - return Optional.of(Utils.createAttachment(file)); + return Optional.of(AttachmentUtils.createAttachment(file)); } private Optional createContactAvatarAttachment(String number) throws IOException { @@ -705,7 +711,7 @@ public class Manager implements Closeable { return Optional.absent(); } - return Optional.of(Utils.createAttachment(file)); + return Optional.of(AttachmentUtils.createAttachment(file)); } private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { @@ -751,7 +757,7 @@ public class Manager implements Closeable { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() .withBody(messageText); if (attachments != null) { - messageBuilder.withAttachments(Utils.getSignalServiceAttachments(attachments)); + messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments)); } return sendGroupMessage(messageBuilder, groupId); @@ -928,7 +934,7 @@ public class Manager implements Closeable { newE164Members.remove(contact.getNumber()); } throw new IOException("Failed to add members " - + Util.join(", ", newE164Members) + + String.join(", ", newE164Members) + " to group: Not registered on Signal"); } @@ -971,7 +977,7 @@ public class Manager implements Closeable { File aFile = getGroupAvatarFile(g.getGroupId()); if (aFile.exists()) { try { - group.withAvatar(Utils.createAttachment(aFile)); + group.withAvatar(AttachmentUtils.createAttachment(aFile)); } catch (IOException e) { throw new AttachmentInvalidException(aFile.toString(), e); } @@ -1022,7 +1028,7 @@ public class Manager implements Closeable { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() .withBody(messageText); if (attachments != null) { - List attachmentStreams = Utils.getSignalServiceAttachments(attachments); + List attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments); // Upload attachments here, so we only upload once even for multiple recipients SignalServiceMessageSender messageSender = createMessageSender(); @@ -1510,7 +1516,7 @@ public class Manager implements Closeable { private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException { SignalServiceCipher cipher = new SignalServiceCipher(account.getSelfAddress(), account.getSignalProtocolStore(), - Utils.getCertificateValidator()); + certificateValidator); try { return cipher.decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { @@ -1820,7 +1826,7 @@ public class Manager implements Closeable { ) { SignalServiceEnvelope envelope; try { - envelope = Utils.loadEnvelope(fileEntry); + envelope = MessageCacheUtils.loadEnvelope(fileEntry); if (envelope == null) { return; } @@ -1887,7 +1893,7 @@ public class Manager implements Closeable { try { String source = envelope1.getSourceE164().isPresent() ? envelope1.getSourceE164().get() : ""; File cacheFile = getMessageCacheFile(source, now, envelope1.getTimestamp()); - Utils.storeEnvelope(envelope1, cacheFile); + MessageCacheUtils.storeEnvelope(envelope1, cacheFile); } catch (IOException e) { logger.warn("Failed to store encrypted message in disk cache, ignoring: {}", e.getMessage()); } @@ -2240,7 +2246,7 @@ public class Manager implements Closeable { return retrieveAttachment(pointer, getContactAvatarFile(number), false); } else { SignalServiceAttachmentStream stream = attachment.asStream(); - return Utils.retrieveAttachment(stream, getContactAvatarFile(number)); + return AttachmentUtils.retrieveAttachment(stream, getContactAvatarFile(number)); } } @@ -2257,7 +2263,7 @@ public class Manager implements Closeable { return retrieveAttachment(pointer, getGroupAvatarFile(groupId), false); } else { SignalServiceAttachmentStream stream = attachment.asStream(); - return Utils.retrieveAttachment(stream, getGroupAvatarFile(groupId)); + return AttachmentUtils.retrieveAttachment(stream, getGroupAvatarFile(groupId)); } } @@ -2509,7 +2515,7 @@ public class Manager implements Closeable { } public ContactInfo getContact(String number) { - return account.getContactStore().getContact(Util.getSignalServiceAddressFromIdentifier(number)); + return account.getContactStore().getContact(Utils.getSignalServiceAddressFromIdentifier(number)); } public GroupInfo getGroup(GroupId groupId) { @@ -2613,7 +2619,8 @@ public class Manager implements Closeable { public String computeSafetyNumber( SignalServiceAddress theirAddress, IdentityKey theirIdentityKey ) { - return Utils.computeSafetyNumber(account.getSelfAddress(), + return Utils.computeSafetyNumber(ServiceConfig.capabilities.isUuid(), + account.getSelfAddress(), getIdentityKeyPair().getPublicKey(), theirAddress, theirIdentityKey); @@ -2626,12 +2633,12 @@ public class Manager implements Closeable { public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException { String canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier - : Util.canonicalizeNumber(identifier, account.getUsername()); + : PhoneNumberFormatter.formatNumber(identifier, account.getUsername()); return resolveSignalServiceAddress(canonicalizedNumber); } public SignalServiceAddress resolveSignalServiceAddress(String identifier) { - SignalServiceAddress address = Util.getSignalServiceAddressFromIdentifier(identifier); + SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(identifier); return resolveSignalServiceAddress(address); } diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 95e92c7a..8b3f0eb4 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -17,6 +17,7 @@ package org.asamk.signal.manager; import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.util.KeyUtils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.IdentityKeyPair; @@ -71,8 +72,7 @@ public class ProvisioningManager { public String getDeviceLinkUri() throws TimeoutException, IOException { String deviceUuid = accountManager.getNewDeviceUuid(); - return Utils.createDeviceLinkUri(new Utils.DeviceLinkInfo(deviceUuid, - identityKey.getPublicKey().getPublicKey())); + return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri(); } public String finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists { diff --git a/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/src/main/java/org/asamk/signal/manager/ServiceConfig.java index 353670ae..939d5b5b 100644 --- a/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ b/src/main/java/org/asamk/signal/manager/ServiceConfig.java @@ -1,6 +1,9 @@ package org.asamk.signal.manager; import org.signal.zkgroup.ServerPublicParams; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.account.AccountAttributes; import org.whispersystems.signalservice.api.push.TrustStore; @@ -106,6 +109,14 @@ public class ServiceConfig { } } + static ECPublicKey getUnidentifiedSenderTrustRoot() { + try { + return Curve.decodePoint(Base64.decode(UNIDENTIFIED_SENDER_TRUST_ROOT), 0); + } catch (InvalidKeyException | IOException e) { + throw new AssertionError(e); + } + } + private static Map makeSignalCdnUrlMapFor( SignalCdnUrl[] cdn0Urls, SignalCdnUrl[] cdn2Urls ) { diff --git a/src/main/java/org/asamk/signal/manager/Utils.java b/src/main/java/org/asamk/signal/manager/Utils.java deleted file mode 100644 index 0a815ea9..00000000 --- a/src/main/java/org/asamk/signal/manager/Utils.java +++ /dev/null @@ -1,304 +0,0 @@ -package org.asamk.signal.manager; - -import org.signal.libsignal.metadata.certificate.CertificateValidator; -import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.fingerprint.Fingerprint; -import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; -import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.util.StreamDetails; -import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; -import org.whispersystems.util.Base64; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -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.net.URI; -import java.net.URLConnection; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static org.whispersystems.signalservice.internal.util.Util.isEmpty; - -class Utils { - - static List getSignalServiceAttachments(List attachments) throws AttachmentInvalidException { - List signalServiceAttachments = null; - if (attachments != null) { - signalServiceAttachments = new ArrayList<>(attachments.size()); - for (String attachment : attachments) { - try { - signalServiceAttachments.add(createAttachment(new File(attachment))); - } catch (IOException e) { - throw new AttachmentInvalidException(attachment, e); - } - } - } - return signalServiceAttachments; - } - - static String getFileMimeType(File file, String defaultMimeType) throws IOException { - String mime = Files.probeContentType(file.toPath()); - if (mime == null) { - try (InputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) { - mime = URLConnection.guessContentTypeFromStream(bufferedStream); - } - } - if (mime == null) { - return defaultMimeType; - } - return mime; - } - - static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { - InputStream attachmentStream = new FileInputStream(attachmentFile); - final long attachmentSize = attachmentFile.length(); - final String mime = getFileMimeType(attachmentFile, "application/octet-stream"); - // TODO mabybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option - final long uploadTimestamp = System.currentTimeMillis(); - Optional preview = Optional.absent(); - Optional caption = Optional.absent(); - Optional blurHash = Optional.absent(); - final Optional resumableUploadSpec = Optional.absent(); - return new SignalServiceAttachmentStream(attachmentStream, - mime, - attachmentSize, - Optional.of(attachmentFile.getName()), - false, - false, - preview, - 0, - 0, - uploadTimestamp, - caption, - blurHash, - null, - null, - resumableUploadSpec); - } - - static StreamDetails createStreamDetailsFromFile(File file) throws IOException { - InputStream stream = new FileInputStream(file); - final long size = file.length(); - String mime = Files.probeContentType(file.toPath()); - if (mime == null) { - mime = "application/octet-stream"; - } - return new StreamDetails(stream, mime, size); - } - - static CertificateValidator getCertificateValidator() { - try { - ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(ServiceConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), - 0); - return new CertificateValidator(unidentifiedSenderTrustRoot); - } catch (InvalidKeyException | IOException e) { - throw new AssertionError(e); - } - } - - private static Map getQueryMap(String query) { - String[] params = query.split("&"); - Map map = new HashMap<>(); - for (String param : params) { - final String[] paramParts = param.split("="); - String name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8); - String value = URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8); - map.put(name, value); - } - return map; - } - - static String createDeviceLinkUri(DeviceLinkInfo info) { - return "tsdevice:/?uuid=" - + URLEncoder.encode(info.deviceIdentifier, StandardCharsets.UTF_8) - + "&pub_key=" - + URLEncoder.encode(Base64.encodeBytesWithoutPadding(info.deviceKey.serialize()), - StandardCharsets.UTF_8); - } - - static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws IOException, InvalidKeyException { - Map query = getQueryMap(linkUri.getRawQuery()); - String deviceIdentifier = query.get("uuid"); - String publicKeyEncoded = query.get("pub_key"); - - if (isEmpty(deviceIdentifier) || isEmpty(publicKeyEncoded)) { - throw new RuntimeException("Invalid device link uri"); - } - - ECPublicKey deviceKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); - - return new DeviceLinkInfo(deviceIdentifier, deviceKey); - } - - static SignalServiceEnvelope loadEnvelope(File file) throws IOException { - try (FileInputStream f = new FileInputStream(file)) { - DataInputStream in = new DataInputStream(f); - int version = in.readInt(); - if (version > 4) { - return null; - } - int type = in.readInt(); - String source = in.readUTF(); - UUID sourceUuid = null; - if (version >= 3) { - sourceUuid = UuidUtil.parseOrNull(in.readUTF()); - } - int sourceDevice = in.readInt(); - if (version == 1) { - // read legacy relay field - in.readUTF(); - } - long timestamp = in.readLong(); - byte[] content = null; - int contentLen = in.readInt(); - if (contentLen > 0) { - content = new byte[contentLen]; - in.readFully(content); - } - byte[] legacyMessage = null; - int legacyMessageLen = in.readInt(); - if (legacyMessageLen > 0) { - legacyMessage = new byte[legacyMessageLen]; - in.readFully(legacyMessage); - } - long serverReceivedTimestamp = 0; - String uuid = null; - if (version >= 2) { - serverReceivedTimestamp = in.readLong(); - uuid = in.readUTF(); - if ("".equals(uuid)) { - uuid = null; - } - } - long serverDeliveredTimestamp = 0; - if (version >= 4) { - serverDeliveredTimestamp = in.readLong(); - } - Optional addressOptional = sourceUuid == null && source.isEmpty() - ? Optional.absent() - : Optional.of(new SignalServiceAddress(sourceUuid, source)); - return new SignalServiceEnvelope(type, - addressOptional, - sourceDevice, - timestamp, - legacyMessage, - content, - serverReceivedTimestamp, - serverDeliveredTimestamp, - uuid); - } - } - - static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { - try (FileOutputStream f = new FileOutputStream(file)) { - try (DataOutputStream out = new DataOutputStream(f)) { - out.writeInt(4); // version - out.writeInt(envelope.getType()); - out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); - out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : ""); - out.writeInt(envelope.getSourceDevice()); - out.writeLong(envelope.getTimestamp()); - if (envelope.hasContent()) { - out.writeInt(envelope.getContent().length); - out.write(envelope.getContent()); - } else { - out.writeInt(0); - } - if (envelope.hasLegacyMessage()) { - out.writeInt(envelope.getLegacyMessage().length); - out.write(envelope.getLegacyMessage()); - } else { - out.writeInt(0); - } - out.writeLong(envelope.getServerReceivedTimestamp()); - String uuid = envelope.getUuid(); - out.writeUTF(uuid == null ? "" : uuid); - out.writeLong(envelope.getServerDeliveredTimestamp()); - } - } - } - - static File retrieveAttachment(SignalServiceAttachmentStream stream, File outputFile) throws IOException { - InputStream input = stream.getInputStream(); - - try (OutputStream output = new FileOutputStream(outputFile)) { - byte[] buffer = new byte[4096]; - int read; - - while ((read = input.read(buffer)) != -1) { - output.write(buffer, 0, read); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; - } - return outputFile; - } - - static String computeSafetyNumber( - SignalServiceAddress ownAddress, - IdentityKey ownIdentityKey, - SignalServiceAddress theirAddress, - IdentityKey theirIdentityKey - ) { - int version; - byte[] ownId; - byte[] theirId; - - if (ServiceConfig.capabilities.isUuid() && ownAddress.getUuid().isPresent() && theirAddress.getUuid() - .isPresent()) { - // Version 2: UUID user - version = 2; - ownId = UuidUtil.toByteArray(ownAddress.getUuid().get()); - theirId = UuidUtil.toByteArray(theirAddress.getUuid().get()); - } else { - // Version 1: E164 user - version = 1; - if (!ownAddress.getNumber().isPresent() || !theirAddress.getNumber().isPresent()) { - return "INVALID ID"; - } - ownId = ownAddress.getNumber().get().getBytes(); - theirId = theirAddress.getNumber().get().getBytes(); - } - - Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(version, - ownId, - ownIdentityKey, - theirId, - theirIdentityKey); - return fingerprint.getDisplayableFingerprint().getDisplayText(); - } - - static class DeviceLinkInfo { - - final String deviceIdentifier; - final ECPublicKey deviceKey; - - DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) { - this.deviceIdentifier = deviceIdentifier; - this.deviceKey = deviceKey; - } - } -} diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java b/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java index d865356e..237a34b6 100644 --- a/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.groups; -import static org.asamk.signal.manager.KeyUtils.getSecretBytes; +import static org.asamk.signal.manager.util.KeyUtils.getSecretBytes; public class GroupIdV1 extends GroupId { diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java b/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java index 41be672a..7edc7afb 100644 --- a/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java @@ -1,6 +1,6 @@ package org.asamk.signal.manager.groups; -import org.asamk.signal.manager.KeyUtils; +import org.asamk.signal.manager.util.KeyUtils; import java.util.Arrays; diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 394eba57..8a2320e0 100644 --- a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -7,7 +7,7 @@ import org.asamk.signal.manager.groups.GroupLinkPassword; import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.asamk.signal.manager.storage.profiles.SignalProfile; -import org.asamk.signal.util.IOUtils; +import org.asamk.signal.manager.util.IOUtils; import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.Member; diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index c3573209..c787471f 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -25,8 +25,8 @@ import org.asamk.signal.manager.storage.protocol.SignalServiceAddressResolver; import org.asamk.signal.manager.storage.stickers.StickerStore; import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore; import org.asamk.signal.manager.storage.threads.ThreadInfo; -import org.asamk.signal.util.IOUtils; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.Utils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.slf4j.Logger; @@ -211,28 +211,28 @@ public class SignalAccount implements Closeable { deviceId = node.asInt(); } if (rootNode.has("isMultiDevice")) { - isMultiDevice = Util.getNotNullNode(rootNode, "isMultiDevice").asBoolean(); + isMultiDevice = Utils.getNotNullNode(rootNode, "isMultiDevice").asBoolean(); } - username = Util.getNotNullNode(rootNode, "username").asText(); - password = Util.getNotNullNode(rootNode, "password").asText(); + username = Utils.getNotNullNode(rootNode, "username").asText(); + password = Utils.getNotNullNode(rootNode, "password").asText(); JsonNode pinNode = rootNode.get("registrationLockPin"); registrationLockPin = pinNode == null || pinNode.isNull() ? null : pinNode.asText(); if (rootNode.has("signalingKey")) { - signalingKey = Util.getNotNullNode(rootNode, "signalingKey").asText(); + signalingKey = Utils.getNotNullNode(rootNode, "signalingKey").asText(); } if (rootNode.has("preKeyIdOffset")) { - preKeyIdOffset = Util.getNotNullNode(rootNode, "preKeyIdOffset").asInt(0); + preKeyIdOffset = Utils.getNotNullNode(rootNode, "preKeyIdOffset").asInt(0); } else { preKeyIdOffset = 0; } if (rootNode.has("nextSignedPreKeyId")) { - nextSignedPreKeyId = Util.getNotNullNode(rootNode, "nextSignedPreKeyId").asInt(); + nextSignedPreKeyId = Utils.getNotNullNode(rootNode, "nextSignedPreKeyId").asInt(); } else { nextSignedPreKeyId = 0; } if (rootNode.has("profileKey")) { try { - profileKey = new ProfileKey(Base64.decode(Util.getNotNullNode(rootNode, "profileKey").asText())); + profileKey = new ProfileKey(Base64.decode(Utils.getNotNullNode(rootNode, "profileKey").asText())); } catch (InvalidInputException e) { throw new IOException( "Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes", @@ -240,9 +240,9 @@ public class SignalAccount implements Closeable { } } - signalProtocolStore = jsonProcessor.convertValue(Util.getNotNullNode(rootNode, "axolotlStore"), + signalProtocolStore = jsonProcessor.convertValue(Utils.getNotNullNode(rootNode, "axolotlStore"), JsonSignalProtocolStore.class); - registered = Util.getNotNullNode(rootNode, "registered").asBoolean(); + registered = Utils.getNotNullNode(rootNode, "registered").asBoolean(); JsonNode groupStoreNode = rootNode.get("groupStore"); if (groupStoreNode != null) { groupStore = jsonProcessor.convertValue(groupStoreNode, JsonGroupStore.class); diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java index 2b4dbcf5..fdcd28a3 100644 --- a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java @@ -16,8 +16,8 @@ import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupUtils; +import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.util.Hex; -import org.asamk.signal.util.IOUtils; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.groups.GroupMasterKey; diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index 517b384e..5bc1c11f 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -9,7 +9,7 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import org.asamk.signal.manager.TrustLevel; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKey; @@ -51,7 +51,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { if (resolver != null) { return resolver.resolveSignalServiceAddress(identifier); } else { - return Util.getSignalServiceAddressFromIdentifier(identifier); + return Utils.getSignalServiceAddressFromIdentifier(identifier); } } @@ -213,7 +213,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { UUID uuid = trustedKey.hasNonNull("uuid") ? UuidUtil.parseOrNull(trustedKey.get("uuid") .asText()) : null; final SignalServiceAddress serviceAddress = uuid == null - ? Util.getSignalServiceAddressFromIdentifier(trustedKeyName) + ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName) : new SignalServiceAddress(uuid, trustedKeyName); try { IdentityKey id = new IdentityKey(Base64.decode(trustedKey.get("identityKey").asText()), 0); diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java index f55aff14..6e300214 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import org.asamk.signal.util.Util; +import org.asamk.signal.manager.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.SignalProtocolAddress; @@ -43,7 +43,7 @@ class JsonSessionStore implements SessionStore { if (resolver != null) { return resolver.resolveSignalServiceAddress(identifier); } else { - return Util.getSignalServiceAddressFromIdentifier(identifier); + return Utils.getSignalServiceAddressFromIdentifier(identifier); } } @@ -147,7 +147,7 @@ class JsonSessionStore implements SessionStore { UUID uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null; final SignalServiceAddress serviceAddress = uuid == null - ? Util.getSignalServiceAddressFromIdentifier(sessionName) + ? Utils.getSignalServiceAddressFromIdentifier(sessionName) : new SignalServiceAddress(uuid, sessionName); final int deviceId = session.get("deviceId").asInt(); final String record = session.get("record").asText(); diff --git a/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java b/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java new file mode 100644 index 00000000..b9a97073 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java @@ -0,0 +1,79 @@ +package org.asamk.signal.manager.util; + +import org.asamk.signal.manager.AttachmentInvalidException; +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; +import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; + +import java.io.File; +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.util.ArrayList; +import java.util.List; + +public class AttachmentUtils { + + public static List getSignalServiceAttachments(List attachments) throws AttachmentInvalidException { + List signalServiceAttachments = null; + if (attachments != null) { + signalServiceAttachments = new ArrayList<>(attachments.size()); + for (String attachment : attachments) { + try { + signalServiceAttachments.add(createAttachment(new File(attachment))); + } catch (IOException e) { + throw new AttachmentInvalidException(attachment, e); + } + } + } + return signalServiceAttachments; + } + + public static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { + InputStream attachmentStream = new FileInputStream(attachmentFile); + final long attachmentSize = attachmentFile.length(); + final String mime = Utils.getFileMimeType(attachmentFile, "application/octet-stream"); + // TODO mabybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option + final long uploadTimestamp = System.currentTimeMillis(); + Optional preview = Optional.absent(); + Optional caption = Optional.absent(); + Optional blurHash = Optional.absent(); + final Optional resumableUploadSpec = Optional.absent(); + return new SignalServiceAttachmentStream(attachmentStream, + mime, + attachmentSize, + Optional.of(attachmentFile.getName()), + false, + false, + preview, + 0, + 0, + uploadTimestamp, + caption, + blurHash, + null, + null, + resumableUploadSpec); + } + + public static File retrieveAttachment(SignalServiceAttachmentStream stream, File outputFile) throws IOException { + InputStream input = stream.getInputStream(); + + try (OutputStream output = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[4096]; + int read; + + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + return outputFile; + } +} diff --git a/src/main/java/org/asamk/signal/manager/util/IOUtils.java b/src/main/java/org/asamk/signal/manager/util/IOUtils.java new file mode 100644 index 00000000..06f8aa22 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/IOUtils.java @@ -0,0 +1,72 @@ +package org.asamk.signal.manager.util; + +import org.whispersystems.signalservice.internal.util.Util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.EnumSet; +import java.util.Set; + +import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; + +public class IOUtils { + + public static File createTempFile() throws IOException { + return File.createTempFile("signal_tmp_", ".tmp"); + } + + public static byte[] readFully(InputStream in) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Util.copy(in, baos); + return baos.toByteArray(); + } + + public static void createPrivateDirectories(File file) throws IOException { + if (file.exists()) { + return; + } + + final Path path = file.toPath(); + try { + Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE); + Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms)); + } catch (UnsupportedOperationException e) { + Files.createDirectories(path); + } + } + + public static void createPrivateFile(File path) throws IOException { + final Path file = path.toPath(); + try { + Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE); + Files.createFile(file, PosixFilePermissions.asFileAttribute(perms)); + } catch (UnsupportedOperationException e) { + Files.createFile(file); + } + } + + public static void copyStreamToFile(InputStream input, File outputFile) throws IOException { + copyStreamToFile(input, outputFile, 8192); + } + + public static void copyStreamToFile(InputStream input, File outputFile, int bufferSize) throws IOException { + try (OutputStream output = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[bufferSize]; + int read; + + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); + } + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/KeyUtils.java b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java similarity index 79% rename from src/main/java/org/asamk/signal/manager/KeyUtils.java rename to src/main/java/org/asamk/signal/manager/util/KeyUtils.java index 6ac093db..2b4bc371 100644 --- a/src/main/java/org/asamk/signal/manager/KeyUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.util; import org.asamk.signal.util.RandomUtils; import org.signal.zkgroup.InvalidInputException; @@ -10,11 +10,11 @@ public class KeyUtils { private KeyUtils() { } - static String createSignalingKey() { + public static String createSignalingKey() { return getSecret(52); } - static ProfileKey createProfileKey() { + public static ProfileKey createProfileKey() { try { return new ProfileKey(getSecretBytes(32)); } catch (InvalidInputException e) { @@ -22,11 +22,11 @@ public class KeyUtils { } } - static String createPassword() { + public static String createPassword() { return getSecret(18); } - static byte[] createStickerUploadKey() { + public static byte[] createStickerUploadKey() { return getSecretBytes(32); } diff --git a/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java new file mode 100644 index 00000000..8661c10b --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -0,0 +1,105 @@ +package org.asamk.signal.manager.util; + +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.UuidUtil; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.UUID; + +public class MessageCacheUtils { + + public static SignalServiceEnvelope loadEnvelope(File file) throws IOException { + try (FileInputStream f = new FileInputStream(file)) { + DataInputStream in = new DataInputStream(f); + int version = in.readInt(); + if (version > 4) { + return null; + } + int type = in.readInt(); + String source = in.readUTF(); + UUID sourceUuid = null; + if (version >= 3) { + sourceUuid = UuidUtil.parseOrNull(in.readUTF()); + } + int sourceDevice = in.readInt(); + if (version == 1) { + // read legacy relay field + in.readUTF(); + } + long timestamp = in.readLong(); + byte[] content = null; + int contentLen = in.readInt(); + if (contentLen > 0) { + content = new byte[contentLen]; + in.readFully(content); + } + byte[] legacyMessage = null; + int legacyMessageLen = in.readInt(); + if (legacyMessageLen > 0) { + legacyMessage = new byte[legacyMessageLen]; + in.readFully(legacyMessage); + } + long serverReceivedTimestamp = 0; + String uuid = null; + if (version >= 2) { + serverReceivedTimestamp = in.readLong(); + uuid = in.readUTF(); + if ("".equals(uuid)) { + uuid = null; + } + } + long serverDeliveredTimestamp = 0; + if (version >= 4) { + serverDeliveredTimestamp = in.readLong(); + } + Optional addressOptional = sourceUuid == null && source.isEmpty() + ? Optional.absent() + : Optional.of(new SignalServiceAddress(sourceUuid, source)); + return new SignalServiceEnvelope(type, + addressOptional, + sourceDevice, + timestamp, + legacyMessage, + content, + serverReceivedTimestamp, + serverDeliveredTimestamp, + uuid); + } + } + + public static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { + try (FileOutputStream f = new FileOutputStream(file)) { + try (DataOutputStream out = new DataOutputStream(f)) { + out.writeInt(4); // version + out.writeInt(envelope.getType()); + out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); + out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : ""); + out.writeInt(envelope.getSourceDevice()); + out.writeLong(envelope.getTimestamp()); + if (envelope.hasContent()) { + out.writeInt(envelope.getContent().length); + out.write(envelope.getContent()); + } else { + out.writeInt(0); + } + if (envelope.hasLegacyMessage()) { + out.writeInt(envelope.getLegacyMessage().length); + out.write(envelope.getLegacyMessage()); + } else { + out.writeInt(0); + } + out.writeLong(envelope.getServerReceivedTimestamp()); + String uuid = envelope.getUuid(); + out.writeUTF(uuid == null ? "" : uuid); + out.writeLong(envelope.getServerDeliveredTimestamp()); + } + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/util/Utils.java b/src/main/java/org/asamk/signal/manager/util/Utils.java new file mode 100644 index 00000000..e68b5ce3 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -0,0 +1,97 @@ +package org.asamk.signal.manager.util; + +import com.fasterxml.jackson.databind.JsonNode; + +import org.whispersystems.libsignal.IdentityKey; +import org.whispersystems.libsignal.fingerprint.Fingerprint; +import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.StreamDetails; +import org.whispersystems.signalservice.api.util.UuidUtil; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidObjectException; +import java.net.URLConnection; +import java.nio.file.Files; + +public class Utils { + + public static String getFileMimeType(File file, String defaultMimeType) throws IOException { + String mime = Files.probeContentType(file.toPath()); + if (mime == null) { + try (InputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) { + mime = URLConnection.guessContentTypeFromStream(bufferedStream); + } + } + if (mime == null) { + return defaultMimeType; + } + return mime; + } + + public static StreamDetails createStreamDetailsFromFile(File file) throws IOException { + InputStream stream = new FileInputStream(file); + final long size = file.length(); + String mime = Files.probeContentType(file.toPath()); + if (mime == null) { + mime = "application/octet-stream"; + } + return new StreamDetails(stream, mime, size); + } + + public static String computeSafetyNumber( + boolean isUuidCapable, + SignalServiceAddress ownAddress, + IdentityKey ownIdentityKey, + SignalServiceAddress theirAddress, + IdentityKey theirIdentityKey + ) { + int version; + byte[] ownId; + byte[] theirId; + + if (isUuidCapable && ownAddress.getUuid().isPresent() && theirAddress.getUuid().isPresent()) { + // Version 2: UUID user + version = 2; + ownId = UuidUtil.toByteArray(ownAddress.getUuid().get()); + theirId = UuidUtil.toByteArray(theirAddress.getUuid().get()); + } else { + // Version 1: E164 user + version = 1; + if (!ownAddress.getNumber().isPresent() || !theirAddress.getNumber().isPresent()) { + return "INVALID ID"; + } + ownId = ownAddress.getNumber().get().getBytes(); + theirId = theirAddress.getNumber().get().getBytes(); + } + + Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(version, + ownId, + ownIdentityKey, + theirId, + theirIdentityKey); + return fingerprint.getDisplayableFingerprint().getDisplayText(); + } + + public static SignalServiceAddress getSignalServiceAddressFromIdentifier(final String identifier) { + if (UuidUtil.isUuid(identifier)) { + return new SignalServiceAddress(UuidUtil.parseOrNull(identifier), null); + } else { + return new SignalServiceAddress(null, identifier); + } + } + + public static JsonNode getNotNullNode(JsonNode parent, String name) throws InvalidObjectException { + JsonNode node = parent.get(name); + if (node == null) { + throw new InvalidObjectException(String.format("Incorrect file format: expected parameter %s not found ", + name)); + } + + return node; + } +} diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index 59727a9a..766d1905 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -1,35 +1,16 @@ package org.asamk.signal.util; -import org.whispersystems.signalservice.internal.util.Util; - -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.io.StringWriter; import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.PosixFilePermissions; -import java.util.EnumSet; -import java.util.Set; - -import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; -import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; -import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; public class IOUtils { private IOUtils() { } - public static File createTempFile() throws IOException { - return File.createTempFile("signal_tmp_", ".tmp"); - } - public static String readAll(InputStream in, Charset charset) throws IOException { StringWriter output = new StringWriter(); byte[] buffer = new byte[4096]; @@ -40,36 +21,6 @@ public class IOUtils { return output.toString(); } - public static byte[] readFully(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Util.copy(in, baos); - return baos.toByteArray(); - } - - public static void createPrivateDirectories(File file) throws IOException { - if (file.exists()) { - return; - } - - final Path path = file.toPath(); - try { - Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE); - Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms)); - } catch (UnsupportedOperationException e) { - Files.createDirectories(path); - } - } - - public static void createPrivateFile(File path) throws IOException { - final Path file = path.toPath(); - try { - Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE); - Files.createFile(file, PosixFilePermissions.asFileAttribute(perms)); - } catch (UnsupportedOperationException e) { - Files.createFile(file); - } - } - public static File getDataHomeDir() { String dataHome = System.getenv("XDG_DATA_HOME"); if (dataHome != null) { @@ -78,19 +29,4 @@ public class IOUtils { return new File(new File(System.getProperty("user.home"), ".local"), "share"); } - - public static void copyStreamToFile(InputStream input, File outputFile) throws IOException { - copyStreamToFile(input, outputFile, 8192); - } - - public static void copyStreamToFile(InputStream input, File outputFile, int bufferSize) throws IOException { - try (OutputStream output = new FileOutputStream(outputFile)) { - byte[] buffer = new byte[bufferSize]; - int read; - - while ((read = input.read(buffer)) != -1) { - output.write(buffer, 0, read); - } - } - } } diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index 79a6587b..92bfae7b 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -1,15 +1,7 @@ package org.asamk.signal.util; -import com.fasterxml.jackson.databind.JsonNode; - import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; -import org.whispersystems.signalservice.api.util.UuidUtil; - -import java.io.InvalidObjectException; public class Util { @@ -26,41 +18,7 @@ public class Util { return f.toString(); } - public static String join(CharSequence separator, Iterable list) { - StringBuilder buf = new StringBuilder(); - for (CharSequence str : list) { - if (buf.length() > 0) { - buf.append(separator); - } - buf.append(str); - } - - return buf.toString(); - } - - public static JsonNode getNotNullNode(JsonNode parent, String name) throws InvalidObjectException { - JsonNode node = parent.get(name); - if (node == null) { - throw new InvalidObjectException(String.format("Incorrect file format: expected parameter %s not found ", - name)); - } - - return node; - } - public static GroupId decodeGroupId(String groupId) throws GroupIdFormatException { return GroupId.fromBase64(groupId); } - - public static String canonicalizeNumber(String number, String localNumber) throws InvalidNumberException { - return PhoneNumberFormatter.formatNumber(number, localNumber); - } - - public static SignalServiceAddress getSignalServiceAddressFromIdentifier(final String identifier) { - if (UuidUtil.isUuid(identifier)) { - return new SignalServiceAddress(UuidUtil.parseOrNull(identifier), null); - } else { - return new SignalServiceAddress(null, identifier); - } - } } From a52f6a6657585fbad5afa4c57ce37752118317e9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 30 Dec 2020 11:59:24 +0100 Subject: [PATCH 004/124] Replace Collections with Set.of/Map.of/List.of --- .../org/asamk/signal/dbus/DbusSignalImpl.java | 3 +-- .../org/asamk/signal/manager/Manager.java | 19 +++++++++---------- .../asamk/signal/manager/ServiceConfig.java | 3 +-- .../manager/storage/groups/GroupInfoV2.java | 7 +++---- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index d19116a4..278fbbd4 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -16,7 +16,6 @@ import org.whispersystems.signalservice.api.util.InvalidNumberException; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -167,7 +166,7 @@ public class DbusSignalImpl implements Signal { public List getGroupMembers(final byte[] groupId) { GroupInfo group = m.getGroup(GroupId.unknownVersion(groupId)); if (group == null) { - return Collections.emptyList(); + return List.of(); } else { return group.getMembers() .stream() diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 5ed8fc4e..25b2ec54 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -164,7 +164,6 @@ import java.security.SignatureException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -211,7 +210,7 @@ public class Manager implements Closeable { private final ProfileHelper profileHelper; private final GroupHelper groupHelper; - public Manager( + Manager( SignalAccount account, PathConfig pathConfig, SignalServiceConfiguration serviceConfiguration, @@ -810,7 +809,7 @@ public class Manager implements Closeable { GroupInfoV2 gv2 = groupHelper.createGroupV2(name, members, avatarFile); if (gv2 == null) { GroupInfoV1 gv1 = new GroupInfoV1(GroupIdV1.createRandom()); - gv1.addMembers(Collections.singleton(account.getSelfAddress())); + gv1.addMembers(List.of(account.getSelfAddress())); updateGroupV1(gv1, name, members, avatarFile); messageBuilder = getGroupUpdateMessageBuilder(gv1); g = gv1; @@ -965,7 +964,7 @@ public class Manager implements Closeable { SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(g); // Send group message only to the recipient who requested it - return sendMessage(messageBuilder, Collections.singleton(recipient)); + return sendMessage(messageBuilder, List.of(recipient)); } private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException { @@ -1007,14 +1006,14 @@ public class Manager implements Closeable { .asGroupMessage(group.build()); // Send group info request message to the recipient who sent us a message with this groupId - return sendMessage(messageBuilder, Collections.singleton(recipient)); + return sendMessage(messageBuilder, List.of(recipient)); } void sendReceipt( SignalServiceAddress remoteAddress, long messageId ) throws IOException, UntrustedIdentityException { SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, - Collections.singletonList(messageId), + List.of(messageId), System.currentTimeMillis()); createMessageSender().sendReceipt(remoteAddress, @@ -1141,7 +1140,7 @@ public class Manager implements Closeable { private void sendExpirationTimerUpdate(SignalServiceAddress address) throws IOException { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() .asExpirationUpdate(); - sendMessage(messageBuilder, Collections.singleton(address)); + sendMessage(messageBuilder, List.of(address)); } /** @@ -1434,7 +1433,7 @@ public class Manager implements Closeable { .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED); - return new Pair<>(timestamp, Collections.emptyList()); + return new Pair<>(timestamp, List.of()); } } else { // Send to all individually, so sync messages are sent correctly @@ -1477,7 +1476,7 @@ public class Manager implements Closeable { message.getTimestamp(), message, message.getExpiresInSeconds(), - Collections.singletonMap(recipient, unidentifiedAccess.isPresent()), + Map.of(recipient, unidentifiedAccess.isPresent()), false); SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript); @@ -2085,7 +2084,7 @@ public class Manager implements Closeable { syncGroup.removeMember(account.getSelfAddress()); } else { // Add ourself to the member set as it's marked as active - syncGroup.addMembers(Collections.singleton(account.getSelfAddress())); + syncGroup.addMembers(List.of(account.getSelfAddress())); } syncGroup.blocked = g.isBlocked(); if (g.getColor().isPresent()) { diff --git a/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/src/main/java/org/asamk/signal/manager/ServiceConfig.java index 939d5b5b..0ccd826a 100644 --- a/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ b/src/main/java/org/asamk/signal/manager/ServiceConfig.java @@ -20,7 +20,6 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -77,7 +76,7 @@ public class ServiceConfig { .header("User-Agent", userAgent) .build()); - final List interceptors = Collections.singletonList(userAgentInterceptor); + final List interceptors = List.of(userAgentInterceptor); return new SignalServiceConfiguration(new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)}, makeSignalCdnUrlMapFor(new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)}, diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java index 17c23925..2092c03a 100644 --- a/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java @@ -8,7 +8,6 @@ import org.signal.zkgroup.groups.GroupMasterKey; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; -import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; @@ -66,7 +65,7 @@ public class GroupInfoV2 extends GroupInfo { @Override public Set getMembers() { if (this.group == null) { - return Collections.emptySet(); + return Set.of(); } return group.getMembersList() .stream() @@ -77,7 +76,7 @@ public class GroupInfoV2 extends GroupInfo { @Override public Set getPendingMembers() { if (this.group == null) { - return Collections.emptySet(); + return Set.of(); } return group.getPendingMembersList() .stream() @@ -88,7 +87,7 @@ public class GroupInfoV2 extends GroupInfo { @Override public Set getRequestingMembers() { if (this.group == null) { - return Collections.emptySet(); + return Set.of(); } return group.getRequestingMembersList() .stream() From 425626ef9475cbc90ef8ada95dee172389baf521 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 22 Mar 2020 16:02:31 +0100 Subject: [PATCH 005/124] Implement registration pin lock with KBS Fixes #323 Fixes #268 --- build.gradle | 2 +- .../signal/commands/RemovePinCommand.java | 3 +- .../asamk/signal/commands/SetPinCommand.java | 3 +- .../asamk/signal/commands/VerifyCommand.java | 8 ++ .../org/asamk/signal/manager/Manager.java | 112 ++++++++++++++---- .../asamk/signal/manager/ServiceConfig.java | 25 ++-- .../signal/manager/helper/PinHelper.java | 90 ++++++++++++++ .../signal/manager/storage/SignalAccount.java | 19 ++- .../asamk/signal/manager/util/KeyUtils.java | 5 + .../asamk/signal/manager/util/PinHashing.java | 31 +++++ 10 files changed, 254 insertions(+), 44 deletions(-) create mode 100644 src/main/java/org/asamk/signal/manager/helper/PinHelper.java create mode 100644 src/main/java/org/asamk/signal/manager/util/PinHashing.java diff --git a/build.gradle b/build.gradle index 1fbf5948..1c42834b 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ repositories { dependencies { implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_15' - implementation 'org.bouncycastle:bcprov-jdk15on:1.67' + implementation 'org.bouncycastle:bcprov-jdk15on:1.68' implementation 'net.sourceforge.argparse4j:argparse4j:0.8.1' implementation 'com.github.hypfvieh:dbus-java:3.2.4' implementation 'org.slf4j:slf4j-simple:1.7.30' diff --git a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java index b7de5402..95531249 100644 --- a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java @@ -5,6 +5,7 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; import java.io.IOException; @@ -23,7 +24,7 @@ public class RemovePinCommand implements LocalCommand { try { m.setRegistrationLockPin(Optional.absent()); return 0; - } catch (IOException e) { + } catch (IOException | UnauthenticatedResponseException e) { System.err.println("Remove pin error: " + e.getMessage()); return 3; } diff --git a/src/main/java/org/asamk/signal/commands/SetPinCommand.java b/src/main/java/org/asamk/signal/commands/SetPinCommand.java index 9351dad0..c68ea3a9 100644 --- a/src/main/java/org/asamk/signal/commands/SetPinCommand.java +++ b/src/main/java/org/asamk/signal/commands/SetPinCommand.java @@ -5,6 +5,7 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; import java.io.IOException; @@ -26,7 +27,7 @@ public class SetPinCommand implements LocalCommand { String registrationLockPin = ns.getString("registrationLockPin"); m.setRegistrationLockPin(Optional.of(registrationLockPin)); return 0; - } catch (IOException e) { + } catch (IOException | UnauthenticatedResponseException e) { System.err.println("Set pin error: " + e.getMessage()); return 3; } diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index b6ad100b..d4b0a7cb 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -4,6 +4,8 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; +import org.whispersystems.signalservice.api.KeyBackupServicePinException; +import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.internal.push.LockedException; import java.io.IOException; @@ -31,6 +33,12 @@ public class VerifyCommand implements LocalCommand { System.err.println("Verification failed! This number is locked with a pin. Hours remaining until reset: " + (e.getTimeRemaining() / 1000 / 60 / 60)); System.err.println("Use '--pin PIN_CODE' to specify the registration lock PIN"); + return 1; + } catch (KeyBackupServicePinException e) { + System.err.println("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining()); + return 1; + } catch (KeyBackupSystemNoDataException e) { + System.err.println("Verification failed! No KBS data."); return 3; } catch (IOException e) { System.err.println("Verify error: " + e.getMessage()); diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 25b2ec54..7ad7b888 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -26,6 +26,7 @@ import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.helper.GroupHelper; +import org.asamk.signal.manager.helper.PinHelper; import org.asamk.signal.manager.helper.ProfileHelper; import org.asamk.signal.manager.helper.UnidentifiedAccessHelper; import org.asamk.signal.manager.storage.SignalAccount; @@ -82,6 +83,10 @@ import org.whispersystems.libsignal.util.KeyHelper; import org.whispersystems.libsignal.util.Medium; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.KbsPinData; +import org.whispersystems.signalservice.api.KeyBackupService; +import org.whispersystems.signalservice.api.KeyBackupServicePinException; +import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessagePipe; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; @@ -96,6 +101,7 @@ import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; +import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; @@ -138,6 +144,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConf import org.whispersystems.signalservice.internal.contacts.crypto.Quote; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; +import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; @@ -160,6 +167,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.security.KeyStore; import java.security.SignatureException; import java.util.ArrayList; import java.util.Arrays; @@ -193,6 +201,8 @@ public class Manager implements Closeable { private final SignalServiceConfiguration serviceConfiguration; private final String userAgent; + + // TODO make configurable private final boolean discoverableByPhoneNumber = true; private final boolean unrestrictedUnidentifiedAccess = false; @@ -209,6 +219,7 @@ public class Manager implements Closeable { private final UnidentifiedAccessHelper unidentifiedAccessHelper; private final ProfileHelper profileHelper; private final GroupHelper groupHelper; + private PinHelper pinHelper; Manager( SignalAccount account, @@ -222,8 +233,7 @@ public class Manager implements Closeable { this.userAgent = userAgent; this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( serviceConfiguration)) : null; - this.accountManager = createSignalServiceAccountManager(); - this.groupsV2Api = accountManager.getGroupsV2Api(); + createSignalServiceAccountManager(); this.account.setResolver(this::resolveSignalServiceAddress); @@ -251,8 +261,8 @@ public class Manager implements Closeable { return account.getSelfAddress(); } - private SignalServiceAccountManager createSignalServiceAccountManager() { - return new SignalServiceAccountManager(serviceConfiguration, + private void createSignalServiceAccountManager() { + this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider(account.getUuid(), account.getUsername(), account.getPassword(), @@ -261,6 +271,18 @@ public class Manager implements Closeable { userAgent, groupsV2Operations, timer); + this.groupsV2Api = accountManager.getGroupsV2Api(); + this.pinHelper = new PinHelper(createKeyBackupService()); + } + + private KeyBackupService createKeyBackupService() { + KeyStore keyStore = ServiceConfig.getIasKeyStore(); + + return accountManager.getKeyBackupService(keyStore, + ServiceConfig.KEY_BACKUP_ENCLAVE_NAME, + ServiceConfig.KEY_BACKUP_SERVICE_ID, + ServiceConfig.KEY_BACKUP_MRENCLAVE, + 10); } private IdentityKeyPair getIdentityKeyPair() { @@ -366,8 +388,7 @@ public class Manager implements Closeable { // Resetting UUID, because registering doesn't work otherwise account.setUuid(null); - accountManager = createSignalServiceAccountManager(); - this.groupsV2Api = accountManager.getGroupsV2Api(); + createSignalServiceAccountManager(); if (voiceVerification) { accountManager.requestVoiceVerificationCode(Locale.getDefault(), @@ -385,8 +406,9 @@ public class Manager implements Closeable { accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, - account.getRegistrationLockPin(), - account.getRegistrationLock(), + // set legacy pin only if no KBS master key is set + account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null, + account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), unidentifiedAccessHelper.getSelfUnidentifiedAccessKey(), unrestrictedUnidentifiedAccess, capabilities, @@ -479,26 +501,39 @@ public class Manager implements Closeable { } } - public void verifyAccount(String verificationCode, String pin) throws IOException { + public void verifyAccount( + String verificationCode, + String pin + ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { verificationCode = verificationCode.replace("-", ""); account.setSignalingKey(KeyUtils.createSignalingKey()); - // TODO make unrestricted unidentified access configurable - VerifyAccountResponse response = accountManager.verifyAccountWithCode(verificationCode, - account.getSignalingKey(), - account.getSignalProtocolStore().getLocalRegistrationId(), - true, - pin, - null, - unidentifiedAccessHelper.getSelfUnidentifiedAccessKey(), - unrestrictedUnidentifiedAccess, - capabilities, - discoverableByPhoneNumber); + VerifyAccountResponse response; + try { + response = verifyAccountWithCode(verificationCode, pin, null); + } catch (LockedException e) { + if (pin == null) { + throw e; + } + + KbsPinData registrationLockData = pinHelper.getRegistrationLockData(pin, e); + if (registrationLockData == null) { + throw e; + } + + String registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock(); + try { + response = verifyAccountWithCode(verificationCode, null, registrationLock); + } catch (LockedException _e) { + throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!"); + } + account.setPinMasterKey(registrationLockData.getMasterKey()); + } - UUID uuid = UuidUtil.parseOrNull(response.getUuid()); // TODO response.isStorageCapable() //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); + account.setRegistered(true); - account.setUuid(uuid); + account.setUuid(UuidUtil.parseOrNull(response.getUuid())); account.setRegistrationLockPin(pin); account.getSignalProtocolStore() .saveIdentity(account.getSelfAddress(), @@ -509,13 +544,40 @@ public class Manager implements Closeable { account.save(); } - public void setRegistrationLockPin(Optional pin) throws IOException { + private VerifyAccountResponse verifyAccountWithCode( + final String verificationCode, final String legacyPin, final String registrationLock + ) throws IOException { + return accountManager.verifyAccountWithCode(verificationCode, + account.getSignalingKey(), + account.getSignalProtocolStore().getLocalRegistrationId(), + true, + legacyPin, + registrationLock, + unidentifiedAccessHelper.getSelfUnidentifiedAccessKey(), + unrestrictedUnidentifiedAccess, + capabilities, + discoverableByPhoneNumber); + } + + public void setRegistrationLockPin(Optional pin) throws IOException, UnauthenticatedResponseException { if (pin.isPresent()) { + final MasterKey masterKey = account.getPinMasterKey() != null + ? account.getPinMasterKey() + : KeyUtils.createMasterKey(); + + pinHelper.setRegistrationLockPin(pin.get(), masterKey); + account.setRegistrationLockPin(pin.get()); - throw new RuntimeException("Not implemented anymore, will be replaced with KBS"); + account.setPinMasterKey(masterKey); } else { - account.setRegistrationLockPin(null); + // Remove legacy registration lock accountManager.removeRegistrationLockV1(); + + // Remove KBS Pin + pinHelper.removeRegistrationLockPin(); + + account.setRegistrationLockPin(null); + account.setPinMasterKey(null); } account.save(); } diff --git a/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/src/main/java/org/asamk/signal/manager/ServiceConfig.java index 0ccd826a..55935ced 100644 --- a/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ b/src/main/java/org/asamk/signal/manager/ServiceConfig.java @@ -1,5 +1,6 @@ package org.asamk.signal.manager; +import org.bouncycastle.util.encoders.Hex; import org.signal.zkgroup.ServerPublicParams; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.Curve; @@ -13,13 +14,13 @@ import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupSe import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl; import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl; -import org.whispersystems.util.Base64; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import java.util.Base64; import java.util.List; import java.util.Map; @@ -28,7 +29,8 @@ import okhttp3.Interceptor; public class ServiceConfig { - final static String UNIDENTIFIED_SENDER_TRUST_ROOT = "BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"; + final static byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() + .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); final static int PREKEY_MINIMUM_COUNT = 20; final static int PREKEY_BATCH_SIZE = 100; final static int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024; @@ -37,6 +39,11 @@ public class ServiceConfig { final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; + final static String KEY_BACKUP_ENCLAVE_NAME = "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"; + final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( + "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"); + final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; + private final static String URL = "https://textsecure-service.whispersystems.org"; private final static String CDN_URL = "https://cdn.signal.org"; private final static String CDN2_URL = "https://cdn2.signal.org"; @@ -48,18 +55,12 @@ public class ServiceConfig { private final static Optional dns = Optional.absent(); - private final static String zkGroupServerPublicParamsHex = "AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0="; - private final static byte[] zkGroupServerPublicParams; + private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() + .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0="); static final AccountAttributes.Capabilities capabilities; static { - try { - zkGroupServerPublicParams = Base64.decode(zkGroupServerPublicParamsHex); - } catch (IOException e) { - throw new AssertionError(e); - } - boolean zkGroupAvailable; try { new ServerPublicParams(zkGroupServerPublicParams); @@ -110,8 +111,8 @@ public class ServiceConfig { static ECPublicKey getUnidentifiedSenderTrustRoot() { try { - return Curve.decodePoint(Base64.decode(UNIDENTIFIED_SENDER_TRUST_ROOT), 0); - } catch (InvalidKeyException | IOException e) { + return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0); + } catch (InvalidKeyException e) { throw new AssertionError(e); } } diff --git a/src/main/java/org/asamk/signal/manager/helper/PinHelper.java b/src/main/java/org/asamk/signal/manager/helper/PinHelper.java new file mode 100644 index 00000000..47ee6b40 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/helper/PinHelper.java @@ -0,0 +1,90 @@ +package org.asamk.signal.manager.helper; + +import org.asamk.signal.manager.util.PinHashing; +import org.whispersystems.signalservice.api.KbsPinData; +import org.whispersystems.signalservice.api.KeyBackupService; +import org.whispersystems.signalservice.api.KeyBackupServicePinException; +import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; +import org.whispersystems.signalservice.api.kbs.HashedPin; +import org.whispersystems.signalservice.api.kbs.MasterKey; +import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; +import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse; +import org.whispersystems.signalservice.internal.push.LockedException; + +import java.io.IOException; + +public class PinHelper { + + private final KeyBackupService keyBackupService; + + public PinHelper(final KeyBackupService keyBackupService) { + this.keyBackupService = keyBackupService; + } + + public void setRegistrationLockPin( + String pin, MasterKey masterKey + ) throws IOException, UnauthenticatedResponseException { + final KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession(); + final HashedPin hashedPin = PinHashing.hashPin(pin, pinChangeSession); + + pinChangeSession.setPin(hashedPin, masterKey); + pinChangeSession.enableRegistrationLock(masterKey); + } + + public void removeRegistrationLockPin() throws IOException, UnauthenticatedResponseException { + final KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession(); + pinChangeSession.removePin(); + } + + public KbsPinData getRegistrationLockData( + String pin, + LockedException e + ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { + String basicStorageCredentials = e.getBasicStorageCredentials(); + if (basicStorageCredentials == null) { + return null; + } + + return getRegistrationLockData(pin, basicStorageCredentials); + } + + private KbsPinData getRegistrationLockData( + String pin, + String basicStorageCredentials + ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { + TokenResponse tokenResponse = keyBackupService.getToken(basicStorageCredentials); + if (tokenResponse == null || tokenResponse.getTries() == 0) { + throw new IOException("KBS Account locked"); + } + + KbsPinData registrationLockData = restoreMasterKey(pin, basicStorageCredentials, tokenResponse); + if (registrationLockData == null) { + throw new AssertionError("Failed to restore master key"); + } + return registrationLockData; + } + + private KbsPinData restoreMasterKey( + String pin, String basicStorageCredentials, TokenResponse tokenResponse + ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { + if (pin == null) return null; + + if (basicStorageCredentials == null) { + throw new AssertionError("Cannot restore KBS key, no storage credentials supplied"); + } + + KeyBackupService.RestoreSession session = keyBackupService.newRegistrationSession(basicStorageCredentials, + tokenResponse); + + try { + HashedPin hashedPin = PinHashing.hashPin(pin, session); + KbsPinData kbsData = session.restorePin(hashedPin); + if (kbsData == null) { + throw new AssertionError("Null not expected"); + } + return kbsData; + } catch (UnauthenticatedResponseException e) { + throw new IOException(e); + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index c787471f..1c35b1fb 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -36,6 +36,7 @@ import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Medium; import org.whispersystems.libsignal.util.Pair; +import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.util.Base64; @@ -66,6 +67,7 @@ public class SignalAccount implements Closeable { private boolean isMultiDevice = false; private String password; private String registrationLockPin; + private MasterKey pinMasterKey; private String signalingKey; private ProfileKey profileKey; private int preKeyIdOffset; @@ -217,6 +219,10 @@ public class SignalAccount implements Closeable { password = Utils.getNotNullNode(rootNode, "password").asText(); JsonNode pinNode = rootNode.get("registrationLockPin"); registrationLockPin = pinNode == null || pinNode.isNull() ? null : pinNode.asText(); + JsonNode pinMasterKeyNode = rootNode.get("pinMasterKey"); + pinMasterKey = pinMasterKeyNode == null || pinMasterKeyNode.isNull() + ? null + : new MasterKey(Base64.decode(pinMasterKeyNode.asText())); if (rootNode.has("signalingKey")) { signalingKey = Utils.getNotNullNode(rootNode, "signalingKey").asText(); } @@ -345,6 +351,7 @@ public class SignalAccount implements Closeable { .put("isMultiDevice", isMultiDevice) .put("password", password) .put("registrationLockPin", registrationLockPin) + .put("pinMasterKey", pinMasterKey == null ? null : Base64.encodeBytes(pinMasterKey.serialize())) .put("signalingKey", signalingKey) .put("preKeyIdOffset", preKeyIdOffset) .put("nextSignedPreKeyId", nextSignedPreKeyId) @@ -456,14 +463,18 @@ public class SignalAccount implements Closeable { return registrationLockPin; } - public String getRegistrationLock() { - return null; // TODO implement KBS - } - public void setRegistrationLockPin(final String registrationLockPin) { this.registrationLockPin = registrationLockPin; } + public MasterKey getPinMasterKey() { + return pinMasterKey; + } + + public void setPinMasterKey(final MasterKey pinMasterKey) { + this.pinMasterKey = pinMasterKey; + } + public String getSignalingKey() { return signalingKey; } diff --git a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java index 2b4bc371..3f9ec08f 100644 --- a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -3,6 +3,7 @@ package org.asamk.signal.manager.util; import org.asamk.signal.util.RandomUtils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; +import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.util.Base64; public class KeyUtils { @@ -30,6 +31,10 @@ public class KeyUtils { return getSecretBytes(32); } + public static MasterKey createMasterKey() { + return MasterKey.createNew(RandomUtils.getSecureRandom()); + } + private static String getSecret(int size) { byte[] secret = getSecretBytes(size); return Base64.encodeBytes(secret); diff --git a/src/main/java/org/asamk/signal/manager/util/PinHashing.java b/src/main/java/org/asamk/signal/manager/util/PinHashing.java new file mode 100644 index 00000000..2adf8148 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/PinHashing.java @@ -0,0 +1,31 @@ +package org.asamk.signal.manager.util; + +import org.bouncycastle.crypto.generators.Argon2BytesGenerator; +import org.bouncycastle.crypto.params.Argon2Parameters; +import org.whispersystems.signalservice.api.KeyBackupService; +import org.whispersystems.signalservice.api.kbs.HashedPin; +import org.whispersystems.signalservice.internal.registrationpin.PinHasher; + +public final class PinHashing { + + private PinHashing() { + } + + public static HashedPin hashPin(String pin, KeyBackupService.HashSession hashSession) { + final Argon2Parameters params = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id).withParallelism(1) + .withIterations(32) + .withVersion(13) + .withMemoryAsKB(16 * 1024) + .withSalt(hashSession.hashSalt()) + .build(); + + final Argon2BytesGenerator generator = new Argon2BytesGenerator(); + generator.init(params); + + return PinHasher.hashPin(PinHasher.normalize(pin), password -> { + byte[] output = new byte[64]; + generator.generateBytes(password, output); + return output; + }); + } +} From f1e5fc6c0ba85ac95055b760d31d8fe0a1b27ab6 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 31 Dec 2020 16:14:30 +0100 Subject: [PATCH 006/124] Request profiles without uuid if libzkgroup is not available Fixes #397 --- .../signal/manager/helper/ProfileHelper.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index c81e2ff7..60c47d8b 100644 --- a/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -95,7 +95,17 @@ public final class ProfileHelper { ? unidentifiedPipe : messagePipeProvider.getMessagePipe(false); if (pipe != null) { - return pipe.getProfile(address, profileKey, unidentifiedAccess, requestType); + try { + return pipe.getProfile(address, profileKey, unidentifiedAccess, requestType); + } catch (NoClassDefFoundError e) { + // Native zkgroup lib not available for ProfileKey + if (!address.getNumber().isPresent()) { + throw new NotFoundException("Can't request profile without number"); + } + SignalServiceAddress addressWithoutUuid = new SignalServiceAddress(Optional.absent(), + address.getNumber()); + return pipe.getProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); + } } throw new IOException("No pipe available!"); @@ -106,9 +116,18 @@ public final class ProfileHelper { Optional profileKey, Optional unidentifiedAccess, SignalServiceProfile.RequestType requestType - ) { + ) throws NotFoundException { SignalServiceMessageReceiver receiver = messageReceiverProvider.getMessageReceiver(); - return receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType); + try { + return receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType); + } catch (NoClassDefFoundError e) { + // Native zkgroup lib not available for ProfileKey + if (!address.getNumber().isPresent()) { + throw new NotFoundException("Can't request profile without number"); + } + SignalServiceAddress addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); + return receiver.retrieveProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); + } } private Optional getUnidentifiedAccess(SignalServiceAddress recipient) { From a475bc50e92ef5b7f5b8d765d942379e877432e9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 31 Dec 2020 18:07:06 +0100 Subject: [PATCH 007/124] Bump version --- CHANGELOG.md | 9 +++++++++ build.gradle | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38907d6f..e6eb3c39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +## [0.7.2] - 2020-12-31 +### Added +- Implement new registration lock PIN with `setPin` and `removePin` (with KBS) +- Include quotes, mentions and reactions in json output (Thanks @Atomic-Bean) + +### Fixed +- Retrieve avatars for v2 groups +- Download attachment thumbnail for quoted attachments + ## [0.7.1] - 2020-12-21 ### Added - Accept group invitation with `updateGroup -g GROUP_ID` diff --git a/build.gradle b/build.gradle index 1c42834b..ca346dbd 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ targetCompatibility = JavaVersion.VERSION_11 mainClassName = 'org.asamk.signal.Main' -version = '0.7.1' +version = '0.7.2' compileJava.options.encoding = 'UTF-8' From 1c5de83370e1108271bf72836c887fdea9cb46db Mon Sep 17 00:00:00 2001 From: Atomic-Bean <75401809+Atomic-Bean@users.noreply.github.com> Date: Mon, 4 Jan 2021 06:32:34 +1030 Subject: [PATCH 008/124] Command to check if number is registered (#391) * Added the isRegistered command * Minor fixes * Corrected description * Added AsamK's suggestions Fixes #178 --- man/signal-cli.1.adoc | 9 ++ .../org/asamk/signal/commands/Commands.java | 1 + .../signal/commands/GetUserStatusCommand.java | 87 +++++++++++++++++++ .../org/asamk/signal/manager/Manager.java | 22 +++++ 4 files changed, 119 insertions(+) create mode 100644 src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index b5c22167..fa2db7c3 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -124,6 +124,15 @@ Only works, if this is the master device. Specify the device you want to remove. Use listDevices to see the deviceIds. +=== getUserStatus + +Uses a list of phone numbers to determine the statuses of those users. Shows if they are registered on the Signal Servers or not. + +[NUMBER [NUMBER ...]]:: +One or more numbers to check. +*--json*:: +Output the statuses as an array of json objects. + === send Send a message to another user or group. diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index 85e7af32..1e081dff 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -11,6 +11,7 @@ public class Commands { addCommand("addDevice", new AddDeviceCommand()); addCommand("block", new BlockCommand()); addCommand("daemon", new DaemonCommand()); + addCommand("getUserStatus", new GetUserStatusCommand()); addCommand("link", new LinkCommand()); addCommand("listContacts", new ListContactsCommand()); addCommand("listDevices", new ListDevicesCommand()); diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java new file mode 100644 index 00000000..c4bdf3d9 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -0,0 +1,87 @@ +package org.asamk.signal.commands; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import net.sourceforge.argparse4j.impl.Arguments; +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + +import org.asamk.signal.manager.Manager; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.Map; +import java.util.List; + +public class GetUserStatusCommand implements LocalCommand { + + @Override + public void attachToSubparser(final Subparser subparser) { + subparser.addArgument("number").help("Phone number").nargs("+"); + subparser.help("Check if the specified phone number/s have been registered"); + subparser.addArgument("--json") + .help("Output received messages in json format, one json object per line.") + .action(Arguments.storeTrue()); + } + + @Override + public int handleCommand(final Namespace ns, final Manager m) { + if (!m.isRegistered()) { + System.err.println("User is not registered."); + return 1; + } + + // Setup the json object mapper + ObjectMapper jsonProcessor = new ObjectMapper(); + jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // disable autodetect + jsonProcessor.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + + // Get a map of registration statuses + Map registered; + try { + registered = m.areUsersRegistered(new HashSet<>(ns.getList("number"))); + } catch (IOException e) { + System.err.println("Unable to check if users are registered"); + return 1; + } + + // Output + if (ns.getBoolean("json")) { + List objects = new ArrayList<>(); + for (Map.Entry entry : registered.entrySet()) { + objects.add(new JsonIsRegistered(entry.getKey(), entry.getValue())); + } + + try { + System.out.println(jsonProcessor.writeValueAsString(objects)); + } catch (IOException e) { + System.err.println(e.getMessage()); + } + + } else { + for (Map.Entry entry : registered.entrySet()) { + System.out.println(entry.getKey() + ": " + entry.getValue()); + } + } + + return 0; + } + + private class JsonIsRegistered { + String name; + boolean isRegistered; + + public JsonIsRegistered(String name, boolean isRegistered) { + this.name = name; + this.isRegistered = isRegistered; + } + } + +} diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 7ad7b888..e964d218 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -383,6 +383,28 @@ public class Manager implements Closeable { return account.isRegistered(); } + /** + * This is used for checking a set of phone numbers for registration on Signal + * + * @param numbers The set of phone number in question + * @return A map of numbers to booleans. True if registered, false otherwise. Should never be null + * @throws IOException if its unable to check if the users are registered + */ + public Map areUsersRegistered(Set numbers) throws IOException { + // Note "contactDetails" has no optionals. It only gives us info on users who are registered + List contactDetails = this.accountManager.getContacts(numbers); + + // Make the initial map with all numbers set to false for now + Map usersRegistered = numbers.stream().collect(Collectors.toMap(x -> x, x -> false)); + + // Override the contacts we did obtain + for (ContactTokenDetails contactDetail : contactDetails) { + usersRegistered.put(contactDetail.getNumber(), true); + } + + return usersRegistered; + } + public void register(boolean voiceVerification, String captcha) throws IOException { account.setPassword(KeyUtils.createPassword()); From 00339b1abede407251c4b4f63c2cb7fefcf9b5e2 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 3 Jan 2021 21:04:32 +0100 Subject: [PATCH 009/124] Improve user status command --- .../signal/JsonReceiveMessageHandler.java | 4 +-- .../signal/commands/GetUserStatusCommand.java | 33 ++++++++----------- .../asamk/signal/commands/ReceiveCommand.java | 2 +- .../org/asamk/signal/manager/Manager.java | 15 +++------ 4 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java index 363fc304..eb135e13 100644 --- a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java @@ -3,7 +3,6 @@ package org.asamk.signal; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -23,8 +22,7 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler public JsonReceiveMessageHandler(Manager m) { this.m = m; this.jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // disable autodetect - jsonProcessor.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); } diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index c4bdf3d9..882f4a5c 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -1,9 +1,6 @@ package org.asamk.signal.commands; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import net.sourceforge.argparse4j.impl.Arguments; @@ -13,11 +10,10 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; import java.io.IOException; -import java.util.ArrayList; import java.util.HashSet; -import java.util.Set; -import java.util.Map; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; public class GetUserStatusCommand implements LocalCommand { @@ -39,14 +35,12 @@ public class GetUserStatusCommand implements LocalCommand { // Setup the json object mapper ObjectMapper jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // disable autodetect - jsonProcessor.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); // Get a map of registration statuses Map registered; try { - registered = m.areUsersRegistered(new HashSet<>(ns.getList("number"))); + registered = m.areUsersRegistered(new HashSet<>(ns.getList("number"))); } catch (IOException e) { System.err.println("Unable to check if users are registered"); return 1; @@ -54,17 +48,17 @@ public class GetUserStatusCommand implements LocalCommand { // Output if (ns.getBoolean("json")) { - List objects = new ArrayList<>(); - for (Map.Entry entry : registered.entrySet()) { - objects.add(new JsonIsRegistered(entry.getKey(), entry.getValue())); - } + List objects = registered.entrySet() + .stream() + .map(entry -> new JsonIsRegistered(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); try { - System.out.println(jsonProcessor.writeValueAsString(objects)); + jsonProcessor.writeValue(System.out, objects); + System.out.println(); } catch (IOException e) { System.err.println(e.getMessage()); } - } else { for (Map.Entry entry : registered.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); @@ -74,14 +68,15 @@ public class GetUserStatusCommand implements LocalCommand { return 0; } - private class JsonIsRegistered { - String name; - boolean isRegistered; + private static final class JsonIsRegistered { + + public String name; + + public boolean isRegistered; public JsonIsRegistered(String name, boolean isRegistered) { this.name = name; this.isRegistered = isRegistered; } } - } diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index bc68565a..84a918fb 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -44,7 +44,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { final ObjectMapper jsonProcessor; if (ns.getBoolean("json")) { jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // disable autodetect + jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); } else { jsonProcessor = null; diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index e964d218..9a6afe70 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -394,15 +394,11 @@ public class Manager implements Closeable { // Note "contactDetails" has no optionals. It only gives us info on users who are registered List contactDetails = this.accountManager.getContacts(numbers); - // Make the initial map with all numbers set to false for now - Map usersRegistered = numbers.stream().collect(Collectors.toMap(x -> x, x -> false)); + Set registeredUsers = contactDetails.stream() + .map(ContactTokenDetails::getNumber) + .collect(Collectors.toSet()); - // Override the contacts we did obtain - for (ContactTokenDetails contactDetail : contactDetails) { - usersRegistered.put(contactDetail.getNumber(), true); - } - - return usersRegistered; + return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains)); } public void register(boolean voiceVerification, String captcha) throws IOException { @@ -524,8 +520,7 @@ public class Manager implements Closeable { } public void verifyAccount( - String verificationCode, - String pin + String verificationCode, String pin ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { verificationCode = verificationCode.replace("-", ""); account.setSignalingKey(KeyUtils.createSignalingKey()); From 88d81c7a634938ca58d003861f6272f5bf56ee9b Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 21 Dec 2020 10:25:36 +0100 Subject: [PATCH 010/124] Move IdentityKeyPair generation to KeyUtils in preparation for rust libsignal which doesn't provide the method --- src/main/java/org/asamk/signal/manager/Manager.java | 2 +- .../asamk/signal/manager/ProvisioningManager.java | 2 +- .../org/asamk/signal/manager/util/KeyUtils.java | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 9a6afe70..666c085e 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -317,7 +317,7 @@ public class Manager implements Closeable { PathConfig pathConfig = PathConfig.createDefault(settingsPath); if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { - IdentityKeyPair identityKey = KeyHelper.generateIdentityKeyPair(); + IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair(); int registrationId = KeyHelper.generateRegistrationId(false); ProfileKey profileKey = KeyUtils.createProfileKey(); diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 8b3f0eb4..4195e8af 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -52,7 +52,7 @@ public class ProvisioningManager { this.serviceConfiguration = serviceConfiguration; this.userAgent = userAgent; - identityKey = KeyHelper.generateIdentityKeyPair(); + identityKey = KeyUtils.generateIdentityKeyPair(); registrationId = KeyHelper.generateRegistrationId(false); password = KeyUtils.createPassword(); final SleepTimer timer = new UptimeSleepTimer(); diff --git a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java index 3f9ec08f..d8861b1b 100644 --- a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -3,6 +3,11 @@ package org.asamk.signal.manager.util; import org.asamk.signal.util.RandomUtils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; +import org.whispersystems.libsignal.IdentityKey; +import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; +import org.whispersystems.libsignal.ecc.ECPrivateKey; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.util.Base64; @@ -11,6 +16,14 @@ public class KeyUtils { private KeyUtils() { } + public static IdentityKeyPair generateIdentityKeyPair() { + ECKeyPair djbKeyPair = Curve.generateKeyPair(); + IdentityKey djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey()); + ECPrivateKey djbPrivateKey = djbKeyPair.getPrivateKey(); + + return new IdentityKeyPair(djbIdentityKey, djbPrivateKey); + } + public static String createSignalingKey() { return getSecret(52); } From 0c7da68d985a3703a6843338d55ef1723f6447e5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 10 Jan 2021 15:26:45 +0100 Subject: [PATCH 011/124] Download group info if it's missing in the cache --- .../org/asamk/signal/manager/Manager.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 666c085e..576dbb5a 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -793,7 +793,7 @@ public class Manager implements Closeable { } private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { - GroupInfo g = account.getGroupStore().getGroup(groupId); + GroupInfo g = getGroup(groupId); if (g == null) { throw new GroupNotFoundException(groupId); } @@ -804,7 +804,7 @@ public class Manager implements Closeable { } private GroupInfo getGroupForUpdating(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { - GroupInfo g = account.getGroupStore().getGroup(groupId); + GroupInfo g = getGroup(groupId); if (g == null) { throw new GroupNotFoundException(groupId); } @@ -1236,7 +1236,7 @@ public class Manager implements Closeable { * Change the expiration timer for a group */ public void setExpirationTimer(GroupId groupId, int messageExpirationTimer) { - GroupInfo g = account.getGroupStore().getGroup(groupId); + GroupInfo g = getGroup(groupId); if (g instanceof GroupInfoV1) { GroupInfoV1 groupInfoV1 = (GroupInfoV1) g; groupInfoV1.messageExpirationTime = messageExpirationTimer; @@ -1649,7 +1649,7 @@ public class Manager implements Closeable { if (message.getGroupContext().get().getGroupV1().isPresent()) { SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get(); GroupIdV1 groupId = GroupId.v1(groupInfo.getGroupId()); - GroupInfo group = account.getGroupStore().getGroup(groupId); + GroupInfo group = getGroup(groupId); if (group == null || group instanceof GroupInfoV1) { GroupInfoV1 groupV1 = (GroupInfoV1) group; switch (groupInfo.getType()) { @@ -1820,7 +1820,7 @@ public class Manager implements Closeable { final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); GroupIdV2 groupId = GroupUtils.getGroupIdV2(groupSecretParams); - GroupInfo groupInfo = account.getGroupStore().getGroup(groupId); + GroupInfo groupInfo = getGroup(groupId); final GroupInfoV2 groupInfoV2; if (groupInfo instanceof GroupInfoV1) { // Received a v2 group message for a v1 group, we need to locally migrate the group @@ -2078,7 +2078,7 @@ public class Manager implements Closeable { } } GroupId groupId = GroupUtils.getGroupId(message.getGroupContext().get()); - GroupInfo group = account.getGroupStore().getGroup(groupId); + GroupInfo group = getGroup(groupId); if (group != null && group.isBlocked()) { return true; } @@ -2461,7 +2461,7 @@ public class Manager implements Closeable { try { try (OutputStream fos = new FileOutputStream(groupsFile)) { DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(fos); - for (GroupInfo record : account.getGroupStore().getGroups()) { + for (GroupInfo record : getGroups()) { if (record instanceof GroupInfoV1) { GroupInfoV1 groupInfo = (GroupInfoV1) record; out.write(new DeviceGroup(groupInfo.getGroupId().serialize(), @@ -2570,7 +2570,7 @@ public class Manager implements Closeable { } } List groupIds = new ArrayList<>(); - for (GroupInfo record : account.getGroupStore().getGroups()) { + for (GroupInfo record : getGroups()) { if (record.isBlocked()) { groupIds.add(record.getGroupId().serialize()); } @@ -2597,7 +2597,13 @@ public class Manager implements Closeable { } public GroupInfo getGroup(GroupId groupId) { - return account.getGroupStore().getGroup(groupId); + final GroupInfo group = account.getGroupStore().getGroup(groupId); + if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) { + final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(((GroupInfoV2) group).getMasterKey()); + ((GroupInfoV2) group).setGroup(groupHelper.getDecryptedGroup(groupSecretParams)); + account.getGroupStore().updateGroup(group); + } + return group; } public List getIdentities() { From 9244d1e8a861ce505083e813cf069a96a6622822 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 10 Jan 2021 17:07:26 +0100 Subject: [PATCH 012/124] Disable registration lock before removing pin --- CHANGELOG.md | 3 +++ .../java/org/asamk/signal/manager/helper/PinHelper.java | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6eb3c39..df9aa47f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Fixed +- Disable registration lock before removing the PIN + ## [0.7.2] - 2020-12-31 ### Added - Implement new registration lock PIN with `setPin` and `removePin` (with KBS) diff --git a/src/main/java/org/asamk/signal/manager/helper/PinHelper.java b/src/main/java/org/asamk/signal/manager/helper/PinHelper.java index 47ee6b40..b4fa04c4 100644 --- a/src/main/java/org/asamk/signal/manager/helper/PinHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/PinHelper.java @@ -33,12 +33,12 @@ public class PinHelper { public void removeRegistrationLockPin() throws IOException, UnauthenticatedResponseException { final KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession(); + pinChangeSession.disableRegistrationLock(); pinChangeSession.removePin(); } public KbsPinData getRegistrationLockData( - String pin, - LockedException e + String pin, LockedException e ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { String basicStorageCredentials = e.getBasicStorageCredentials(); if (basicStorageCredentials == null) { @@ -49,8 +49,7 @@ public class PinHelper { } private KbsPinData getRegistrationLockData( - String pin, - String basicStorageCredentials + String pin, String basicStorageCredentials ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { TokenResponse tokenResponse = keyBackupService.getToken(basicStorageCredentials); if (tokenResponse == null || tokenResponse.getTries() == 0) { From 51db5495c0a2a1b0c67637f5c77846abb9158c1e Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 10 Jan 2021 18:14:33 +0100 Subject: [PATCH 013/124] Fix pin hash version to match android --- CHANGELOG.md | 2 ++ src/main/java/org/asamk/signal/manager/util/PinHashing.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df9aa47f..1af850b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixed - Disable registration lock before removing the PIN +- Fix PIN hash version to match the official clients. + If you had previously set a PIN you need to set it again to be able to unlock the registration lock later. ## [0.7.2] - 2020-12-31 ### Added diff --git a/src/main/java/org/asamk/signal/manager/util/PinHashing.java b/src/main/java/org/asamk/signal/manager/util/PinHashing.java index 2adf8148..2fd2d802 100644 --- a/src/main/java/org/asamk/signal/manager/util/PinHashing.java +++ b/src/main/java/org/asamk/signal/manager/util/PinHashing.java @@ -14,7 +14,7 @@ public final class PinHashing { public static HashedPin hashPin(String pin, KeyBackupService.HashSession hashSession) { final Argon2Parameters params = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id).withParallelism(1) .withIterations(32) - .withVersion(13) + .withVersion(Argon2Parameters.ARGON2_VERSION_13) .withMemoryAsKB(16 * 1024) .withSalt(hashSession.hashSalt()) .build(); From 263fdceb94d8de0e355254446b26f9aff3e4c40a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 10 Jan 2021 22:41:39 +0100 Subject: [PATCH 014/124] Fix expectedV2Id serialization --- .../org/asamk/signal/manager/storage/groups/GroupInfoV1.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java index 39591647..d1230b27 100644 --- a/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java @@ -101,7 +101,7 @@ public class GroupInfoV1 extends GroupInfo { @JsonProperty("expectedV2Id") private byte[] getExpectedV2IdJackson() { - return expectedV2Id.serialize(); + return getExpectedV2Id().serialize(); } @Override From 6c8a1ff3d30b7973b9459ffa4da4c7345d14defd Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 10 Jan 2021 22:31:18 +0100 Subject: [PATCH 015/124] Check for null query when decoding device link uri --- src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java b/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java index 5b9fbe28..779642b6 100644 --- a/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java +++ b/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java @@ -21,7 +21,12 @@ public class DeviceLinkInfo { final ECPublicKey deviceKey; public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws IOException, InvalidKeyException { - Map query = getQueryMap(linkUri.getRawQuery()); + final String rawQuery = linkUri.getRawQuery(); + if (isEmpty(rawQuery)) { + throw new RuntimeException("Invalid device link uri"); + } + + Map query = getQueryMap(rawQuery); String deviceIdentifier = query.get("uuid"); String publicKeyEncoded = query.get("pub_key"); From e74be0c345321888c1fbfa05616cb90cf3f07ffb Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 10 Jan 2021 17:07:06 +0100 Subject: [PATCH 016/124] Refactor register and verify --- src/main/java/org/asamk/signal/Main.java | 126 +++++---- .../signal/commands/AddDeviceCommand.java | 4 - .../asamk/signal/commands/BlockCommand.java | 5 - .../asamk/signal/commands/DaemonCommand.java | 4 - .../signal/commands/GetUserStatusCommand.java | 5 - .../signal/commands/JoinGroupCommand.java | 5 - .../signal/commands/ListContactsCommand.java | 4 - .../signal/commands/ListDevicesCommand.java | 4 - .../signal/commands/ListGroupsCommand.java | 5 - .../commands/ListIdentitiesCommand.java | 4 - .../signal/commands/QuitGroupCommand.java | 5 - .../asamk/signal/commands/ReceiveCommand.java | 4 - .../signal/commands/RegisterCommand.java | 6 +- .../signal/commands/RegistrationCommand.java | 10 + .../signal/commands/RemoveDeviceCommand.java | 4 - .../signal/commands/RemovePinCommand.java | 4 - .../signal/commands/SendContactsCommand.java | 4 - .../signal/commands/SendReactionCommand.java | 5 - .../asamk/signal/commands/SetPinCommand.java | 4 - .../asamk/signal/commands/TrustCommand.java | 4 - .../asamk/signal/commands/UnblockCommand.java | 5 - .../signal/commands/UnregisterCommand.java | 4 - .../signal/commands/UpdateAccountCommand.java | 4 - .../signal/commands/UpdateContactCommand.java | 5 - .../signal/commands/UpdateProfileCommand.java | 5 - .../asamk/signal/commands/VerifyCommand.java | 10 +- .../org/asamk/signal/manager/Manager.java | 250 ++++-------------- .../manager/NotRegisteredException.java | 8 + .../signal/manager/ProvisioningManager.java | 4 +- .../signal/manager/RegistrationManager.java | 194 ++++++++++++++ .../asamk/signal/manager/ServiceConfig.java | 12 + .../helper/UnidentifiedAccessHelper.java | 2 +- .../signal/manager/storage/SignalAccount.java | 48 ++++ 33 files changed, 405 insertions(+), 362 deletions(-) create mode 100644 src/main/java/org/asamk/signal/commands/RegistrationCommand.java create mode 100644 src/main/java/org/asamk/signal/manager/NotRegisteredException.java create mode 100644 src/main/java/org/asamk/signal/manager/RegistrationManager.java diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 6204778d..b9209631 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -32,9 +32,12 @@ import org.asamk.signal.commands.DbusCommand; import org.asamk.signal.commands.ExtendedDbusCommand; import org.asamk.signal.commands.LocalCommand; import org.asamk.signal.commands.ProvisioningCommand; +import org.asamk.signal.commands.RegistrationCommand; import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.ProvisioningManager; +import org.asamk.signal.manager.RegistrationManager; import org.asamk.signal.manager.ServiceConfig; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.SecurityProvider; @@ -43,7 +46,6 @@ import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; @@ -75,8 +77,14 @@ public class Main { } public static int init(Namespace ns) { + Command command = getCommand(ns); + if (command == null) { + logger.error("Command not implemented!"); + return 3; + } + if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) { - return initDbusClient(ns, ns.getBoolean("dbus_system")); + return initDbusClient(command, ns, ns.getBoolean("dbus_system")); } final String username = ns.getString("username"); @@ -99,12 +107,31 @@ public class Main { if (username == null) { ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT); - return handleCommands(ns, pm); + return handleCommand(command, ns, pm); + } + + if (command instanceof RegistrationCommand) { + final RegistrationManager manager; + try { + manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + } catch (Throwable e) { + logger.error("Error loading or creating state file: {}", e.getMessage()); + return 2; + } + try (RegistrationManager m = manager) { + return handleCommand(command, ns, m); + } catch (Exception e) { + logger.error("Cleanup failed", e); + return 3; + } } Manager manager; try { manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + } catch (NotRegisteredException e) { + System.err.println("User is not registered."); + return 1; } catch (Throwable e) { logger.error("Error loading state file: {}", e.getMessage()); return 2; @@ -113,25 +140,19 @@ public class Main { try (Manager m = manager) { try { m.checkAccountState(); - } catch (AuthorizationFailedException e) { - if (!"register".equals(ns.getString("command"))) { - // Register command should still be possible, if current authorization fails - System.err.println("Authorization failed, was the number registered elsewhere?"); - return 2; - } } catch (IOException e) { logger.error("Error while checking account: {}", e.getMessage()); return 2; } - return handleCommands(ns, m); + return handleCommand(command, ns, m); } catch (IOException e) { logger.error("Cleanup failed", e); return 3; } } - private static int initDbusClient(final Namespace ns, final boolean systemBus) { + private static int initDbusClient(final Command command, final Namespace ns, final boolean systemBus) { try { DBusConnection.DBusBusType busType; if (systemBus) { @@ -144,7 +165,7 @@ public class Main { DbusConfig.SIGNAL_OBJECTPATH, Signal.class); - return handleCommands(ns, ts, dBusConn); + return handleCommand(command, ns, ts, dBusConn); } } catch (DBusException | IOException e) { logger.error("Dbus client failed", e); @@ -152,56 +173,51 @@ public class Main { } } - private static int handleCommands(Namespace ns, Signal ts, DBusConnection dBusConn) { + private static Command getCommand(Namespace ns) { String commandKey = ns.getString("command"); final Map commands = Commands.getCommands(); - if (commands.containsKey(commandKey)) { - Command command = commands.get(commandKey); - - if (command instanceof ExtendedDbusCommand) { - return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn); - } else if (command instanceof DbusCommand) { - return ((DbusCommand) command).handleCommand(ns, ts); - } else { - System.err.println(commandKey + " is not yet implemented via dbus"); - return 1; - } + if (!commands.containsKey(commandKey)) { + return null; } - return 0; + return commands.get(commandKey); } - private static int handleCommands(Namespace ns, ProvisioningManager pm) { - String commandKey = ns.getString("command"); - final Map commands = Commands.getCommands(); - if (commands.containsKey(commandKey)) { - Command command = commands.get(commandKey); - - if (command instanceof ProvisioningCommand) { - return ((ProvisioningCommand) command).handleCommand(ns, pm); - } else { - System.err.println(commandKey + " only works with a username"); - return 1; - } - } - return 0; - } - - private static int handleCommands(Namespace ns, Manager m) { - String commandKey = ns.getString("command"); - final Map commands = Commands.getCommands(); - if (commands.containsKey(commandKey)) { - Command command = commands.get(commandKey); - - if (command instanceof LocalCommand) { - return ((LocalCommand) command).handleCommand(ns, m); - } else if (command instanceof DbusCommand) { - return ((DbusCommand) command).handleCommand(ns, new DbusSignalImpl(m)); - } else if (command instanceof ExtendedDbusCommand) { - System.err.println(commandKey + " only works via dbus"); - } + private static int handleCommand(Command command, Namespace ns, Signal ts, DBusConnection dBusConn) { + if (command instanceof ExtendedDbusCommand) { + return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn); + } else if (command instanceof DbusCommand) { + return ((DbusCommand) command).handleCommand(ns, ts); + } else { + System.err.println("Command is not yet implemented via dbus"); + return 1; + } + } + + private static int handleCommand(Command command, Namespace ns, ProvisioningManager pm) { + if (command instanceof ProvisioningCommand) { + return ((ProvisioningCommand) command).handleCommand(ns, pm); + } else { + System.err.println("Command only works with a username"); + return 1; + } + } + + private static int handleCommand(Command command, Namespace ns, RegistrationManager m) { + if (command instanceof RegistrationCommand) { + return ((RegistrationCommand) command).handleCommand(ns, m); + } + return 1; + } + + private static int handleCommand(Command command, Namespace ns, Manager m) { + if (command instanceof LocalCommand) { + return ((LocalCommand) command).handleCommand(ns, m); + } else if (command instanceof DbusCommand) { + return ((DbusCommand) command).handleCommand(ns, new DbusSignalImpl(m)); + } else { + System.err.println("Command only works via dbus"); return 1; } - return 0; } /** diff --git a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java index dab886d7..c5d18ab1 100644 --- a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java @@ -23,10 +23,6 @@ public class AddDeviceCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.addDeviceLink(new URI(ns.getString("uri"))); return 0; diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index 627be5a9..60009cfb 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -21,11 +21,6 @@ public class BlockCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - for (String contact_number : ns.getList("contact")) { try { m.setContactBlocked(contact_number, true); diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 3caaaa37..c5ee2edc 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -35,10 +35,6 @@ public class DaemonCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } DBusConnection conn = null; try { try { diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 882f4a5c..0a1ddc4c 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -28,11 +28,6 @@ public class GetUserStatusCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - // Setup the json object mapper ObjectMapper jsonProcessor = new ObjectMapper(); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index 305bf55b..c5975b0c 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -29,11 +29,6 @@ public class JoinGroupCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - final GroupInviteLinkUrl linkUrl; String uri = ns.getString("uri"); try { diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 2c98ec6b..1a14e8df 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -16,10 +16,6 @@ public class ListContactsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } List contacts = m.getContacts(); for (ContactInfo c : contacts) { System.out.println(String.format("Number: %s Name: %s Blocked: %b", c.number, c.name, c.blocked)); diff --git a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java index 4b9dac5c..a03b078f 100644 --- a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java @@ -18,10 +18,6 @@ public class ListDevicesCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { List devices = m.getLinkedDevices(); for (DeviceInfo d : devices) { diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index b4be4ad0..97af502e 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -64,11 +64,6 @@ public class ListGroupsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - List groups = m.getGroups(); boolean detailed = ns.getBoolean("detailed"); diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index 3f422cbd..4caeca29 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -30,10 +30,6 @@ public class ListIdentitiesCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } if (ns.get("number") == null) { for (IdentityInfo identity : m.getIdentities()) { printIdentityFingerprint(m, identity); diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index c1f6bfbf..f258ae24 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -31,11 +31,6 @@ public class QuitGroupCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - try { final GroupId groupId = Util.decodeGroupId(ns.getString("group")); final Pair> results = m.sendQuitGroupMessage(groupId); diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 84a918fb..7dc9dcaf 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -146,10 +146,6 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } double timeout = 5; if (ns.getDouble("timeout") != null) { timeout = ns.getDouble("timeout"); diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index f69e0844..da652f7d 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -4,12 +4,12 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; -import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.RegistrationManager; import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException; import java.io.IOException; -public class RegisterCommand implements LocalCommand { +public class RegisterCommand implements RegistrationCommand { @Override public void attachToSubparser(final Subparser subparser) { @@ -21,7 +21,7 @@ public class RegisterCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public int handleCommand(final Namespace ns, final RegistrationManager m) { try { final boolean voiceVerification = ns.getBoolean("voice"); final String captcha = ns.getString("captcha"); diff --git a/src/main/java/org/asamk/signal/commands/RegistrationCommand.java b/src/main/java/org/asamk/signal/commands/RegistrationCommand.java new file mode 100644 index 00000000..8683570f --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/RegistrationCommand.java @@ -0,0 +1,10 @@ +package org.asamk.signal.commands; + +import net.sourceforge.argparse4j.inf.Namespace; + +import org.asamk.signal.manager.RegistrationManager; + +public interface RegistrationCommand extends Command { + + int handleCommand(Namespace ns, RegistrationManager m); +} diff --git a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java index 1e2343e7..78d14bbd 100644 --- a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java @@ -19,10 +19,6 @@ public class RemoveDeviceCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { int deviceId = ns.getInt("deviceId"); m.removeLinkedDevices(deviceId); diff --git a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java index 95531249..ada9c446 100644 --- a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java @@ -17,10 +17,6 @@ public class RemovePinCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.setRegistrationLockPin(Optional.absent()); return 0; diff --git a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java index 20e81a60..aaca283a 100644 --- a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java @@ -17,10 +17,6 @@ public class SendContactsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.sendContacts(); return 0; diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index c680bfd7..2a9afa74 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -47,11 +47,6 @@ public class SendReactionCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - if ((ns.getList("recipient") == null || ns.getList("recipient").size() == 0) && ns.getString("group") == null) { System.err.println("No recipients given"); System.err.println("Aborting sending."); diff --git a/src/main/java/org/asamk/signal/commands/SetPinCommand.java b/src/main/java/org/asamk/signal/commands/SetPinCommand.java index c68ea3a9..ac601b3b 100644 --- a/src/main/java/org/asamk/signal/commands/SetPinCommand.java +++ b/src/main/java/org/asamk/signal/commands/SetPinCommand.java @@ -19,10 +19,6 @@ public class SetPinCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { String registrationLockPin = ns.getString("registrationLockPin"); m.setRegistrationLockPin(Optional.of(registrationLockPin)); diff --git a/src/main/java/org/asamk/signal/commands/TrustCommand.java b/src/main/java/org/asamk/signal/commands/TrustCommand.java index 076a86db..58c7371f 100644 --- a/src/main/java/org/asamk/signal/commands/TrustCommand.java +++ b/src/main/java/org/asamk/signal/commands/TrustCommand.java @@ -27,10 +27,6 @@ public class TrustCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } String number = ns.getString("number"); if (ns.getBoolean("trust_all_known_keys")) { boolean res = m.trustIdentityAllKeys(number); diff --git a/src/main/java/org/asamk/signal/commands/UnblockCommand.java b/src/main/java/org/asamk/signal/commands/UnblockCommand.java index 14ea2996..d191ef22 100644 --- a/src/main/java/org/asamk/signal/commands/UnblockCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnblockCommand.java @@ -21,11 +21,6 @@ public class UnblockCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - for (String contact_number : ns.getList("contact")) { try { m.setContactBlocked(contact_number, false); diff --git a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java index 7a7616bd..079070e6 100644 --- a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java @@ -16,10 +16,6 @@ public class UnregisterCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.unregister(); return 0; diff --git a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java index 79459fe6..8211e190 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java @@ -16,10 +16,6 @@ public class UpdateAccountCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } try { m.updateAccountAttributes(); return 0; diff --git a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java index da090209..c4da94a2 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java @@ -23,11 +23,6 @@ public class UpdateContactCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - String number = ns.getString("number"); String name = ns.getString("name"); diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index 1e332fb4..968a8733 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -25,11 +25,6 @@ public class UpdateProfileCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (!m.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - String name = ns.getString("name"); String avatarPath = ns.getString("avatar"); boolean removeAvatar = ns.getBoolean("remove_avatar"); diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index d4b0a7cb..7fa10b6a 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -3,14 +3,14 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; -import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.RegistrationManager; import org.whispersystems.signalservice.api.KeyBackupServicePinException; import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.internal.push.LockedException; import java.io.IOException; -public class VerifyCommand implements LocalCommand { +public class VerifyCommand implements RegistrationCommand { @Override public void attachToSubparser(final Subparser subparser) { @@ -19,11 +19,7 @@ public class VerifyCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { - if (m.isRegistered()) { - System.err.println("User registration is already verified"); - return 1; - } + public int handleCommand(final Namespace ns, final RegistrationManager m) { try { String verificationCode = ns.getString("verificationCode"); String pin = ns.getString("pin"); diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 576dbb5a..9d090a58 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -79,14 +79,10 @@ import org.whispersystems.libsignal.ecc.ECKeyPair; import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.util.KeyHelper; import org.whispersystems.libsignal.util.Medium; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.KbsPinData; import org.whispersystems.signalservice.api.KeyBackupService; -import org.whispersystems.signalservice.api.KeyBackupServicePinException; -import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessagePipe; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; @@ -144,10 +140,8 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConf import org.whispersystems.signalservice.internal.contacts.crypto.Quote; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; -import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException; -import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.Hex; import org.whispersystems.util.Base64; @@ -167,7 +161,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.security.KeyStore; import java.security.SignatureException; import java.util.ArrayList; import java.util.Arrays; @@ -176,7 +169,6 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -202,24 +194,21 @@ public class Manager implements Closeable { private final SignalServiceConfiguration serviceConfiguration; private final String userAgent; - // TODO make configurable - private final boolean discoverableByPhoneNumber = true; - private final boolean unrestrictedUnidentifiedAccess = false; - - private final SignalAccount account; + private SignalAccount account; private final PathConfig pathConfig; - private SignalServiceAccountManager accountManager; - private GroupsV2Api groupsV2Api; + private final SignalServiceAccountManager accountManager; + private final GroupsV2Api groupsV2Api; private final GroupsV2Operations groupsV2Operations; + private final SignalServiceMessageReceiver messageReceiver; + private final ClientZkProfileOperations clientZkProfileOperations; - private SignalServiceMessageReceiver messageReceiver = null; private SignalServiceMessagePipe messagePipe = null; private SignalServiceMessagePipe unidentifiedMessagePipe = null; private final UnidentifiedAccessHelper unidentifiedAccessHelper; private final ProfileHelper profileHelper; private final GroupHelper groupHelper; - private PinHelper pinHelper; + private final PinHelper pinHelper; Manager( SignalAccount account, @@ -233,7 +222,30 @@ public class Manager implements Closeable { this.userAgent = userAgent; this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( serviceConfiguration)) : null; - createSignalServiceAccountManager(); + this.accountManager = new SignalServiceAccountManager(serviceConfiguration, + new DynamicCredentialsProvider(account.getUuid(), + account.getUsername(), + account.getPassword(), + account.getSignalingKey(), + account.getDeviceId()), + userAgent, + groupsV2Operations, + timer); + this.groupsV2Api = accountManager.getGroupsV2Api(); + final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); + this.pinHelper = new PinHelper(keyBackupService); + this.clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(serviceConfiguration) + .getProfileOperations() : null; + this.messageReceiver = new SignalServiceMessageReceiver(serviceConfiguration, + account.getUuid(), + account.getUsername(), + account.getPassword(), + account.getDeviceId(), + account.getSignalingKey(), + userAgent, + null, + timer, + clientZkProfileOperations); this.account.setResolver(this::resolveSignalServiceAddress); @@ -244,7 +256,7 @@ public class Manager implements Closeable { this.profileHelper = new ProfileHelper(account.getProfileStore()::getProfileKey, unidentifiedAccessHelper::getAccessFor, unidentified -> unidentified ? getOrCreateUnidentifiedMessagePipe() : getOrCreateMessagePipe(), - this::getOrCreateMessageReceiver); + () -> messageReceiver); this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential, this::getRecipientProfile, account::getSelfAddress, @@ -261,30 +273,6 @@ public class Manager implements Closeable { return account.getSelfAddress(); } - private void createSignalServiceAccountManager() { - this.accountManager = new SignalServiceAccountManager(serviceConfiguration, - new DynamicCredentialsProvider(account.getUuid(), - account.getUsername(), - account.getPassword(), - null, - account.getDeviceId()), - userAgent, - groupsV2Operations, - timer); - this.groupsV2Api = accountManager.getGroupsV2Api(); - this.pinHelper = new PinHelper(createKeyBackupService()); - } - - private KeyBackupService createKeyBackupService() { - KeyStore keyStore = ServiceConfig.getIasKeyStore(); - - return accountManager.getKeyBackupService(keyStore, - ServiceConfig.KEY_BACKUP_ENCLAVE_NAME, - ServiceConfig.KEY_BACKUP_SERVICE_ID, - ServiceConfig.KEY_BACKUP_MRENCLAVE, - 10); - } - private IdentityKeyPair getIdentityKeyPair() { return account.getSignalProtocolStore().getIdentityKeyPair(); } @@ -313,56 +301,20 @@ public class Manager implements Closeable { public static Manager init( String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent - ) throws IOException { + ) throws IOException, NotRegisteredException { PathConfig pathConfig = PathConfig.createDefault(settingsPath); if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { - IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair(); - int registrationId = KeyHelper.generateRegistrationId(false); - - ProfileKey profileKey = KeyUtils.createProfileKey(); - SignalAccount account = SignalAccount.create(pathConfig.getDataPath(), - username, - identityKey, - registrationId, - profileKey); - account.save(); - - return new Manager(account, pathConfig, serviceConfiguration, userAgent); + throw new NotRegisteredException(); } SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username); - Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent); - - m.migrateLegacyConfigs(); - - return m; - } - - private void migrateLegacyConfigs() { - if (account.getProfileKey() == null && isRegistered()) { - // Old config file, creating new profile key - account.setProfileKey(KeyUtils.createProfileKey()); - account.save(); + if (!account.isRegistered()) { + throw new NotRegisteredException(); } - // Store profile keys only in profile store - for (ContactInfo contact : account.getContactStore().getContacts()) { - String profileKeyString = contact.profileKey; - if (profileKeyString == null) { - continue; - } - final ProfileKey profileKey; - try { - profileKey = new ProfileKey(Base64.decode(profileKeyString)); - } catch (InvalidInputException | IOException e) { - continue; - } - contact.profileKey = null; - account.getProfileStore().storeProfileKey(contact.getAddress(), profileKey); - } - // Ensure our profile key is stored in profile store - account.getProfileStore().storeProfileKey(getSelfAddress(), account.getProfileKey()); + + return new Manager(account, pathConfig, serviceConfiguration, userAgent); } public void checkAccountState() throws IOException { @@ -401,25 +353,6 @@ public class Manager implements Closeable { return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains)); } - public void register(boolean voiceVerification, String captcha) throws IOException { - account.setPassword(KeyUtils.createPassword()); - - // Resetting UUID, because registering doesn't work otherwise - account.setUuid(null); - createSignalServiceAccountManager(); - - if (voiceVerification) { - accountManager.requestVoiceVerificationCode(Locale.getDefault(), - Optional.fromNullable(captcha), - Optional.absent()); - } else { - accountManager.requestSmsVerificationCode(false, Optional.fromNullable(captcha), Optional.absent()); - } - - account.setRegistered(false); - account.save(); - } - public void updateAccountAttributes() throws IOException { accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), @@ -427,10 +360,10 @@ public class Manager implements Closeable { // set legacy pin only if no KBS master key is set account.getPinMasterKey() == null ? account.getRegistrationLockPin() : null, account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(), - unidentifiedAccessHelper.getSelfUnidentifiedAccessKey(), - unrestrictedUnidentifiedAccess, + account.getSelfUnidentifiedAccessKey(), + account.isUnrestrictedUnidentifiedAccess(), capabilities, - discoverableByPhoneNumber); + account.isDiscoverableByPhoneNumber()); } public void setProfile(String name, File avatar) throws IOException { @@ -519,63 +452,6 @@ public class Manager implements Closeable { } } - public void verifyAccount( - String verificationCode, String pin - ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { - verificationCode = verificationCode.replace("-", ""); - account.setSignalingKey(KeyUtils.createSignalingKey()); - VerifyAccountResponse response; - try { - response = verifyAccountWithCode(verificationCode, pin, null); - } catch (LockedException e) { - if (pin == null) { - throw e; - } - - KbsPinData registrationLockData = pinHelper.getRegistrationLockData(pin, e); - if (registrationLockData == null) { - throw e; - } - - String registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock(); - try { - response = verifyAccountWithCode(verificationCode, null, registrationLock); - } catch (LockedException _e) { - throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!"); - } - account.setPinMasterKey(registrationLockData.getMasterKey()); - } - - // TODO response.isStorageCapable() - //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); - - account.setRegistered(true); - account.setUuid(UuidUtil.parseOrNull(response.getUuid())); - account.setRegistrationLockPin(pin); - account.getSignalProtocolStore() - .saveIdentity(account.getSelfAddress(), - getIdentityKeyPair().getPublicKey(), - TrustLevel.TRUSTED_VERIFIED); - - refreshPreKeys(); - account.save(); - } - - private VerifyAccountResponse verifyAccountWithCode( - final String verificationCode, final String legacyPin, final String registrationLock - ) throws IOException { - return accountManager.verifyAccountWithCode(verificationCode, - account.getSignalingKey(), - account.getSignalProtocolStore().getLocalRegistrationId(), - true, - legacyPin, - registrationLock, - unidentifiedAccessHelper.getSelfUnidentifiedAccessKey(), - unrestrictedUnidentifiedAccess, - capabilities, - discoverableByPhoneNumber); - } - public void setRegistrationLockPin(Optional pin) throws IOException, UnauthenticatedResponseException { if (pin.isPresent()) { final MasterKey masterKey = account.getPinMasterKey() != null @@ -607,45 +483,21 @@ public class Manager implements Closeable { accountManager.setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); } - private SignalServiceMessageReceiver createMessageReceiver() { - final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create( - serviceConfiguration).getProfileOperations() : null; - return new SignalServiceMessageReceiver(serviceConfiguration, - account.getUuid(), - account.getUsername(), - account.getPassword(), - account.getDeviceId(), - account.getSignalingKey(), - userAgent, - null, - timer, - clientZkProfileOperations); - } - - private SignalServiceMessageReceiver getOrCreateMessageReceiver() { - if (messageReceiver == null) { - messageReceiver = createMessageReceiver(); - } - return messageReceiver; - } - private SignalServiceMessagePipe getOrCreateMessagePipe() { if (messagePipe == null) { - messagePipe = getOrCreateMessageReceiver().createMessagePipe(); + messagePipe = messageReceiver.createMessagePipe(); } return messagePipe; } private SignalServiceMessagePipe getOrCreateUnidentifiedMessagePipe() { if (unidentifiedMessagePipe == null) { - unidentifiedMessagePipe = getOrCreateMessageReceiver().createUnidentifiedMessagePipe(); + unidentifiedMessagePipe = messageReceiver.createUnidentifiedMessagePipe(); } return unidentifiedMessagePipe; } private SignalServiceMessageSender createMessageSender() { - final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create( - serviceConfiguration).getProfileOperations() : null; final ExecutorService executor = null; return new SignalServiceMessageSender(serviceConfiguration, account.getUuid(), @@ -2349,13 +2201,12 @@ public class Manager implements Closeable { GroupId groupId, GroupSecretParams groupSecretParams, String cdnKey ) throws IOException { IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - SignalServiceMessageReceiver receiver = getOrCreateMessageReceiver(); File outputFile = getGroupAvatarFile(groupId); GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); File tmpFile = IOUtils.createTempFile(); tmpFile.deleteOnExit(); - try (InputStream input = receiver.retrieveGroupsV2ProfileAvatar(cdnKey, + try (InputStream input = messageReceiver.retrieveGroupsV2ProfileAvatar(cdnKey, tmpFile, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { byte[] encryptedData = IOUtils.readFully(input); @@ -2384,11 +2235,10 @@ public class Manager implements Closeable { SignalServiceAddress address, String avatarPath, ProfileKey profileKey ) throws IOException { IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - SignalServiceMessageReceiver receiver = getOrCreateMessageReceiver(); File outputFile = getProfileAvatarFile(address); File tmpFile = IOUtils.createTempFile(); - try (InputStream input = receiver.retrieveProfileAvatar(avatarPath, + try (InputStream input = messageReceiver.retrieveProfileAvatar(avatarPath, tmpFile, profileKey, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { @@ -2429,8 +2279,6 @@ public class Manager implements Closeable { } } - final SignalServiceMessageReceiver messageReceiver = getOrCreateMessageReceiver(); - File tmpFile = IOUtils.createTempFile(); try (InputStream input = messageReceiver.retrieveAttachment(pointer, tmpFile, @@ -2451,7 +2299,6 @@ public class Manager implements Closeable { private InputStream retrieveAttachmentAsStream( SignalServiceAttachmentPointer pointer, File tmpFile ) throws IOException, InvalidMessageException, MissingConfigurationException { - final SignalServiceMessageReceiver messageReceiver = getOrCreateMessageReceiver(); return messageReceiver.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE); } @@ -2737,6 +2584,10 @@ public class Manager implements Closeable { @Override public void close() throws IOException { + close(true); + } + + void close(boolean closeAccount) throws IOException { if (messagePipe != null) { messagePipe.shutdown(); messagePipe = null; @@ -2747,7 +2598,10 @@ public class Manager implements Closeable { unidentifiedMessagePipe = null; } - account.close(); + if (closeAccount && account != null) { + account.close(); + } + account = null; } public interface ReceiveMessageHandler { diff --git a/src/main/java/org/asamk/signal/manager/NotRegisteredException.java b/src/main/java/org/asamk/signal/manager/NotRegisteredException.java new file mode 100644 index 00000000..c1b35a1c --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/NotRegisteredException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.manager; + +public class NotRegisteredException extends Exception { + + public NotRegisteredException() { + super("User is not registered."); + } +} diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 4195e8af..475c90b8 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -124,8 +124,10 @@ public class ProvisioningManager { m.requestSyncBlocked(); m.requestSyncConfiguration(); - m.saveAccount(); + m.close(false); } + + account.save(); } return username; diff --git a/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/src/main/java/org/asamk/signal/manager/RegistrationManager.java new file mode 100644 index 00000000..e740bb91 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -0,0 +1,194 @@ +/* + Copyright (C) 2015-2021 AsamK and contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +package org.asamk.signal.manager; + +import org.asamk.signal.manager.helper.PinHelper; +import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.util.KeyUtils; +import org.signal.zkgroup.profiles.ProfileKey; +import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.util.KeyHelper; +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.KbsPinData; +import org.whispersystems.signalservice.api.KeyBackupService; +import org.whispersystems.signalservice.api.KeyBackupServicePinException; +import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; +import org.whispersystems.signalservice.api.SignalServiceAccountManager; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.SleepTimer; +import org.whispersystems.signalservice.api.util.UptimeSleepTimer; +import org.whispersystems.signalservice.api.util.UuidUtil; +import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; +import org.whispersystems.signalservice.internal.push.LockedException; +import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; +import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; + +import java.io.File; +import java.io.IOException; +import java.util.Locale; + +public class RegistrationManager implements AutoCloseable { + + private SignalAccount account; + private final PathConfig pathConfig; + private final SignalServiceConfiguration serviceConfiguration; + private final String userAgent; + + private final SignalServiceAccountManager accountManager; + private final PinHelper pinHelper; + + public RegistrationManager( + SignalAccount account, + PathConfig pathConfig, + SignalServiceConfiguration serviceConfiguration, + String userAgent + ) { + this.account = account; + this.pathConfig = pathConfig; + this.serviceConfiguration = serviceConfiguration; + this.userAgent = userAgent; + + final SleepTimer timer = new UptimeSleepTimer(); + this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider( + // Using empty UUID, because registering doesn't work otherwise + null, + account.getUsername(), + account.getPassword(), + account.getSignalingKey(), + SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, timer); + final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); + this.pinHelper = new PinHelper(keyBackupService); + } + + public static RegistrationManager init( + String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent + ) throws IOException { + PathConfig pathConfig = PathConfig.createDefault(settingsPath); + + if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { + IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair(); + int registrationId = KeyHelper.generateRegistrationId(false); + + ProfileKey profileKey = KeyUtils.createProfileKey(); + SignalAccount account = SignalAccount.create(pathConfig.getDataPath(), + username, + identityKey, + registrationId, + profileKey); + account.save(); + + return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); + } + + SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username); + + return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); + } + + public void register(boolean voiceVerification, String captcha) throws IOException { + if (account.getPassword() == null) { + account.setPassword(KeyUtils.createPassword()); + } + + if (voiceVerification) { + accountManager.requestVoiceVerificationCode(Locale.getDefault(), + Optional.fromNullable(captcha), + Optional.absent()); + } else { + accountManager.requestSmsVerificationCode(false, Optional.fromNullable(captcha), Optional.absent()); + } + + account.setRegistered(false); + account.save(); + } + + public void verifyAccount( + String verificationCode, String pin + ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { + verificationCode = verificationCode.replace("-", ""); + if (account.getSignalingKey() == null) { + account.setSignalingKey(KeyUtils.createSignalingKey()); + } + VerifyAccountResponse response; + try { + response = verifyAccountWithCode(verificationCode, pin, null); + account.setPinMasterKey(null); + } catch (LockedException e) { + if (pin == null) { + throw e; + } + + KbsPinData registrationLockData = pinHelper.getRegistrationLockData(pin, e); + if (registrationLockData == null) { + throw e; + } + + String registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock(); + try { + response = verifyAccountWithCode(verificationCode, null, registrationLock); + } catch (LockedException _e) { + throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!"); + } + account.setPinMasterKey(registrationLockData.getMasterKey()); + } + + // TODO response.isStorageCapable() + //accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID))); + + account.setDeviceId(SignalServiceAddress.DEFAULT_DEVICE_ID); + account.setMultiDevice(false); + account.setRegistered(true); + account.setUuid(UuidUtil.parseOrNull(response.getUuid())); + account.setRegistrationLockPin(pin); + account.getSignalProtocolStore() + .saveIdentity(account.getSelfAddress(), + account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(), + TrustLevel.TRUSTED_VERIFIED); + + try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) { + + m.refreshPreKeys(); + + m.close(false); + } + + account.save(); + } + + private VerifyAccountResponse verifyAccountWithCode( + final String verificationCode, final String legacyPin, final String registrationLock + ) throws IOException { + return accountManager.verifyAccountWithCode(verificationCode, + account.getSignalingKey(), + account.getSignalProtocolStore().getLocalRegistrationId(), + true, + legacyPin, + registrationLock, + account.getSelfUnidentifiedAccessKey(), + account.isUnrestrictedUnidentifiedAccess(), + ServiceConfig.capabilities, + account.isDiscoverableByPhoneNumber()); + } + + @Override + public void close() throws Exception { + if (account != null) { + account.close(); + account = null; + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/src/main/java/org/asamk/signal/manager/ServiceConfig.java index 55935ced..b6d4f4fd 100644 --- a/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ b/src/main/java/org/asamk/signal/manager/ServiceConfig.java @@ -6,6 +6,8 @@ import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.KeyBackupService; +import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.account.AccountAttributes; import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; @@ -109,6 +111,16 @@ public class ServiceConfig { } } + static KeyBackupService createKeyBackupService(SignalServiceAccountManager accountManager) { + KeyStore keyStore = ServiceConfig.getIasKeyStore(); + + return accountManager.getKeyBackupService(keyStore, + ServiceConfig.KEY_BACKUP_ENCLAVE_NAME, + ServiceConfig.KEY_BACKUP_SERVICE_ID, + ServiceConfig.KEY_BACKUP_MRENCLAVE, + 10); + } + static ECPublicKey getUnidentifiedSenderTrustRoot() { try { return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0); diff --git a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java b/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java index a994c40a..3930154c 100644 --- a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java @@ -36,7 +36,7 @@ public class UnidentifiedAccessHelper { this.senderCertificateProvider = senderCertificateProvider; } - public byte[] getSelfUnidentifiedAccessKey() { + private byte[] getSelfUnidentifiedAccessKey() { return UnidentifiedAccess.deriveAccessKeyFrom(selfProfileKeyProvider.getProfileKey()); } diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 1c35b1fb..a030af3f 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -26,6 +26,7 @@ import org.asamk.signal.manager.storage.stickers.StickerStore; import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore; import org.asamk.signal.manager.storage.threads.ThreadInfo; import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.Utils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; @@ -36,6 +37,7 @@ import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Medium; import org.whispersystems.libsignal.util.Pair; +import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.util.Base64; @@ -98,6 +100,8 @@ public class SignalAccount implements Closeable { try { SignalAccount account = new SignalAccount(pair.first(), pair.second()); account.load(dataPath); + account.migrateLegacyConfigs(); + return account; } catch (Throwable e) { pair.second().close(); @@ -169,6 +173,31 @@ public class SignalAccount implements Closeable { return account; } + public void migrateLegacyConfigs() { + if (getProfileKey() == null && isRegistered()) { + // Old config file, creating new profile key + setProfileKey(KeyUtils.createProfileKey()); + save(); + } + // Store profile keys only in profile store + for (ContactInfo contact : getContactStore().getContacts()) { + String profileKeyString = contact.profileKey; + if (profileKeyString == null) { + continue; + } + final ProfileKey profileKey; + try { + profileKey = new ProfileKey(Base64.decode(profileKeyString)); + } catch (InvalidInputException | IOException e) { + continue; + } + contact.profileKey = null; + getProfileStore().storeProfileKey(contact.getAddress(), profileKey); + } + // Ensure our profile key is stored in profile store + getProfileStore().storeProfileKey(getSelfAddress(), getProfileKey()); + } + public static File getFileName(File dataPath, String username) { return new File(dataPath, username); } @@ -451,6 +480,10 @@ public class SignalAccount implements Closeable { return deviceId; } + public void setDeviceId(final int deviceId) { + this.deviceId = deviceId; + } + public String getPassword() { return password; } @@ -491,6 +524,10 @@ public class SignalAccount implements Closeable { this.profileKey = profileKey; } + public byte[] getSelfUnidentifiedAccessKey() { + return UnidentifiedAccess.deriveAccessKeyFrom(getProfileKey()); + } + public int getPreKeyIdOffset() { return preKeyIdOffset; } @@ -515,8 +552,19 @@ public class SignalAccount implements Closeable { isMultiDevice = multiDevice; } + public boolean isUnrestrictedUnidentifiedAccess() { + // TODO make configurable + return false; + } + + public boolean isDiscoverableByPhoneNumber() { + // TODO make configurable + return true; + } + @Override public void close() throws IOException { + save(); synchronized (fileChannel) { try { lock.close(); From 38267fa2a121319c0c28cd5d50a4b939f59229d5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 11 Jan 2021 19:50:19 +0100 Subject: [PATCH 017/124] Update copyright notices --- src/main/java/org/asamk/signal/Main.java | 2 +- src/main/java/org/asamk/signal/manager/Manager.java | 2 +- src/main/java/org/asamk/signal/manager/ProvisioningManager.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index b9209631..1eb36b2a 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2015-2020 AsamK and contributors + Copyright (C) 2015-2021 AsamK and contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 9d090a58..097c4c0d 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2015-2020 AsamK and contributors + Copyright (C) 2015-2021 AsamK and contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 475c90b8..0648c0d3 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2015-2020 AsamK and contributors + Copyright (C) 2015-2021 AsamK and contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From bc47c0d5d6ade54db2997f7d6ed5f7154781fcc7 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 11 Jan 2021 21:18:03 +0100 Subject: [PATCH 018/124] Refactor message cache --- .../org/asamk/signal/manager/Manager.java | 94 +++---------------- .../signal/manager/storage/SignalAccount.java | 15 +++ .../storage/messageCache/CachedMessage.java | 38 ++++++++ .../storage/messageCache/MessageCache.java | 79 ++++++++++++++++ 4 files changed, 147 insertions(+), 79 deletions(-) create mode 100644 src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java create mode 100644 src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 097c4c0d..d0a9e277 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -34,6 +34,7 @@ import org.asamk.signal.manager.storage.contacts.ContactInfo; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV2; +import org.asamk.signal.manager.storage.messageCache.CachedMessage; import org.asamk.signal.manager.storage.profiles.SignalProfile; import org.asamk.signal.manager.storage.profiles.SignalProfileEntry; import org.asamk.signal.manager.storage.protocol.IdentityInfo; @@ -41,7 +42,6 @@ import org.asamk.signal.manager.storage.stickers.Sticker; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.KeyUtils; -import org.asamk.signal.manager.util.MessageCacheUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.metadata.InvalidMetadataMessageException; import org.signal.libsignal.metadata.InvalidMetadataVersionException; @@ -170,7 +170,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; @@ -188,7 +187,6 @@ public class Manager implements Closeable { final static Logger logger = LoggerFactory.getLogger(Manager.class); - private final SleepTimer timer = new UptimeSleepTimer(); private final CertificateValidator certificateValidator = new CertificateValidator(ServiceConfig.getUnidentifiedSenderTrustRoot()); private final SignalServiceConfiguration serviceConfiguration; @@ -222,6 +220,7 @@ public class Manager implements Closeable { this.userAgent = userAgent; this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( serviceConfiguration)) : null; + final SleepTimer timer = new UptimeSleepTimer(); this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider(account.getUuid(), account.getUsername(), @@ -281,24 +280,6 @@ public class Manager implements Closeable { return account.getDeviceId(); } - private File getMessageCachePath() { - return SignalAccount.getMessageCachePath(pathConfig.getDataPath(), account.getUsername()); - } - - private File getMessageCachePath(String sender) { - if (sender == null || sender.isEmpty()) { - return getMessageCachePath(); - } - - return new File(getMessageCachePath(), sender.replace("/", "_")); - } - - private File getMessageCacheFile(String sender, long now, long timestamp) throws IOException { - File cachePath = getMessageCachePath(sender); - IOUtils.createPrivateDirectories(cachePath); - return new File(cachePath, now + "_" + timestamp); - } - public static Manager init( String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent ) throws IOException, NotRegisteredException { @@ -1727,41 +1708,17 @@ public class Manager implements Closeable { } } - private void retryFailedReceivedMessages( - ReceiveMessageHandler handler, boolean ignoreAttachments - ) { - final File cachePath = getMessageCachePath(); - if (!cachePath.exists()) { - return; - } - for (final File dir : Objects.requireNonNull(cachePath.listFiles())) { - if (!dir.isDirectory()) { - retryFailedReceivedMessage(handler, ignoreAttachments, dir); - continue; - } - - for (final File fileEntry : Objects.requireNonNull(dir.listFiles())) { - if (!fileEntry.isFile()) { - continue; - } - retryFailedReceivedMessage(handler, ignoreAttachments, fileEntry); - } - // Try to delete directory if empty - dir.delete(); + private void retryFailedReceivedMessages(ReceiveMessageHandler handler, boolean ignoreAttachments) { + for (CachedMessage cachedMessage : account.getMessageCache().getCachedMessages()) { + retryFailedReceivedMessage(handler, ignoreAttachments, cachedMessage); } } private void retryFailedReceivedMessage( - final ReceiveMessageHandler handler, final boolean ignoreAttachments, final File fileEntry + final ReceiveMessageHandler handler, final boolean ignoreAttachments, final CachedMessage cachedMessage ) { - SignalServiceEnvelope envelope; - try { - envelope = MessageCacheUtils.loadEnvelope(fileEntry); - if (envelope == null) { - return; - } - } catch (IOException e) { - e.printStackTrace(); + SignalServiceEnvelope envelope = cachedMessage.loadEnvelope(); + if (envelope == null) { return; } SignalServiceContent content = null; @@ -1772,11 +1729,7 @@ public class Manager implements Closeable { return; } catch (Exception er) { // All other errors are not recoverable, so delete the cached message - try { - Files.delete(fileEntry.toPath()); - } catch (IOException e) { - logger.warn("Failed to delete cached message file “{}”, ignoring: {}", fileEntry, e.getMessage()); - } + cachedMessage.delete(); return; } List actions = handleMessage(envelope, content, ignoreAttachments); @@ -1790,11 +1743,7 @@ public class Manager implements Closeable { } account.save(); handler.handleMessage(envelope, content, null); - try { - Files.delete(fileEntry.toPath()); - } catch (IOException e) { - logger.warn("Failed to delete cached message file “{}”, ignoring: {}", fileEntry, e.getMessage()); - } + cachedMessage.delete(); } public void receiveMessages( @@ -1808,7 +1757,7 @@ public class Manager implements Closeable { Set queuedActions = null; - getOrCreateMessagePipe(); + final SignalServiceMessagePipe messagePipe = getOrCreateMessagePipe(); boolean hasCaughtUpWithOldMessages = false; @@ -1816,17 +1765,11 @@ public class Manager implements Closeable { SignalServiceEnvelope envelope; SignalServiceContent content = null; Exception exception = null; - final long now = new Date().getTime(); + final CachedMessage[] cachedMessage = {null}; try { Optional result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> { // store message on disk, before acknowledging receipt to the server - try { - String source = envelope1.getSourceE164().isPresent() ? envelope1.getSourceE164().get() : ""; - File cacheFile = getMessageCacheFile(source, now, envelope1.getTimestamp()); - MessageCacheUtils.storeEnvelope(envelope1, cacheFile); - } catch (IOException e) { - logger.warn("Failed to store encrypted message in disk cache, ignoring: {}", e.getMessage()); - } + cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1); }); if (result.isPresent()) { envelope = result.get(); @@ -1890,15 +1833,8 @@ public class Manager implements Closeable { handler.handleMessage(envelope, content, exception); } if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) { - File cacheFile = null; - try { - String source = envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""; - cacheFile = getMessageCacheFile(source, now, envelope.getTimestamp()); - Files.delete(cacheFile.toPath()); - // Try to delete directory if empty - getMessageCachePath().delete(); - } catch (IOException e) { - logger.warn("Failed to delete cached message file “{}”, ignoring: {}", cacheFile, e.getMessage()); + if (cachedMessage[0] != null) { + cachedMessage[0].delete(); } } } diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index a030af3f..6d592573 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -16,6 +16,7 @@ import org.asamk.signal.manager.storage.contacts.JsonContactsStore; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.JsonGroupStore; +import org.asamk.signal.manager.storage.messageCache.MessageCache; import org.asamk.signal.manager.storage.profiles.ProfileStore; import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.manager.storage.protocol.JsonSignalProtocolStore; @@ -84,6 +85,8 @@ public class SignalAccount implements Closeable { private ProfileStore profileStore; private StickerStore stickerStore; + private MessageCache messageCache; + private SignalAccount(final FileChannel fileChannel, final FileLock lock) { this.fileChannel = fileChannel; this.lock = lock; @@ -130,6 +133,9 @@ public class SignalAccount implements Closeable { account.recipientStore = new RecipientStore(); account.profileStore = new ProfileStore(); account.stickerStore = new StickerStore(); + + account.messageCache = new MessageCache(getMessageCachePath(dataPath, username)); + account.registered = false; return account; @@ -167,6 +173,9 @@ public class SignalAccount implements Closeable { account.recipientStore = new RecipientStore(); account.profileStore = new ProfileStore(); account.stickerStore = new StickerStore(); + + account.messageCache = new MessageCache(getMessageCachePath(dataPath, username)); + account.registered = true; account.isMultiDevice = true; @@ -342,6 +351,8 @@ public class SignalAccount implements Closeable { stickerStore = new StickerStore(); } + messageCache = new MessageCache(getMessageCachePath(dataPath, username)); + JsonNode threadStoreNode = rootNode.get("threadStore"); if (threadStoreNode != null) { LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode, @@ -460,6 +471,10 @@ public class SignalAccount implements Closeable { return stickerStore; } + public MessageCache getMessageCache() { + return messageCache; + } + public String getUsername() { return username; } diff --git a/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java b/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java new file mode 100644 index 00000000..6c20cf62 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java @@ -0,0 +1,38 @@ +package org.asamk.signal.manager.storage.messageCache; + +import org.asamk.signal.manager.util.MessageCacheUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +public final class CachedMessage { + + final static Logger logger = LoggerFactory.getLogger(CachedMessage.class); + + private final File file; + + CachedMessage(final File file) { + this.file = file; + } + + public SignalServiceEnvelope loadEnvelope() { + try { + return MessageCacheUtils.loadEnvelope(file); + } catch (IOException e) { + logger.error("Failed to load cached message envelope “{}”: {}", file, e.getMessage()); + return null; + } + } + + public void delete() { + try { + Files.delete(file.toPath()); + } catch (IOException e) { + logger.warn("Failed to delete cached message file “{}”, ignoring: {}", file, e.getMessage()); + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java b/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java new file mode 100644 index 00000000..4e48ee76 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java @@ -0,0 +1,79 @@ +package org.asamk.signal.manager.storage.messageCache; + +import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.MessageCacheUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MessageCache { + + final static Logger logger = LoggerFactory.getLogger(MessageCache.class); + + private final File messageCachePath; + + public MessageCache(final File messageCachePath) { + this.messageCachePath = messageCachePath; + } + + public Iterable getCachedMessages() { + if (!messageCachePath.exists()) { + return Collections.emptyList(); + } + + return Arrays.stream(Objects.requireNonNull(messageCachePath.listFiles())).flatMap(dir -> { + if (dir.isFile()) { + return Stream.of(dir); + } + + final File[] files = Objects.requireNonNull(dir.listFiles()); + if (files.length == 0) { + try { + Files.delete(dir.toPath()); + } catch (IOException e) { + logger.warn("Failed to delete cache dir “{}”, ignoring: {}", dir, e.getMessage()); + } + return Stream.empty(); + } + return Arrays.stream(files).filter(File::isFile); + }).map(CachedMessage::new).collect(Collectors.toList()); + } + + public CachedMessage cacheMessage(SignalServiceEnvelope envelope) { + final long now = new Date().getTime(); + final String source = envelope.hasSource() ? envelope.getSourceAddress().getLegacyIdentifier() : ""; + + try { + File cacheFile = getMessageCacheFile(source, now, envelope.getTimestamp()); + MessageCacheUtils.storeEnvelope(envelope, cacheFile); + return new CachedMessage(cacheFile); + } catch (IOException e) { + logger.warn("Failed to store encrypted message in disk cache, ignoring: {}", e.getMessage()); + return null; + } + } + + private File getMessageCachePath(String sender) { + if (sender == null || sender.isEmpty()) { + return messageCachePath; + } + + return new File(messageCachePath, sender.replace("/", "_")); + } + + private File getMessageCacheFile(String sender, long now, long timestamp) throws IOException { + File cachePath = getMessageCachePath(sender); + IOUtils.createPrivateDirectories(cachePath); + return new File(cachePath, now + "_" + timestamp); + } +} From c9fa28d844c372535df24aea057e740a797f7229 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 12 Jan 2021 20:40:17 +0100 Subject: [PATCH 019/124] Extract pre key generation to KeyUtils --- .../org/asamk/signal/manager/Manager.java | 89 +++++++------------ .../asamk/signal/manager/util/KeyUtils.java | 32 +++++++ 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index d0a9e277..0c7a5e00 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -74,12 +74,9 @@ import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidVersionException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; -import org.whispersystems.libsignal.util.Medium; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.KeyBackupService; @@ -299,21 +296,15 @@ public class Manager implements Closeable { } public void checkAccountState() throws IOException { - if (account.isRegistered()) { - if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) { - refreshPreKeys(); - account.save(); - } - if (account.getUuid() == null) { - account.setUuid(accountManager.getOwnUuid()); - account.save(); - } - updateAccountAttributes(); + if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) { + refreshPreKeys(); + account.save(); } - } - - public boolean isRegistered() { - return account.isRegistered(); + if (account.getUuid() == null) { + account.setUuid(accountManager.getOwnUuid()); + account.save(); + } + updateAccountAttributes(); } /** @@ -396,43 +387,6 @@ public class Manager implements Closeable { account.save(); } - private List generatePreKeys() { - List records = new ArrayList<>(ServiceConfig.PREKEY_BATCH_SIZE); - - final int offset = account.getPreKeyIdOffset(); - for (int i = 0; i < ServiceConfig.PREKEY_BATCH_SIZE; i++) { - int preKeyId = (offset + i) % Medium.MAX_VALUE; - ECKeyPair keyPair = Curve.generateKeyPair(); - PreKeyRecord record = new PreKeyRecord(preKeyId, keyPair); - - records.add(record); - } - - account.addPreKeys(records); - account.save(); - - return records; - } - - private SignedPreKeyRecord generateSignedPreKey(IdentityKeyPair identityKeyPair) { - try { - ECKeyPair keyPair = Curve.generateKeyPair(); - byte[] signature = Curve.calculateSignature(identityKeyPair.getPrivateKey(), - keyPair.getPublicKey().serialize()); - SignedPreKeyRecord record = new SignedPreKeyRecord(account.getNextSignedPreKeyId(), - System.currentTimeMillis(), - keyPair, - signature); - - account.addSignedPreKey(record); - account.save(); - - return record; - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - public void setRegistrationLockPin(Optional pin) throws IOException, UnauthenticatedResponseException { if (pin.isPresent()) { final MasterKey masterKey = account.getPinMasterKey() != null @@ -464,6 +418,26 @@ public class Manager implements Closeable { accountManager.setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); } + private List generatePreKeys() { + final int offset = account.getPreKeyIdOffset(); + + List records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE); + account.addPreKeys(records); + account.save(); + + return records; + } + + private SignedPreKeyRecord generateSignedPreKey(IdentityKeyPair identityKeyPair) { + final int signedPreKeyId = account.getNextSignedPreKeyId(); + + SignedPreKeyRecord record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId); + account.addSignedPreKey(record); + account.save(); + + return record; + } + private SignalServiceMessagePipe getOrCreateMessagePipe() { if (messagePipe == null) { messagePipe = messageReceiver.createMessagePipe(); @@ -496,10 +470,6 @@ public class Manager implements Closeable { ServiceConfig.MAX_ENVELOPE_SIZE); } - private SignalServiceProfile getEncryptedRecipientProfile(SignalServiceAddress address) throws IOException { - return profileHelper.retrieveProfileSync(address, SignalServiceProfile.RequestType.PROFILE).getProfile(); - } - private SignalProfile getRecipientProfile( SignalServiceAddress address ) { @@ -560,7 +530,8 @@ public class Manager implements Closeable { private SignalProfile retrieveRecipientProfile( SignalServiceAddress address, ProfileKey profileKey ) throws IOException { - final SignalServiceProfile encryptedProfile = getEncryptedRecipientProfile(address); + final SignalServiceProfile encryptedProfile = profileHelper.retrieveProfileSync(address, + SignalServiceProfile.RequestType.PROFILE).getProfile(); return decryptProfile(address, profileKey, encryptedProfile); } diff --git a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java index d8861b1b..171e7a42 100644 --- a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -5,12 +5,19 @@ import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.ECKeyPair; import org.whispersystems.libsignal.ecc.ECPrivateKey; +import org.whispersystems.libsignal.state.PreKeyRecord; +import org.whispersystems.libsignal.state.SignedPreKeyRecord; +import org.whispersystems.libsignal.util.Medium; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.util.Base64; +import java.util.ArrayList; +import java.util.List; + public class KeyUtils { private KeyUtils() { @@ -24,6 +31,31 @@ public class KeyUtils { return new IdentityKeyPair(djbIdentityKey, djbPrivateKey); } + public static List generatePreKeyRecords(final int offset, final int batchSize) { + List records = new ArrayList<>(batchSize); + for (int i = 0; i < batchSize; i++) { + int preKeyId = (offset + i) % Medium.MAX_VALUE; + ECKeyPair keyPair = Curve.generateKeyPair(); + PreKeyRecord record = new PreKeyRecord(preKeyId, keyPair); + + records.add(record); + } + return records; + } + + public static SignedPreKeyRecord generateSignedPreKeyRecord( + final IdentityKeyPair identityKeyPair, final int signedPreKeyId + ) { + ECKeyPair keyPair = Curve.generateKeyPair(); + byte[] signature; + try { + signature = Curve.calculateSignature(identityKeyPair.getPrivateKey(), keyPair.getPublicKey().serialize()); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + return new SignedPreKeyRecord(signedPreKeyId, System.currentTimeMillis(), keyPair, signature); + } + public static String createSignalingKey() { return getSecret(52); } From 90f5cd79c982c9d9f5f5103ca9af4ee289ee9ba1 Mon Sep 17 00:00:00 2001 From: Atomic-Bean <75401809+Atomic-Bean@users.noreply.github.com> Date: Thu, 14 Jan 2021 02:21:31 +1030 Subject: [PATCH 020/124] Added JSON output to listGroups and allowed json to be activated with -j (#401) * Added json output to listGroups and allowed json to be activated with -j * Renamed classes * Added AsamK's suggestions * Removed isRegisted check since that is causing a conflict with upstream * Fixed an issue in the help message for listGroupsCommand * Re-enabled --json for receive and getUserStatuses commands as deprecated * Added better depricated warning message and clarified some java doc stuff --- man/signal-cli.1.adoc | 15 ++- src/main/java/org/asamk/signal/Main.java | 3 + .../signal/commands/GetUserStatusCommand.java | 16 ++- .../signal/commands/ListGroupsCommand.java | 113 +++++++++++++----- .../asamk/signal/commands/ReceiveCommand.java | 26 +++- .../org/asamk/signal/manager/Manager.java | 2 +- 6 files changed, 134 insertions(+), 41 deletions(-) diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index fa2db7c3..ba0d72d4 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -44,6 +44,9 @@ Make request via user dbus. *--dbus-system*:: Make request via system dbus. +*-o* OUTPUT-MODE, *--output* OUTPUT-MODE:: +Specify if you want commands to output in either "plain-text" mode or in "json". Defaults to "plain-text" + == Commands === register @@ -126,12 +129,10 @@ Use listDevices to see the deviceIds. === getUserStatus -Uses a list of phone numbers to determine the statuses of those users. Shows if they are registered on the Signal Servers or not. +Uses a list of phone numbers to determine the statuses of those users. Shows if they are registered on the Signal Servers or not. In json mode this is outputted as a list of objects. [NUMBER [NUMBER ...]]:: One or more numbers to check. -*--json*:: -Output the statuses as an array of json objects. === send @@ -177,15 +178,13 @@ Remove a reaction. === receive Query the server for new messages. -New messages are printed on standardoutput and attachments are downloaded to the config directory. +New messages are printed on standard output and attachments are downloaded to the config directory. In json mode this is outputted as one json object per line. *-t* TIMEOUT, *--timeout* TIMEOUT:: Number of seconds to wait for new messages (negative values disable timeout). Default is 5 seconds. *--ignore-attachments*:: Don’t download attachments of received messages. -*--json*:: -Output received messages in json format, one object per line. === joinGroup @@ -222,10 +221,10 @@ Specify the recipient group ID in base64 encoding. === listGroups -Show a list of known groups. +Show a list of known groups and related information. In json mode this is outputted as an list of objects and is always in detailed mode. *-d*, *--detailed*:: -Include the list of members of each group. +Include the list of members of each group and the group invite link. === listIdentities diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 1eb36b2a..b90b7cba 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -299,6 +299,9 @@ public class Main { mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue()); + parser.addArgument("-o", "--output").help("Choose to output in plain text or JSON") + .choices("plain-text", "json").setDefault("plain-text"); + Subparsers subparsers = parser.addSubparsers() .title("subcommands") .dest("command") diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 0a1ddc4c..8078de10 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -8,6 +8,8 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashSet; @@ -17,12 +19,15 @@ import java.util.stream.Collectors; public class GetUserStatusCommand implements LocalCommand { + // TODO delete later when "json" variable is removed + final static Logger logger = LoggerFactory.getLogger(GetUserStatusCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("number").help("Phone number").nargs("+"); subparser.help("Check if the specified phone number/s have been registered"); subparser.addArgument("--json") - .help("Output received messages in json format, one json object per line.") + .help("WARNING: This parameter is now deprecated! Please use the \"output\" option instead.\n\nOutput received messages in json format, one json object per line.") .action(Arguments.storeTrue()); } @@ -32,6 +37,13 @@ public class GetUserStatusCommand implements LocalCommand { ObjectMapper jsonProcessor = new ObjectMapper(); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + boolean inJson = ns.getString("output").equals("json"); + + // TODO delete later when "json" variable is removed + if (ns.getBoolean("json")) { + logger.warn("\"--json\" option has been deprecated, please use \"output\" instead."); + } + // Get a map of registration statuses Map registered; try { @@ -42,7 +54,7 @@ public class GetUserStatusCommand implements LocalCommand { } // Output - if (ns.getBoolean("json")) { + if (inJson) { List objects = registered.entrySet() .stream() .map(entry -> new JsonIsRegistered(entry.getKey(), entry.getValue())) diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 97af502e..debc2a42 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -9,32 +9,39 @@ import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class ListGroupsCommand implements LocalCommand { - private static void printGroup(Manager m, GroupInfo group, boolean detailed) { + private static Set resolveMembers(Manager m, Set addresses) { + return addresses.stream().map(m::resolveSignalServiceAddress) + .map(SignalServiceAddress::getLegacyIdentifier) + .collect(Collectors.toSet()); + } + + private static int printGroupsJson(ObjectMapper jsonProcessor, List objects) { + try { + jsonProcessor.writeValue(System.out, objects); + System.out.println(); + } catch (IOException e) { + System.err.println(e.getMessage()); + return 1; + } + + return 0; + } + + private static void printGroupPlainText(Manager m, GroupInfo group, boolean detailed) { if (detailed) { - Set members = group.getMembers() - .stream() - .map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier) - .collect(Collectors.toSet()); - - Set pendingMembers = group.getPendingMembers() - .stream() - .map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier) - .collect(Collectors.toSet()); - - Set requestingMembers = group.getRequestingMembers() - .stream() - .map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier) - .collect(Collectors.toSet()); - final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); System.out.println(String.format( @@ -43,9 +50,9 @@ public class ListGroupsCommand implements LocalCommand { group.getTitle(), group.isMember(m.getSelfAddress()), group.isBlocked(), - members, - pendingMembers, - requestingMembers, + resolveMembers(m, group.getMembers()), + resolveMembers(m, group.getPendingMembers()), + resolveMembers(m, group.getRequestingMembers()), groupInviteLink == null ? '-' : groupInviteLink.getUrl())); } else { System.out.println(String.format("Id: %s Name: %s Active: %s Blocked: %b", @@ -58,18 +65,68 @@ public class ListGroupsCommand implements LocalCommand { @Override public void attachToSubparser(final Subparser subparser) { - subparser.addArgument("-d", "--detailed").action(Arguments.storeTrue()).help("List members of each group"); - subparser.help("List group name and ids"); + subparser.addArgument("-d", "--detailed").action(Arguments.storeTrue()) + .help("List the members and group invite links of each group. If output=json, then this is always set"); + + subparser.help("List group information including names, ids, active status, blocked status and members"); } @Override public int handleCommand(final Namespace ns, final Manager m) { - List groups = m.getGroups(); - boolean detailed = ns.getBoolean("detailed"); + if (ns.getString("output").equals("json")) { + final ObjectMapper jsonProcessor = new ObjectMapper(); + jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - for (GroupInfo group : groups) { - printGroup(m, group, detailed); + List objects = new ArrayList<>(); + for (GroupInfo group : m.getGroups()) { + final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); + + objects.add(new JsonGroup(group.getGroupId().toBase64(), + group.getTitle(), + group.isMember(m.getSelfAddress()), + group.isBlocked(), + resolveMembers(m, group.getMembers()), + resolveMembers(m, group.getPendingMembers()), + resolveMembers(m, group.getRequestingMembers()), + groupInviteLink == null ? null : groupInviteLink.getUrl())); + } + return printGroupsJson(jsonProcessor, objects); + } else { + boolean detailed = ns.getBoolean("detailed"); + for (GroupInfo group : m.getGroups()) { + printGroupPlainText(m, group, detailed); + } } + return 0; } + + private static final class JsonGroup { + + public String id; + public String name; + public boolean isMember; + public boolean isBlocked; + + public Set members; + public Set pendingMembers; + public Set requestingMembers; + public String groupInviteLink; + + public JsonGroup(String id, String name, boolean isMember, boolean isBlocked, + Set members, Set pendingMembers, + Set requestingMembers, String groupInviteLink) + { + this.id = id; + this.name = name; + this.isMember = isMember; + this.isBlocked = isBlocked; + + this.members = members; + this.pendingMembers = pendingMembers; + this.requestingMembers = requestingMembers; + this.groupInviteLink = groupInviteLink; + } + } } diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 7dc9dcaf..f7a49157 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -20,6 +20,10 @@ import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.whispersystems.util.Base64; +// TODO delete later when "json" variable is removed +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -27,6 +31,9 @@ import static org.asamk.signal.util.ErrorUtils.handleAssertionError; public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { + // TODO delete later when "json" variable is removed + final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("-t", "--timeout") @@ -36,13 +43,21 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { .help("Don’t download attachments of received messages.") .action(Arguments.storeTrue()); subparser.addArgument("--json") - .help("Output received messages in json format, one json object per line.") + .help("WARNING: This parameter is now deprecated! Please use the \"output\" option instead.\n\nOutput received messages in json format, one json object per line.") .action(Arguments.storeTrue()); } public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) { final ObjectMapper jsonProcessor; + + boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + + // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { + logger.warn("\"--json\" option has been deprecated, please use \"output\" instead."); + } + + if (inJson) { jsonProcessor = new ObjectMapper(); jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); @@ -146,6 +161,13 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { + boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + + // TODO delete later when "json" variable is removed + if (ns.getBoolean("json")) { + logger.warn("\"--json\" option has been deprecated, please use \"output\" instead."); + } + double timeout = 5; if (ns.getDouble("timeout") != null) { timeout = ns.getDouble("timeout"); @@ -157,7 +179,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } boolean ignoreAttachments = ns.getBoolean("ignore_attachments"); try { - final Manager.ReceiveMessageHandler handler = ns.getBoolean("json") + final Manager.ReceiveMessageHandler handler = inJson ? new JsonReceiveMessageHandler(m) : new ReceiveMessageHandler(m); m.receiveMessages((long) (timeout * 1000), diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 0c7a5e00..c3e7a638 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -312,7 +312,7 @@ public class Manager implements Closeable { * * @param numbers The set of phone number in question * @return A map of numbers to booleans. True if registered, false otherwise. Should never be null - * @throws IOException if its unable to check if the users are registered + * @throws IOException if its unable to get the contacts to check if they're registered */ public Map areUsersRegistered(Set numbers) throws IOException { // Note "contactDetails" has no optionals. It only gives us info on users who are registered From f0834c7a47dc165ef906d4964ad62003b85e0c87 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 13 Jan 2021 17:04:58 +0100 Subject: [PATCH 021/124] Reformat man doc and clarify help text --- man/signal-cli.1.adoc | 16 +++++++++++----- .../signal/commands/GetUserStatusCommand.java | 6 +++--- .../asamk/signal/commands/ReceiveCommand.java | 10 ++++------ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index ba0d72d4..35070270 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -100,7 +100,8 @@ Remove the registration lock pin. === link Link to an existing device, instead of registering a new number. -This shows a "tsdevice:/…" URI. If you want to connect to another signal-cli instance, you can just use this URI. If you want to link to an Android/iOS device, create a QR code with the URI (e.g. with qrencode) and scan that in the Signal app. +This shows a "tsdevice:/…" URI. If you want to connect to another signal-cli instance, you can just use this URI. +If you want to link to an Android/iOS device, create a QR code with the URI (e.g. with qrencode) and scan that in the Signal app. *-n* NAME, *--name* NAME:: Optionally specify a name to describe this new device. @@ -112,7 +113,8 @@ Link another device to this device. Only works, if this is the master device. *--uri* URI:: -Specify the uri contained in the QR code shown by the new device. You will need the full uri enclosed in quotation marks, such as "tsdevice:/?uuid=....." +Specify the uri contained in the QR code shown by the new device. +You will need the full uri enclosed in quotation marks, such as "tsdevice:/?uuid=....." === listDevices @@ -129,7 +131,9 @@ Use listDevices to see the deviceIds. === getUserStatus -Uses a list of phone numbers to determine the statuses of those users. Shows if they are registered on the Signal Servers or not. In json mode this is outputted as a list of objects. +Uses a list of phone numbers to determine the statuses of those users. +Shows if they are registered on the Signal Servers or not. +In json mode this is outputted as a list of objects. [NUMBER [NUMBER ...]]:: One or more numbers to check. @@ -178,7 +182,8 @@ Remove a reaction. === receive Query the server for new messages. -New messages are printed on standard output and attachments are downloaded to the config directory. In json mode this is outputted as one json object per line. +New messages are printed on standard output and attachments are downloaded to the config directory. +In json mode this is outputted as one json object per line. *-t* TIMEOUT, *--timeout* TIMEOUT:: Number of seconds to wait for new messages (negative values disable timeout). @@ -221,7 +226,8 @@ Specify the recipient group ID in base64 encoding. === listGroups -Show a list of known groups and related information. In json mode this is outputted as an list of objects and is always in detailed mode. +Show a list of known groups and related information. +In json mode this is outputted as an list of objects and is always in detailed mode. *-d*, *--detailed*:: Include the list of members of each group and the group invite link. diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 8078de10..cd347137 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -27,7 +27,7 @@ public class GetUserStatusCommand implements LocalCommand { subparser.addArgument("number").help("Phone number").nargs("+"); subparser.help("Check if the specified phone number/s have been registered"); subparser.addArgument("--json") - .help("WARNING: This parameter is now deprecated! Please use the \"output\" option instead.\n\nOutput received messages in json format, one json object per line.") + .help("WARNING: This parameter is now deprecated! Please use the global \"--output=json\" option instead.\n\nOutput received messages in json format, one json object per line.") .action(Arguments.storeTrue()); } @@ -37,11 +37,11 @@ public class GetUserStatusCommand implements LocalCommand { ObjectMapper jsonProcessor = new ObjectMapper(); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - boolean inJson = ns.getString("output").equals("json"); + boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { - logger.warn("\"--json\" option has been deprecated, please use \"output\" instead."); + logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); } // Get a map of registration statuses diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index f7a49157..4d4fdc10 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -18,11 +18,9 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.util.DateUtils; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; -import org.whispersystems.util.Base64; - -// TODO delete later when "json" variable is removed import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.util.Base64; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -43,7 +41,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { .help("Don’t download attachments of received messages.") .action(Arguments.storeTrue()); subparser.addArgument("--json") - .help("WARNING: This parameter is now deprecated! Please use the \"output\" option instead.\n\nOutput received messages in json format, one json object per line.") + .help("WARNING: This parameter is now deprecated! Please use the global \"--output=json\" option instead.\n\nOutput received messages in json format, one json object per line.") .action(Arguments.storeTrue()); } @@ -54,7 +52,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { - logger.warn("\"--json\" option has been deprecated, please use \"output\" instead."); + logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); } if (inJson) { @@ -165,7 +163,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { - logger.warn("\"--json\" option has been deprecated, please use \"output\" instead."); + logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); } double timeout = 5; From 783c88cb6ad90d9961a140b86ba66c062983fe0f Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 13 Jan 2021 19:13:03 +0100 Subject: [PATCH 022/124] Ignore group message if the sender is not in the group --- .../org/asamk/signal/manager/Manager.java | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index c3e7a638..bfb74724 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -1800,7 +1800,11 @@ public class Manager implements Closeable { } } account.save(); - if (!isMessageBlocked(envelope, content)) { + if (isMessageBlocked(envelope, content)) { + logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp()); + } else if (isNotAGroupMember(envelope, content)) { + logger.info("Ignoring a message from a non group member: {}", envelope.getTimestamp()); + } else { handler.handleMessage(envelope, content, exception); } if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) { @@ -1827,18 +1831,43 @@ public class Manager implements Closeable { return true; } + if (content != null && content.getDataMessage().isPresent()) { + SignalServiceDataMessage message = content.getDataMessage().get(); + if (message.getGroupContext().isPresent()) { + GroupId groupId = GroupUtils.getGroupId(message.getGroupContext().get()); + GroupInfo group = getGroup(groupId); + if (group != null && group.isBlocked()) { + return true; + } + } + } + return false; + } + + private boolean isNotAGroupMember( + SignalServiceEnvelope envelope, SignalServiceContent content + ) { + SignalServiceAddress source; + if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { + source = envelope.getSourceAddress(); + } else if (content != null) { + source = content.getSender(); + } else { + return false; + } + if (content != null && content.getDataMessage().isPresent()) { SignalServiceDataMessage message = content.getDataMessage().get(); if (message.getGroupContext().isPresent()) { if (message.getGroupContext().get().getGroupV1().isPresent()) { SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get(); - if (groupInfo.getType() != SignalServiceGroup.Type.DELIVER) { + if (groupInfo.getType() == SignalServiceGroup.Type.QUIT) { return false; } } GroupId groupId = GroupUtils.getGroupId(message.getGroupContext().get()); GroupInfo group = getGroup(groupId); - if (group != null && group.isBlocked()) { + if (group != null && !group.isMember(source)) { return true; } } From 0624d6a808b8b2a247aadd96450319bc94e3729f Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 13 Jan 2021 20:19:34 +0100 Subject: [PATCH 023/124] Make loggers private --- src/main/java/org/asamk/signal/Main.java | 2 +- .../java/org/asamk/signal/commands/GetUserStatusCommand.java | 2 +- src/main/java/org/asamk/signal/commands/ReceiveCommand.java | 2 +- src/main/java/org/asamk/signal/manager/Manager.java | 2 +- src/main/java/org/asamk/signal/manager/helper/GroupHelper.java | 2 +- .../java/org/asamk/signal/manager/storage/SignalAccount.java | 2 +- .../org/asamk/signal/manager/storage/groups/JsonGroupStore.java | 2 +- .../signal/manager/storage/messageCache/CachedMessage.java | 2 +- .../asamk/signal/manager/storage/messageCache/MessageCache.java | 2 +- .../signal/manager/storage/protocol/JsonIdentityKeyStore.java | 2 +- .../asamk/signal/manager/storage/protocol/JsonPreKeyStore.java | 2 +- .../asamk/signal/manager/storage/protocol/JsonSessionStore.java | 2 +- .../signal/manager/storage/protocol/JsonSignedPreKeyStore.java | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index b90b7cba..2185e2f7 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -56,7 +56,7 @@ import java.util.Map; public class Main { - final static Logger logger = LoggerFactory.getLogger(Main.class); + private final static Logger logger = LoggerFactory.getLogger(Main.class); public static void main(String[] args) { installSecurityProviderWorkaround(); diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index cd347137..8b694cb6 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -20,7 +20,7 @@ import java.util.stream.Collectors; public class GetUserStatusCommand implements LocalCommand { // TODO delete later when "json" variable is removed - final static Logger logger = LoggerFactory.getLogger(GetUserStatusCommand.class); + private final static Logger logger = LoggerFactory.getLogger(GetUserStatusCommand.class); @Override public void attachToSubparser(final Subparser subparser) { diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 4d4fdc10..9d718abb 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -30,7 +30,7 @@ import static org.asamk.signal.util.ErrorUtils.handleAssertionError; public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { // TODO delete later when "json" variable is removed - final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class); + private final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class); @Override public void attachToSubparser(final Subparser subparser) { diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index bfb74724..4dff4b82 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -182,7 +182,7 @@ import static org.asamk.signal.manager.ServiceConfig.getIasKeyStore; public class Manager implements Closeable { - final static Logger logger = LoggerFactory.getLogger(Manager.class); + private final static Logger logger = LoggerFactory.getLogger(Manager.class); private final CertificateValidator certificateValidator = new CertificateValidator(ServiceConfig.getUnidentifiedSenderTrustRoot()); diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 8a2320e0..75c2fd61 100644 --- a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -47,7 +47,7 @@ import java.util.stream.Collectors; public class GroupHelper { - final static Logger logger = LoggerFactory.getLogger(GroupHelper.class); + private final static Logger logger = LoggerFactory.getLogger(GroupHelper.class); private final ProfileKeyCredentialProvider profileKeyCredentialProvider; diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 6d592573..87b1efd4 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -59,7 +59,7 @@ import java.util.stream.Collectors; public class SignalAccount implements Closeable { - final static Logger logger = LoggerFactory.getLogger(SignalAccount.class); + private final static Logger logger = LoggerFactory.getLogger(SignalAccount.class); private final ObjectMapper jsonProcessor = new ObjectMapper(); private final FileChannel fileChannel; diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java index fdcd28a3..5c06aeee 100644 --- a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java @@ -37,7 +37,7 @@ import java.util.Map; public class JsonGroupStore { - final static Logger logger = LoggerFactory.getLogger(JsonGroupStore.class); + private final static Logger logger = LoggerFactory.getLogger(JsonGroupStore.class); private static final ObjectMapper jsonProcessor = new ObjectMapper(); public File groupCachePath; diff --git a/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java b/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java index 6c20cf62..8ea723cc 100644 --- a/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java +++ b/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java @@ -11,7 +11,7 @@ import java.nio.file.Files; public final class CachedMessage { - final static Logger logger = LoggerFactory.getLogger(CachedMessage.class); + private final static Logger logger = LoggerFactory.getLogger(CachedMessage.class); private final File file; diff --git a/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java b/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java index 4e48ee76..3e728c28 100644 --- a/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java +++ b/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java @@ -18,7 +18,7 @@ import java.util.stream.Stream; public class MessageCache { - final static Logger logger = LoggerFactory.getLogger(MessageCache.class); + private final static Logger logger = LoggerFactory.getLogger(MessageCache.class); private final File messageCachePath; diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index 5bc1c11f..19131e13 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -29,7 +29,7 @@ import java.util.UUID; public class JsonIdentityKeyStore implements IdentityKeyStore { - final static Logger logger = LoggerFactory.getLogger(JsonIdentityKeyStore.class); + private final static Logger logger = LoggerFactory.getLogger(JsonIdentityKeyStore.class); private final List identities = new ArrayList<>(); diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java index 4d884c3e..9ec4b64f 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java @@ -21,7 +21,7 @@ import java.util.Map; class JsonPreKeyStore implements PreKeyStore { - final static Logger logger = LoggerFactory.getLogger(JsonPreKeyStore.class); + private final static Logger logger = LoggerFactory.getLogger(JsonPreKeyStore.class); private final Map store = new HashMap<>(); diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java index 6e300214..79790598 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java @@ -26,7 +26,7 @@ import java.util.UUID; class JsonSessionStore implements SessionStore { - final static Logger logger = LoggerFactory.getLogger(JsonSessionStore.class); + private final static Logger logger = LoggerFactory.getLogger(JsonSessionStore.class); private final List sessions = new ArrayList<>(); diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java index 5eae4500..00543620 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java @@ -23,7 +23,7 @@ import java.util.Map; class JsonSignedPreKeyStore implements SignedPreKeyStore { - final static Logger logger = LoggerFactory.getLogger(JsonSignedPreKeyStore.class); + private final static Logger logger = LoggerFactory.getLogger(JsonSignedPreKeyStore.class); private final Map store = new HashMap<>(); From 6bd857ad8bf51bdb207bcdaf6ce058c8311f030e Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 13 Jan 2021 20:56:23 +0100 Subject: [PATCH 024/124] Always delete temporary files on exit --- src/main/java/org/asamk/signal/manager/Manager.java | 1 - src/main/java/org/asamk/signal/manager/util/IOUtils.java | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 4dff4b82..aacb49c4 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -2141,7 +2141,6 @@ public class Manager implements Closeable { GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); File tmpFile = IOUtils.createTempFile(); - tmpFile.deleteOnExit(); try (InputStream input = messageReceiver.retrieveGroupsV2ProfileAvatar(cdnKey, tmpFile, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { diff --git a/src/main/java/org/asamk/signal/manager/util/IOUtils.java b/src/main/java/org/asamk/signal/manager/util/IOUtils.java index 06f8aa22..c0918ae7 100644 --- a/src/main/java/org/asamk/signal/manager/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/IOUtils.java @@ -22,7 +22,9 @@ import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; public class IOUtils { public static File createTempFile() throws IOException { - return File.createTempFile("signal_tmp_", ".tmp"); + final File tempFile = File.createTempFile("signal-cli_tmp_", ".tmp"); + tempFile.deleteOnExit(); + return tempFile; } public static byte[] readFully(InputStream in) throws IOException { From a643609ed2b1875c5f5de96aaf8e30b4f1e35e96 Mon Sep 17 00:00:00 2001 From: AsamK Date: Wed, 13 Jan 2021 22:35:58 +0100 Subject: [PATCH 025/124] Create an AvatarStore --- .../signal/commands/UpdateProfileCommand.java | 7 +- .../org/asamk/signal/dbus/DbusSignalImpl.java | 3 +- .../org/asamk/signal/manager/AvatarStore.java | 91 ++++++++ .../org/asamk/signal/manager/Manager.java | 205 +++++++++--------- .../signal/manager/helper/GroupHelper.java | 7 +- .../storage/profiles/SignalProfile.java | 12 - .../signal/manager/util/AttachmentUtils.java | 40 ++-- .../asamk/signal/manager/util/IOUtils.java | 28 +-- .../org/asamk/signal/manager/util/Utils.java | 5 +- 9 files changed, 239 insertions(+), 159 deletions(-) create mode 100644 src/main/java/org/asamk/signal/manager/AvatarStore.java diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index 968a8733..c2ff2e5e 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -6,6 +6,7 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; +import org.whispersystems.libsignal.util.guava.Optional; import java.io.File; import java.io.IOException; @@ -14,7 +15,7 @@ public class UpdateProfileCommand implements LocalCommand { @Override public void attachToSubparser(final Subparser subparser) { - final MutuallyExclusiveGroup avatarOptions = subparser.addMutuallyExclusiveGroup().required(true); + final MutuallyExclusiveGroup avatarOptions = subparser.addMutuallyExclusiveGroup(); avatarOptions.addArgument("--avatar").help("Path to new profile avatar"); avatarOptions.addArgument("--remove-avatar").action(Arguments.storeTrue()); @@ -30,7 +31,9 @@ public class UpdateProfileCommand implements LocalCommand { boolean removeAvatar = ns.getBoolean("remove_avatar"); try { - File avatarFile = removeAvatar ? null : new File(avatarPath); + Optional avatarFile = removeAvatar + ? Optional.absent() + : avatarPath == null ? null : Optional.of(new File(avatarPath)); m.setProfile(name, avatarFile); } catch (IOException e) { System.err.println("UpdateAccount error: " + e.getMessage()); diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 278fbbd4..69747b65 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -14,6 +14,7 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.InvalidNumberException; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -193,7 +194,7 @@ public class DbusSignalImpl implements Signal { } final Pair> results = m.updateGroup(groupId == null ? null - : GroupId.unknownVersion(groupId), name, members, avatar); + : GroupId.unknownVersion(groupId), name, members, avatar == null ? null : new File(avatar)); checkSendMessageResults(0, results.second()); return results.first().serialize(); } catch (IOException e) { diff --git a/src/main/java/org/asamk/signal/manager/AvatarStore.java b/src/main/java/org/asamk/signal/manager/AvatarStore.java new file mode 100644 index 00000000..fbb99a1c --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/AvatarStore.java @@ -0,0 +1,91 @@ +package org.asamk.signal.manager; + +import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.util.IOUtils; +import org.asamk.signal.manager.util.Utils; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.util.StreamDetails; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; + +public class AvatarStore { + + private final File avatarsPath; + + public AvatarStore(final File avatarsPath) { + this.avatarsPath = avatarsPath; + } + + public StreamDetails retrieveContactAvatar(SignalServiceAddress address) throws IOException { + return retrieveAvatar(getContactAvatarFile(address)); + } + + public StreamDetails retrieveProfileAvatar(SignalServiceAddress address) throws IOException { + return retrieveAvatar(getProfileAvatarFile(address)); + } + + public StreamDetails retrieveGroupAvatar(GroupId groupId) throws IOException { + final File groupAvatarFile = getGroupAvatarFile(groupId); + return retrieveAvatar(groupAvatarFile); + } + + public void storeContactAvatar(SignalServiceAddress address, AvatarStorer storer) throws IOException { + storeAvatar(getContactAvatarFile(address), storer); + } + + public void storeProfileAvatar(SignalServiceAddress address, AvatarStorer storer) throws IOException { + storeAvatar(getProfileAvatarFile(address), storer); + } + + public void storeGroupAvatar(GroupId groupId, AvatarStorer storer) throws IOException { + storeAvatar(getGroupAvatarFile(groupId), storer); + } + + public void deleteProfileAvatar(SignalServiceAddress address) throws IOException { + deleteAvatar(getProfileAvatarFile(address)); + } + + private StreamDetails retrieveAvatar(final File avatarFile) throws IOException { + if (!avatarFile.exists()) { + return null; + } + return Utils.createStreamDetailsFromFile(avatarFile); + } + + private void storeAvatar(final File avatarFile, final AvatarStorer storer) throws IOException { + createAvatarsDir(); + try (OutputStream output = new FileOutputStream(avatarFile)) { + storer.store(output); + } + } + + private void deleteAvatar(final File avatarFile) throws IOException { + Files.delete(avatarFile.toPath()); + } + + private File getGroupAvatarFile(GroupId groupId) { + return new File(avatarsPath, "group-" + groupId.toBase64().replace("/", "_")); + } + + private File getContactAvatarFile(SignalServiceAddress address) { + return new File(avatarsPath, "contact-" + address); + } + + private File getProfileAvatarFile(SignalServiceAddress address) { + return new File(avatarsPath, "profile-" + address.getLegacyIdentifier()); + } + + private void createAvatarsDir() throws IOException { + IOUtils.createPrivateDirectories(avatarsPath); + } + + @FunctionalInterface + public interface AvatarStorer { + + void store(OutputStream outputStream) throws IOException; + } +} diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index aacb49c4..23e37eef 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -156,8 +156,6 @@ import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; import java.security.SignatureException; import java.util.ArrayList; import java.util.Arrays; @@ -204,6 +202,7 @@ public class Manager implements Closeable { private final ProfileHelper profileHelper; private final GroupHelper groupHelper; private final PinHelper pinHelper; + private final AvatarStore avatarStore; Manager( SignalAccount account, @@ -259,6 +258,7 @@ public class Manager implements Closeable { groupsV2Operations, groupsV2Api, this::getGroupAuthForToday); + this.avatarStore = new AvatarStore(pathConfig.getAvatarsPath()); } public String getUsername() { @@ -338,10 +338,25 @@ public class Manager implements Closeable { account.isDiscoverableByPhoneNumber()); } - public void setProfile(String name, File avatar) throws IOException { - try (final StreamDetails streamDetails = avatar == null ? null : Utils.createStreamDetailsFromFile(avatar)) { + /** + * @param avatar if avatar is null the image from the local avatar store is used (if present), + * if it's Optional.absent(), the avatar will be removed + */ + public void setProfile(String name, Optional avatar) throws IOException { + try (final StreamDetails streamDetails = avatar == null + ? avatarStore.retrieveProfileAvatar(getSelfAddress()) + : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) { accountManager.setVersionedProfile(account.getUuid(), account.getProfileKey(), name, streamDetails); } + + if (avatar != null) { + if (avatar.isPresent()) { + avatarStore.storeProfileAvatar(getSelfAddress(), + outputStream -> IOUtils.copyFileToStream(avatar.get(), outputStream)); + } else { + avatarStore.deleteProfileAvatar(getSelfAddress()); + } + } } public void unregister() throws IOException { @@ -539,13 +554,12 @@ public class Manager implements Closeable { private SignalProfile decryptProfile( final SignalServiceAddress address, final ProfileKey profileKey, final SignalServiceProfile encryptedProfile ) { - File avatarFile = null; - try { - avatarFile = encryptedProfile.getAvatar() == null - ? null - : retrieveProfileAvatar(address, encryptedProfile.getAvatar(), profileKey); - } catch (Throwable e) { - logger.warn("Failed to retrieve profile avatar, ignoring: {}", e.getMessage()); + if (encryptedProfile.getAvatar() != null) { + try { + retrieveProfileAvatar(address, encryptedProfile.getAvatar(), profileKey); + } catch (Throwable e) { + logger.warn("Failed to retrieve profile avatar, ignoring: {}", e.getMessage()); + } } ProfileCipher profileCipher = new ProfileCipher(profileKey); @@ -569,7 +583,6 @@ public class Manager implements Closeable { } return new SignalProfile(encryptedProfile.getIdentityKey(), name, - avatarFile, unidentifiedAccess, encryptedProfile.isUnrestrictedUnidentifiedAccess(), encryptedProfile.getCapabilities()); @@ -579,21 +592,21 @@ public class Manager implements Closeable { } private Optional createGroupAvatarAttachment(GroupId groupId) throws IOException { - File file = getGroupAvatarFile(groupId); - if (!file.exists()) { + final StreamDetails streamDetails = avatarStore.retrieveGroupAvatar(groupId); + if (streamDetails == null) { return Optional.absent(); } - return Optional.of(AttachmentUtils.createAttachment(file)); + return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.absent())); } - private Optional createContactAvatarAttachment(String number) throws IOException { - File file = getContactAvatarFile(number); - if (!file.exists()) { + private Optional createContactAvatarAttachment(SignalServiceAddress address) throws IOException { + final StreamDetails streamDetails = avatarStore.retrieveContactAvatar(address); + if (streamDetails == null) { return Optional.absent(); } - return Optional.of(AttachmentUtils.createAttachment(file)); + return Optional.of(AttachmentUtils.createAttachment(streamDetails, Optional.absent())); } private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { @@ -683,13 +696,15 @@ public class Manager implements Closeable { } private Pair> sendUpdateGroupMessage( - GroupId groupId, String name, Collection members, String avatarFile + GroupId groupId, String name, Collection members, File avatarFile ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException { GroupInfo g; SignalServiceDataMessage.Builder messageBuilder; if (groupId == null) { // Create new group - GroupInfoV2 gv2 = groupHelper.createGroupV2(name, members, avatarFile); + GroupInfoV2 gv2 = groupHelper.createGroupV2(name == null ? "" : name, + members == null ? List.of() : members, + avatarFile); if (gv2 == null) { GroupInfoV1 gv1 = new GroupInfoV1(GroupIdV1.createRandom()); gv1.addMembers(List.of(account.getSelfAddress())); @@ -697,6 +712,10 @@ public class Manager implements Closeable { messageBuilder = getGroupUpdateMessageBuilder(gv1); g = gv1; } else { + if (avatarFile != null) { + avatarStore.storeGroupAvatar(gv2.getGroupId(), + outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); + } messageBuilder = getGroupUpdateMessageBuilder(gv2, null); g = gv2; } @@ -731,6 +750,10 @@ public class Manager implements Closeable { Pair groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, name, avatarFile); + if (avatarFile != null) { + avatarStore.storeGroupAvatar(groupInfoV2.getGroupId(), + outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); + } result = sendUpdateGroupMessage(groupInfoV2, groupGroupChangePair.first(), groupGroupChangePair.second()); @@ -794,7 +817,7 @@ public class Manager implements Closeable { final GroupInfoV1 g, final String name, final Collection members, - final String avatarFile + final File avatarFile ) throws IOException { if (name != null) { g.name = name; @@ -824,9 +847,8 @@ public class Manager implements Closeable { } if (avatarFile != null) { - IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - File aFile = getGroupAvatarFile(g.getGroupId()); - Files.copy(Paths.get(avatarFile), aFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + avatarStore.storeGroupAvatar(g.getGroupId(), + outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); } } @@ -856,13 +878,13 @@ public class Manager implements Closeable { .withName(g.name) .withMembers(new ArrayList<>(g.getMembers())); - File aFile = getGroupAvatarFile(g.getGroupId()); - if (aFile.exists()) { - try { - group.withAvatar(AttachmentUtils.createAttachment(aFile)); - } catch (IOException e) { - throw new AttachmentInvalidException(aFile.toString(), e); + try { + final Optional attachment = createGroupAvatarAttachment(g.getGroupId()); + if (attachment.isPresent()) { + group.withAvatar(attachment.get()); } + } catch (IOException e) { + throw new AttachmentInvalidException(g.getGroupId().toBase64(), e); } return SignalServiceDataMessage.newBuilder() @@ -1001,12 +1023,12 @@ public class Manager implements Closeable { } public Pair> updateGroup( - GroupId groupId, String name, List members, String avatar + GroupId groupId, String name, List members, File avatarFile ) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException { return sendUpdateGroupMessage(groupId, name, members == null ? null : getSignalServiceAddresses(members), - avatar); + avatarFile); } /** @@ -1467,7 +1489,7 @@ public class Manager implements Closeable { if (avatar.isPointer()) { try { retrieveGroupAvatarAttachment(avatar.asPointer(), groupV1.getGroupId()); - } catch (IOException | InvalidMessageException | MissingConfigurationException e) { + } catch (IOException e) { logger.warn("Failed to retrieve avatar for group {}, ignoring: {}", groupId.toBase64(), e.getMessage()); @@ -1556,7 +1578,7 @@ public class Manager implements Closeable { if (attachment.isPointer()) { try { retrieveAttachment(attachment.asPointer()); - } catch (IOException | InvalidMessageException | MissingConfigurationException e) { + } catch (IOException e) { logger.warn("Failed to retrieve attachment ({}), ignoring: {}", attachment.asPointer().getRemoteId(), e.getMessage()); @@ -1583,7 +1605,7 @@ public class Manager implements Closeable { SignalServiceAttachmentPointer attachment = preview.getImage().get().asPointer(); try { retrieveAttachment(attachment); - } catch (IOException | InvalidMessageException | MissingConfigurationException e) { + } catch (IOException e) { logger.warn("Failed to retrieve preview image ({}), ignoring: {}", attachment.getRemoteId(), e.getMessage()); @@ -1599,7 +1621,7 @@ public class Manager implements Closeable { if (attachment != null && attachment.isPointer()) { try { retrieveAttachment(attachment.asPointer()); - } catch (IOException | InvalidMessageException | MissingConfigurationException e) { + } catch (IOException e) { logger.warn("Failed to retrieve quote attachment thumbnail ({}), ignoring: {}", attachment.asPointer().getRemoteId(), e.getMessage()); @@ -2047,7 +2069,7 @@ public class Manager implements Closeable { account.getContactStore().updateContact(contact); if (c.getAvatar().isPresent()) { - retrieveContactAvatarAttachment(c.getAvatar().get(), contact.number); + retrieveContactAvatarAttachment(c.getAvatar().get(), contact.getAddress()); } } } @@ -2099,45 +2121,21 @@ public class Manager implements Closeable { return actions; } - private File getContactAvatarFile(String number) { - return new File(pathConfig.getAvatarsPath(), "contact-" + number); + private void retrieveContactAvatarAttachment( + SignalServiceAttachment attachment, SignalServiceAddress address + ) throws IOException { + avatarStore.storeContactAvatar(address, outputStream -> retrieveAttachment(attachment, outputStream)); } - private File retrieveContactAvatarAttachment( - SignalServiceAttachment attachment, String number - ) throws IOException, InvalidMessageException, MissingConfigurationException { - IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - if (attachment.isPointer()) { - SignalServiceAttachmentPointer pointer = attachment.asPointer(); - return retrieveAttachment(pointer, getContactAvatarFile(number), false); - } else { - SignalServiceAttachmentStream stream = attachment.asStream(); - return AttachmentUtils.retrieveAttachment(stream, getContactAvatarFile(number)); - } - } - - private File getGroupAvatarFile(GroupId groupId) { - return new File(pathConfig.getAvatarsPath(), "group-" + groupId.toBase64().replace("/", "_")); - } - - private File retrieveGroupAvatarAttachment( + private void retrieveGroupAvatarAttachment( SignalServiceAttachment attachment, GroupId groupId - ) throws IOException, InvalidMessageException, MissingConfigurationException { - IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - if (attachment.isPointer()) { - SignalServiceAttachmentPointer pointer = attachment.asPointer(); - return retrieveAttachment(pointer, getGroupAvatarFile(groupId), false); - } else { - SignalServiceAttachmentStream stream = attachment.asStream(); - return AttachmentUtils.retrieveAttachment(stream, getGroupAvatarFile(groupId)); - } + ) throws IOException { + avatarStore.storeGroupAvatar(groupId, outputStream -> retrieveAttachment(attachment, outputStream)); } - private File retrieveGroupAvatar( + private void retrieveGroupAvatar( GroupId groupId, GroupSecretParams groupSecretParams, String cdnKey ) throws IOException { - IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - File outputFile = getGroupAvatarFile(groupId); GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); File tmpFile = IOUtils.createTempFile(); @@ -2147,9 +2145,7 @@ public class Manager implements Closeable { byte[] encryptedData = IOUtils.readFully(input); byte[] decryptedData = groupOperations.decryptAvatar(encryptedData); - try (OutputStream output = new FileOutputStream(outputFile)) { - output.write(decryptedData); - } + avatarStore.storeGroupAvatar(groupId, outputStream -> outputStream.write(decryptedData)); } finally { try { Files.delete(tmpFile.toPath()); @@ -2159,26 +2155,20 @@ public class Manager implements Closeable { e.getMessage()); } } - return outputFile; } - private File getProfileAvatarFile(SignalServiceAddress address) { - return new File(pathConfig.getAvatarsPath(), "profile-" + address.getLegacyIdentifier()); - } - - private File retrieveProfileAvatar( + private void retrieveProfileAvatar( SignalServiceAddress address, String avatarPath, ProfileKey profileKey ) throws IOException { - IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath()); - File outputFile = getProfileAvatarFile(address); - File tmpFile = IOUtils.createTempFile(); try (InputStream input = messageReceiver.retrieveProfileAvatar(avatarPath, tmpFile, profileKey, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { - // Use larger buffer size to prevent AssertionError: Need: 12272 but only have: 8192 ... - IOUtils.copyStreamToFile(input, outputFile, (int) ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE); + avatarStore.storeProfileAvatar(address, outputStream -> { + // Use larger buffer size to prevent AssertionError: Need: 12272 but only have: 8192 ... + IOUtils.copyStream(input, outputStream, (int) ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE); + }); } finally { try { Files.delete(tmpFile.toPath()); @@ -2188,37 +2178,57 @@ public class Manager implements Closeable { e.getMessage()); } } - return outputFile; } public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) { return new File(pathConfig.getAttachmentsPath(), attachmentId.toString()); } - private File retrieveAttachment(SignalServiceAttachmentPointer pointer) throws IOException, InvalidMessageException, MissingConfigurationException { + private void retrieveAttachment(SignalServiceAttachmentPointer pointer) throws IOException { IOUtils.createPrivateDirectories(pathConfig.getAttachmentsPath()); - return retrieveAttachment(pointer, getAttachmentFile(pointer.getRemoteId()), true); + retrieveAttachment(pointer, getAttachmentFile(pointer.getRemoteId())); } - private File retrieveAttachment( - SignalServiceAttachmentPointer pointer, File outputFile, boolean storePreview - ) throws IOException, InvalidMessageException, MissingConfigurationException { - if (storePreview && pointer.getPreview().isPresent()) { + private void retrieveAttachment( + SignalServiceAttachmentPointer pointer, File outputFile + ) throws IOException { + if (pointer.getPreview().isPresent()) { File previewFile = new File(outputFile + ".preview"); try (OutputStream output = new FileOutputStream(previewFile)) { byte[] preview = pointer.getPreview().get(); output.write(preview, 0, preview.length); } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; + logger.warn("Failed to retrieve attachment preview, ignoring: {}", e.getMessage()); } } + try (OutputStream output = new FileOutputStream(outputFile)) { + retrieveAttachment(pointer, output); + } + } + + private void retrieveAttachment( + final SignalServiceAttachment attachment, final OutputStream outputStream + ) throws IOException { + if (attachment.isPointer()) { + SignalServiceAttachmentPointer pointer = attachment.asPointer(); + retrieveAttachment(pointer, outputStream); + } else { + SignalServiceAttachmentStream stream = attachment.asStream(); + AttachmentUtils.retrieveAttachment(stream, outputStream); + } + } + + private void retrieveAttachment( + SignalServiceAttachmentPointer pointer, OutputStream outputStream + ) throws IOException { File tmpFile = IOUtils.createTempFile(); try (InputStream input = messageReceiver.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE)) { - IOUtils.copyStreamToFile(input, outputFile); + IOUtils.copyStream(input, outputStream); + } catch (MissingConfigurationException | InvalidMessageException e) { + throw new IOException(e); } finally { try { Files.delete(tmpFile.toPath()); @@ -2228,7 +2238,6 @@ public class Manager implements Closeable { e.getMessage()); } } - return outputFile; } private InputStream retrieveAttachmentAsStream( @@ -2299,7 +2308,7 @@ public class Manager implements Closeable { ProfileKey profileKey = account.getProfileStore().getProfileKey(record.getAddress()); out.write(new DeviceContact(record.getAddress(), Optional.fromNullable(record.name), - createContactAvatarAttachment(record.number), + createContactAvatarAttachment(record.getAddress()), Optional.fromNullable(record.color), Optional.fromNullable(verifiedMessage), Optional.fromNullable(profileKey), @@ -2492,10 +2501,6 @@ public class Manager implements Closeable { theirIdentityKey); } - void saveAccount() { - account.save(); - } - public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException { String canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 75c2fd61..d39da8a3 100644 --- a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -36,6 +36,7 @@ import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2Change import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -99,7 +100,7 @@ public class GroupHelper { } public GroupInfoV2 createGroupV2( - String name, Collection members, String avatarFile + String name, Collection members, File avatarFile ) throws IOException { final byte[] avatarBytes = readAvatarBytes(avatarFile); final GroupsV2Operations.NewGroup newGroup = buildNewGroupV2(name, members, avatarBytes); @@ -132,7 +133,7 @@ public class GroupHelper { return g; } - private byte[] readAvatarBytes(final String avatarFile) throws IOException { + private byte[] readAvatarBytes(final File avatarFile) throws IOException { final byte[] avatarBytes; try (InputStream avatar = avatarFile == null ? null : new FileInputStream(avatarFile)) { avatarBytes = avatar == null ? null : IOUtils.readFully(avatar); @@ -194,7 +195,7 @@ public class GroupHelper { } public Pair updateGroupV2( - GroupInfoV2 groupInfoV2, String name, String avatarFile + GroupInfoV2 groupInfoV2, String name, File avatarFile ) throws IOException { final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); diff --git a/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java index 48a38578..1ec2eeaa 100644 --- a/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java +++ b/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java @@ -5,8 +5,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import java.io.File; - public class SignalProfile { @JsonProperty @@ -15,8 +13,6 @@ public class SignalProfile { @JsonProperty private final String name; - private final File avatarFile; - @JsonProperty private final String unidentifiedAccess; @@ -29,14 +25,12 @@ public class SignalProfile { public SignalProfile( final String identityKey, final String name, - final File avatarFile, final String unidentifiedAccess, final boolean unrestrictedUnidentifiedAccess, final SignalServiceProfile.Capabilities capabilities ) { this.identityKey = identityKey; this.name = name; - this.avatarFile = avatarFile; this.unidentifiedAccess = unidentifiedAccess; this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess; this.capabilities = new Capabilities(); @@ -54,7 +48,6 @@ public class SignalProfile { ) { this.identityKey = identityKey; this.name = name; - this.avatarFile = null; this.unidentifiedAccess = unidentifiedAccess; this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess; this.capabilities = capabilities; @@ -68,10 +61,6 @@ public class SignalProfile { return name; } - public File getAvatarFile() { - return avatarFile; - } - public String getUnidentifiedAccess() { return unidentifiedAccess; } @@ -94,7 +83,6 @@ public class SignalProfile { + name + '\'' + ", avatarFile=" - + avatarFile + ", unidentifiedAccess='" + unidentifiedAccess + '\'' diff --git a/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java b/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java index b9a97073..4be85ddd 100644 --- a/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java @@ -4,12 +4,10 @@ import org.asamk.signal.manager.AttachmentInvalidException; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; +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.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -34,19 +32,23 @@ public class AttachmentUtils { } public static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { - InputStream attachmentStream = new FileInputStream(attachmentFile); - final long attachmentSize = attachmentFile.length(); - final String mime = Utils.getFileMimeType(attachmentFile, "application/octet-stream"); + final StreamDetails streamDetails = Utils.createStreamDetailsFromFile(attachmentFile); + return createAttachment(streamDetails, Optional.of(attachmentFile.getName())); + } + + public static SignalServiceAttachmentStream createAttachment( + StreamDetails streamDetails, Optional name + ) { // TODO mabybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option final long uploadTimestamp = System.currentTimeMillis(); Optional preview = Optional.absent(); Optional caption = Optional.absent(); Optional blurHash = Optional.absent(); final Optional resumableUploadSpec = Optional.absent(); - return new SignalServiceAttachmentStream(attachmentStream, - mime, - attachmentSize, - Optional.of(attachmentFile.getName()), + return new SignalServiceAttachmentStream(streamDetails.getStream(), + streamDetails.getContentType(), + streamDetails.getLength(), + name, false, false, preview, @@ -60,20 +62,10 @@ public class AttachmentUtils { resumableUploadSpec); } - public static File retrieveAttachment(SignalServiceAttachmentStream stream, File outputFile) throws IOException { + public static void retrieveAttachment( + SignalServiceAttachmentStream stream, OutputStream output + ) throws IOException { InputStream input = stream.getInputStream(); - - try (OutputStream output = new FileOutputStream(outputFile)) { - byte[] buffer = new byte[4096]; - int read; - - while ((read = input.read(buffer)) != -1) { - output.write(buffer, 0, read); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; - } - return outputFile; + IOUtils.copyStream(input, output); } } diff --git a/src/main/java/org/asamk/signal/manager/util/IOUtils.java b/src/main/java/org/asamk/signal/manager/util/IOUtils.java index c0918ae7..8f47c9f4 100644 --- a/src/main/java/org/asamk/signal/manager/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/IOUtils.java @@ -1,10 +1,8 @@ package org.asamk.signal.manager.util; -import org.whispersystems.signalservice.internal.util.Util; - import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileOutputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -29,7 +27,7 @@ public class IOUtils { public static byte[] readFully(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Util.copy(in, baos); + IOUtils.copyStream(in, baos); return baos.toByteArray(); } @@ -57,18 +55,22 @@ public class IOUtils { } } - public static void copyStreamToFile(InputStream input, File outputFile) throws IOException { - copyStreamToFile(input, outputFile, 8192); + public static void copyFileToStream(File inputFile, OutputStream output) throws IOException { + try (InputStream inputStream = new FileInputStream(inputFile)) { + copyStream(inputStream, output); + } } - public static void copyStreamToFile(InputStream input, File outputFile, int bufferSize) throws IOException { - try (OutputStream output = new FileOutputStream(outputFile)) { - byte[] buffer = new byte[bufferSize]; - int read; + public static void copyStream(InputStream input, OutputStream output) throws IOException { + copyStream(input, output, 4096); + } - while ((read = input.read(buffer)) != -1) { - output.write(buffer, 0, read); - } + public static void copyStream(InputStream input, OutputStream output, int bufferSize) throws IOException { + byte[] buffer = new byte[bufferSize]; + int read; + + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); } } } diff --git a/src/main/java/org/asamk/signal/manager/util/Utils.java b/src/main/java/org/asamk/signal/manager/util/Utils.java index e68b5ce3..65f2811b 100644 --- a/src/main/java/org/asamk/signal/manager/util/Utils.java +++ b/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -36,10 +36,7 @@ public class Utils { public static StreamDetails createStreamDetailsFromFile(File file) throws IOException { InputStream stream = new FileInputStream(file); final long size = file.length(); - String mime = Files.probeContentType(file.toPath()); - if (mime == null) { - mime = "application/octet-stream"; - } + final String mime = getFileMimeType(file, "application/octet-stream"); return new StreamDetails(stream, mime, size); } From 9bba7a85ab6fbe7cc8de6ece4b3850c883907e01 Mon Sep 17 00:00:00 2001 From: Atomic-Bean <75401809+Atomic-Bean@users.noreply.github.com> Date: Fri, 15 Jan 2021 03:40:35 +1030 Subject: [PATCH 026/124] Added sticker field to json output. Also added hidden mac file to .gitignore (#418) --- .gitignore | 1 + .../org/asamk/signal/json/JsonDataMessage.java | 10 ++++++++-- .../org/asamk/signal/json/JsonSticker.java | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/asamk/signal/json/JsonSticker.java diff --git a/.gitignore b/.gitignore index 3dc9875b..8fa9c8bd 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ local.properties .project .settings/ out/ +.DS_Store diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 57201eda..787f47ab 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -19,6 +19,7 @@ class JsonDataMessage { JsonQuote quote; List mentions; List attachments; + JsonSticker sticker; JsonGroupInfo groupInfo; JsonDataMessage(SignalServiceDataMessage dataMessage, Manager m) { @@ -60,15 +61,19 @@ class JsonDataMessage { } else { this.attachments = List.of(); } + if (dataMessage.getSticker().isPresent()) { + this.sticker = new JsonSticker(dataMessage.getSticker().get()); + } } public JsonDataMessage(Signal.MessageReceived messageReceived) { timestamp = messageReceived.getTimestamp(); message = messageReceived.getMessage(); groupInfo = new JsonGroupInfo(messageReceived.getGroupId()); - reaction = null; // TODO Replace these 3 with the proper commands + reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; + sticker = null; attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); } @@ -76,9 +81,10 @@ class JsonDataMessage { timestamp = messageReceived.getTimestamp(); message = messageReceived.getMessage(); groupInfo = new JsonGroupInfo(messageReceived.getGroupId()); - reaction = null; // TODO Replace these 3 with the proper commands + reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; + sticker = null; attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); } } diff --git a/src/main/java/org/asamk/signal/json/JsonSticker.java b/src/main/java/org/asamk/signal/json/JsonSticker.java new file mode 100644 index 00000000..228d2883 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonSticker.java @@ -0,0 +1,18 @@ +package org.asamk.signal.json; + +import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.whispersystems.util.Base64; + +public class JsonSticker { + + String packId; + String packKey; + int stickerId; + + public JsonSticker(SignalServiceDataMessage.Sticker sticker) { + this.packId = Base64.encodeBytes(sticker.getPackId()); + this.packKey = Base64.encodeBytes(sticker.getPackKey()); + this.stickerId = sticker.getStickerId(); + // TODO also download sticker image ?? + } +} From 9bb935b11f8e632f48140bc6cf53c6867cf10cbd Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 14 Jan 2021 18:41:21 +0100 Subject: [PATCH 027/124] Always include profile key in direct messages Adjusts behavior to official client Fixes #416 --- src/main/java/org/asamk/signal/manager/Manager.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 23e37eef..426ee885 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -1342,16 +1342,12 @@ public class Manager implements Closeable { } } else { // Send to all individually, so sync messages are sent correctly + messageBuilder.withProfileKey(account.getProfileKey().serialize()); List results = new ArrayList<>(recipients.size()); for (SignalServiceAddress address : recipients) { - ContactInfo contact = account.getContactStore().getContact(address); - if (contact != null) { - messageBuilder.withExpiration(contact.messageExpirationTime); - messageBuilder.withProfileKey(account.getProfileKey().serialize()); - } else { - messageBuilder.withExpiration(0); - messageBuilder.withProfileKey(null); - } + final ContactInfo contact = account.getContactStore().getContact(address); + final int expirationTime = contact != null ? contact.messageExpirationTime : 0; + messageBuilder.withExpiration(expirationTime); message = messageBuilder.build(); if (address.matches(account.getSelfAddress())) { results.add(sendSelfMessage(message)); From 96d316b1dd1fec2959ae2ba1f27b68ceb355d7f4 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 14 Jan 2021 21:26:01 +0100 Subject: [PATCH 028/124] Create an AttachmentStore --- .../asamk/signal/manager/AttachmentStore.java | 55 +++++ .../org/asamk/signal/manager/Manager.java | 211 ++++++++---------- .../signal/manager/util/AttachmentUtils.java | 9 - 3 files changed, 149 insertions(+), 126 deletions(-) create mode 100644 src/main/java/org/asamk/signal/manager/AttachmentStore.java diff --git a/src/main/java/org/asamk/signal/manager/AttachmentStore.java b/src/main/java/org/asamk/signal/manager/AttachmentStore.java new file mode 100644 index 00000000..f983a90b --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/AttachmentStore.java @@ -0,0 +1,55 @@ +package org.asamk.signal.manager; + +import org.asamk.signal.manager.util.IOUtils; +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class AttachmentStore { + + private final File attachmentsPath; + + public AttachmentStore(final File attachmentsPath) { + this.attachmentsPath = attachmentsPath; + } + + public void storeAttachmentPreview( + final SignalServiceAttachmentRemoteId attachmentId, final AttachmentStorer storer + ) throws IOException { + storeAttachment(getAttachmentPreviewFile(attachmentId), storer); + } + + public void storeAttachment( + final SignalServiceAttachmentRemoteId attachmentId, final AttachmentStorer storer + ) throws IOException { + storeAttachment(getAttachmentFile(attachmentId), storer); + } + + private void storeAttachment(final File attachmentFile, final AttachmentStorer storer) throws IOException { + createAttachmentsDir(); + try (OutputStream output = new FileOutputStream(attachmentFile)) { + storer.store(output); + } + } + + private File getAttachmentPreviewFile(SignalServiceAttachmentRemoteId attachmentId) { + return new File(attachmentsPath, attachmentId.toString() + ".preview"); + } + + public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) { + return new File(attachmentsPath, attachmentId.toString()); + } + + private void createAttachmentsDir() throws IOException { + IOUtils.createPrivateDirectories(attachmentsPath); + } + + @FunctionalInterface + public interface AttachmentStorer { + + void store(OutputStream outputStream) throws IOException; + } +} diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 426ee885..977ff8b7 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -146,7 +146,6 @@ import org.whispersystems.util.Base64; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -188,7 +187,6 @@ public class Manager implements Closeable { private final String userAgent; private SignalAccount account; - private final PathConfig pathConfig; private final SignalServiceAccountManager accountManager; private final GroupsV2Api groupsV2Api; private final GroupsV2Operations groupsV2Operations; @@ -203,6 +201,7 @@ public class Manager implements Closeable { private final GroupHelper groupHelper; private final PinHelper pinHelper; private final AvatarStore avatarStore; + private final AttachmentStore attachmentStore; Manager( SignalAccount account, @@ -211,7 +210,6 @@ public class Manager implements Closeable { String userAgent ) { this.account = account; - this.pathConfig = pathConfig; this.serviceConfiguration = serviceConfiguration; this.userAgent = userAgent; this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( @@ -259,6 +257,7 @@ public class Manager implements Closeable { groupsV2Api, this::getGroupAuthForToday); this.avatarStore = new AvatarStore(pathConfig.getAvatarsPath()); + this.attachmentStore = new AttachmentStore(pathConfig.getAttachmentsPath()); } public String getUsername() { @@ -497,17 +496,20 @@ public class Manager implements Closeable { if (!profileEntry.isRequestPending() && ( profileEntry.getProfile() == null || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000 )) { - ProfileKey profileKey = profileEntry.getProfileKey(); profileEntry.setRequestPending(true); - SignalProfile profile; + final SignalServiceProfile encryptedProfile; try { - profile = retrieveRecipientProfile(address, profileKey); + encryptedProfile = profileHelper.retrieveProfileSync(address, SignalServiceProfile.RequestType.PROFILE) + .getProfile(); } catch (IOException e) { logger.warn("Failed to retrieve profile, ignoring: {}", e.getMessage()); - profileEntry.setRequestPending(false); return null; + } finally { + profileEntry.setRequestPending(false); } - profileEntry.setRequestPending(false); + + ProfileKey profileKey = profileEntry.getProfileKey(); + SignalProfile profile = decryptProfile(address, profileKey, encryptedProfile); account.getProfileStore() .updateProfile(address, profileKey, now, profile, profileEntry.getProfileKeyCredential()); return profile; @@ -542,24 +544,11 @@ public class Manager implements Closeable { return profileEntry.getProfileKeyCredential(); } - private SignalProfile retrieveRecipientProfile( - SignalServiceAddress address, ProfileKey profileKey - ) throws IOException { - final SignalServiceProfile encryptedProfile = profileHelper.retrieveProfileSync(address, - SignalServiceProfile.RequestType.PROFILE).getProfile(); - - return decryptProfile(address, profileKey, encryptedProfile); - } - private SignalProfile decryptProfile( final SignalServiceAddress address, final ProfileKey profileKey, final SignalServiceProfile encryptedProfile ) { if (encryptedProfile.getAvatar() != null) { - try { - retrieveProfileAvatar(address, encryptedProfile.getAvatar(), profileKey); - } catch (Throwable e) { - logger.warn("Failed to retrieve profile avatar, ignoring: {}", e.getMessage()); - } + downloadProfileAvatar(address, encryptedProfile.getAvatar(), profileKey); } ProfileCipher profileCipher = new ProfileCipher(profileKey); @@ -1482,15 +1471,7 @@ public class Manager implements Closeable { if (groupInfo.getAvatar().isPresent()) { SignalServiceAttachment avatar = groupInfo.getAvatar().get(); - if (avatar.isPointer()) { - try { - retrieveGroupAvatarAttachment(avatar.asPointer(), groupV1.getGroupId()); - } catch (IOException e) { - logger.warn("Failed to retrieve avatar for group {}, ignoring: {}", - groupId.toBase64(), - e.getMessage()); - } - } + downloadGroupAvatar(avatar, groupV1.getGroupId()); } if (groupInfo.getName().isPresent()) { @@ -1571,15 +1552,7 @@ public class Manager implements Closeable { } if (message.getAttachments().isPresent() && !ignoreAttachments) { for (SignalServiceAttachment attachment : message.getAttachments().get()) { - if (attachment.isPointer()) { - try { - retrieveAttachment(attachment.asPointer()); - } catch (IOException e) { - logger.warn("Failed to retrieve attachment ({}), ignoring: {}", - attachment.asPointer().getRemoteId(), - e.getMessage()); - } - } + downloadAttachment(attachment); } } if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { @@ -1597,15 +1570,8 @@ public class Manager implements Closeable { if (message.getPreviews().isPresent()) { final List previews = message.getPreviews().get(); for (SignalServiceDataMessage.Preview preview : previews) { - if (preview.getImage().isPresent() && preview.getImage().get().isPointer()) { - SignalServiceAttachmentPointer attachment = preview.getImage().get().asPointer(); - try { - retrieveAttachment(attachment); - } catch (IOException e) { - logger.warn("Failed to retrieve preview image ({}), ignoring: {}", - attachment.getRemoteId(), - e.getMessage()); - } + if (preview.getImage().isPresent()) { + downloadAttachment(preview.getImage().get()); } } } @@ -1613,15 +1579,9 @@ public class Manager implements Closeable { final SignalServiceDataMessage.Quote quote = message.getQuote().get(); for (SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment : quote.getAttachments()) { - final SignalServiceAttachment attachment = quotedAttachment.getThumbnail(); - if (attachment != null && attachment.isPointer()) { - try { - retrieveAttachment(attachment.asPointer()); - } catch (IOException e) { - logger.warn("Failed to retrieve quote attachment thumbnail ({}), ignoring: {}", - attachment.asPointer().getRemoteId(), - e.getMessage()); - } + final SignalServiceAttachment thumbnail = quotedAttachment.getThumbnail(); + if (thumbnail != null) { + downloadAttachment(thumbnail); } } } @@ -1671,11 +1631,7 @@ public class Manager implements Closeable { storeProfileKeysFromMembers(group); final String avatar = group.getAvatar(); if (avatar != null && !avatar.isEmpty()) { - try { - retrieveGroupAvatar(groupId, groupSecretParams, avatar); - } catch (IOException e) { - logger.warn("Failed to download group avatar, ignoring: {}", e.getMessage()); - } + downloadGroupAvatar(groupId, groupSecretParams, avatar); } } groupInfoV2.setGroup(group); @@ -1949,9 +1905,9 @@ public class Manager implements Closeable { File tmpFile = null; try { tmpFile = IOUtils.createTempFile(); - try (InputStream attachmentAsStream = retrieveAttachmentAsStream(syncMessage.getGroups() - .get() - .asPointer(), tmpFile)) { + final SignalServiceAttachment groupsMessage = syncMessage.getGroups().get(); + try (InputStream attachmentAsStream = retrieveAttachmentAsStream(groupsMessage.asPointer(), + tmpFile)) { DeviceGroupsInputStream s = new DeviceGroupsInputStream(attachmentAsStream); DeviceGroup g; while ((g = s.read()) != null) { @@ -1977,7 +1933,7 @@ public class Manager implements Closeable { } if (g.getAvatar().isPresent()) { - retrieveGroupAvatarAttachment(g.getAvatar().get(), syncGroup.getGroupId()); + downloadGroupAvatar(g.getAvatar().get(), syncGroup.getGroupId()); } syncGroup.inboxPosition = g.getInboxPosition().orNull(); syncGroup.archived = g.isArchived(); @@ -2065,7 +2021,7 @@ public class Manager implements Closeable { account.getContactStore().updateContact(contact); if (c.getAvatar().isPresent()) { - retrieveContactAvatarAttachment(c.getAvatar().get(), contact.getAddress()); + downloadContactAvatar(c.getAvatar().get(), contact.getAddress()); } } } @@ -2117,20 +2073,72 @@ public class Manager implements Closeable { return actions; } - private void retrieveContactAvatarAttachment( - SignalServiceAttachment attachment, SignalServiceAddress address - ) throws IOException { - avatarStore.storeContactAvatar(address, outputStream -> retrieveAttachment(attachment, outputStream)); + private void downloadContactAvatar(SignalServiceAttachment avatar, SignalServiceAddress address) { + try { + avatarStore.storeContactAvatar(address, outputStream -> retrieveAttachment(avatar, outputStream)); + } catch (IOException e) { + logger.warn("Failed to download avatar for contact {}, ignoring: {}", address, e.getMessage()); + } } - private void retrieveGroupAvatarAttachment( - SignalServiceAttachment attachment, GroupId groupId - ) throws IOException { - avatarStore.storeGroupAvatar(groupId, outputStream -> retrieveAttachment(attachment, outputStream)); + private void downloadGroupAvatar(SignalServiceAttachment avatar, GroupId groupId) { + try { + avatarStore.storeGroupAvatar(groupId, outputStream -> retrieveAttachment(avatar, outputStream)); + } catch (IOException e) { + logger.warn("Failed to download avatar for group {}, ignoring: {}", groupId.toBase64(), e.getMessage()); + } } - private void retrieveGroupAvatar( - GroupId groupId, GroupSecretParams groupSecretParams, String cdnKey + private void downloadGroupAvatar(GroupId groupId, GroupSecretParams groupSecretParams, String cdnKey) { + try { + avatarStore.storeGroupAvatar(groupId, + outputStream -> retrieveGroupV2Avatar(groupSecretParams, cdnKey, outputStream)); + } catch (IOException e) { + logger.warn("Failed to download avatar for group {}, ignoring: {}", groupId.toBase64(), e.getMessage()); + } + } + + private void downloadProfileAvatar( + SignalServiceAddress address, String avatarPath, ProfileKey profileKey + ) { + try { + avatarStore.storeProfileAvatar(address, + outputStream -> retrieveProfileAvatar(avatarPath, profileKey, outputStream)); + } catch (Throwable e) { + logger.warn("Failed to download profile avatar, ignoring: {}", e.getMessage()); + } + } + + public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) { + return attachmentStore.getAttachmentFile(attachmentId); + } + + private void downloadAttachment(final SignalServiceAttachment attachment) { + if (!attachment.isPointer()) { + logger.warn("Invalid state, can't store an attachment stream."); + } + + SignalServiceAttachmentPointer pointer = attachment.asPointer(); + if (pointer.getPreview().isPresent()) { + final byte[] preview = pointer.getPreview().get(); + try { + attachmentStore.storeAttachmentPreview(pointer.getRemoteId(), + outputStream -> outputStream.write(preview, 0, preview.length)); + } catch (IOException e) { + logger.warn("Failed to download attachment preview, ignoring: {}", e.getMessage()); + } + } + + try { + attachmentStore.storeAttachment(pointer.getRemoteId(), + outputStream -> retrieveAttachmentPointer(pointer, outputStream)); + } catch (IOException e) { + logger.warn("Failed to download attachment ({}), ignoring: {}", pointer.getRemoteId(), e.getMessage()); + } + } + + private void retrieveGroupV2Avatar( + GroupSecretParams groupSecretParams, String cdnKey, OutputStream outputStream ) throws IOException { GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); @@ -2141,7 +2149,7 @@ public class Manager implements Closeable { byte[] encryptedData = IOUtils.readFully(input); byte[] decryptedData = groupOperations.decryptAvatar(encryptedData); - avatarStore.storeGroupAvatar(groupId, outputStream -> outputStream.write(decryptedData)); + outputStream.write(decryptedData); } finally { try { Files.delete(tmpFile.toPath()); @@ -2154,17 +2162,15 @@ public class Manager implements Closeable { } private void retrieveProfileAvatar( - SignalServiceAddress address, String avatarPath, ProfileKey profileKey + String avatarPath, ProfileKey profileKey, OutputStream outputStream ) throws IOException { File tmpFile = IOUtils.createTempFile(); try (InputStream input = messageReceiver.retrieveProfileAvatar(avatarPath, tmpFile, profileKey, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { - avatarStore.storeProfileAvatar(address, outputStream -> { - // Use larger buffer size to prevent AssertionError: Need: 12272 but only have: 8192 ... - IOUtils.copyStream(input, outputStream, (int) ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE); - }); + // Use larger buffer size to prevent AssertionError: Need: 12272 but only have: 8192 ... + IOUtils.copyStream(input, outputStream, (int) ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE); } finally { try { Files.delete(tmpFile.toPath()); @@ -2176,52 +2182,23 @@ public class Manager implements Closeable { } } - public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) { - return new File(pathConfig.getAttachmentsPath(), attachmentId.toString()); - } - - private void retrieveAttachment(SignalServiceAttachmentPointer pointer) throws IOException { - IOUtils.createPrivateDirectories(pathConfig.getAttachmentsPath()); - retrieveAttachment(pointer, getAttachmentFile(pointer.getRemoteId())); - } - - private void retrieveAttachment( - SignalServiceAttachmentPointer pointer, File outputFile - ) throws IOException { - if (pointer.getPreview().isPresent()) { - File previewFile = new File(outputFile + ".preview"); - try (OutputStream output = new FileOutputStream(previewFile)) { - byte[] preview = pointer.getPreview().get(); - output.write(preview, 0, preview.length); - } catch (FileNotFoundException e) { - logger.warn("Failed to retrieve attachment preview, ignoring: {}", e.getMessage()); - } - } - - try (OutputStream output = new FileOutputStream(outputFile)) { - retrieveAttachment(pointer, output); - } - } - private void retrieveAttachment( final SignalServiceAttachment attachment, final OutputStream outputStream ) throws IOException { if (attachment.isPointer()) { SignalServiceAttachmentPointer pointer = attachment.asPointer(); - retrieveAttachment(pointer, outputStream); + retrieveAttachmentPointer(pointer, outputStream); } else { SignalServiceAttachmentStream stream = attachment.asStream(); - AttachmentUtils.retrieveAttachment(stream, outputStream); + IOUtils.copyStream(stream.getInputStream(), outputStream); } } - private void retrieveAttachment( + private void retrieveAttachmentPointer( SignalServiceAttachmentPointer pointer, OutputStream outputStream ) throws IOException { File tmpFile = IOUtils.createTempFile(); - try (InputStream input = messageReceiver.retrieveAttachment(pointer, - tmpFile, - ServiceConfig.MAX_ATTACHMENT_SIZE)) { + try (InputStream input = retrieveAttachmentAsStream(pointer, tmpFile)) { IOUtils.copyStream(input, outputStream); } catch (MissingConfigurationException | InvalidMessageException e) { throw new IOException(e); diff --git a/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java b/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java index 4be85ddd..ec043cfd 100644 --- a/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java @@ -9,8 +9,6 @@ import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.List; @@ -61,11 +59,4 @@ public class AttachmentUtils { null, resumableUploadSpec); } - - public static void retrieveAttachment( - SignalServiceAttachmentStream stream, OutputStream output - ) throws IOException { - InputStream input = stream.getInputStream(); - IOUtils.copyStream(input, output); - } } From c95897d17b6ed21d345cc860c8afd6d685d2b4a1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 14 Jan 2021 21:26:20 +0100 Subject: [PATCH 029/124] Fix file name for contact avatars --- src/main/java/org/asamk/signal/manager/AvatarStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/manager/AvatarStore.java b/src/main/java/org/asamk/signal/manager/AvatarStore.java index fbb99a1c..b7244ce2 100644 --- a/src/main/java/org/asamk/signal/manager/AvatarStore.java +++ b/src/main/java/org/asamk/signal/manager/AvatarStore.java @@ -72,7 +72,7 @@ public class AvatarStore { } private File getContactAvatarFile(SignalServiceAddress address) { - return new File(avatarsPath, "contact-" + address); + return new File(avatarsPath, "contact-" + address.getLegacyIdentifier()); } private File getProfileAvatarFile(SignalServiceAddress address) { From 5760a538310823ee7644b1284b784baf6ad92ff1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 14 Jan 2021 22:19:37 +0100 Subject: [PATCH 030/124] Update man page --- man/signal-cli.1.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 35070270..df5f75c3 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -21,6 +21,9 @@ For registering you need a phone number where you can receive SMS or incoming ca signal-cli was primarily developed to be used on servers to notify admins of important events. For this use-case, it has a dbus interface, that can be used to send messages from any programming language that has dbus bindings. +For some functionality the Signal protocol requires that all messages have been received from the server. +The `receive` command should be regularly executed. In daemon mode messages are continuously received. + == Options *-h*, *--help*:: From 20681b8d89ebaf1d437c4f6b57fefeb83dc8de45 Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 14 Jan 2021 22:18:40 +0100 Subject: [PATCH 031/124] Extract some utils from manager --- .../asamk/signal/manager/HandleAction.java | 8 +- .../asamk/signal/manager/JsonStickerPack.java | 2 +- .../org/asamk/signal/manager/Manager.java | 330 ++++++------------ .../signal/manager/storage/SignalAccount.java | 4 + .../signal/manager/util/ProfileUtils.java | 45 +++ .../signal/manager/util/StickerUtils.java | 113 ++++++ 6 files changed, 271 insertions(+), 231 deletions(-) create mode 100644 src/main/java/org/asamk/signal/manager/util/ProfileUtils.java create mode 100644 src/main/java/org/asamk/signal/manager/util/StickerUtils.java diff --git a/src/main/java/org/asamk/signal/manager/HandleAction.java b/src/main/java/org/asamk/signal/manager/HandleAction.java index 0dd151a9..8338e4e6 100644 --- a/src/main/java/org/asamk/signal/manager/HandleAction.java +++ b/src/main/java/org/asamk/signal/manager/HandleAction.java @@ -124,19 +124,19 @@ class SendGroupInfoRequestAction implements HandleAction { } } -class SendGroupUpdateAction implements HandleAction { +class SendGroupInfoAction implements HandleAction { private final SignalServiceAddress address; private final GroupIdV1 groupId; - public SendGroupUpdateAction(final SignalServiceAddress address, final GroupIdV1 groupId) { + public SendGroupInfoAction(final SignalServiceAddress address, final GroupIdV1 groupId) { this.address = address; this.groupId = groupId; } @Override public void execute(Manager m) throws Throwable { - m.sendUpdateGroupMessage(groupId, address); + m.sendGroupInfoMessage(groupId, address); } @Override @@ -144,7 +144,7 @@ class SendGroupUpdateAction implements HandleAction { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - final SendGroupUpdateAction that = (SendGroupUpdateAction) o; + final SendGroupInfoAction that = (SendGroupInfoAction) o; if (!address.equals(that.address)) return false; return groupId.equals(that.groupId); diff --git a/src/main/java/org/asamk/signal/manager/JsonStickerPack.java b/src/main/java/org/asamk/signal/manager/JsonStickerPack.java index a7e5eb7f..e5e0e445 100644 --- a/src/main/java/org/asamk/signal/manager/JsonStickerPack.java +++ b/src/main/java/org/asamk/signal/manager/JsonStickerPack.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; -class JsonStickerPack { +public class JsonStickerPack { @JsonProperty public String title; diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 977ff8b7..a0f6aa53 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -16,8 +16,6 @@ */ package org.asamk.signal.manager; -import com.fasterxml.jackson.databind.ObjectMapper; - import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.groups.GroupIdV2; @@ -42,6 +40,8 @@ import org.asamk.signal.manager.storage.stickers.Sticker; import org.asamk.signal.manager.util.AttachmentUtils; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.KeyUtils; +import org.asamk.signal.manager.util.ProfileUtils; +import org.asamk.signal.manager.util.StickerUtils; import org.asamk.signal.manager.util.Utils; import org.signal.libsignal.metadata.InvalidMetadataMessageException; import org.signal.libsignal.metadata.InvalidMetadataVersionException; @@ -84,8 +84,6 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessagePipe; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; -import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; -import org.whispersystems.signalservice.api.crypto.ProfileCipher; import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; @@ -107,7 +105,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload.StickerInfo; import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact; @@ -141,7 +138,6 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.Hex; -import org.whispersystems.util.Base64; import java.io.Closeable; import java.io.File; @@ -170,8 +166,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import static org.asamk.signal.manager.ServiceConfig.CDS_MRENCLAVE; import static org.asamk.signal.manager.ServiceConfig.capabilities; @@ -492,7 +486,7 @@ public class Manager implements Closeable { return null; } long now = new Date().getTime(); - // Profiles are cache for 24h before retrieving them again + // Profiles are cached for 24h before retrieving them again if (!profileEntry.isRequestPending() && ( profileEntry.getProfile() == null || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000 )) { @@ -508,8 +502,8 @@ public class Manager implements Closeable { profileEntry.setRequestPending(false); } - ProfileKey profileKey = profileEntry.getProfileKey(); - SignalProfile profile = decryptProfile(address, profileKey, encryptedProfile); + final ProfileKey profileKey = profileEntry.getProfileKey(); + final SignalProfile profile = decryptProfileAndDownloadAvatar(address, profileKey, encryptedProfile); account.getProfileStore() .updateProfile(address, profileKey, now, profile, profileEntry.getProfileKeyCredential()); return profile; @@ -534,7 +528,7 @@ public class Manager implements Closeable { long now = new Date().getTime(); final ProfileKeyCredential profileKeyCredential = profileAndCredential.getProfileKeyCredential().orNull(); - final SignalProfile profile = decryptProfile(address, + final SignalProfile profile = decryptProfileAndDownloadAvatar(address, profileEntry.getProfileKey(), profileAndCredential.getProfile()); account.getProfileStore() @@ -544,40 +538,14 @@ public class Manager implements Closeable { return profileEntry.getProfileKeyCredential(); } - private SignalProfile decryptProfile( + private SignalProfile decryptProfileAndDownloadAvatar( final SignalServiceAddress address, final ProfileKey profileKey, final SignalServiceProfile encryptedProfile ) { if (encryptedProfile.getAvatar() != null) { downloadProfileAvatar(address, encryptedProfile.getAvatar(), profileKey); } - ProfileCipher profileCipher = new ProfileCipher(profileKey); - try { - String name; - try { - name = encryptedProfile.getName() == null - ? null - : new String(profileCipher.decryptName(Base64.decode(encryptedProfile.getName()))); - } catch (IOException e) { - name = null; - } - String unidentifiedAccess; - try { - unidentifiedAccess = encryptedProfile.getUnidentifiedAccess() == null - || !profileCipher.verifyUnidentifiedAccess(Base64.decode(encryptedProfile.getUnidentifiedAccess())) - ? null - : encryptedProfile.getUnidentifiedAccess(); - } catch (IOException e) { - unidentifiedAccess = null; - } - return new SignalProfile(encryptedProfile.getIdentityKey(), - name, - unidentifiedAccess, - encryptedProfile.isUnrestrictedUnidentifiedAccess(), - encryptedProfile.getCapabilities()); - } catch (InvalidCiphertextException e) { - return null; - } + return ProfileUtils.decryptProfile(profileKey, encryptedProfile); } private Optional createGroupAvatarAttachment(GroupId groupId) throws IOException { @@ -624,17 +592,6 @@ public class Manager implements Closeable { return account.getGroupStore().getGroups(); } - public Pair> sendGroupMessage( - SignalServiceDataMessage.Builder messageBuilder, GroupId groupId - ) throws IOException, GroupNotFoundException, NotAGroupMemberException { - final GroupInfo g = getGroupForSending(groupId); - - GroupUtils.setGroupContext(messageBuilder, g); - messageBuilder.withExpiration(g.getMessageExpirationTime()); - - return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfAddress())); - } - public Pair> sendGroupMessage( String messageText, List attachments, GroupId groupId ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException { @@ -660,8 +617,18 @@ public class Manager implements Closeable { return sendGroupMessage(messageBuilder, groupId); } - public Pair> sendQuitGroupMessage(GroupId groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException { + public Pair> sendGroupMessage( + SignalServiceDataMessage.Builder messageBuilder, GroupId groupId + ) throws IOException, GroupNotFoundException, NotAGroupMemberException { + final GroupInfo g = getGroupForSending(groupId); + GroupUtils.setGroupContext(messageBuilder, g); + messageBuilder.withExpiration(g.getMessageExpirationTime()); + + return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfAddress())); + } + + public Pair> sendQuitGroupMessage(GroupId groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException { SignalServiceDataMessage.Builder messageBuilder; final GroupInfo g = getGroupForUpdating(groupId); @@ -684,6 +651,15 @@ public class Manager implements Closeable { return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfAddress())); } + public Pair> updateGroup( + GroupId groupId, String name, List members, File avatarFile + ) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException { + return sendUpdateGroupMessage(groupId, + name, + members == null ? null : getSignalServiceAddresses(members), + avatarFile); + } + private Pair> sendUpdateGroupMessage( GroupId groupId, String name, Collection members, File avatarFile ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException { @@ -764,44 +740,6 @@ public class Manager implements Closeable { return new Pair<>(g.getGroupId(), result.second()); } - public Pair> joinGroup( - GroupInviteLinkUrl inviteLinkUrl - ) throws IOException, GroupLinkNotActiveException { - return sendJoinGroupMessage(inviteLinkUrl); - } - - private Pair> sendJoinGroupMessage( - GroupInviteLinkUrl inviteLinkUrl - ) throws IOException, GroupLinkNotActiveException { - final DecryptedGroupJoinInfo groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), - inviteLinkUrl.getPassword()); - final GroupChange groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(), - inviteLinkUrl.getPassword(), - groupJoinInfo); - final GroupInfoV2 group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), - groupJoinInfo.getRevision() + 1, - groupChange.toByteArray()); - - if (group.getGroup() == null) { - // Only requested member, can't send update to group members - return new Pair<>(group.getGroupId(), List.of()); - } - - final Pair> result = sendUpdateGroupMessage(group, group.getGroup(), groupChange); - - return new Pair<>(group.getGroupId(), result.second()); - } - - private Pair> sendUpdateGroupMessage( - GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange - ) throws IOException { - group.setGroup(newDecryptedGroup); - final SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(group, - groupChange.toByteArray()); - account.getGroupStore().updateGroup(group); - return sendMessage(messageBuilder, group.getMembersIncludingPendingWithout(account.getSelfAddress())); - } - private void updateGroupV1( final GroupInfoV1 g, final String name, @@ -841,7 +779,67 @@ public class Manager implements Closeable { } } - Pair> sendUpdateGroupMessage( + public Pair> joinGroup( + GroupInviteLinkUrl inviteLinkUrl + ) throws IOException, GroupLinkNotActiveException { + return sendJoinGroupMessage(inviteLinkUrl); + } + + private Pair> sendJoinGroupMessage( + GroupInviteLinkUrl inviteLinkUrl + ) throws IOException, GroupLinkNotActiveException { + final DecryptedGroupJoinInfo groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), + inviteLinkUrl.getPassword()); + final GroupChange groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(), + inviteLinkUrl.getPassword(), + groupJoinInfo); + final GroupInfoV2 group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), + groupJoinInfo.getRevision() + 1, + groupChange.toByteArray()); + + if (group.getGroup() == null) { + // Only requested member, can't send update to group members + return new Pair<>(group.getGroupId(), List.of()); + } + + final Pair> result = sendUpdateGroupMessage(group, group.getGroup(), groupChange); + + return new Pair<>(group.getGroupId(), result.second()); + } + + private static int currentTimeDays() { + return (int) TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()); + } + + private GroupsV2AuthorizationString getGroupAuthForToday( + final GroupSecretParams groupSecretParams + ) throws IOException { + final int today = currentTimeDays(); + // Returns credentials for the next 7 days + final HashMap credentials = groupsV2Api.getCredentials(today); + // TODO cache credentials until they expire + AuthCredentialResponse authCredentialResponse = credentials.get(today); + try { + return groupsV2Api.getGroupsV2AuthorizationString(account.getUuid(), + today, + groupSecretParams, + authCredentialResponse); + } catch (VerificationFailedException e) { + throw new IOException(e); + } + } + + private Pair> sendUpdateGroupMessage( + GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange + ) throws IOException { + group.setGroup(newDecryptedGroup); + final SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(group, + groupChange.toByteArray()); + account.getGroupStore().updateGroup(group); + return sendMessage(messageBuilder, group.getMembersIncludingPendingWithout(account.getSelfAddress())); + } + + Pair> sendGroupInfoMessage( GroupIdV1 groupId, SignalServiceAddress recipient ) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException { GroupInfoV1 g; @@ -1011,15 +1009,6 @@ public class Manager implements Closeable { account.save(); } - public Pair> updateGroup( - GroupId groupId, String name, List members, File avatarFile - ) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException { - return sendUpdateGroupMessage(groupId, - name, - members == null ? null : getSignalServiceAddresses(members), - avatarFile); - } - /** * Change the expiration timer for a contact */ @@ -1068,7 +1057,7 @@ public class Manager implements Closeable { * @return if successful, returns the URL to install the sticker pack in the signal app */ public String uploadStickerPack(File path) throws IOException, StickerPackInvalidException { - SignalServiceStickerManifestUpload manifest = getSignalServiceStickerManifestUpload(path); + SignalServiceStickerManifestUpload manifest = StickerUtils.getSignalServiceStickerManifestUpload(path); SignalServiceMessageSender messageSender = createMessageSender(); @@ -1091,96 +1080,6 @@ public class Manager implements Closeable { } } - private SignalServiceStickerManifestUpload getSignalServiceStickerManifestUpload( - final File file - ) throws IOException, StickerPackInvalidException { - ZipFile zip = null; - String rootPath = null; - - if (file.getName().endsWith(".zip")) { - zip = new ZipFile(file); - } else if (file.getName().equals("manifest.json")) { - rootPath = file.getParent(); - } else { - throw new StickerPackInvalidException("Could not find manifest.json"); - } - - JsonStickerPack pack = parseStickerPack(rootPath, zip); - - if (pack.stickers == null) { - throw new StickerPackInvalidException("Must set a 'stickers' field."); - } - - if (pack.stickers.isEmpty()) { - throw new StickerPackInvalidException("Must include stickers."); - } - - List stickers = new ArrayList<>(pack.stickers.size()); - for (JsonStickerPack.JsonSticker sticker : pack.stickers) { - if (sticker.file == null) { - throw new StickerPackInvalidException("Must set a 'file' field on each sticker."); - } - - Pair data; - try { - data = getInputStreamAndLength(rootPath, zip, sticker.file); - } catch (IOException ignored) { - throw new StickerPackInvalidException("Could not find find " + sticker.file); - } - - String contentType = Utils.getFileMimeType(new File(sticker.file), null); - StickerInfo stickerInfo = new StickerInfo(data.first(), - data.second(), - Optional.fromNullable(sticker.emoji).or(""), - contentType); - stickers.add(stickerInfo); - } - - StickerInfo cover = null; - if (pack.cover != null) { - if (pack.cover.file == null) { - throw new StickerPackInvalidException("Must set a 'file' field on the cover."); - } - - Pair data; - try { - data = getInputStreamAndLength(rootPath, zip, pack.cover.file); - } catch (IOException ignored) { - throw new StickerPackInvalidException("Could not find find " + pack.cover.file); - } - - String contentType = Utils.getFileMimeType(new File(pack.cover.file), null); - cover = new StickerInfo(data.first(), - data.second(), - Optional.fromNullable(pack.cover.emoji).or(""), - contentType); - } - - return new SignalServiceStickerManifestUpload(pack.title, pack.author, cover, stickers); - } - - private static JsonStickerPack parseStickerPack(String rootPath, ZipFile zip) throws IOException { - InputStream inputStream; - if (zip != null) { - inputStream = zip.getInputStream(zip.getEntry("manifest.json")); - } else { - inputStream = new FileInputStream((new File(rootPath, "manifest.json"))); - } - return new ObjectMapper().readValue(inputStream, JsonStickerPack.class); - } - - private static Pair getInputStreamAndLength( - final String rootPath, final ZipFile zip, final String subfile - ) throws IOException { - if (zip != null) { - final ZipEntry entry = zip.getEntry(subfile); - return new Pair<>(zip.getInputStream(entry), entry.getSize()); - } else { - final File file = new File(rootPath, subfile); - return new Pair<>(new FileInputStream(file), file.length()); - } - } - void requestSyncGroups() throws IOException { SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS) @@ -1189,7 +1088,7 @@ public class Manager implements Closeable { try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { - e.printStackTrace(); + throw new AssertionError(e); } } @@ -1201,7 +1100,7 @@ public class Manager implements Closeable { try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { - e.printStackTrace(); + throw new AssertionError(e); } } @@ -1213,7 +1112,7 @@ public class Manager implements Closeable { try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { - e.printStackTrace(); + throw new AssertionError(e); } } @@ -1225,7 +1124,7 @@ public class Manager implements Closeable { try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { - e.printStackTrace(); + throw new AssertionError(e); } } @@ -1426,28 +1325,6 @@ public class Manager implements Closeable { account.getSignalProtocolStore().deleteAllSessions(source); } - private static int currentTimeDays() { - return (int) TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()); - } - - private GroupsV2AuthorizationString getGroupAuthForToday( - final GroupSecretParams groupSecretParams - ) throws IOException { - final int today = currentTimeDays(); - // Returns credentials for the next 7 days - final HashMap credentials = groupsV2Api.getCredentials(today); - // TODO cache credentials until they expire - AuthCredentialResponse authCredentialResponse = credentials.get(today); - try { - return groupsV2Api.getGroupsV2AuthorizationString(account.getUuid(), - today, - groupSecretParams, - authCredentialResponse); - } catch (VerificationFailedException e) { - throw new IOException(e); - } - } - private List handleSignalServiceDataMessage( SignalServiceDataMessage message, boolean isSync, @@ -1503,7 +1380,7 @@ public class Manager implements Closeable { } case REQUEST_INFO: if (groupV1 != null && !isSync) { - actions.add(new SendGroupUpdateAction(source, groupV1.getGroupId())); + actions.add(new SendGroupInfoAction(source, groupV1.getGroupId())); } break; } @@ -1682,7 +1559,7 @@ public class Manager implements Closeable { try { action.execute(this); } catch (Throwable e) { - e.printStackTrace(); + logger.warn("Message action failed.", e); } } } @@ -1727,7 +1604,7 @@ public class Manager implements Closeable { try { action.execute(this); } catch (Throwable e) { - e.printStackTrace(); + logger.warn("Message action failed.", e); } } account.save(); @@ -1763,7 +1640,7 @@ public class Manager implements Closeable { try { action.execute(this); } catch (Throwable e) { - e.printStackTrace(); + logger.warn("Message action failed.", e); } } } else { @@ -1945,7 +1822,6 @@ public class Manager implements Closeable { logger.warn("Failed to handle received sync groups “{}”, ignoring: {}", tmpFile, e.getMessage()); - e.printStackTrace(); } finally { if (tmpFile != null) { try { @@ -2026,7 +1902,9 @@ public class Manager implements Closeable { } } } catch (Exception e) { - e.printStackTrace(); + logger.warn("Failed to handle received sync contacts “{}”, ignoring: {}", + tmpFile, + e.getMessage()); } finally { if (tmpFile != null) { try { @@ -2400,7 +2278,7 @@ public class Manager implements Closeable { try { sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); } catch (IOException | UntrustedIdentityException e) { - e.printStackTrace(); + logger.warn("Failed to send verification sync message: {}", e.getMessage()); } account.save(); return true; @@ -2430,7 +2308,7 @@ public class Manager implements Closeable { try { sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); } catch (IOException | UntrustedIdentityException e) { - e.printStackTrace(); + logger.warn("Failed to send verification sync message: {}", e.getMessage()); } account.save(); return true; @@ -2456,7 +2334,7 @@ public class Manager implements Closeable { try { sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED); } catch (IOException | UntrustedIdentityException e) { - e.printStackTrace(); + logger.warn("Failed to send verification sync message: {}", e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 87b1efd4..236c7996 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -138,6 +138,8 @@ public class SignalAccount implements Closeable { account.registered = false; + account.migrateLegacyConfigs(); + return account; } @@ -179,6 +181,8 @@ public class SignalAccount implements Closeable { account.registered = true; account.isMultiDevice = true; + account.migrateLegacyConfigs(); + return account; } diff --git a/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java b/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java new file mode 100644 index 00000000..13ce3cb2 --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java @@ -0,0 +1,45 @@ +package org.asamk.signal.manager.util; + +import org.asamk.signal.manager.storage.profiles.SignalProfile; +import org.signal.zkgroup.profiles.ProfileKey; +import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; +import org.whispersystems.signalservice.api.crypto.ProfileCipher; +import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; +import org.whispersystems.util.Base64; + +import java.io.IOException; + +public class ProfileUtils { + + public static SignalProfile decryptProfile( + final ProfileKey profileKey, final SignalServiceProfile encryptedProfile + ) { + ProfileCipher profileCipher = new ProfileCipher(profileKey); + try { + String name; + try { + name = encryptedProfile.getName() == null + ? null + : new String(profileCipher.decryptName(Base64.decode(encryptedProfile.getName()))); + } catch (IOException e) { + name = null; + } + String unidentifiedAccess; + try { + unidentifiedAccess = encryptedProfile.getUnidentifiedAccess() == null + || !profileCipher.verifyUnidentifiedAccess(Base64.decode(encryptedProfile.getUnidentifiedAccess())) + ? null + : encryptedProfile.getUnidentifiedAccess(); + } catch (IOException e) { + unidentifiedAccess = null; + } + return new SignalProfile(encryptedProfile.getIdentityKey(), + name, + unidentifiedAccess, + encryptedProfile.isUnrestrictedUnidentifiedAccess(), + encryptedProfile.getCapabilities()); + } catch (InvalidCiphertextException e) { + return null; + } + } +} diff --git a/src/main/java/org/asamk/signal/manager/util/StickerUtils.java b/src/main/java/org/asamk/signal/manager/util/StickerUtils.java new file mode 100644 index 00000000..fd5ce77b --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/util/StickerUtils.java @@ -0,0 +1,113 @@ +package org.asamk.signal.manager.util; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.asamk.signal.manager.JsonStickerPack; +import org.asamk.signal.manager.StickerPackInvalidException; +import org.whispersystems.libsignal.util.Pair; +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class StickerUtils { + + public static SignalServiceStickerManifestUpload getSignalServiceStickerManifestUpload( + final File file + ) throws IOException, StickerPackInvalidException { + ZipFile zip = null; + String rootPath = null; + + if (file.getName().endsWith(".zip")) { + zip = new ZipFile(file); + } else if (file.getName().equals("manifest.json")) { + rootPath = file.getParent(); + } else { + throw new StickerPackInvalidException("Could not find manifest.json"); + } + + JsonStickerPack pack = parseStickerPack(rootPath, zip); + + if (pack.stickers == null) { + throw new StickerPackInvalidException("Must set a 'stickers' field."); + } + + if (pack.stickers.isEmpty()) { + throw new StickerPackInvalidException("Must include stickers."); + } + + List stickers = new ArrayList<>(pack.stickers.size()); + for (JsonStickerPack.JsonSticker sticker : pack.stickers) { + if (sticker.file == null) { + throw new StickerPackInvalidException("Must set a 'file' field on each sticker."); + } + + Pair data; + try { + data = getInputStreamAndLength(rootPath, zip, sticker.file); + } catch (IOException ignored) { + throw new StickerPackInvalidException("Could not find find " + sticker.file); + } + + String contentType = Utils.getFileMimeType(new File(sticker.file), null); + SignalServiceStickerManifestUpload.StickerInfo stickerInfo = new SignalServiceStickerManifestUpload.StickerInfo( + data.first(), + data.second(), + Optional.fromNullable(sticker.emoji).or(""), + contentType); + stickers.add(stickerInfo); + } + + SignalServiceStickerManifestUpload.StickerInfo cover = null; + if (pack.cover != null) { + if (pack.cover.file == null) { + throw new StickerPackInvalidException("Must set a 'file' field on the cover."); + } + + Pair data; + try { + data = getInputStreamAndLength(rootPath, zip, pack.cover.file); + } catch (IOException ignored) { + throw new StickerPackInvalidException("Could not find find " + pack.cover.file); + } + + String contentType = Utils.getFileMimeType(new File(pack.cover.file), null); + cover = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), + data.second(), + Optional.fromNullable(pack.cover.emoji).or(""), + contentType); + } + + return new SignalServiceStickerManifestUpload(pack.title, pack.author, cover, stickers); + } + + private static JsonStickerPack parseStickerPack(String rootPath, ZipFile zip) throws IOException { + InputStream inputStream; + if (zip != null) { + inputStream = zip.getInputStream(zip.getEntry("manifest.json")); + } else { + inputStream = new FileInputStream((new File(rootPath, "manifest.json"))); + } + return new ObjectMapper().readValue(inputStream, JsonStickerPack.class); + } + + private static Pair getInputStreamAndLength( + final String rootPath, final ZipFile zip, final String subfile + ) throws IOException { + if (zip != null) { + final ZipEntry entry = zip.getEntry(subfile); + return new Pair<>(zip.getInputStream(entry), entry.getSize()); + } else { + final File file = new File(rootPath, subfile); + return new Pair<>(new FileInputStream(file), file.length()); + } + } + +} From 5938d54784b54b49bf949f3db2479b2e2d1aa6dd Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 11:43:53 +0100 Subject: [PATCH 032/124] Add --verbose flag to show extended logging Fixes #379 --- CHANGELOG.md | 2 + man/signal-cli.1.adoc | 3 + src/main/java/org/asamk/signal/Cli.java | 206 ++++++++++++++ src/main/java/org/asamk/signal/Main.java | 263 ++++-------------- .../org/asamk/signal/commands/Commands.java | 7 + .../asamk/signal/manager/LibSignalLogger.java | 41 +++ 6 files changed, 315 insertions(+), 207 deletions(-) create mode 100644 src/main/java/org/asamk/signal/Cli.java create mode 100644 src/main/java/org/asamk/signal/manager/LibSignalLogger.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 1af850b1..be5b5ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +### Added +- `--verbose` flag to increase log level ### Fixed - Disable registration lock before removing the PIN diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index df5f75c3..39585668 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -32,6 +32,9 @@ Show help message and quit. *-v*, *--version*:: Print the version and quit. +*--verbose*:: +Raise log level and include lib signal logs. + *--config* CONFIG:: Set the path, where to store the config. Make sure you have full read/write access to the given directory. diff --git a/src/main/java/org/asamk/signal/Cli.java b/src/main/java/org/asamk/signal/Cli.java new file mode 100644 index 00000000..a0349a31 --- /dev/null +++ b/src/main/java/org/asamk/signal/Cli.java @@ -0,0 +1,206 @@ +package org.asamk.signal; + +import net.sourceforge.argparse4j.inf.Namespace; + +import org.asamk.Signal; +import org.asamk.signal.commands.Command; +import org.asamk.signal.commands.Commands; +import org.asamk.signal.commands.DbusCommand; +import org.asamk.signal.commands.ExtendedDbusCommand; +import org.asamk.signal.commands.LocalCommand; +import org.asamk.signal.commands.ProvisioningCommand; +import org.asamk.signal.commands.RegistrationCommand; +import org.asamk.signal.dbus.DbusSignalImpl; +import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.NotRegisteredException; +import org.asamk.signal.manager.ProvisioningManager; +import org.asamk.signal.manager.RegistrationManager; +import org.asamk.signal.manager.ServiceConfig; +import org.asamk.signal.util.IOUtils; +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.exceptions.DBusException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; + +import java.io.File; +import java.io.IOException; + +public class Cli { + + private final static Logger logger = LoggerFactory.getLogger(Main.class); + + private final Namespace ns; + + public Cli(final Namespace ns) { + this.ns = ns; + } + + public int init() { + Command command = getCommand(); + if (command == null) { + logger.error("Command not implemented!"); + return 2; + } + + if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) { + return initDbusClient(command, ns.getBoolean("dbus_system")); + } + + final String username = ns.getString("username"); + + final File dataPath; + String config = ns.getString("config"); + if (config != null) { + dataPath = new File(config); + } else { + dataPath = getDefaultDataPath(); + } + + final SignalServiceConfiguration serviceConfiguration = ServiceConfig.createDefaultServiceConfiguration( + BaseConfig.USER_AGENT); + + if (!ServiceConfig.getCapabilities().isGv2()) { + logger.warn("WARNING: Support for new group V2 is disabled," + + " because the required native library dependency is missing: libzkgroup"); + } + + if (username == null) { + ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + return handleCommand(command, pm); + } + + if (command instanceof RegistrationCommand) { + final RegistrationManager manager; + try { + manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + } catch (Throwable e) { + logger.error("Error loading or creating state file: {}", e.getMessage()); + return 1; + } + try (RegistrationManager m = manager) { + return handleCommand(command, m); + } catch (Exception e) { + logger.error("Cleanup failed", e); + return 2; + } + } + + Manager manager; + try { + manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + } catch (NotRegisteredException e) { + System.err.println("User is not registered."); + return 0; + } catch (Throwable e) { + logger.error("Error loading state file: {}", e.getMessage()); + return 1; + } + + try (Manager m = manager) { + try { + m.checkAccountState(); + } catch (IOException e) { + logger.error("Error while checking account: {}", e.getMessage()); + return 1; + } + + return handleCommand(command, m); + } catch (IOException e) { + logger.error("Cleanup failed", e); + return 2; + } + } + + private Command getCommand() { + String commandKey = ns.getString("command"); + return Commands.getCommand(commandKey); + } + + private int initDbusClient(final Command command, final boolean systemBus) { + try { + DBusConnection.DBusBusType busType; + if (systemBus) { + busType = DBusConnection.DBusBusType.SYSTEM; + } else { + busType = DBusConnection.DBusBusType.SESSION; + } + try (DBusConnection dBusConn = DBusConnection.getConnection(busType)) { + Signal ts = dBusConn.getRemoteObject(DbusConfig.SIGNAL_BUSNAME, + DbusConfig.SIGNAL_OBJECTPATH, + Signal.class); + + return handleCommand(command, ts, dBusConn); + } + } catch (DBusException | IOException e) { + logger.error("Dbus client failed", e); + return 2; + } + } + + private int handleCommand(Command command, Signal ts, DBusConnection dBusConn) { + if (command instanceof ExtendedDbusCommand) { + return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn); + } else if (command instanceof DbusCommand) { + return ((DbusCommand) command).handleCommand(ns, ts); + } else { + System.err.println("Command is not yet implemented via dbus"); + return 1; + } + } + + private int handleCommand(Command command, ProvisioningManager pm) { + if (command instanceof ProvisioningCommand) { + return ((ProvisioningCommand) command).handleCommand(ns, pm); + } else { + System.err.println("Command only works with a username"); + return 1; + } + } + + private int handleCommand(Command command, RegistrationManager m) { + if (command instanceof RegistrationCommand) { + return ((RegistrationCommand) command).handleCommand(ns, m); + } + return 1; + } + + private int handleCommand(Command command, Manager m) { + if (command instanceof LocalCommand) { + return ((LocalCommand) command).handleCommand(ns, m); + } else if (command instanceof DbusCommand) { + return ((DbusCommand) command).handleCommand(ns, new DbusSignalImpl(m)); + } else { + System.err.println("Command only works via dbus"); + return 1; + } + } + + /** + * Uses $XDG_DATA_HOME/signal-cli if it exists, or if none of the legacy directories exist: + * - $HOME/.config/signal + * - $HOME/.config/textsecure + * + * @return the data directory to be used by signal-cli. + */ + private static File getDefaultDataPath() { + File dataPath = new File(IOUtils.getDataHomeDir(), "signal-cli"); + if (dataPath.exists()) { + return dataPath; + } + + File configPath = new File(System.getProperty("user.home"), ".config"); + + File legacySettingsPath = new File(configPath, "signal"); + if (legacySettingsPath.exists()) { + return legacySettingsPath; + } + + legacySettingsPath = new File(configPath, "textsecure"); + if (legacySettingsPath.exists()) { + return legacySettingsPath; + } + + return dataPath; + } +} diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 2185e2f7..edbe44fd 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -25,227 +25,62 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import net.sourceforge.argparse4j.inf.Subparsers; -import org.asamk.Signal; import org.asamk.signal.commands.Command; import org.asamk.signal.commands.Commands; -import org.asamk.signal.commands.DbusCommand; -import org.asamk.signal.commands.ExtendedDbusCommand; -import org.asamk.signal.commands.LocalCommand; -import org.asamk.signal.commands.ProvisioningCommand; -import org.asamk.signal.commands.RegistrationCommand; -import org.asamk.signal.dbus.DbusSignalImpl; -import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.NotRegisteredException; -import org.asamk.signal.manager.ProvisioningManager; -import org.asamk.signal.manager.RegistrationManager; -import org.asamk.signal.manager.ServiceConfig; -import org.asamk.signal.util.IOUtils; +import org.asamk.signal.manager.LibSignalLogger; import org.asamk.signal.util.SecurityProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.freedesktop.dbus.connections.impl.DBusConnection; -import org.freedesktop.dbus.exceptions.DBusException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; -import java.io.File; -import java.io.IOException; import java.security.Security; import java.util.Map; public class Main { - private final static Logger logger = LoggerFactory.getLogger(Main.class); - public static void main(String[] args) { installSecurityProviderWorkaround(); + // Configuring the logger needs to happen before any logger is initialized + if (isVerbose(args)) { + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace"); + System.setProperty("org.slf4j.simpleLogger.showThreadName", "true"); + System.setProperty("org.slf4j.simpleLogger.showShortLogName", "false"); + System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); + System.setProperty("org.slf4j.simpleLogger.dateTimeFormat", "yyyy-MM-dd'T'HH:mm:ss.SSSXX"); + LibSignalLogger.initLogger(); + } else { + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info"); + System.setProperty("org.slf4j.simpleLogger.showThreadName", "false"); + System.setProperty("org.slf4j.simpleLogger.showShortLogName", "true"); + System.setProperty("org.slf4j.simpleLogger.showDateTime", "false"); + } + Namespace ns = parseArgs(args); if (ns == null) { System.exit(1); } - int res = init(ns); + int res = new Cli(ns).init(); System.exit(res); } - public static void installSecurityProviderWorkaround() { + private static void installSecurityProviderWorkaround() { // Register our own security provider Security.insertProviderAt(new SecurityProvider(), 1); Security.addProvider(new BouncyCastleProvider()); } - public static int init(Namespace ns) { - Command command = getCommand(ns); - if (command == null) { - logger.error("Command not implemented!"); - return 3; - } + private static boolean isVerbose(String[] args) { + ArgumentParser parser = buildBaseArgumentParser(); - if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) { - return initDbusClient(command, ns, ns.getBoolean("dbus_system")); - } - - final String username = ns.getString("username"); - - final File dataPath; - String config = ns.getString("config"); - if (config != null) { - dataPath = new File(config); - } else { - dataPath = getDefaultDataPath(); - } - - final SignalServiceConfiguration serviceConfiguration = ServiceConfig.createDefaultServiceConfiguration( - BaseConfig.USER_AGENT); - - if (!ServiceConfig.getCapabilities().isGv2()) { - logger.warn("WARNING: Support for new group V2 is disabled," - + " because the required native library dependency is missing: libzkgroup"); - } - - if (username == null) { - ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT); - return handleCommand(command, ns, pm); - } - - if (command instanceof RegistrationCommand) { - final RegistrationManager manager; - try { - manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); - } catch (Throwable e) { - logger.error("Error loading or creating state file: {}", e.getMessage()); - return 2; - } - try (RegistrationManager m = manager) { - return handleCommand(command, ns, m); - } catch (Exception e) { - logger.error("Cleanup failed", e); - return 3; - } - } - - Manager manager; + Namespace ns; try { - manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); - } catch (NotRegisteredException e) { - System.err.println("User is not registered."); - return 1; - } catch (Throwable e) { - logger.error("Error loading state file: {}", e.getMessage()); - return 2; + ns = parser.parseKnownArgs(args, null); + } catch (ArgumentParserException e) { + return false; } - try (Manager m = manager) { - try { - m.checkAccountState(); - } catch (IOException e) { - logger.error("Error while checking account: {}", e.getMessage()); - return 2; - } - - return handleCommand(command, ns, m); - } catch (IOException e) { - logger.error("Cleanup failed", e); - return 3; - } - } - - private static int initDbusClient(final Command command, final Namespace ns, final boolean systemBus) { - try { - DBusConnection.DBusBusType busType; - if (systemBus) { - busType = DBusConnection.DBusBusType.SYSTEM; - } else { - busType = DBusConnection.DBusBusType.SESSION; - } - try (DBusConnection dBusConn = DBusConnection.getConnection(busType)) { - Signal ts = dBusConn.getRemoteObject(DbusConfig.SIGNAL_BUSNAME, - DbusConfig.SIGNAL_OBJECTPATH, - Signal.class); - - return handleCommand(command, ns, ts, dBusConn); - } - } catch (DBusException | IOException e) { - logger.error("Dbus client failed", e); - return 3; - } - } - - private static Command getCommand(Namespace ns) { - String commandKey = ns.getString("command"); - final Map commands = Commands.getCommands(); - if (!commands.containsKey(commandKey)) { - return null; - } - return commands.get(commandKey); - } - - private static int handleCommand(Command command, Namespace ns, Signal ts, DBusConnection dBusConn) { - if (command instanceof ExtendedDbusCommand) { - return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn); - } else if (command instanceof DbusCommand) { - return ((DbusCommand) command).handleCommand(ns, ts); - } else { - System.err.println("Command is not yet implemented via dbus"); - return 1; - } - } - - private static int handleCommand(Command command, Namespace ns, ProvisioningManager pm) { - if (command instanceof ProvisioningCommand) { - return ((ProvisioningCommand) command).handleCommand(ns, pm); - } else { - System.err.println("Command only works with a username"); - return 1; - } - } - - private static int handleCommand(Command command, Namespace ns, RegistrationManager m) { - if (command instanceof RegistrationCommand) { - return ((RegistrationCommand) command).handleCommand(ns, m); - } - return 1; - } - - private static int handleCommand(Command command, Namespace ns, Manager m) { - if (command instanceof LocalCommand) { - return ((LocalCommand) command).handleCommand(ns, m); - } else if (command instanceof DbusCommand) { - return ((DbusCommand) command).handleCommand(ns, new DbusSignalImpl(m)); - } else { - System.err.println("Command only works via dbus"); - return 1; - } - } - - /** - * Uses $XDG_DATA_HOME/signal-cli if it exists, or if none of the legacy directories exist: - * - $HOME/.config/signal - * - $HOME/.config/textsecure - * - * @return the data directory to be used by signal-cli. - */ - private static File getDefaultDataPath() { - File dataPath = new File(IOUtils.getDataHomeDir(), "signal-cli"); - if (dataPath.exists()) { - return dataPath; - } - - File configPath = new File(System.getProperty("user.home"), ".config"); - - File legacySettingsPath = new File(configPath, "signal"); - if (legacySettingsPath.exists()) { - return legacySettingsPath; - } - - legacySettingsPath = new File(configPath, "textsecure"); - if (legacySettingsPath.exists()) { - return legacySettingsPath; - } - - return dataPath; + return ns.getBoolean("verbose"); } private static Namespace parseArgs(String[] args) { @@ -276,31 +111,17 @@ public class Main { System.exit(2); } } + if (ns.getList("recipient") != null && !ns.getList("recipient").isEmpty() && ns.getString("group") != null) { System.err.println("You cannot specify recipients by phone number and groups at the same time"); System.exit(2); } + return ns; } private static ArgumentParser buildArgumentParser() { - ArgumentParser parser = ArgumentParsers.newFor("signal-cli") - .build() - .defaultHelp(true) - .description("Commandline interface for Signal.") - .version(BaseConfig.PROJECT_NAME + " " + BaseConfig.PROJECT_VERSION); - - parser.addArgument("-v", "--version").help("Show package version.").action(Arguments.version()); - parser.addArgument("--config") - .help("Set the path, where to store the config (Default: $XDG_DATA_HOME/signal-cli , $HOME/.local/share/signal-cli)."); - - MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup(); - mut.addArgument("-u", "--username").help("Specify your phone number, that will be used for verification."); - mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); - mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue()); - - parser.addArgument("-o", "--output").help("Choose to output in plain text or JSON") - .choices("plain-text", "json").setDefault("plain-text"); + ArgumentParser parser = buildBaseArgumentParser(); Subparsers subparsers = parser.addSubparsers() .title("subcommands") @@ -313,6 +134,34 @@ public class Main { Subparser subparser = subparsers.addParser(entry.getKey()); entry.getValue().attachToSubparser(subparser); } + + return parser; + } + + private static ArgumentParser buildBaseArgumentParser() { + ArgumentParser parser = ArgumentParsers.newFor("signal-cli") + .build() + .defaultHelp(true) + .description("Commandline interface for Signal.") + .version(BaseConfig.PROJECT_NAME + " " + BaseConfig.PROJECT_VERSION); + + parser.addArgument("-v", "--version").help("Show package version.").action(Arguments.version()); + parser.addArgument("--verbose") + .help("Raise log level and include lib signal logs.") + .action(Arguments.storeTrue()); + parser.addArgument("--config") + .help("Set the path, where to store the config (Default: $XDG_DATA_HOME/signal-cli , $HOME/.local/share/signal-cli)."); + + MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup(); + mut.addArgument("-u", "--username").help("Specify your phone number, that will be used for verification."); + mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); + mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue()); + + parser.addArgument("-o", "--output") + .help("Choose to output in plain text or JSON") + .choices("plain-text", "json") + .setDefault("plain-text"); + return parser; } } diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index 1e081dff..4bc17930 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -42,6 +42,13 @@ public class Commands { return commands; } + public static Command getCommand(String commandKey) { + if (!commands.containsKey(commandKey)) { + return null; + } + return commands.get(commandKey); + } + private static void addCommand(String name, Command command) { commands.put(name, command); } diff --git a/src/main/java/org/asamk/signal/manager/LibSignalLogger.java b/src/main/java/org/asamk/signal/manager/LibSignalLogger.java new file mode 100644 index 00000000..9118846d --- /dev/null +++ b/src/main/java/org/asamk/signal/manager/LibSignalLogger.java @@ -0,0 +1,41 @@ +package org.asamk.signal.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.libsignal.logging.SignalProtocolLogger; +import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider; + +public class LibSignalLogger implements SignalProtocolLogger { + + private final static Logger logger = LoggerFactory.getLogger("LibSignal"); + + public static void initLogger() { + SignalProtocolLoggerProvider.setProvider(new LibSignalLogger()); + } + + private LibSignalLogger() { + } + + @Override + public void log(final int priority, final String tag, final String message) { + final String logMessage = String.format("[%s]: %s", tag, message); + switch (priority) { + case SignalProtocolLogger.VERBOSE: + logger.trace(logMessage); + break; + case SignalProtocolLogger.DEBUG: + logger.debug(logMessage); + break; + case SignalProtocolLogger.INFO: + logger.info(logMessage); + break; + case SignalProtocolLogger.WARN: + logger.warn(logMessage); + break; + case SignalProtocolLogger.ERROR: + case SignalProtocolLogger.ASSERT: + logger.error(logMessage); + break; + } + } +} From cc40c437d13745aae82030cb1edfa030bfa70c03 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 12:09:37 +0100 Subject: [PATCH 033/124] Use global output flag for daemon command --- .../org/asamk/signal/commands/DaemonCommand.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index c5ee2edc..7ebecea6 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -10,6 +10,8 @@ import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -20,6 +22,8 @@ import static org.asamk.signal.util.ErrorUtils.handleAssertionError; public class DaemonCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("--system") @@ -29,12 +33,19 @@ public class DaemonCommand implements LocalCommand { .help("Don’t download attachments of received messages.") .action(Arguments.storeTrue()); subparser.addArgument("--json") - .help("Output received messages in json format, one json object per line.") + .help("WARNING: This parameter is now deprecated! Please use the global \"--output=json\" option instead.\n\nOutput received messages in json format, one json object per line.") .action(Arguments.storeTrue()); } @Override public int handleCommand(final Namespace ns, final Manager m) { + boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + + // TODO delete later when "json" variable is removed + if (ns.getBoolean("json")) { + logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); + } + DBusConnection conn = null; try { try { @@ -60,7 +71,7 @@ public class DaemonCommand implements LocalCommand { TimeUnit.HOURS, false, ignoreAttachments, - ns.getBoolean("json") + inJson ? new JsonDbusReceiveMessageHandler(m, conn, SIGNAL_OBJECTPATH) : new DbusReceiveMessageHandler(m, conn, SIGNAL_OBJECTPATH)); return 0; From fc76c9badf720c647aef99f6a9390111dc68e0ef Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 12:21:19 +0100 Subject: [PATCH 034/124] Remove unnecessary isRegistered checks --- src/main/java/org/asamk/signal/commands/SendCommand.java | 5 ----- .../java/org/asamk/signal/commands/UpdateGroupCommand.java | 5 ----- 2 files changed, 10 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index ee51dd91..a4bdc89b 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -33,11 +33,6 @@ public class SendCommand implements DbusCommand { @Override public int handleCommand(final Namespace ns, final Signal signal) { - if (!signal.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - if ((ns.getList("recipient") == null || ns.getList("recipient").size() == 0) && ( ns.getBoolean("endsession") || ns.getString("group") == null )) { diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index a6f40ef2..b5da274e 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -27,11 +27,6 @@ public class UpdateGroupCommand implements DbusCommand { @Override public int handleCommand(final Namespace ns, final Signal signal) { - if (!signal.isRegistered()) { - System.err.println("User is not registered."); - return 1; - } - byte[] groupId = null; if (ns.getString("group") != null) { try { From 54a2a95fb53f8e2681f340d64769dc393dd7785a Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 18:29:13 +0100 Subject: [PATCH 035/124] Fix loading some account files without legacy store --- .../java/org/asamk/signal/manager/storage/SignalAccount.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 236c7996..9721ebea 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -358,7 +358,7 @@ public class SignalAccount implements Closeable { messageCache = new MessageCache(getMessageCachePath(dataPath, username)); JsonNode threadStoreNode = rootNode.get("threadStore"); - if (threadStoreNode != null) { + if (threadStoreNode != null && !threadStoreNode.isNull()) { LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode, LegacyJsonThreadStore.class); // Migrate thread info to group and contact store From 8d985d84fb926f976934bcb8fc743163a32337c3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 18:29:33 +0100 Subject: [PATCH 036/124] Set log level to debug for verbose mode --- src/main/java/org/asamk/signal/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index edbe44fd..a8fd3e2b 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -42,7 +42,7 @@ public class Main { // Configuring the logger needs to happen before any logger is initialized if (isVerbose(args)) { - System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace"); + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug"); System.setProperty("org.slf4j.simpleLogger.showThreadName", "true"); System.setProperty("org.slf4j.simpleLogger.showShortLogName", "false"); System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); From e6ea5d55f8128633f8bcc8faf346754e24f671ae Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 22:12:01 +0100 Subject: [PATCH 037/124] Update man page --- man/signal-cli.1.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 39585668..bd51033f 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -200,6 +200,8 @@ Don’t download attachments of received messages. === joinGroup Join a group via an invitation link. +To be able to join a v2 group the account needs to have a profile (can be created +with the `updateProfile` command) *--uri*:: The invitation link URI (starts with `https://signal.group/#`) @@ -208,6 +210,8 @@ The invitation link URI (starts with `https://signal.group/#`) Create or update a group. If the user is a pending member, this command will accept the group invitation. +To be able to join or create a v2 group the account needs to have a profile (can +be created with the `updateProfile` command) *-g* GROUP, *--group* GROUP:: Specify the recipient group ID in base64 encoding. From 9a775171b573089592e82f7c3bf8f5b0fdd7d975 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 22:31:40 +0100 Subject: [PATCH 038/124] Replace Base64 class from libsignal with java.util.Base64 --- .../asamk/signal/ReceiveMessageHandler.java | 16 ++++++++------ .../asamk/signal/commands/ReceiveCommand.java | 6 ++--- .../signal/commands/UpdateGroupCommand.java | 4 ++-- .../org/asamk/signal/json/JsonGroupInfo.java | 6 ++--- .../org/asamk/signal/json/JsonSticker.java | 7 +++--- .../asamk/signal/manager/DeviceLinkInfo.java | 16 +++++++++----- .../asamk/signal/manager/groups/GroupId.java | 5 ++--- .../signal/manager/storage/SignalAccount.java | 16 ++++++++------ .../storage/groups/JsonGroupStore.java | 13 ++++++----- .../storage/profiles/ProfileStore.java | 13 ++++++----- .../protocol/JsonIdentityKeyStore.java | 22 +++++++++++++------ .../storage/protocol/JsonPreKeyStore.java | 14 +++++------- .../storage/protocol/JsonSessionStore.java | 14 +++++------- .../protocol/JsonSignedPreKeyStore.java | 14 +++++------- .../storage/stickers/StickerStore.java | 14 +++++------- .../asamk/signal/manager/util/KeyUtils.java | 4 ++-- .../signal/manager/util/ProfileUtils.java | 12 +++++----- 17 files changed, 100 insertions(+), 96 deletions(-) diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index db78f454..6ce21acd 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -36,9 +36,9 @@ import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage; import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.util.Base64; import java.io.File; +import java.util.Base64; import java.util.List; public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { @@ -244,10 +244,12 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { for (StickerPackOperationMessage m : stickerPackOperationMessages) { System.out.println(" - " + m.getType().toString()); if (m.getPackId().isPresent()) { - System.out.println(" packId: " + Base64.encodeBytes(m.getPackId().get())); + System.out.println(" packId: " + Base64.getEncoder() + .encodeToString(m.getPackId().get())); } if (m.getPackKey().isPresent()) { - System.out.println(" packKey: " + Base64.encodeBytes(m.getPackKey().get())); + System.out.println(" packKey: " + Base64.getEncoder() + .encodeToString(m.getPackKey().get())); } } } @@ -257,8 +259,8 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { System.out.println("Received message request response:"); System.out.println(" Type: " + requestResponseMessage.getType()); if (requestResponseMessage.getGroupId().isPresent()) { - System.out.println(" Group id: " + Base64.encodeBytes(requestResponseMessage.getGroupId() - .get())); + System.out.println(" Group id: " + Base64.getEncoder() + .encodeToString(requestResponseMessage.getGroupId().get())); } if (requestResponseMessage.getPerson().isPresent()) { System.out.println(" Person: " + requestResponseMessage.getPerson() @@ -418,8 +420,8 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (message.getSticker().isPresent()) { final SignalServiceDataMessage.Sticker sticker = message.getSticker().get(); System.out.println("Sticker:"); - System.out.println(" - Pack id: " + Base64.encodeBytes(sticker.getPackId())); - System.out.println(" - Pack key: " + Base64.encodeBytes(sticker.getPackKey())); + System.out.println(" - Pack id: " + Base64.getEncoder().encodeToString(sticker.getPackId())); + System.out.println(" - Pack key: " + Base64.getEncoder().encodeToString(sticker.getPackKey())); System.out.println(" - Sticker id: " + sticker.getStickerId()); // TODO also download sticker image ?? } diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 9d718abb..e2002258 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -20,9 +20,9 @@ import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.util.Base64; import java.io.IOException; +import java.util.Base64; import java.util.concurrent.TimeUnit; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; @@ -81,7 +81,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { messageReceived.getMessage())); if (messageReceived.getGroupId().length > 0) { System.out.println("Group info:"); - System.out.println(" Id: " + Base64.encodeBytes(messageReceived.getGroupId())); + System.out.println(" Id: " + Base64.getEncoder().encodeToString(messageReceived.getGroupId())); } if (messageReceived.getAttachments().size() > 0) { System.out.println("Attachments: "); @@ -130,7 +130,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { syncReceived.getMessage())); if (syncReceived.getGroupId().length > 0) { System.out.println("Group info:"); - System.out.println(" Id: " + Base64.encodeBytes(syncReceived.getGroupId())); + System.out.println(" Id: " + Base64.getEncoder().encodeToString(syncReceived.getGroupId())); } if (syncReceived.getAttachments().size() > 0) { System.out.println("Attachments: "); diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index b5da274e..de8564f0 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -7,9 +7,9 @@ import org.asamk.Signal; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.Util; import org.freedesktop.dbus.exceptions.DBusExecutionException; -import org.whispersystems.util.Base64; import java.util.ArrayList; +import java.util.Base64; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; @@ -58,7 +58,7 @@ public class UpdateGroupCommand implements DbusCommand { try { byte[] newGroupId = signal.updateGroup(groupId, groupName, groupMembers, groupAvatar); if (groupId.length != newGroupId.length) { - System.out.println("Creating new group \"" + Base64.encodeBytes(newGroupId) + "\" …"); + System.out.println("Creating new group \"" + Base64.getEncoder().encodeToString(newGroupId) + "\" …"); } return 0; } catch (AssertionError e) { diff --git a/src/main/java/org/asamk/signal/json/JsonGroupInfo.java b/src/main/java/org/asamk/signal/json/JsonGroupInfo.java index 79967955..bc3e7e0e 100644 --- a/src/main/java/org/asamk/signal/json/JsonGroupInfo.java +++ b/src/main/java/org/asamk/signal/json/JsonGroupInfo.java @@ -4,9 +4,9 @@ import org.asamk.signal.manager.groups.GroupUtils; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.util.Base64; import java.util.ArrayList; +import java.util.Base64; import java.util.List; class JsonGroupInfo { @@ -17,7 +17,7 @@ class JsonGroupInfo { String type; JsonGroupInfo(SignalServiceGroup groupInfo) { - this.groupId = Base64.encodeBytes(groupInfo.getGroupId()); + this.groupId = Base64.getEncoder().encodeToString(groupInfo.getGroupId()); if (groupInfo.getMembers().isPresent()) { this.members = new ArrayList<>(groupInfo.getMembers().get().size()); for (SignalServiceAddress address : groupInfo.getMembers().get()) { @@ -36,6 +36,6 @@ class JsonGroupInfo { } JsonGroupInfo(byte[] groupId) { - this.groupId = Base64.encodeBytes(groupId); + this.groupId = Base64.getEncoder().encodeToString(groupId); } } diff --git a/src/main/java/org/asamk/signal/json/JsonSticker.java b/src/main/java/org/asamk/signal/json/JsonSticker.java index 228d2883..ea7a5815 100644 --- a/src/main/java/org/asamk/signal/json/JsonSticker.java +++ b/src/main/java/org/asamk/signal/json/JsonSticker.java @@ -1,7 +1,8 @@ package org.asamk.signal.json; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.util.Base64; + +import java.util.Base64; public class JsonSticker { @@ -10,8 +11,8 @@ public class JsonSticker { int stickerId; public JsonSticker(SignalServiceDataMessage.Sticker sticker) { - this.packId = Base64.encodeBytes(sticker.getPackId()); - this.packKey = Base64.encodeBytes(sticker.getPackKey()); + this.packId = Base64.getEncoder().encodeToString(sticker.getPackId()); + this.packKey = Base64.getEncoder().encodeToString(sticker.getPackKey()); this.stickerId = sticker.getStickerId(); // TODO also download sticker image ?? } diff --git a/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java b/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java index 779642b6..b8b27eaa 100644 --- a/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java +++ b/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java @@ -3,13 +3,12 @@ package org.asamk.signal.manager; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.util.Base64; -import java.io.IOException; import java.net.URI; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -20,7 +19,7 @@ public class DeviceLinkInfo { final String deviceIdentifier; final ECPublicKey deviceKey; - public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws IOException, InvalidKeyException { + public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws InvalidKeyException { final String rawQuery = linkUri.getRawQuery(); if (isEmpty(rawQuery)) { throw new RuntimeException("Invalid device link uri"); @@ -34,7 +33,13 @@ public class DeviceLinkInfo { throw new RuntimeException("Invalid device link uri"); } - ECPublicKey deviceKey = Curve.decodePoint(Base64.decode(publicKeyEncoded), 0); + final byte[] publicKeyBytes; + try { + publicKeyBytes = Base64.getDecoder().decode(publicKeyEncoded); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Invalid device link uri", e); + } + ECPublicKey deviceKey = Curve.decodePoint(publicKeyBytes, 0); return new DeviceLinkInfo(deviceIdentifier, deviceKey); } @@ -57,9 +62,10 @@ public class DeviceLinkInfo { } public String createDeviceLinkUri() { + final String deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", ""); return "tsdevice:/?uuid=" + URLEncoder.encode(deviceIdentifier, StandardCharsets.UTF_8) + "&pub_key=" - + URLEncoder.encode(Base64.encodeBytesWithoutPadding(deviceKey.serialize()), StandardCharsets.UTF_8); + + URLEncoder.encode(deviceKeyString, StandardCharsets.UTF_8); } } diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupId.java b/src/main/java/org/asamk/signal/manager/groups/GroupId.java index 9a15de65..f56e5d38 100644 --- a/src/main/java/org/asamk/signal/manager/groups/GroupId.java +++ b/src/main/java/org/asamk/signal/manager/groups/GroupId.java @@ -1,8 +1,7 @@ package org.asamk.signal.manager.groups; -import org.whispersystems.util.Base64; - import java.util.Arrays; +import java.util.Base64; public abstract class GroupId { @@ -43,7 +42,7 @@ public abstract class GroupId { } public String toBase64() { - return Base64.encodeBytes(id); + return Base64.getEncoder().encodeToString(id); } @Override diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 9721ebea..9922ca57 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -41,7 +41,6 @@ import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.util.Base64; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -53,6 +52,7 @@ import java.nio.channels.Channels; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; +import java.util.Base64; import java.util.Collection; import java.util.UUID; import java.util.stream.Collectors; @@ -200,8 +200,8 @@ public class SignalAccount implements Closeable { } final ProfileKey profileKey; try { - profileKey = new ProfileKey(Base64.decode(profileKeyString)); - } catch (InvalidInputException | IOException e) { + profileKey = new ProfileKey(Base64.getDecoder().decode(profileKeyString)); + } catch (InvalidInputException ignored) { continue; } contact.profileKey = null; @@ -264,7 +264,7 @@ public class SignalAccount implements Closeable { JsonNode pinMasterKeyNode = rootNode.get("pinMasterKey"); pinMasterKey = pinMasterKeyNode == null || pinMasterKeyNode.isNull() ? null - : new MasterKey(Base64.decode(pinMasterKeyNode.asText())); + : new MasterKey(Base64.getDecoder().decode(pinMasterKeyNode.asText())); if (rootNode.has("signalingKey")) { signalingKey = Utils.getNotNullNode(rootNode, "signalingKey").asText(); } @@ -280,7 +280,8 @@ public class SignalAccount implements Closeable { } if (rootNode.has("profileKey")) { try { - profileKey = new ProfileKey(Base64.decode(Utils.getNotNullNode(rootNode, "profileKey").asText())); + profileKey = new ProfileKey(Base64.getDecoder() + .decode(Utils.getNotNullNode(rootNode, "profileKey").asText())); } catch (InvalidInputException e) { throw new IOException( "Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes", @@ -395,11 +396,12 @@ public class SignalAccount implements Closeable { .put("isMultiDevice", isMultiDevice) .put("password", password) .put("registrationLockPin", registrationLockPin) - .put("pinMasterKey", pinMasterKey == null ? null : Base64.encodeBytes(pinMasterKey.serialize())) + .put("pinMasterKey", + pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize())) .put("signalingKey", signalingKey) .put("preKeyIdOffset", preKeyIdOffset) .put("nextSignedPreKeyId", nextSignedPreKeyId) - .put("profileKey", Base64.encodeBytes(profileKey.serialize())) + .put("profileKey", Base64.getEncoder().encodeToString(profileKey.serialize())) .put("registered", registered) .putPOJO("axolotlStore", signalProtocolStore) .putPOJO("groupStore", groupStore) diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java index 5c06aeee..8d0ae630 100644 --- a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java @@ -23,13 +23,13 @@ import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.groups.GroupMasterKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.util.Base64; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Base64; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -161,7 +161,8 @@ public class JsonGroupStore { final GroupInfoV2 groupV2 = (GroupInfoV2) group; jgen.writeStartObject(); jgen.writeStringField("groupId", groupV2.getGroupId().toBase64()); - jgen.writeStringField("masterKey", Base64.encodeBytes(groupV2.getMasterKey().serialize())); + jgen.writeStringField("masterKey", + Base64.getEncoder().encodeToString(groupV2.getMasterKey().serialize())); jgen.writeBooleanField("blocked", groupV2.isBlocked()); jgen.writeEndObject(); } else { @@ -186,15 +187,15 @@ public class JsonGroupStore { // a v2 group GroupIdV2 groupId = GroupIdV2.fromBase64(n.get("groupId").asText()); try { - GroupMasterKey masterKey = new GroupMasterKey(Base64.decode(n.get("masterKey").asText())); + GroupMasterKey masterKey = new GroupMasterKey(Base64.getDecoder() + .decode(n.get("masterKey").asText())); g = new GroupInfoV2(groupId, masterKey); - } catch (InvalidInputException e) { + } catch (InvalidInputException | IllegalArgumentException e) { throw new AssertionError("Invalid master key for group " + groupId.toBase64()); } g.setBlocked(n.get("blocked").asBoolean(false)); } else { - GroupInfoV1 gv1 = jsonProcessor.treeToValue(n, GroupInfoV1.class); - g = gv1; + g = jsonProcessor.treeToValue(n, GroupInfoV1.class); } groups.put(g.getGroupId(), g); } diff --git a/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java b/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java index bff2f17e..516b57dc 100644 --- a/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java @@ -17,10 +17,10 @@ import org.signal.zkgroup.profiles.ProfileKey; import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.util.Base64; import java.io.IOException; import java.util.ArrayList; +import java.util.Base64; import java.util.List; import java.util.UUID; @@ -104,14 +104,14 @@ public class ProfileStore { final SignalServiceAddress serviceAddress = new SignalServiceAddress(uuid, name); ProfileKey profileKey = null; try { - profileKey = new ProfileKey(Base64.decode(entry.get("profileKey").asText())); + profileKey = new ProfileKey(Base64.getDecoder().decode(entry.get("profileKey").asText())); } catch (InvalidInputException ignored) { } ProfileKeyCredential profileKeyCredential = null; if (entry.hasNonNull("profileKeyCredential")) { try { - profileKeyCredential = new ProfileKeyCredential(Base64.decode(entry.get( - "profileKeyCredential").asText())); + profileKeyCredential = new ProfileKeyCredential(Base64.getDecoder() + .decode(entry.get("profileKeyCredential").asText())); } catch (Throwable ignored) { } } @@ -145,12 +145,13 @@ public class ProfileStore { if (address.getUuid().isPresent()) { json.writeStringField("uuid", address.getUuid().get().toString()); } - json.writeStringField("profileKey", Base64.encodeBytes(profileEntry.getProfileKey().serialize())); + json.writeStringField("profileKey", + Base64.getEncoder().encodeToString(profileEntry.getProfileKey().serialize())); json.writeNumberField("lastUpdateTimestamp", profileEntry.getLastUpdateTimestamp()); json.writeObjectField("profile", profileEntry.getProfile()); if (profileEntry.getProfileKeyCredential() != null) { json.writeStringField("profileKeyCredential", - Base64.encodeBytes(profileEntry.getProfileKeyCredential().serialize())); + Base64.getEncoder().encodeToString(profileEntry.getProfileKeyCredential().serialize())); } json.writeEndObject(); } diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index 19131e13..06db5560 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -19,10 +19,10 @@ import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.state.IdentityKeyStore; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.util.Base64; import java.io.IOException; import java.util.ArrayList; +import java.util.Base64; import java.util.Date; import java.util.List; import java.util.UUID; @@ -196,7 +196,8 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { try { int localRegistrationId = node.get("registrationId").asInt(); - IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.decode(node.get("identityKey").asText())); + IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.getDecoder() + .decode(node.get("identityKey").asText())); JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId); @@ -216,13 +217,14 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName) : new SignalServiceAddress(uuid, trustedKeyName); try { - IdentityKey id = new IdentityKey(Base64.decode(trustedKey.get("identityKey").asText()), 0); + IdentityKey id = new IdentityKey(Base64.getDecoder() + .decode(trustedKey.get("identityKey").asText()), 0); TrustLevel trustLevel = trustedKey.has("trustLevel") ? TrustLevel.fromInt(trustedKey.get( "trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED; Date added = trustedKey.has("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp") .asLong()) : new Date(); keyStore.saveIdentity(serviceAddress, id, trustLevel, added); - } catch (InvalidKeyException | IOException e) { + } catch (InvalidKeyException e) { logger.warn("Error while decoding key for {}: {}", trustedKeyName, e.getMessage()); } } @@ -244,7 +246,13 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { json.writeStartObject(); json.writeNumberField("registrationId", jsonIdentityKeyStore.getLocalRegistrationId()); json.writeStringField("identityKey", - Base64.encodeBytes(jsonIdentityKeyStore.getIdentityKeyPair().serialize())); + Base64.getEncoder().encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().serialize())); + json.writeStringField("identityPrivateKey", + Base64.getEncoder() + .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPrivateKey().serialize())); + json.writeStringField("identityPublicKey", + Base64.getEncoder() + .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPublicKey().serialize())); json.writeArrayFieldStart("trustedKeys"); for (IdentityInfo trustedKey : jsonIdentityKeyStore.identities) { json.writeStartObject(); @@ -254,7 +262,8 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { if (trustedKey.getAddress().getUuid().isPresent()) { json.writeStringField("uuid", trustedKey.getAddress().getUuid().get().toString()); } - json.writeStringField("identityKey", Base64.encodeBytes(trustedKey.identityKey.serialize())); + json.writeStringField("identityKey", + Base64.getEncoder().encodeToString(trustedKey.identityKey.serialize())); json.writeNumberField("trustLevel", trustedKey.trustLevel.ordinal()); json.writeNumberField("addedTimestamp", trustedKey.added.getTime()); json.writeEndObject(); @@ -263,5 +272,4 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { json.writeEndObject(); } } - } diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java index 9ec4b64f..8529d0dd 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java @@ -13,9 +13,9 @@ import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyIdException; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.PreKeyStore; -import org.whispersystems.util.Base64; import java.io.IOException; +import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -72,12 +72,9 @@ class JsonPreKeyStore implements PreKeyStore { Map preKeyMap = new HashMap<>(); if (node.isArray()) { for (JsonNode preKey : node) { - Integer preKeyId = preKey.get("id").asInt(); - try { - preKeyMap.put(preKeyId, Base64.decode(preKey.get("record").asText())); - } catch (IOException e) { - logger.warn("Error while decoding prekey for {}: {}", preKeyId, e.getMessage()); - } + final int preKeyId = preKey.get("id").asInt(); + final byte[] preKeyRecord = Base64.getDecoder().decode(preKey.get("record").asText()); + preKeyMap.put(preKeyId, preKeyRecord); } } @@ -85,7 +82,6 @@ class JsonPreKeyStore implements PreKeyStore { keyStore.addPreKeys(preKeyMap); return keyStore; - } } @@ -99,7 +95,7 @@ class JsonPreKeyStore implements PreKeyStore { for (Map.Entry preKey : jsonPreKeyStore.store.entrySet()) { json.writeStartObject(); json.writeNumberField("id", preKey.getKey()); - json.writeStringField("record", Base64.encodeBytes(preKey.getValue())); + json.writeStringField("record", Base64.getEncoder().encodeToString(preKey.getValue())); json.writeEndObject(); } json.writeEndArray(); diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java index 79790598..de84c895 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java @@ -16,10 +16,10 @@ import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.libsignal.state.SessionStore; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.util.Base64; import java.io.IOException; import java.util.ArrayList; +import java.util.Base64; import java.util.LinkedList; import java.util.List; import java.util.UUID; @@ -150,13 +150,9 @@ class JsonSessionStore implements SessionStore { ? Utils.getSignalServiceAddressFromIdentifier(sessionName) : new SignalServiceAddress(uuid, sessionName); final int deviceId = session.get("deviceId").asInt(); - final String record = session.get("record").asText(); - try { - SessionInfo sessionInfo = new SessionInfo(serviceAddress, deviceId, Base64.decode(record)); - sessionStore.sessions.add(sessionInfo); - } catch (IOException e) { - logger.warn("Error while decoding session for {}: {}", sessionName, e.getMessage()); - } + final byte[] record = Base64.getDecoder().decode(session.get("record").asText()); + SessionInfo sessionInfo = new SessionInfo(serviceAddress, deviceId, record); + sessionStore.sessions.add(sessionInfo); } } @@ -180,7 +176,7 @@ class JsonSessionStore implements SessionStore { json.writeStringField("uuid", sessionInfo.address.getUuid().get().toString()); } json.writeNumberField("deviceId", sessionInfo.deviceId); - json.writeStringField("record", Base64.encodeBytes(sessionInfo.sessionRecord)); + json.writeStringField("record", Base64.getEncoder().encodeToString(sessionInfo.sessionRecord)); json.writeEndObject(); } json.writeEndArray(); diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java index 00543620..a45287b4 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java @@ -13,9 +13,9 @@ import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyIdException; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyStore; -import org.whispersystems.util.Base64; import java.io.IOException; +import java.util.Base64; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -89,12 +89,9 @@ class JsonSignedPreKeyStore implements SignedPreKeyStore { Map preKeyMap = new HashMap<>(); if (node.isArray()) { for (JsonNode preKey : node) { - Integer preKeyId = preKey.get("id").asInt(); - try { - preKeyMap.put(preKeyId, Base64.decode(preKey.get("record").asText())); - } catch (IOException e) { - logger.warn("Error while decoding prekey for {}: {}", preKeyId, e.getMessage()); - } + final int preKeyId = preKey.get("id").asInt(); + final byte[] preKeyRecord = Base64.getDecoder().decode(preKey.get("record").asText()); + preKeyMap.put(preKeyId, preKeyRecord); } } @@ -102,7 +99,6 @@ class JsonSignedPreKeyStore implements SignedPreKeyStore { keyStore.addSignedPreKeys(preKeyMap); return keyStore; - } } @@ -116,7 +112,7 @@ class JsonSignedPreKeyStore implements SignedPreKeyStore { for (Map.Entry signedPreKey : jsonPreKeyStore.store.entrySet()) { json.writeStartObject(); json.writeNumberField("id", signedPreKey.getKey()); - json.writeStringField("record", Base64.encodeBytes(signedPreKey.getValue())); + json.writeStringField("record", Base64.getEncoder().encodeToString(signedPreKey.getValue())); json.writeEndObject(); } json.writeEndArray(); diff --git a/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java b/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java index 10cd2e99..710be035 100644 --- a/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java @@ -6,22 +6,18 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.whispersystems.util.Base64; - import java.io.IOException; +import java.util.Base64; import java.util.Collection; import java.util.HashMap; import java.util.Map; public class StickerStore { - private static final ObjectMapper jsonProcessor = new ObjectMapper(); - @JsonSerialize(using = StickersSerializer.class) @JsonDeserialize(using = StickersDeserializer.class) private final Map stickers = new HashMap<>(); @@ -44,8 +40,8 @@ public class StickerStore { jgen.writeStartArray(stickers.size()); for (Sticker sticker : stickers) { jgen.writeStartObject(); - jgen.writeStringField("packId", Base64.encodeBytes(sticker.getPackId())); - jgen.writeStringField("packKey", Base64.encodeBytes(sticker.getPackKey())); + jgen.writeStringField("packId", Base64.getEncoder().encodeToString(sticker.getPackId())); + jgen.writeStringField("packKey", Base64.getEncoder().encodeToString(sticker.getPackKey())); jgen.writeBooleanField("installed", sticker.isInstalled()); jgen.writeEndObject(); } @@ -62,8 +58,8 @@ public class StickerStore { Map stickers = new HashMap<>(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); for (JsonNode n : node) { - byte[] packId = Base64.decode(n.get("packId").asText()); - byte[] packKey = Base64.decode(n.get("packKey").asText()); + byte[] packId = Base64.getDecoder().decode(n.get("packId").asText()); + byte[] packKey = Base64.getDecoder().decode(n.get("packKey").asText()); boolean installed = n.get("installed").asBoolean(false); stickers.put(packId, new Sticker(packId, packKey, installed)); } diff --git a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java index 171e7a42..3285e65a 100644 --- a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -13,9 +13,9 @@ import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Medium; import org.whispersystems.signalservice.api.kbs.MasterKey; -import org.whispersystems.util.Base64; import java.util.ArrayList; +import java.util.Base64; import java.util.List; public class KeyUtils { @@ -82,7 +82,7 @@ public class KeyUtils { private static String getSecret(int size) { byte[] secret = getSecretBytes(size); - return Base64.encodeBytes(secret); + return Base64.getEncoder().encodeToString(secret); } public static byte[] getSecretBytes(int size) { diff --git a/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java b/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java index 13ce3cb2..b91e864a 100644 --- a/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java +++ b/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java @@ -5,9 +5,8 @@ import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; import org.whispersystems.signalservice.api.crypto.ProfileCipher; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import org.whispersystems.util.Base64; -import java.io.IOException; +import java.util.Base64; public class ProfileUtils { @@ -20,17 +19,18 @@ public class ProfileUtils { try { name = encryptedProfile.getName() == null ? null - : new String(profileCipher.decryptName(Base64.decode(encryptedProfile.getName()))); - } catch (IOException e) { + : new String(profileCipher.decryptName(Base64.getDecoder().decode(encryptedProfile.getName()))); + } catch (IllegalArgumentException e) { name = null; } String unidentifiedAccess; try { unidentifiedAccess = encryptedProfile.getUnidentifiedAccess() == null - || !profileCipher.verifyUnidentifiedAccess(Base64.decode(encryptedProfile.getUnidentifiedAccess())) + || !profileCipher.verifyUnidentifiedAccess(Base64.getDecoder() + .decode(encryptedProfile.getUnidentifiedAccess())) ? null : encryptedProfile.getUnidentifiedAccess(); - } catch (IOException e) { + } catch (IllegalArgumentException e) { unidentifiedAccess = null; } return new SignalProfile(encryptedProfile.getIdentityKey(), From c0f5ff8805d9585f7edd330239bba85f3ba3cdd6 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 08:58:59 +0100 Subject: [PATCH 039/124] Prevent incorrect error log about saving failed if an exception was thrown --- .../java/org/asamk/signal/manager/storage/SignalAccount.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 9922ca57..c9051335 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -585,7 +585,9 @@ public class SignalAccount implements Closeable { @Override public void close() throws IOException { - save(); + if (fileChannel.isOpen()) { + save(); + } synchronized (fileChannel) { try { lock.close(); From a97bbf8608bd08e8b083456d7de470e56e1d7bb4 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 08:59:15 +0100 Subject: [PATCH 040/124] Improve logging for provisioning --- .../signal/manager/ProvisioningManager.java | 24 +++++++++++++++---- .../signal/manager/RegistrationManager.java | 1 - 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 0648c0d3..6b8a3797 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -20,6 +20,8 @@ import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.util.KeyHelper; @@ -38,6 +40,8 @@ import java.util.concurrent.TimeoutException; public class ProvisioningManager { + private final static Logger logger = LoggerFactory.getLogger(Manager.class); + private final PathConfig pathConfig; private final SignalServiceConfiguration serviceConfiguration; private final String userAgent; @@ -117,12 +121,22 @@ public class ProvisioningManager { try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) { - m.refreshPreKeys(); + try { + m.refreshPreKeys(); + } catch (Exception e) { + logger.error("Failed to refresh prekeys."); + throw e; + } - m.requestSyncGroups(); - m.requestSyncContacts(); - m.requestSyncBlocked(); - m.requestSyncConfiguration(); + try { + m.requestSyncGroups(); + m.requestSyncContacts(); + m.requestSyncBlocked(); + m.requestSyncConfiguration(); + } catch (Exception e) { + logger.error("Failed to request sync messages from linked device."); + throw e; + } m.close(false); } diff --git a/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/src/main/java/org/asamk/signal/manager/RegistrationManager.java index e740bb91..b3df9376 100644 --- a/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -112,7 +112,6 @@ public class RegistrationManager implements AutoCloseable { accountManager.requestSmsVerificationCode(false, Optional.fromNullable(captcha), Optional.absent()); } - account.setRegistered(false); account.save(); } From ca86c421ebb1a3930fa309a2d95a4090108c2e62 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 18:28:54 +0100 Subject: [PATCH 041/124] Allow calling signal-cli without -u flag For daemon command all local users will be exposed as dbus objects If only one local user exists, all other commands will use that user, otherwise a user has to be specified. --- man/signal-cli.1.adoc | 12 + src/main/java/org/asamk/signal/Cli.java | 205 ++++++++++++------ .../java/org/asamk/signal/DbusConfig.java | 20 +- src/main/java/org/asamk/signal/Main.java | 24 +- .../asamk/signal/commands/DaemonCommand.java | 137 ++++++++---- .../asamk/signal/commands/DbusCommand.java | 8 +- .../signal/commands/GetUserStatusCommand.java | 2 +- .../signal/commands/JoinGroupCommand.java | 16 +- .../signal/commands/ListGroupsCommand.java | 32 ++- .../signal/commands/MultiLocalCommand.java | 17 ++ .../asamk/signal/commands/ReceiveCommand.java | 5 +- .../signal/commands/RemovePinCommand.java | 5 +- .../asamk/signal/commands/SendCommand.java | 10 +- .../signal/commands/SendContactsCommand.java | 5 +- .../asamk/signal/commands/SetPinCommand.java | 5 +- .../signal/commands/UpdateGroupCommand.java | 2 +- .../commands/UploadStickerPackCommand.java | 2 +- .../org/asamk/signal/manager/Manager.java | 16 ++ .../signal/manager/RegistrationManager.java | 5 +- 19 files changed, 356 insertions(+), 172 deletions(-) create mode 100644 src/main/java/org/asamk/signal/commands/MultiLocalCommand.java diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index bd51033f..5b1653c0 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -44,6 +44,11 @@ Make sure you have full read/write access to the given directory. Specify your phone number, that will be your identifier. The phone number must include the country calling code, i.e. the number must start with a "+" sign. +This flag must not be given for the `link` command. +It is optional for the `daemon` command. +For all other commands it is only optional if there is exactly one local user in the +config directory. + *--dbus*:: Make request via user dbus. @@ -352,6 +357,8 @@ The path of the manifest.json or a zip file containing the sticker pack you wish === daemon signal-cli can run in daemon mode and provides an experimental dbus interface. +If no `-u` username is given, all local users will be exported as separate dbus +objects under the same bus name. *--system*:: Use DBus system bus instead of user bus. @@ -390,6 +397,11 @@ signal-cli -u USERNAME trust -v SAFETY_NUMBER NUMBER Trust new key, without having verified it. Only use this if you don't care about security:: signal-cli -u USERNAME trust -a NUMBER +== Exit codes +* *1*: Error is probably caused and fixable by the user +* *2*: Some unexpected error +* *3*: Server or IO error + == Files The password and cryptographic keys are created when registering and stored in the current users home directory, the directory can be changed with *--config*: diff --git a/src/main/java/org/asamk/signal/Cli.java b/src/main/java/org/asamk/signal/Cli.java index a0349a31..4579f3f5 100644 --- a/src/main/java/org/asamk/signal/Cli.java +++ b/src/main/java/org/asamk/signal/Cli.java @@ -8,9 +8,9 @@ import org.asamk.signal.commands.Commands; import org.asamk.signal.commands.DbusCommand; import org.asamk.signal.commands.ExtendedDbusCommand; import org.asamk.signal.commands.LocalCommand; +import org.asamk.signal.commands.MultiLocalCommand; import org.asamk.signal.commands.ProvisioningCommand; import org.asamk.signal.commands.RegistrationCommand; -import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.ProvisioningManager; @@ -21,10 +21,14 @@ import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import java.io.File; import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; public class Cli { @@ -40,15 +44,16 @@ public class Cli { Command command = getCommand(); if (command == null) { logger.error("Command not implemented!"); - return 2; + return 1; } + String username = ns.getString("username"); + if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) { - return initDbusClient(command, ns.getBoolean("dbus_system")); + // If username is null, it will connect to the default object path + return initDbusClient(command, username, ns.getBoolean("dbus_system")); } - final String username = ns.getString("username"); - final File dataPath; String config = ns.getString("config"); if (config != null) { @@ -65,59 +70,150 @@ public class Cli { + " because the required native library dependency is missing: libzkgroup"); } - if (username == null) { - ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT); - return handleCommand(command, pm); - } - - if (command instanceof RegistrationCommand) { - final RegistrationManager manager; - try { - manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); - } catch (Throwable e) { - logger.error("Error loading or creating state file: {}", e.getMessage()); + if (command instanceof ProvisioningCommand) { + if (username != null) { + System.err.println("You cannot specify a username (phone number) when linking"); return 1; } - try (RegistrationManager m = manager) { - return handleCommand(command, m); - } catch (Exception e) { - logger.error("Cleanup failed", e); - return 2; - } + + return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceConfiguration); } - Manager manager; - try { - manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); - } catch (NotRegisteredException e) { - System.err.println("User is not registered."); - return 0; - } catch (Throwable e) { - logger.error("Error loading state file: {}", e.getMessage()); + if (username == null) { + List usernames = Manager.getAllLocalUsernames(dataPath); + if (usernames.size() == 0) { + System.err.println("No local users found, you first need to register or link an account"); + return 1; + } + + if (command instanceof MultiLocalCommand) { + return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceConfiguration, usernames); + } + + if (usernames.size() > 1) { + System.err.println("Multiple users found, you need to specify a username (phone number) with -u"); + return 1; + } + + username = usernames.get(0); + } else if (!PhoneNumberFormatter.isValidNumber(username, null)) { + System.err.println("Invalid username (phone number), make sure you include the country code."); return 1; } - try (Manager m = manager) { - try { - m.checkAccountState(); - } catch (IOException e) { - logger.error("Error while checking account: {}", e.getMessage()); - return 1; - } + if (command instanceof RegistrationCommand) { + return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceConfiguration); + } - return handleCommand(command, m); + if (!(command instanceof LocalCommand)) { + System.err.println("Command only works via dbus"); + return 1; + } + + return handleLocalCommand((LocalCommand) command, username, dataPath, serviceConfiguration); + } + + private int handleProvisioningCommand( + final ProvisioningCommand command, + final File dataPath, + final SignalServiceConfiguration serviceConfiguration + ) { + ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + return command.handleCommand(ns, pm); + } + + private int handleRegistrationCommand( + final RegistrationCommand command, + final String username, + final File dataPath, + final SignalServiceConfiguration serviceConfiguration + ) { + final RegistrationManager manager; + try { + manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + } catch (Throwable e) { + logger.error("Error loading or creating state file: {}", e.getMessage()); + return 2; + } + try (RegistrationManager m = manager) { + return command.handleCommand(ns, m); } catch (IOException e) { logger.error("Cleanup failed", e); return 2; } } + private int handleLocalCommand( + final LocalCommand command, + final String username, + final File dataPath, + final SignalServiceConfiguration serviceConfiguration + ) { + try (Manager m = loadManager(username, dataPath, serviceConfiguration)) { + if (m == null) { + return 2; + } + + return command.handleCommand(ns, m); + } catch (IOException e) { + logger.error("Cleanup failed", e); + return 2; + } + } + + private int handleMultiLocalCommand( + final MultiLocalCommand command, + final File dataPath, + final SignalServiceConfiguration serviceConfiguration, + final List usernames + ) { + final List managers = usernames.stream() + .map(u -> loadManager(u, dataPath, serviceConfiguration)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + int result = command.handleCommand(ns, managers); + + for (Manager m : managers) { + try { + m.close(); + } catch (IOException e) { + logger.warn("Cleanup failed", e); + } + } + return result; + } + + private Manager loadManager( + final String username, final File dataPath, final SignalServiceConfiguration serviceConfiguration + ) { + Manager manager; + try { + manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + } catch (NotRegisteredException e) { + logger.error("User " + username + " is not registered."); + return null; + } catch (Throwable e) { + logger.error("Error loading state file for user " + username + ": {}", e.getMessage()); + return null; + } + + try { + manager.checkAccountState(); + } catch (IOException e) { + logger.error("Error while checking account " + username + ": {}", e.getMessage()); + return null; + } + + return manager; + } + private Command getCommand() { String commandKey = ns.getString("command"); return Commands.getCommand(commandKey); } - private int initDbusClient(final Command command, final boolean systemBus) { + private int initDbusClient(final Command command, final String username, final boolean systemBus) { try { DBusConnection.DBusBusType busType; if (systemBus) { @@ -126,8 +222,8 @@ public class Cli { busType = DBusConnection.DBusBusType.SESSION; } try (DBusConnection dBusConn = DBusConnection.getConnection(busType)) { - Signal ts = dBusConn.getRemoteObject(DbusConfig.SIGNAL_BUSNAME, - DbusConfig.SIGNAL_OBJECTPATH, + Signal ts = dBusConn.getRemoteObject(DbusConfig.getBusname(), + DbusConfig.getObjectPath(username), Signal.class); return handleCommand(command, ts, dBusConn); @@ -149,33 +245,6 @@ public class Cli { } } - private int handleCommand(Command command, ProvisioningManager pm) { - if (command instanceof ProvisioningCommand) { - return ((ProvisioningCommand) command).handleCommand(ns, pm); - } else { - System.err.println("Command only works with a username"); - return 1; - } - } - - private int handleCommand(Command command, RegistrationManager m) { - if (command instanceof RegistrationCommand) { - return ((RegistrationCommand) command).handleCommand(ns, m); - } - return 1; - } - - private int handleCommand(Command command, Manager m) { - if (command instanceof LocalCommand) { - return ((LocalCommand) command).handleCommand(ns, m); - } else if (command instanceof DbusCommand) { - return ((DbusCommand) command).handleCommand(ns, new DbusSignalImpl(m)); - } else { - System.err.println("Command only works via dbus"); - return 1; - } - } - /** * Uses $XDG_DATA_HOME/signal-cli if it exists, or if none of the legacy directories exist: * - $HOME/.config/signal diff --git a/src/main/java/org/asamk/signal/DbusConfig.java b/src/main/java/org/asamk/signal/DbusConfig.java index c0d23175..eb457c39 100644 --- a/src/main/java/org/asamk/signal/DbusConfig.java +++ b/src/main/java/org/asamk/signal/DbusConfig.java @@ -2,6 +2,22 @@ package org.asamk.signal; public class DbusConfig { - public static final String SIGNAL_BUSNAME = "org.asamk.Signal"; - public static final String SIGNAL_OBJECTPATH = "/org/asamk/Signal"; + private static final String SIGNAL_BUSNAME = "org.asamk.Signal"; + private static final String SIGNAL_OBJECT_BASE_PATH = "/org/asamk/Signal"; + + public static String getBusname() { + return SIGNAL_BUSNAME; + } + + public static String getObjectPath() { + return getObjectPath(null); + } + + public static String getObjectPath(String username) { + if (username == null) { + return SIGNAL_OBJECT_BASE_PATH; + } + + return SIGNAL_OBJECT_BASE_PATH + "/" + username.replace('+', '_'); + } } diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index a8fd3e2b..ddcf47e1 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -30,7 +30,6 @@ import org.asamk.signal.commands.Commands; import org.asamk.signal.manager.LibSignalLogger; import org.asamk.signal.util.SecurityProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.security.Security; import java.util.Map; @@ -57,7 +56,7 @@ public class Main { Namespace ns = parseArgs(args); if (ns == null) { - System.exit(1); + System.exit(2); } int res = new Cli(ns).init(); @@ -94,24 +93,6 @@ public class Main { return null; } - if ("link".equals(ns.getString("command"))) { - if (ns.getString("username") != null) { - parser.printUsage(); - System.err.println("You cannot specify a username (phone number) when linking"); - System.exit(2); - } - } else if (!ns.getBoolean("dbus") && !ns.getBoolean("dbus_system")) { - if (ns.getString("username") == null) { - parser.printUsage(); - System.err.println("You need to specify a username (phone number)"); - System.exit(2); - } - if (!PhoneNumberFormatter.isValidNumber(ns.getString("username"), null)) { - System.err.println("Invalid username (phone number), make sure you include the country code."); - System.exit(2); - } - } - if (ns.getList("recipient") != null && !ns.getList("recipient").isEmpty() && ns.getString("group") != null) { System.err.println("You cannot specify recipients by phone number and groups at the same time"); System.exit(2); @@ -152,8 +133,9 @@ public class Main { parser.addArgument("--config") .help("Set the path, where to store the config (Default: $XDG_DATA_HOME/signal-cli , $HOME/.local/share/signal-cli)."); + parser.addArgument("-u", "--username").help("Specify your phone number, that will be used for verification."); + MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup(); - mut.addArgument("-u", "--username").help("Specify your phone number, that will be used for verification."); mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue()); diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 7ebecea6..53f45ca3 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -4,6 +4,7 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.DbusConfig; import org.asamk.signal.DbusReceiveMessageHandler; import org.asamk.signal.JsonDbusReceiveMessageHandler; import org.asamk.signal.dbus.DbusSignalImpl; @@ -14,13 +15,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; -import static org.asamk.signal.DbusConfig.SIGNAL_BUSNAME; -import static org.asamk.signal.DbusConfig.SIGNAL_OBJECTPATH; -import static org.asamk.signal.util.ErrorUtils.handleAssertionError; - -public class DaemonCommand implements LocalCommand { +public class DaemonCommand implements MultiLocalCommand { private final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class); @@ -46,46 +45,98 @@ public class DaemonCommand implements LocalCommand { logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); } - DBusConnection conn = null; - try { + boolean ignoreAttachments = ns.getBoolean("ignore_attachments"); + + DBusConnection.DBusBusType busType; + if (ns.getBoolean("system")) { + busType = DBusConnection.DBusBusType.SYSTEM; + } else { + busType = DBusConnection.DBusBusType.SESSION; + } + + try (DBusConnection conn = DBusConnection.getConnection(busType)) { + String objectPath = DbusConfig.getObjectPath(); + Thread t = run(conn, objectPath, m, ignoreAttachments, inJson); + + conn.requestBusName(DbusConfig.getBusname()); + try { - DBusConnection.DBusBusType busType; - if (ns.getBoolean("system")) { - busType = DBusConnection.DBusBusType.SYSTEM; - } else { - busType = DBusConnection.DBusBusType.SESSION; - } - conn = DBusConnection.getConnection(busType); - conn.exportObject(SIGNAL_OBJECTPATH, new DbusSignalImpl(m)); - conn.requestBusName(SIGNAL_BUSNAME); - } catch (UnsatisfiedLinkError e) { - System.err.println("Missing native library dependency for dbus service: " + e.getMessage()); - return 1; - } catch (DBusException e) { - e.printStackTrace(); - return 2; - } - boolean ignoreAttachments = ns.getBoolean("ignore_attachments"); - try { - m.receiveMessages(1, - TimeUnit.HOURS, - false, - ignoreAttachments, - inJson - ? new JsonDbusReceiveMessageHandler(m, conn, SIGNAL_OBJECTPATH) - : new DbusReceiveMessageHandler(m, conn, SIGNAL_OBJECTPATH)); - return 0; - } catch (IOException e) { - System.err.println("Error while receiving messages: " + e.getMessage()); - return 3; - } catch (AssertionError e) { - handleAssertionError(e); - return 1; - } - } finally { - if (conn != null) { - conn.disconnect(); + t.join(); + } catch (InterruptedException ignored) { } + return 0; + } catch (DBusException | IOException e) { + logger.error("Dbus command failed", e); + return 2; } } + + @Override + public int handleCommand(final Namespace ns, final List managers) { + boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + + // TODO delete later when "json" variable is removed + if (ns.getBoolean("json")) { + logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); + } + + boolean ignoreAttachments = ns.getBoolean("ignore_attachments"); + + DBusConnection.DBusBusType busType; + if (ns.getBoolean("system")) { + busType = DBusConnection.DBusBusType.SYSTEM; + } else { + busType = DBusConnection.DBusBusType.SESSION; + } + + try (DBusConnection conn = DBusConnection.getConnection(busType)) { + List receiveThreads = new ArrayList<>(); + for (Manager m : managers) { + String objectPath = DbusConfig.getObjectPath(m.getUsername()); + Thread thread = run(conn, objectPath, m, ignoreAttachments, inJson); + receiveThreads.add(thread); + } + + conn.requestBusName(DbusConfig.getBusname()); + + for (Thread t : receiveThreads) { + try { + t.join(); + } catch (InterruptedException ignored) { + } + } + return 0; + } catch (DBusException | IOException e) { + logger.error("Dbus command failed", e); + return 2; + } + } + + private Thread run( + DBusConnection conn, String objectPath, Manager m, boolean ignoreAttachments, boolean inJson + ) throws DBusException { + conn.exportObject(objectPath, new DbusSignalImpl(m)); + + final Thread thread = new Thread(() -> { + while (true) { + try { + m.receiveMessages(1, + TimeUnit.HOURS, + false, + ignoreAttachments, + inJson + ? new JsonDbusReceiveMessageHandler(m, conn, objectPath) + : new DbusReceiveMessageHandler(m, conn, objectPath)); + } catch (IOException e) { + logger.warn("Receiving messages failed, retrying", e); + } + } + }); + + logger.info("Exported dbus object: " + objectPath); + + thread.start(); + + return thread; + } } diff --git a/src/main/java/org/asamk/signal/commands/DbusCommand.java b/src/main/java/org/asamk/signal/commands/DbusCommand.java index 4dee75b2..1b3a0268 100644 --- a/src/main/java/org/asamk/signal/commands/DbusCommand.java +++ b/src/main/java/org/asamk/signal/commands/DbusCommand.java @@ -3,8 +3,14 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import org.asamk.Signal; +import org.asamk.signal.dbus.DbusSignalImpl; +import org.asamk.signal.manager.Manager; -public interface DbusCommand extends Command { +public interface DbusCommand extends LocalCommand { int handleCommand(Namespace ns, Signal signal); + + default int handleCommand(final Namespace ns, final Manager m) { + return handleCommand(ns, new DbusSignalImpl(m)); + } } diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 8b694cb6..f17fb967 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -50,7 +50,7 @@ public class GetUserStatusCommand implements LocalCommand { registered = m.areUsersRegistered(new HashSet<>(ns.getList("number"))); } catch (IOException e) { System.err.println("Unable to check if users are registered"); - return 1; + return 3; } // Output diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index c5975b0c..bfe46650 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -3,7 +3,6 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; -import org.asamk.Signal; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; @@ -35,15 +34,15 @@ public class JoinGroupCommand implements LocalCommand { linkUrl = GroupInviteLinkUrl.fromUri(uri); } catch (GroupInviteLinkUrl.InvalidGroupLinkException e) { System.err.println("Group link is invalid: " + e.getMessage()); - return 2; + return 1; } catch (GroupInviteLinkUrl.UnknownGroupLinkVersionException e) { System.err.println("Group link was created with an incompatible version: " + e.getMessage()); - return 2; + return 1; } if (linkUrl == null) { System.err.println("Link is not a signal group invitation link"); - return 2; + return 1; } try { @@ -64,16 +63,13 @@ public class JoinGroupCommand implements LocalCommand { } catch (IOException e) { e.printStackTrace(); handleIOException(e); - return 1; - } catch (Signal.Error.AttachmentInvalid e) { - System.err.println("Failed to add avatar attachment for group\": " + e.getMessage()); - return 1; + return 3; } catch (DBusExecutionException e) { System.err.println("Failed to send message: " + e.getMessage()); - return 1; + return 2; } catch (GroupLinkNotActiveException e) { System.err.println("Group link is not valid: " + e.getMessage()); - return 2; + return 1; } } } diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index debc2a42..2a267a9a 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -1,5 +1,10 @@ package org.asamk.signal.commands; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; + import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; @@ -9,11 +14,6 @@ import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; - import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -23,7 +23,8 @@ import java.util.stream.Collectors; public class ListGroupsCommand implements LocalCommand { private static Set resolveMembers(Manager m, Set addresses) { - return addresses.stream().map(m::resolveSignalServiceAddress) + return addresses.stream() + .map(m::resolveSignalServiceAddress) .map(SignalServiceAddress::getLegacyIdentifier) .collect(Collectors.toSet()); } @@ -34,7 +35,7 @@ public class ListGroupsCommand implements LocalCommand { System.out.println(); } catch (IOException e) { System.err.println(e.getMessage()); - return 1; + return 3; } return 0; @@ -65,7 +66,8 @@ public class ListGroupsCommand implements LocalCommand { @Override public void attachToSubparser(final Subparser subparser) { - subparser.addArgument("-d", "--detailed").action(Arguments.storeTrue()) + subparser.addArgument("-d", "--detailed") + .action(Arguments.storeTrue()) .help("List the members and group invite links of each group. If output=json, then this is always set"); subparser.help("List group information including names, ids, active status, blocked status and members"); @@ -114,10 +116,16 @@ public class ListGroupsCommand implements LocalCommand { public Set requestingMembers; public String groupInviteLink; - public JsonGroup(String id, String name, boolean isMember, boolean isBlocked, - Set members, Set pendingMembers, - Set requestingMembers, String groupInviteLink) - { + public JsonGroup( + String id, + String name, + boolean isMember, + boolean isBlocked, + Set members, + Set pendingMembers, + Set requestingMembers, + String groupInviteLink + ) { this.id = id; this.name = name; this.isMember = isMember; diff --git a/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java b/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java new file mode 100644 index 00000000..e8ee8e1d --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java @@ -0,0 +1,17 @@ +package org.asamk.signal.commands; + +import net.sourceforge.argparse4j.inf.Namespace; + +import org.asamk.signal.manager.Manager; + +import java.util.List; + +public interface MultiLocalCommand extends LocalCommand { + + int handleCommand(Namespace ns, List m); + + @Override + default int handleCommand(final Namespace ns, final Manager m) { + return handleCommand(ns, List.of(m)); + } +} diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index e2002258..30518cb4 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -141,12 +141,9 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { System.out.println(); } }); - } catch (UnsatisfiedLinkError e) { - System.err.println("Missing native library dependency for dbus service: " + e.getMessage()); - return 1; } catch (DBusException e) { e.printStackTrace(); - return 1; + return 2; } while (true) { try { diff --git a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java index ada9c446..52b111ea 100644 --- a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java @@ -20,7 +20,10 @@ public class RemovePinCommand implements LocalCommand { try { m.setRegistrationLockPin(Optional.absent()); return 0; - } catch (IOException | UnauthenticatedResponseException e) { + } catch (UnauthenticatedResponseException e) { + System.err.println("Remove pin error: " + e.getMessage()); + return 2; + } catch (IOException e) { System.err.println("Remove pin error: " + e.getMessage()); return 3; } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index a4bdc89b..235fc53c 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -8,6 +8,7 @@ import org.asamk.Signal; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; +import org.freedesktop.dbus.errors.UnknownObject; import org.freedesktop.dbus.exceptions.DBusExecutionException; import java.io.IOException; @@ -50,7 +51,7 @@ public class SendCommand implements DbusCommand { return 1; } catch (DBusExecutionException e) { System.err.println("Failed to send message: " + e.getMessage()); - return 1; + return 2; } } @@ -89,7 +90,7 @@ public class SendCommand implements DbusCommand { return 1; } catch (DBusExecutionException e) { System.err.println("Failed to send message: " + e.getMessage()); - return 1; + return 2; } try { @@ -99,9 +100,12 @@ public class SendCommand implements DbusCommand { } catch (AssertionError e) { handleAssertionError(e); return 1; + } catch (UnknownObject e) { + System.err.println("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); + return 1; } catch (DBusExecutionException e) { System.err.println("Failed to send message: " + e.getMessage()); - return 1; + return 2; } } } diff --git a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java index aaca283a..f5eacf81 100644 --- a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java @@ -20,7 +20,10 @@ public class SendContactsCommand implements LocalCommand { try { m.sendContacts(); return 0; - } catch (IOException | UntrustedIdentityException e) { + } catch (UntrustedIdentityException e) { + System.err.println("SendContacts error: " + e.getMessage()); + return 2; + } catch (IOException e) { System.err.println("SendContacts error: " + e.getMessage()); return 3; } diff --git a/src/main/java/org/asamk/signal/commands/SetPinCommand.java b/src/main/java/org/asamk/signal/commands/SetPinCommand.java index ac601b3b..dbdc83df 100644 --- a/src/main/java/org/asamk/signal/commands/SetPinCommand.java +++ b/src/main/java/org/asamk/signal/commands/SetPinCommand.java @@ -23,7 +23,10 @@ public class SetPinCommand implements LocalCommand { String registrationLockPin = ns.getString("registrationLockPin"); m.setRegistrationLockPin(Optional.of(registrationLockPin)); return 0; - } catch (IOException | UnauthenticatedResponseException e) { + } catch (UnauthenticatedResponseException e) { + System.err.println("Set pin error: " + e.getMessage()); + return 2; + } catch (IOException e) { System.err.println("Set pin error: " + e.getMessage()); return 3; } diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index de8564f0..736190b3 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -69,7 +69,7 @@ public class UpdateGroupCommand implements DbusCommand { return 1; } catch (DBusExecutionException e) { System.err.println("Failed to send message: " + e.getMessage()); - return 1; + return 2; } } } diff --git a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java index f9f5d95b..725f91b3 100644 --- a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java +++ b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java @@ -29,7 +29,7 @@ public class UploadStickerPackCommand implements LocalCommand { return 3; } catch (StickerPackInvalidException e) { System.err.println("Invalid sticker pack: " + e.getMessage()); - return 3; + return 1; } } } diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index a0f6aa53..09e51445 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -288,6 +288,22 @@ public class Manager implements Closeable { return new Manager(account, pathConfig, serviceConfiguration, userAgent); } + public static List getAllLocalUsernames(File settingsPath) { + PathConfig pathConfig = PathConfig.createDefault(settingsPath); + final File dataPath = pathConfig.getDataPath(); + final File[] files = dataPath.listFiles(); + + if (files == null) { + return List.of(); + } + + return Arrays.stream(files) + .filter(File::isFile) + .map(File::getName) + .filter(file -> PhoneNumberFormatter.isValidNumber(file, null)) + .collect(Collectors.toList()); + } + public void checkAccountState() throws IOException { if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) { refreshPreKeys(); diff --git a/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/src/main/java/org/asamk/signal/manager/RegistrationManager.java index b3df9376..d6bf78b1 100644 --- a/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -37,11 +37,12 @@ import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.Locale; -public class RegistrationManager implements AutoCloseable { +public class RegistrationManager implements Closeable { private SignalAccount account; private final PathConfig pathConfig; @@ -184,7 +185,7 @@ public class RegistrationManager implements AutoCloseable { } @Override - public void close() throws Exception { + public void close() throws IOException { if (account != null) { account.close(); account = null; From 9e061c866720bf56d661a7d1feb664e2bcc92dd5 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 15 Jan 2021 21:12:03 +0100 Subject: [PATCH 042/124] Update systemd service file --- data/{signal.service => signal-cli.service} | 2 +- data/signal-cli@.service | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename data/{signal.service => signal-cli.service} (77%) diff --git a/data/signal.service b/data/signal-cli.service similarity index 77% rename from data/signal.service rename to data/signal-cli.service index 089b0428..1e4a5a5c 100644 --- a/data/signal.service +++ b/data/signal-cli.service @@ -8,7 +8,7 @@ After=network-online.target [Service] Type=dbus Environment="SIGNAL_CLI_OPTS=-Xms2m" -ExecStart=%dir%/bin/signal-cli -u %number% --config /var/lib/signal-cli daemon --system +ExecStart=%dir%/bin/signal-cli --config /var/lib/signal-cli daemon --system User=signal-cli BusName=org.asamk.Signal diff --git a/data/signal-cli@.service b/data/signal-cli@.service index 4cc6e2cf..61addf3b 100644 --- a/data/signal-cli@.service +++ b/data/signal-cli@.service @@ -13,4 +13,4 @@ User=signal-cli BusName=org.asamk.Signal [Install] -WantedBy=multi-user.target +Alias=dbus-org.asamk.Signal.service From b31e97dd2d344dc1d5cf928ed6bdb978cff1faa4 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 10:21:38 +0100 Subject: [PATCH 043/124] Move recipients/group argument check to send commands --- src/main/java/org/asamk/signal/Main.java | 5 ---- .../asamk/signal/commands/SendCommand.java | 23 +++++++++------ .../signal/commands/SendReactionCommand.java | 28 +++++++++++-------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index ddcf47e1..45945c02 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -93,11 +93,6 @@ public class Main { return null; } - if (ns.getList("recipient") != null && !ns.getList("recipient").isEmpty() && ns.getString("group") != null) { - System.err.println("You cannot specify recipients by phone number and groups at the same time"); - System.exit(2); - } - return ns; } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 235fc53c..d3717aa2 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -34,17 +34,24 @@ public class SendCommand implements DbusCommand { @Override public int handleCommand(final Namespace ns, final Signal signal) { - if ((ns.getList("recipient") == null || ns.getList("recipient").size() == 0) && ( - ns.getBoolean("endsession") || ns.getString("group") == null - )) { + final List recipients = ns.getList("recipient"); + final Boolean isEndSession = ns.getBoolean("endsession"); + final String groupIdString = ns.getString("group"); + + final boolean noRecipients = recipients == null || recipients.isEmpty(); + if ((noRecipients && isEndSession) || (noRecipients && groupIdString == null)) { System.err.println("No recipients given"); System.err.println("Aborting sending."); return 1; } + if (!noRecipients && groupIdString != null) { + System.err.println("You cannot specify recipients by phone number and groups at the same time"); + return 1; + } - if (ns.getBoolean("endsession")) { + if (isEndSession) { try { - signal.sendEndSessionMessage(ns.getList("recipient")); + signal.sendEndSessionMessage(recipients); return 0; } catch (AssertionError e) { handleAssertionError(e); @@ -72,10 +79,10 @@ public class SendCommand implements DbusCommand { } try { - if (ns.getString("group") != null) { + if (groupIdString != null) { byte[] groupId; try { - groupId = Util.decodeGroupId(ns.getString("group")).serialize(); + groupId = Util.decodeGroupId(groupIdString).serialize(); } catch (GroupIdFormatException e) { handleGroupIdFormatException(e); return 1; @@ -94,7 +101,7 @@ public class SendCommand implements DbusCommand { } try { - long timestamp = signal.sendMessage(messageText, attachments, ns.getList("recipient")); + long timestamp = signal.sendMessage(messageText, attachments, recipients); System.out.println(timestamp); return 0; } catch (AssertionError e) { diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index 2a9afa74..e9ddc1f0 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -47,28 +47,32 @@ public class SendReactionCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if ((ns.getList("recipient") == null || ns.getList("recipient").size() == 0) && ns.getString("group") == null) { + final List recipients = ns.getList("recipient"); + final String groupIdString = ns.getString("group"); + + final boolean noRecipients = recipients == null || recipients.isEmpty(); + if (noRecipients && groupIdString == null) { System.err.println("No recipients given"); System.err.println("Aborting sending."); return 1; } + if (!noRecipients && groupIdString != null) { + System.err.println("You cannot specify recipients by phone number and groups at the same time"); + return 1; + } - String emoji = ns.getString("emoji"); - boolean isRemove = ns.getBoolean("remove"); - String targetAuthor = ns.getString("target_author"); - long targetTimestamp = ns.getLong("target_timestamp"); + final String emoji = ns.getString("emoji"); + final boolean isRemove = ns.getBoolean("remove"); + final String targetAuthor = ns.getString("target_author"); + final long targetTimestamp = ns.getLong("target_timestamp"); try { final Pair> results; - if (ns.getString("group") != null) { - GroupId groupId = Util.decodeGroupId(ns.getString("group")); + if (groupIdString != null) { + GroupId groupId = Util.decodeGroupId(groupIdString); results = m.sendGroupMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, groupId); } else { - results = m.sendMessageReaction(emoji, - isRemove, - targetAuthor, - targetTimestamp, - ns.getList("recipient")); + results = m.sendMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, recipients); } return handleTimestampAndSendMessageResults(results.first(), results.second()); } catch (IOException e) { From e1134d832a1460cc5e1bcc7edf5e908bf013e0bd Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 11:56:41 +0100 Subject: [PATCH 044/124] Send self messages as normal messages, new flag --note-to-self for sync message Fixes #373 --- CHANGELOG.md | 5 ++ build.gradle | 2 +- man/signal-cli.1.adoc | 3 ++ src/main/java/org/asamk/Signal.java | 4 ++ .../asamk/signal/commands/SendCommand.java | 48 ++++++++++++++----- .../org/asamk/signal/dbus/DbusSignalImpl.java | 15 ++++++ .../org/asamk/signal/manager/Manager.java | 39 +++++++++++++-- 7 files changed, 98 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be5b5ea0..466efd58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ ## [Unreleased] ### Added - `--verbose` flag to increase log level +- `--note-to-self` flag for `send` command to send a note to linked devices + +### Changed +- Messages sent to self number will be sent as normal Signal messages again, to + send a sync message, use the new `--note-to-self` flag ### Fixed - Disable registration lock before removing the PIN diff --git a/build.gradle b/build.gradle index ca346dbd..a6805093 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ repositories { } dependencies { - implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_15' + implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_16' implementation 'org.bouncycastle:bcprov-jdk15on:1.68' implementation 'net.sourceforge.argparse4j:argparse4j:0.8.1' implementation 'com.github.hypfvieh:dbus-java:3.2.4' diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 5b1653c0..3ae54ff0 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -165,6 +165,9 @@ Specify the message, if missing, standard input is used. *-a* [ATTACHMENT [ATTACHMENT ...]], *--attachment* [ATTACHMENT [ATTACHMENT ...]]:: Add one or more files as attachment. +*--note-to-self*:: +Send the message to self without notification. + *-e*, *--endsession*:: Clear session state and send end session message. diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 317c70f2..16aec7c9 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -21,6 +21,10 @@ public interface Signal extends DBusInterface { String message, List attachments, List recipients ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UnregisteredUser, Error.UntrustedIdentity; + long sendNoteToSelfMessage( + String message, List attachments + ) throws Error.AttachmentInvalid, Error.Failure, Error.UnregisteredUser, Error.UntrustedIdentity; + void sendEndSessionMessage(List recipients) throws Error.Failure, Error.InvalidNumber, Error.UnregisteredUser, Error.UntrustedIdentity; long sendGroupMessage( diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index d3717aa2..6d806abc 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -1,6 +1,7 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.impl.Arguments; +import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; @@ -13,7 +14,6 @@ import org.freedesktop.dbus.exceptions.DBusExecutionException; import java.io.IOException; import java.nio.charset.Charset; -import java.util.ArrayList; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; @@ -23,8 +23,13 @@ public class SendCommand implements DbusCommand { @Override public void attachToSubparser(final Subparser subparser) { - subparser.addArgument("-g", "--group").help("Specify the recipient group ID."); subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*"); + final MutuallyExclusiveGroup mutuallyExclusiveGroup = subparser.addMutuallyExclusiveGroup(); + mutuallyExclusiveGroup.addArgument("-g", "--group").help("Specify the recipient group ID."); + mutuallyExclusiveGroup.addArgument("--note-to-self") + .help("Send the message to self without notification.") + .action(Arguments.storeTrue()); + subparser.addArgument("-m", "--message").help("Specify the message, if missing standard input is used."); subparser.addArgument("-a", "--attachment").nargs("*").help("Add file as attachment"); subparser.addArgument("-e", "--endsession") @@ -37,9 +42,10 @@ public class SendCommand implements DbusCommand { final List recipients = ns.getList("recipient"); final Boolean isEndSession = ns.getBoolean("endsession"); final String groupIdString = ns.getString("group"); + final Boolean isNoteToSelf = ns.getBoolean("note_to_self"); final boolean noRecipients = recipients == null || recipients.isEmpty(); - if ((noRecipients && isEndSession) || (noRecipients && groupIdString == null)) { + if ((noRecipients && isEndSession) || (noRecipients && groupIdString == null && !isNoteToSelf)) { System.err.println("No recipients given"); System.err.println("Aborting sending."); return 1; @@ -48,6 +54,10 @@ public class SendCommand implements DbusCommand { System.err.println("You cannot specify recipients by phone number and groups at the same time"); return 1; } + if (!noRecipients && isNoteToSelf) { + System.err.println("You cannot specify recipients by phone number and not to self at the same time"); + return 1; + } if (isEndSession) { try { @@ -75,11 +85,11 @@ public class SendCommand implements DbusCommand { List attachments = ns.getList("attachment"); if (attachments == null) { - attachments = new ArrayList<>(); + attachments = List.of(); } - try { - if (groupIdString != null) { + if (groupIdString != null) { + try { byte[] groupId; try { groupId = Util.decodeGroupId(groupIdString).serialize(); @@ -91,13 +101,27 @@ public class SendCommand implements DbusCommand { long timestamp = signal.sendGroupMessage(messageText, attachments, groupId); System.out.println(timestamp); return 0; + } catch (AssertionError e) { + handleAssertionError(e); + return 1; + } catch (DBusExecutionException e) { + System.err.println("Failed to send group message: " + e.getMessage()); + return 2; + } + } + + if (isNoteToSelf) { + try { + long timestamp = signal.sendNoteToSelfMessage(messageText, attachments); + System.out.println(timestamp); + return 0; + } catch (AssertionError e) { + handleAssertionError(e); + return 1; + } catch (DBusExecutionException e) { + System.err.println("Failed to send note to self message: " + e.getMessage()); + return 2; } - } catch (AssertionError e) { - handleAssertionError(e); - return 1; - } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; } try { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 69747b65..77341491 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -78,6 +78,21 @@ public class DbusSignalImpl implements Signal { } } + @Override + public long sendNoteToSelfMessage( + final String message, final List attachments + ) throws Error.AttachmentInvalid, Error.Failure, Error.UnregisteredUser, Error.UntrustedIdentity { + try { + final Pair> results = m.sendSelfMessage(message, attachments); + checkSendMessageResults(results.first(), results.second()); + return results.first(); + } catch (AttachmentInvalidException e) { + throw new Error.AttachmentInvalid(e.getMessage()); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } + } + @Override public void sendEndSessionMessage(final List recipients) { try { diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 09e51445..38c43753 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -953,6 +953,17 @@ public class Manager implements Closeable { return sendMessage(messageBuilder, getSignalServiceAddresses(recipients)); } + public Pair> sendSelfMessage( + String messageText, List attachments + ) throws IOException, AttachmentInvalidException { + final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() + .withBody(messageText); + if (attachments != null) { + messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments)); + } + return sendSelfMessage(messageBuilder); + } + public Pair> sendMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List recipients ) throws IOException, InvalidNumberException { @@ -1253,11 +1264,7 @@ public class Manager implements Closeable { final int expirationTime = contact != null ? contact.messageExpirationTime : 0; messageBuilder.withExpiration(expirationTime); message = messageBuilder.build(); - if (address.matches(account.getSelfAddress())) { - results.add(sendSelfMessage(message)); - } else { - results.add(sendMessage(address, message)); - } + results.add(sendMessage(address, message)); } return new Pair<>(timestamp, results); } @@ -1271,6 +1278,28 @@ public class Manager implements Closeable { } } + private Pair> sendSelfMessage( + SignalServiceDataMessage.Builder messageBuilder + ) throws IOException { + final long timestamp = System.currentTimeMillis(); + messageBuilder.withTimestamp(timestamp); + getOrCreateMessagePipe(); + getOrCreateUnidentifiedMessagePipe(); + try { + final SignalServiceAddress address = getSelfAddress(); + + final ContactInfo contact = account.getContactStore().getContact(address); + final int expirationTime = contact != null ? contact.messageExpirationTime : 0; + messageBuilder.withExpiration(expirationTime); + + SignalServiceDataMessage message = messageBuilder.build(); + final SendMessageResult result = sendSelfMessage(message); + return new Pair<>(timestamp, List.of(result)); + } finally { + account.save(); + } + } + private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException { SignalServiceMessageSender messageSender = createMessageSender(); From 06404667a1539a9107099a52aabf21edbfb0a066 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 15:38:38 +0100 Subject: [PATCH 045/124] Use exit 4 when sending a single recipient message fails due to untrusted identity key Fixes #88 --- man/signal-cli.1.adoc | 1 + src/main/java/org/asamk/Signal.java | 19 +++++-------- .../asamk/signal/commands/SendCommand.java | 9 +++++++ .../org/asamk/signal/dbus/DbusSignalImpl.java | 27 ++++++++++++++++--- .../org/asamk/signal/manager/Manager.java | 6 ++--- .../org/asamk/signal/util/ErrorUtils.java | 20 +++++++++----- 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 3ae54ff0..16b684ea 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -404,6 +404,7 @@ signal-cli -u USERNAME trust -a NUMBER * *1*: Error is probably caused and fixable by the user * *2*: Some unexpected error * *3*: Server or IO error +* *4*: Sending failed due to untrusted key == Files diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 16aec7c9..7f5d54d6 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -15,21 +15,21 @@ public interface Signal extends DBusInterface { long sendMessage( String message, List attachments, String recipient - ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber; + ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; long sendMessage( String message, List attachments, List recipients - ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UnregisteredUser, Error.UntrustedIdentity; + ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; long sendNoteToSelfMessage( String message, List attachments - ) throws Error.AttachmentInvalid, Error.Failure, Error.UnregisteredUser, Error.UntrustedIdentity; + ) throws Error.AttachmentInvalid, Error.Failure; - void sendEndSessionMessage(List recipients) throws Error.Failure, Error.InvalidNumber, Error.UnregisteredUser, Error.UntrustedIdentity; + void sendEndSessionMessage(List recipients) throws Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; long sendGroupMessage( String message, List attachments, byte[] groupId - ) throws Error.GroupNotFound, Error.Failure, Error.AttachmentInvalid, Error.UnregisteredUser, Error.UntrustedIdentity; + ) throws Error.GroupNotFound, Error.Failure, Error.AttachmentInvalid; String getContactName(String number) throws Error.InvalidNumber; @@ -47,7 +47,7 @@ public interface Signal extends DBusInterface { byte[] updateGroup( byte[] groupId, String name, List members, String avatar - ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound, Error.UnregisteredUser, Error.UntrustedIdentity; + ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound; boolean isRegistered(); @@ -198,13 +198,6 @@ public interface Signal extends DBusInterface { } } - class UnregisteredUser extends DBusExecutionException { - - public UnregisteredUser(final String message) { - super(message); - } - } - class UntrustedIdentity extends DBusExecutionException { public UntrustedIdentity(final String message) { diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 6d806abc..73d8f2ed 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -66,6 +66,9 @@ public class SendCommand implements DbusCommand { } catch (AssertionError e) { handleAssertionError(e); return 1; + } catch (Signal.Error.UntrustedIdentity e) { + System.err.println("Failed to send message: " + e.getMessage()); + return 4; } catch (DBusExecutionException e) { System.err.println("Failed to send message: " + e.getMessage()); return 2; @@ -118,6 +121,9 @@ public class SendCommand implements DbusCommand { } catch (AssertionError e) { handleAssertionError(e); return 1; + } catch (Signal.Error.UntrustedIdentity e) { + System.err.println("Failed to send message: " + e.getMessage()); + return 4; } catch (DBusExecutionException e) { System.err.println("Failed to send note to self message: " + e.getMessage()); return 2; @@ -134,6 +140,9 @@ public class SendCommand implements DbusCommand { } catch (UnknownObject e) { System.err.println("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); return 1; + } catch (Signal.Error.UntrustedIdentity e) { + System.err.println("Failed to send message: " + e.getMessage()); + return 4; } catch (DBusExecutionException e) { System.err.println("Failed to send message: " + e.getMessage()); return 2; diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 77341491..a2f6cace 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -45,9 +45,30 @@ public class DbusSignalImpl implements Signal { return sendMessage(message, attachments, recipients); } + private static void checkSendMessageResult(long timestamp, SendMessageResult result) throws DBusExecutionException { + String error = ErrorUtils.getErrorMessageFromSendMessageResult(result); + + if (error == null) { + return; + } + + final String message = timestamp + "\nFailed to send message:\n" + error + '\n'; + + if (result.getIdentityFailure() != null) { + throw new Error.UntrustedIdentity(message); + } else { + throw new Error.Failure(message); + } + } + private static void checkSendMessageResults( long timestamp, List results ) throws DBusExecutionException { + if (results.size() == 1) { + checkSendMessageResult(timestamp, results.get(0)); + return; + } + List errors = ErrorUtils.getErrorMessagesFromSendMessageResults(results); if (errors.size() == 0) { return; @@ -81,10 +102,10 @@ public class DbusSignalImpl implements Signal { @Override public long sendNoteToSelfMessage( final String message, final List attachments - ) throws Error.AttachmentInvalid, Error.Failure, Error.UnregisteredUser, Error.UntrustedIdentity { + ) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { try { - final Pair> results = m.sendSelfMessage(message, attachments); - checkSendMessageResults(results.first(), results.second()); + final Pair results = m.sendSelfMessage(message, attachments); + checkSendMessageResult(results.first(), results.second()); return results.first(); } catch (AttachmentInvalidException e) { throw new Error.AttachmentInvalid(e.getMessage()); diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 38c43753..3c352cd4 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -953,7 +953,7 @@ public class Manager implements Closeable { return sendMessage(messageBuilder, getSignalServiceAddresses(recipients)); } - public Pair> sendSelfMessage( + public Pair sendSelfMessage( String messageText, List attachments ) throws IOException, AttachmentInvalidException { final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() @@ -1278,7 +1278,7 @@ public class Manager implements Closeable { } } - private Pair> sendSelfMessage( + private Pair sendSelfMessage( SignalServiceDataMessage.Builder messageBuilder ) throws IOException { final long timestamp = System.currentTimeMillis(); @@ -1294,7 +1294,7 @@ public class Manager implements Closeable { SignalServiceDataMessage message = messageBuilder.build(); final SendMessageResult result = sendSelfMessage(message); - return new Pair<>(timestamp, List.of(result)); + return new Pair<>(timestamp, result); } finally { account.save(); } diff --git a/src/main/java/org/asamk/signal/util/ErrorUtils.java b/src/main/java/org/asamk/signal/util/ErrorUtils.java index e9553f98..f7115ea9 100644 --- a/src/main/java/org/asamk/signal/util/ErrorUtils.java +++ b/src/main/java/org/asamk/signal/util/ErrorUtils.java @@ -33,18 +33,26 @@ public class ErrorUtils { public static List getErrorMessagesFromSendMessageResults(List results) { List errors = new ArrayList<>(); for (SendMessageResult result : results) { - if (result.isNetworkFailure()) { - errors.add(String.format("Network failure for \"%s\"", result.getAddress().getLegacyIdentifier())); - } else if (result.isUnregisteredFailure()) { - errors.add(String.format("Unregistered user \"%s\"", result.getAddress().getLegacyIdentifier())); - } else if (result.getIdentityFailure() != null) { - errors.add(String.format("Untrusted Identity for \"%s\"", result.getAddress().getLegacyIdentifier())); + String error = getErrorMessageFromSendMessageResult(result); + if (error != null) { + errors.add(error); } } return errors; } + public static String getErrorMessageFromSendMessageResult(SendMessageResult result) { + if (result.isNetworkFailure()) { + return String.format("Network failure for \"%s\"", result.getAddress().getLegacyIdentifier()); + } else if (result.isUnregisteredFailure()) { + return String.format("Unregistered user \"%s\"", result.getAddress().getLegacyIdentifier()); + } else if (result.getIdentityFailure() != null) { + return String.format("Untrusted Identity for \"%s\"", result.getAddress().getLegacyIdentifier()); + } + return null; + } + private static int handleSendMessageResultErrors(List errors) { if (errors.size() == 0) { return 0; From fe25ae275b9c98cdd44ca189a2780e8c9c929c34 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 17:06:10 +0100 Subject: [PATCH 046/124] Use getRegisteredUsers (CDS2) instead of getContacts for getUserStatus command That's what the official app does, looks like the old contact discovery system is not working anymore. --- .../org/asamk/signal/manager/Manager.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 3c352cd4..7d2681ae 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -325,11 +325,9 @@ public class Manager implements Closeable { */ public Map areUsersRegistered(Set numbers) throws IOException { // Note "contactDetails" has no optionals. It only gives us info on users who are registered - List contactDetails = this.accountManager.getContacts(numbers); + Map contactDetails = getRegisteredUsers(numbers); - Set registeredUsers = contactDetails.stream() - .map(ContactTokenDetails::getNumber) - .collect(Collectors.toSet()); + Set registeredUsers = contactDetails.keySet(); return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains)); } @@ -1184,28 +1182,29 @@ public class Manager implements Closeable { private Collection getSignalServiceAddresses(Collection numbers) throws InvalidNumberException { final Set signalServiceAddresses = new HashSet<>(numbers.size()); - final Set missingUuids = new HashSet<>(); + final Set addressesMissingUuid = new HashSet<>(); for (String number : numbers) { final SignalServiceAddress resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number); if (resolvedAddress.getUuid().isPresent()) { signalServiceAddresses.add(resolvedAddress); } else { - missingUuids.add(resolvedAddress); + addressesMissingUuid.add(resolvedAddress); } } + final Set numbersMissingUuid = addressesMissingUuid.stream() + .map(a -> a.getNumber().get()) + .collect(Collectors.toSet()); Map registeredUsers; try { - registeredUsers = accountManager.getRegisteredUsers(getIasKeyStore(), - missingUuids.stream().map(a -> a.getNumber().get()).collect(Collectors.toSet()), - CDS_MRENCLAVE); - } catch (IOException | Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException e) { + registeredUsers = getRegisteredUsers(numbersMissingUuid); + } catch (IOException e) { logger.warn("Failed to resolve uuids from server, ignoring: {}", e.getMessage()); - registeredUsers = new HashMap<>(); + registeredUsers = Map.of(); } - for (SignalServiceAddress address : missingUuids) { + for (SignalServiceAddress address : addressesMissingUuid) { final String number = address.getNumber().get(); if (registeredUsers.containsKey(number)) { final SignalServiceAddress newAddress = resolveSignalServiceAddress(new SignalServiceAddress( @@ -1220,6 +1219,14 @@ public class Manager implements Closeable { return signalServiceAddresses; } + private Map getRegisteredUsers(final Set numbersMissingUuid) throws IOException { + try { + return accountManager.getRegisteredUsers(getIasKeyStore(), numbersMissingUuid, CDS_MRENCLAVE); + } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException e) { + throw new IOException(e); + } + } + private Pair> sendMessage( SignalServiceDataMessage.Builder messageBuilder, Collection recipients ) throws IOException { From 14c8f07b8d173eb162511075da1b4b1108773768 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 17:45:45 +0100 Subject: [PATCH 047/124] Move buildArgumentParser to Cli class --- src/main/java/org/asamk/signal/Cli.java | 57 +++++++++++-- src/main/java/org/asamk/signal/Main.java | 100 +++++------------------ 2 files changed, 68 insertions(+), 89 deletions(-) diff --git a/src/main/java/org/asamk/signal/Cli.java b/src/main/java/org/asamk/signal/Cli.java index 4579f3f5..9f7048ad 100644 --- a/src/main/java/org/asamk/signal/Cli.java +++ b/src/main/java/org/asamk/signal/Cli.java @@ -1,6 +1,12 @@ package org.asamk.signal; +import net.sourceforge.argparse4j.ArgumentParsers; +import net.sourceforge.argparse4j.impl.Arguments; +import net.sourceforge.argparse4j.inf.ArgumentParser; +import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup; import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; +import net.sourceforge.argparse4j.inf.Subparsers; import org.asamk.Signal; import org.asamk.signal.commands.Command; @@ -27,6 +33,7 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceConf import java.io.File; import java.io.IOException; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -36,12 +43,49 @@ public class Cli { private final Namespace ns; + static ArgumentParser buildArgumentParser() { + ArgumentParser parser = ArgumentParsers.newFor("signal-cli") + .build() + .defaultHelp(true) + .description("Commandline interface for Signal.") + .version(BaseConfig.PROJECT_NAME + " " + BaseConfig.PROJECT_VERSION); + + parser.addArgument("-v", "--version").help("Show package version.").action(Arguments.version()); + parser.addArgument("--verbose") + .help("Raise log level and include lib signal logs.") + .action(Arguments.storeTrue()); + parser.addArgument("--config") + .help("Set the path, where to store the config (Default: $XDG_DATA_HOME/signal-cli , $HOME/.local/share/signal-cli)."); + + parser.addArgument("-u", "--username").help("Specify your phone number, that will be used for verification."); + + MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup(); + mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); + mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue()); + + parser.addArgument("-o", "--output") + .help("Choose to output in plain text or JSON") + .choices("plain-text", "json") + .setDefault("plain-text"); + + Subparsers subparsers = parser.addSubparsers().title("subcommands").dest("command"); + + final Map commands = Commands.getCommands(); + for (Map.Entry entry : commands.entrySet()) { + Subparser subparser = subparsers.addParser(entry.getKey()); + entry.getValue().attachToSubparser(subparser); + } + + return parser; + } + public Cli(final Namespace ns) { this.ns = ns; } public int init() { - Command command = getCommand(); + String commandKey = ns.getString("command"); + Command command = Commands.getCommand(commandKey); if (command == null) { logger.error("Command not implemented!"); return 1; @@ -49,9 +93,11 @@ public class Cli { String username = ns.getString("username"); - if (ns.getBoolean("dbus") || ns.getBoolean("dbus_system")) { + final boolean useDbus = ns.getBoolean("dbus"); + final boolean useDbusSystem = ns.getBoolean("dbus_system"); + if (useDbus || useDbusSystem) { // If username is null, it will connect to the default object path - return initDbusClient(command, username, ns.getBoolean("dbus_system")); + return initDbusClient(command, username, useDbusSystem); } final File dataPath; @@ -208,11 +254,6 @@ public class Cli { return manager; } - private Command getCommand() { - String commandKey = ns.getString("command"); - return Commands.getCommand(commandKey); - } - private int initDbusClient(final Command command, final String username, final boolean systemBus) { try { DBusConnection.DBusBusType busType; diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 45945c02..99c80e1e 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -20,19 +20,13 @@ import net.sourceforge.argparse4j.ArgumentParsers; import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.ArgumentParser; import net.sourceforge.argparse4j.inf.ArgumentParserException; -import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup; import net.sourceforge.argparse4j.inf.Namespace; -import net.sourceforge.argparse4j.inf.Subparser; -import net.sourceforge.argparse4j.inf.Subparsers; -import org.asamk.signal.commands.Command; -import org.asamk.signal.commands.Commands; import org.asamk.signal.manager.LibSignalLogger; import org.asamk.signal.util.SecurityProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.Security; -import java.util.Map; public class Main { @@ -40,24 +34,11 @@ public class Main { installSecurityProviderWorkaround(); // Configuring the logger needs to happen before any logger is initialized - if (isVerbose(args)) { - System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug"); - System.setProperty("org.slf4j.simpleLogger.showThreadName", "true"); - System.setProperty("org.slf4j.simpleLogger.showShortLogName", "false"); - System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); - System.setProperty("org.slf4j.simpleLogger.dateTimeFormat", "yyyy-MM-dd'T'HH:mm:ss.SSSXX"); - LibSignalLogger.initLogger(); - } else { - System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info"); - System.setProperty("org.slf4j.simpleLogger.showThreadName", "false"); - System.setProperty("org.slf4j.simpleLogger.showShortLogName", "true"); - System.setProperty("org.slf4j.simpleLogger.showDateTime", "false"); - } + configureLogging(isVerbose(args)); - Namespace ns = parseArgs(args); - if (ns == null) { - System.exit(2); - } + ArgumentParser parser = Cli.buildArgumentParser(); + + Namespace ns = parser.parseArgsOrFail(args); int res = new Cli(ns).init(); System.exit(res); @@ -70,7 +51,8 @@ public class Main { } private static boolean isVerbose(String[] args) { - ArgumentParser parser = buildBaseArgumentParser(); + ArgumentParser parser = ArgumentParsers.newFor("signal-cli").build().defaultHelp(false); + parser.addArgument("--verbose").action(Arguments.storeTrue()); Namespace ns; try { @@ -82,63 +64,19 @@ public class Main { return ns.getBoolean("verbose"); } - private static Namespace parseArgs(String[] args) { - ArgumentParser parser = buildArgumentParser(); - - Namespace ns; - try { - ns = parser.parseArgs(args); - } catch (ArgumentParserException e) { - parser.handleError(e); - return null; + private static void configureLogging(final boolean verbose) { + if (verbose) { + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug"); + System.setProperty("org.slf4j.simpleLogger.showThreadName", "true"); + System.setProperty("org.slf4j.simpleLogger.showShortLogName", "false"); + System.setProperty("org.slf4j.simpleLogger.showDateTime", "true"); + System.setProperty("org.slf4j.simpleLogger.dateTimeFormat", "yyyy-MM-dd'T'HH:mm:ss.SSSXX"); + LibSignalLogger.initLogger(); + } else { + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info"); + System.setProperty("org.slf4j.simpleLogger.showThreadName", "false"); + System.setProperty("org.slf4j.simpleLogger.showShortLogName", "true"); + System.setProperty("org.slf4j.simpleLogger.showDateTime", "false"); } - - return ns; - } - - private static ArgumentParser buildArgumentParser() { - ArgumentParser parser = buildBaseArgumentParser(); - - Subparsers subparsers = parser.addSubparsers() - .title("subcommands") - .dest("command") - .description("valid subcommands") - .help("additional help"); - - final Map commands = Commands.getCommands(); - for (Map.Entry entry : commands.entrySet()) { - Subparser subparser = subparsers.addParser(entry.getKey()); - entry.getValue().attachToSubparser(subparser); - } - - return parser; - } - - private static ArgumentParser buildBaseArgumentParser() { - ArgumentParser parser = ArgumentParsers.newFor("signal-cli") - .build() - .defaultHelp(true) - .description("Commandline interface for Signal.") - .version(BaseConfig.PROJECT_NAME + " " + BaseConfig.PROJECT_VERSION); - - parser.addArgument("-v", "--version").help("Show package version.").action(Arguments.version()); - parser.addArgument("--verbose") - .help("Raise log level and include lib signal logs.") - .action(Arguments.storeTrue()); - parser.addArgument("--config") - .help("Set the path, where to store the config (Default: $XDG_DATA_HOME/signal-cli , $HOME/.local/share/signal-cli)."); - - parser.addArgument("-u", "--username").help("Specify your phone number, that will be used for verification."); - - MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup(); - mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); - mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue()); - - parser.addArgument("-o", "--output") - .help("Choose to output in plain text or JSON") - .choices("plain-text", "json") - .setDefault("plain-text"); - - return parser; } } From d4b4bd86038025ec69e95c9994e30077d1c9d241 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 18:52:06 +0100 Subject: [PATCH 048/124] Add an enum for output types --- src/main/java/org/asamk/signal/Cli.java | 4 ++-- src/main/java/org/asamk/signal/OutputType.java | 16 ++++++++++++++++ .../org/asamk/signal/commands/DaemonCommand.java | 5 +++-- .../signal/commands/GetUserStatusCommand.java | 3 ++- .../asamk/signal/commands/ListGroupsCommand.java | 3 ++- .../asamk/signal/commands/ReceiveCommand.java | 5 +++-- 6 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/asamk/signal/OutputType.java diff --git a/src/main/java/org/asamk/signal/Cli.java b/src/main/java/org/asamk/signal/Cli.java index 9f7048ad..8d46a0a9 100644 --- a/src/main/java/org/asamk/signal/Cli.java +++ b/src/main/java/org/asamk/signal/Cli.java @@ -65,8 +65,8 @@ public class Cli { parser.addArgument("-o", "--output") .help("Choose to output in plain text or JSON") - .choices("plain-text", "json") - .setDefault("plain-text"); + .type(Arguments.enumStringType(OutputType.class)) + .setDefault(OutputType.PLAIN_TEXT); Subparsers subparsers = parser.addSubparsers().title("subcommands").dest("command"); diff --git a/src/main/java/org/asamk/signal/OutputType.java b/src/main/java/org/asamk/signal/OutputType.java new file mode 100644 index 00000000..383d635f --- /dev/null +++ b/src/main/java/org/asamk/signal/OutputType.java @@ -0,0 +1,16 @@ +package org.asamk.signal; + +public enum OutputType { + PLAIN_TEXT { + @Override + public String toString() { + return "plain-text"; + } + }, + JSON { + @Override + public String toString() { + return "json"; + } + }, +} diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 53f45ca3..e5c62ee7 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -7,6 +7,7 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.DbusConfig; import org.asamk.signal.DbusReceiveMessageHandler; import org.asamk.signal.JsonDbusReceiveMessageHandler; +import org.asamk.signal.OutputType; import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; import org.freedesktop.dbus.connections.impl.DBusConnection; @@ -38,7 +39,7 @@ public class DaemonCommand implements MultiLocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { @@ -73,7 +74,7 @@ public class DaemonCommand implements MultiLocalCommand { @Override public int handleCommand(final Namespace ns, final List managers) { - boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index f17fb967..447cbb03 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -7,6 +7,7 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.OutputType; import org.asamk.signal.manager.Manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,7 +38,7 @@ public class GetUserStatusCommand implements LocalCommand { ObjectMapper jsonProcessor = new ObjectMapper(); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 2a267a9a..323fc098 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -9,6 +9,7 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.OutputType; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.storage.groups.GroupInfo; @@ -75,7 +76,7 @@ public class ListGroupsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - if (ns.getString("output").equals("json")) { + if (ns.get("output") == OutputType.JSON) { final ObjectMapper jsonProcessor = new ObjectMapper(); jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 30518cb4..e1d1581e 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -12,6 +12,7 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; import org.asamk.signal.JsonReceiveMessageHandler; +import org.asamk.signal.OutputType; import org.asamk.signal.ReceiveMessageHandler; import org.asamk.signal.json.JsonMessageEnvelope; import org.asamk.signal.manager.Manager; @@ -48,7 +49,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) { final ObjectMapper jsonProcessor; - boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { @@ -156,7 +157,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json"); + boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { From 03bf0439593dc1c5e02bdd49e18d649d1c24e721 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 20:11:32 +0100 Subject: [PATCH 049/124] Extract JsonWriter for json output --- .../signal/JsonReceiveMessageHandler.java | 31 ++++++------ .../java/org/asamk/signal/JsonWriter.java | 37 ++++++++++++++ .../signal/commands/GetUserStatusCommand.java | 24 ++++----- .../signal/commands/ListGroupsCommand.java | 40 +++++++-------- .../asamk/signal/commands/ReceiveCommand.java | 49 ++++++------------- .../signal/manager/storage/SignalAccount.java | 2 +- 6 files changed, 94 insertions(+), 89 deletions(-) create mode 100644 src/main/java/org/asamk/signal/JsonWriter.java diff --git a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java index eb135e13..818f2881 100644 --- a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java @@ -1,45 +1,42 @@ package org.asamk.signal; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; - import org.asamk.signal.json.JsonError; import org.asamk.signal.json.JsonMessageEnvelope; import org.asamk.signal.manager.Manager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler { - final Manager m; - private final ObjectMapper jsonProcessor; + private final static Logger logger = LoggerFactory.getLogger(JsonReceiveMessageHandler.class); + + protected final Manager m; + private final JsonWriter jsonWriter; public JsonReceiveMessageHandler(Manager m) { this.m = m; - this.jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + jsonWriter = new JsonWriter(System.out); } @Override public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { - ObjectNode result = jsonProcessor.createObjectNode(); + final Map object = new HashMap<>(); if (exception != null) { - result.putPOJO("error", new JsonError(exception)); + object.put("error", new JsonError(exception)); } if (envelope != null) { - result.putPOJO("envelope", new JsonMessageEnvelope(envelope, content, m)); + object.put("envelope", new JsonMessageEnvelope(envelope, content, m)); } try { - jsonProcessor.writeValue(System.out, result); - System.out.println(); + jsonWriter.write(object); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to write json object: {}", e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/JsonWriter.java b/src/main/java/org/asamk/signal/JsonWriter.java new file mode 100644 index 00000000..3cc87514 --- /dev/null +++ b/src/main/java/org/asamk/signal/JsonWriter.java @@ -0,0 +1,37 @@ +package org.asamk.signal; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; + +public class JsonWriter { + + private final OutputStreamWriter writer; + private final ObjectMapper objectMapper; + + public JsonWriter(final OutputStream writer) { + this.writer = new OutputStreamWriter(writer, StandardCharsets.UTF_8); + + objectMapper = new ObjectMapper(); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + } + + public void write(final Object object) throws IOException { + try { + objectMapper.writeValue(writer, object); + } catch (JsonProcessingException e) { + // Some issue with json serialization, probably caused by a bug + throw new AssertionError(e); + } + writer.write(System.lineSeparator()); + writer.flush(); + } +} diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 447cbb03..103d1027 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -1,12 +1,10 @@ package org.asamk.signal.commands; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; - import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.manager.Manager; import org.slf4j.Logger; @@ -20,7 +18,6 @@ import java.util.stream.Collectors; public class GetUserStatusCommand implements LocalCommand { - // TODO delete later when "json" variable is removed private final static Logger logger = LoggerFactory.getLogger(GetUserStatusCommand.class); @Override @@ -35,9 +32,6 @@ public class GetUserStatusCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { // Setup the json object mapper - ObjectMapper jsonProcessor = new ObjectMapper(); - jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -56,16 +50,18 @@ public class GetUserStatusCommand implements LocalCommand { // Output if (inJson) { - List objects = registered.entrySet() + final JsonWriter jsonWriter = new JsonWriter(System.out); + + List jsonUserStatuses = registered.entrySet() .stream() - .map(entry -> new JsonIsRegistered(entry.getKey(), entry.getValue())) + .map(entry -> new JsonUserStatus(entry.getKey(), entry.getValue())) .collect(Collectors.toList()); try { - jsonProcessor.writeValue(System.out, objects); - System.out.println(); + jsonWriter.write(jsonUserStatuses); } catch (IOException e) { - System.err.println(e.getMessage()); + logger.error("Failed to write json object: {}", e.getMessage()); + return 3; } } else { for (Map.Entry entry : registered.entrySet()) { @@ -76,13 +72,13 @@ public class GetUserStatusCommand implements LocalCommand { return 0; } - private static final class JsonIsRegistered { + private static final class JsonUserStatus { public String name; public boolean isRegistered; - public JsonIsRegistered(String name, boolean isRegistered) { + public JsonUserStatus(String name, boolean isRegistered) { this.name = name; this.isRegistered = isRegistered; } diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 323fc098..b178b09c 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -1,18 +1,16 @@ package org.asamk.signal.commands; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; - import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.storage.groups.GroupInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.IOException; @@ -23,6 +21,8 @@ import java.util.stream.Collectors; public class ListGroupsCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(ListGroupsCommand.class); + private static Set resolveMembers(Manager m, Set addresses) { return addresses.stream() .map(m::resolveSignalServiceAddress) @@ -30,18 +30,6 @@ public class ListGroupsCommand implements LocalCommand { .collect(Collectors.toSet()); } - private static int printGroupsJson(ObjectMapper jsonProcessor, List objects) { - try { - jsonProcessor.writeValue(System.out, objects); - System.out.println(); - } catch (IOException e) { - System.err.println(e.getMessage()); - return 3; - } - - return 0; - } - private static void printGroupPlainText(Manager m, GroupInfo group, boolean detailed) { if (detailed) { final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); @@ -77,15 +65,13 @@ public class ListGroupsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { if (ns.get("output") == OutputType.JSON) { - final ObjectMapper jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + final JsonWriter jsonWriter = new JsonWriter(System.out); - List objects = new ArrayList<>(); + List jsonGroups = new ArrayList<>(); for (GroupInfo group : m.getGroups()) { final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); - objects.add(new JsonGroup(group.getGroupId().toBase64(), + jsonGroups.add(new JsonGroup(group.getGroupId().toBase64(), group.getTitle(), group.isMember(m.getSelfAddress()), group.isBlocked(), @@ -94,7 +80,15 @@ public class ListGroupsCommand implements LocalCommand { resolveMembers(m, group.getRequestingMembers()), groupInviteLink == null ? null : groupInviteLink.getUrl())); } - return printGroupsJson(jsonProcessor, objects); + + try { + jsonWriter.write(jsonGroups); + } catch (IOException e) { + logger.error("Failed to write json object: {}", e.getMessage()); + return 3; + } + + return 0; } else { boolean detailed = ns.getBoolean("detailed"); for (GroupInfo group : m.getGroups()) { diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index e1d1581e..445b17c9 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -1,17 +1,12 @@ package org.asamk.signal.commands; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; - import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; import org.asamk.signal.JsonReceiveMessageHandler; +import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.ReceiveMessageHandler; import org.asamk.signal.json.JsonMessageEnvelope; @@ -24,13 +19,13 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Base64; +import java.util.Map; import java.util.concurrent.TimeUnit; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { - // TODO delete later when "json" variable is removed private final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class); @Override @@ -47,8 +42,6 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) { - final ObjectMapper jsonProcessor; - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -56,24 +49,16 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); } - if (inJson) { - jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - } else { - jsonProcessor = null; - } + final JsonWriter jsonWriter = inJson ? new JsonWriter(System.out) : null; try { dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { - if (jsonProcessor != null) { + if (jsonWriter != null) { JsonMessageEnvelope envelope = new JsonMessageEnvelope(messageReceived); - ObjectNode result = jsonProcessor.createObjectNode(); - result.putPOJO("envelope", envelope); + final Map object = Map.of("envelope", envelope); try { - jsonProcessor.writeValue(System.out, result); - System.out.println(); + jsonWriter.write(object); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to write json object: {}", e.getMessage()); } } else { System.out.print(String.format("Envelope from: %s\nTimestamp: %s\nBody: %s\n", @@ -95,15 +80,13 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { }); dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { - if (jsonProcessor != null) { + if (jsonWriter != null) { JsonMessageEnvelope envelope = new JsonMessageEnvelope(receiptReceived); - ObjectNode result = jsonProcessor.createObjectNode(); - result.putPOJO("envelope", envelope); + final Map object = Map.of("envelope", envelope); try { - jsonProcessor.writeValue(System.out, result); - System.out.println(); + jsonWriter.write(object); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to write json object: {}", e.getMessage()); } } else { System.out.print(String.format("Receipt from: %s\nTimestamp: %s\n", @@ -113,15 +96,13 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { }); dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { - if (jsonProcessor != null) { + if (jsonWriter != null) { JsonMessageEnvelope envelope = new JsonMessageEnvelope(syncReceived); - ObjectNode result = jsonProcessor.createObjectNode(); - result.putPOJO("envelope", envelope); + final Map object = Map.of("envelope", envelope); try { - jsonProcessor.writeValue(System.out, result); - System.out.println(); + jsonWriter.write(object); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to write json object: {}", e.getMessage()); } } else { System.out.print(String.format("Sync Envelope from: %s to: %s\nTimestamp: %s\nBody: %s\n", diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index c9051335..a6e243a8 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -91,7 +91,7 @@ public class SignalAccount implements Closeable { this.fileChannel = fileChannel; this.lock = lock; jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); // disable autodetect - jsonProcessor.enable(SerializationFeature.INDENT_OUTPUT); // for pretty print, you can disable it. + jsonProcessor.enable(SerializationFeature.INDENT_OUTPUT); // for pretty print jsonProcessor.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); jsonProcessor.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); From 690ce2829f8ea4b3c5e88c3fef2d427e7f507494 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 20:11:50 +0100 Subject: [PATCH 050/124] Fix logger names --- src/main/java/org/asamk/signal/Cli.java | 2 +- src/main/java/org/asamk/signal/commands/DaemonCommand.java | 2 +- src/main/java/org/asamk/signal/manager/ProvisioningManager.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/asamk/signal/Cli.java b/src/main/java/org/asamk/signal/Cli.java index 8d46a0a9..fa21bec5 100644 --- a/src/main/java/org/asamk/signal/Cli.java +++ b/src/main/java/org/asamk/signal/Cli.java @@ -39,7 +39,7 @@ import java.util.stream.Collectors; public class Cli { - private final static Logger logger = LoggerFactory.getLogger(Main.class); + private final static Logger logger = LoggerFactory.getLogger(Cli.class); private final Namespace ns; diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index e5c62ee7..7de0a71f 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit; public class DaemonCommand implements MultiLocalCommand { - private final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class); + private final static Logger logger = LoggerFactory.getLogger(DaemonCommand.class); @Override public void attachToSubparser(final Subparser subparser) { diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 6b8a3797..24f4dd06 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -40,7 +40,7 @@ import java.util.concurrent.TimeoutException; public class ProvisioningManager { - private final static Logger logger = LoggerFactory.getLogger(Manager.class); + private final static Logger logger = LoggerFactory.getLogger(ProvisioningManager.class); private final PathConfig pathConfig; private final SignalServiceConfiguration serviceConfiguration; From c588130491f4f2cd4e142e829aba58f17289aa01 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 20:12:24 +0100 Subject: [PATCH 051/124] Fix group info for json dbus messages --- src/main/java/org/asamk/signal/json/JsonDataMessage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 787f47ab..fd3f1efc 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -69,7 +69,7 @@ class JsonDataMessage { public JsonDataMessage(Signal.MessageReceived messageReceived) { timestamp = messageReceived.getTimestamp(); message = messageReceived.getMessage(); - groupInfo = new JsonGroupInfo(messageReceived.getGroupId()); + groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; @@ -80,7 +80,7 @@ class JsonDataMessage { public JsonDataMessage(Signal.SyncMessageReceived messageReceived) { timestamp = messageReceived.getTimestamp(); message = messageReceived.getMessage(); - groupInfo = new JsonGroupInfo(messageReceived.getGroupId()); + groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; From a2debdb2347b103c6b49b5b2dacfcfabb27fff71 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 21:12:01 +0100 Subject: [PATCH 052/124] Check if output type is supported by command --- src/main/java/org/asamk/signal/Cli.java | 6 ++++++ src/main/java/org/asamk/signal/commands/Command.java | 8 ++++++++ .../java/org/asamk/signal/commands/DaemonCommand.java | 6 ++++++ .../org/asamk/signal/commands/GetUserStatusCommand.java | 6 ++++++ .../java/org/asamk/signal/commands/ListGroupsCommand.java | 5 +++++ .../java/org/asamk/signal/commands/ReceiveCommand.java | 6 ++++++ 6 files changed, 37 insertions(+) diff --git a/src/main/java/org/asamk/signal/Cli.java b/src/main/java/org/asamk/signal/Cli.java index fa21bec5..f8e2e9ab 100644 --- a/src/main/java/org/asamk/signal/Cli.java +++ b/src/main/java/org/asamk/signal/Cli.java @@ -91,6 +91,12 @@ public class Cli { return 1; } + OutputType outputType = ns.get("output"); + if (!command.getSupportedOutputTypes().contains(outputType)) { + logger.error("Command doesn't support output type {}", outputType.toString()); + return 1; + } + String username = ns.getString("username"); final boolean useDbus = ns.getBoolean("dbus"); diff --git a/src/main/java/org/asamk/signal/commands/Command.java b/src/main/java/org/asamk/signal/commands/Command.java index 1e4abc19..fc55cbe2 100644 --- a/src/main/java/org/asamk/signal/commands/Command.java +++ b/src/main/java/org/asamk/signal/commands/Command.java @@ -2,7 +2,15 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.OutputType; + +import java.util.Set; + public interface Command { void attachToSubparser(Subparser subparser); + + default Set getSupportedOutputTypes() { + return Set.of(OutputType.PLAIN_TEXT); + } } diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 7de0a71f..19859d45 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; public class DaemonCommand implements MultiLocalCommand { @@ -37,6 +38,11 @@ public class DaemonCommand implements MultiLocalCommand { .action(Arguments.storeTrue()); } + @Override + public Set getSupportedOutputTypes() { + return Set.of(OutputType.PLAIN_TEXT, OutputType.JSON); + } + @Override public int handleCommand(final Namespace ns, final Manager m) { boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 103d1027..4bc209cd 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; public class GetUserStatusCommand implements LocalCommand { @@ -29,6 +30,11 @@ public class GetUserStatusCommand implements LocalCommand { .action(Arguments.storeTrue()); } + @Override + public Set getSupportedOutputTypes() { + return Set.of(OutputType.PLAIN_TEXT, OutputType.JSON); + } + @Override public int handleCommand(final Namespace ns, final Manager m) { // Setup the json object mapper diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index b178b09c..e0086cae 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -62,6 +62,11 @@ public class ListGroupsCommand implements LocalCommand { subparser.help("List group information including names, ids, active status, blocked status and members"); } + @Override + public Set getSupportedOutputTypes() { + return Set.of(OutputType.PLAIN_TEXT, OutputType.JSON); + } + @Override public int handleCommand(final Namespace ns, final Manager m) { if (ns.get("output") == OutputType.JSON) { diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 445b17c9..5d41dde0 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Base64; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; @@ -41,6 +42,11 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { .action(Arguments.storeTrue()); } + @Override + public Set getSupportedOutputTypes() { + return Set.of(OutputType.PLAIN_TEXT, OutputType.JSON); + } + public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) { boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); From dd4225dcb193218a6c20a2e12514d0dac2e2a3a0 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 16 Jan 2021 21:20:12 +0100 Subject: [PATCH 053/124] Rename Cli class to App --- src/main/java/org/asamk/signal/{Cli.java => App.java} | 6 +++--- src/main/java/org/asamk/signal/Main.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/org/asamk/signal/{Cli.java => App.java} (99%) diff --git a/src/main/java/org/asamk/signal/Cli.java b/src/main/java/org/asamk/signal/App.java similarity index 99% rename from src/main/java/org/asamk/signal/Cli.java rename to src/main/java/org/asamk/signal/App.java index f8e2e9ab..6dc3d799 100644 --- a/src/main/java/org/asamk/signal/Cli.java +++ b/src/main/java/org/asamk/signal/App.java @@ -37,9 +37,9 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -public class Cli { +public class App { - private final static Logger logger = LoggerFactory.getLogger(Cli.class); + private final static Logger logger = LoggerFactory.getLogger(App.class); private final Namespace ns; @@ -79,7 +79,7 @@ public class Cli { return parser; } - public Cli(final Namespace ns) { + public App(final Namespace ns) { this.ns = ns; } diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 99c80e1e..2ec1470c 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -36,11 +36,11 @@ public class Main { // Configuring the logger needs to happen before any logger is initialized configureLogging(isVerbose(args)); - ArgumentParser parser = Cli.buildArgumentParser(); + ArgumentParser parser = App.buildArgumentParser(); Namespace ns = parser.parseArgsOrFail(args); - int res = new Cli(ns).init(); + int res = new App(ns).init(); System.exit(res); } From 46a4c2c0d0a36b7f230fc2c2d47aa371a6e02d35 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Jan 2021 11:23:02 +0100 Subject: [PATCH 054/124] Make fields in json classes final and omit unnecessary nulls when serializing Fix sender in sync read messages --- .../java/org/asamk/signal/JsonWriter.java | 2 +- .../org/asamk/signal/json/JsonAttachment.java | 38 +++++--- .../asamk/signal/json/JsonCallMessage.java | 47 +++++---- .../asamk/signal/json/JsonDataMessage.java | 77 ++++++++++----- .../java/org/asamk/signal/json/JsonError.java | 5 +- .../org/asamk/signal/json/JsonGroupInfo.java | 46 ++++++--- .../org/asamk/signal/json/JsonMention.java | 13 ++- .../signal/json/JsonMessageEnvelope.java | 97 +++++++++++++------ .../java/org/asamk/signal/json/JsonQuote.java | 24 ++++- .../signal/json/JsonQuotedAttachment.java | 15 ++- .../org/asamk/signal/json/JsonReaction.java | 17 +++- .../asamk/signal/json/JsonReceiptMessage.java | 26 ++--- .../org/asamk/signal/json/JsonSticker.java | 14 ++- .../signal/json/JsonSyncDataMessage.java | 13 ++- .../asamk/signal/json/JsonSyncMessage.java | 73 +++++++++++--- .../signal/json/JsonSyncReadMessage.java | 17 ++++ 16 files changed, 374 insertions(+), 150 deletions(-) create mode 100644 src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java diff --git a/src/main/java/org/asamk/signal/JsonWriter.java b/src/main/java/org/asamk/signal/JsonWriter.java index 3cc87514..26a20a1f 100644 --- a/src/main/java/org/asamk/signal/JsonWriter.java +++ b/src/main/java/org/asamk/signal/JsonWriter.java @@ -20,7 +20,7 @@ public class JsonWriter { this.writer = new OutputStreamWriter(writer, StandardCharsets.UTF_8); objectMapper = new ObjectMapper(); - objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY); objectMapper.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); } diff --git a/src/main/java/org/asamk/signal/json/JsonAttachment.java b/src/main/java/org/asamk/signal/json/JsonAttachment.java index 1949171a..1b2a7bc7 100644 --- a/src/main/java/org/asamk/signal/json/JsonAttachment.java +++ b/src/main/java/org/asamk/signal/json/JsonAttachment.java @@ -1,31 +1,45 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; class JsonAttachment { - String contentType; - String filename; - String id; - int size; + @JsonProperty + final String contentType; + + @JsonProperty + final String filename; + + @JsonProperty + final String id; + + @JsonProperty + final Long size; JsonAttachment(SignalServiceAttachment attachment) { this.contentType = attachment.getContentType(); - final SignalServiceAttachmentPointer pointer = attachment.asPointer(); if (attachment.isPointer()) { - this.id = String.valueOf(pointer.getRemoteId()); - if (pointer.getFileName().isPresent()) { - this.filename = pointer.getFileName().get(); - } - if (pointer.getSize().isPresent()) { - this.size = pointer.getSize().get(); - } + final SignalServiceAttachmentPointer pointer = attachment.asPointer(); + this.id = pointer.getRemoteId().toString(); + this.filename = pointer.getFileName().orNull(); + this.size = pointer.getSize().transform(Integer::longValue).orNull(); + } else { + final SignalServiceAttachmentStream stream = attachment.asStream(); + this.id = null; + this.filename = stream.getFileName().orNull(); + this.size = stream.getLength(); } } JsonAttachment(String filename) { this.filename = filename; + this.contentType = null; + this.id = null; + this.size = null; } } diff --git a/src/main/java/org/asamk/signal/json/JsonCallMessage.java b/src/main/java/org/asamk/signal/json/JsonCallMessage.java index c1b1d443..885c38a4 100644 --- a/src/main/java/org/asamk/signal/json/JsonCallMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonCallMessage.java @@ -1,5 +1,8 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; import org.whispersystems.signalservice.api.messages.calls.BusyMessage; import org.whispersystems.signalservice.api.messages.calls.HangupMessage; @@ -11,27 +14,31 @@ import java.util.List; class JsonCallMessage { - OfferMessage offerMessage; - AnswerMessage answerMessage; - BusyMessage busyMessage; - HangupMessage hangupMessage; - List iceUpdateMessages; + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final OfferMessage offerMessage; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final AnswerMessage answerMessage; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final BusyMessage busyMessage; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final HangupMessage hangupMessage; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List iceUpdateMessages; JsonCallMessage(SignalServiceCallMessage callMessage) { - if (callMessage.getOfferMessage().isPresent()) { - this.offerMessage = callMessage.getOfferMessage().get(); - } - if (callMessage.getAnswerMessage().isPresent()) { - this.answerMessage = callMessage.getAnswerMessage().get(); - } - if (callMessage.getBusyMessage().isPresent()) { - this.busyMessage = callMessage.getBusyMessage().get(); - } - if (callMessage.getHangupMessage().isPresent()) { - this.hangupMessage = callMessage.getHangupMessage().get(); - } - if (callMessage.getIceUpdateMessages().isPresent()) { - this.iceUpdateMessages = callMessage.getIceUpdateMessages().get(); - } + this.offerMessage = callMessage.getOfferMessage().orNull(); + this.answerMessage = callMessage.getAnswerMessage().orNull(); + this.busyMessage = callMessage.getBusyMessage().orNull(); + this.hangupMessage = callMessage.getHangupMessage().orNull(); + this.iceUpdateMessages = callMessage.getIceUpdateMessages().orNull(); } } diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index fd3f1efc..e755acad 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -1,9 +1,13 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.asamk.Signal; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; +import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import java.util.List; @@ -11,38 +15,61 @@ import java.util.stream.Collectors; class JsonDataMessage { - long timestamp; - String message; - int expiresInSeconds; + @JsonProperty + final long timestamp; - JsonReaction reaction; - JsonQuote quote; - List mentions; - List attachments; - JsonSticker sticker; - JsonGroupInfo groupInfo; + @JsonProperty + final String message; + + @JsonProperty + final Integer expiresInSeconds; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonReaction reaction; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonQuote quote; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List mentions; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List attachments; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonSticker sticker; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonGroupInfo groupInfo; JsonDataMessage(SignalServiceDataMessage dataMessage, Manager m) { this.timestamp = dataMessage.getTimestamp(); if (dataMessage.getGroupContext().isPresent()) { - if (dataMessage.getGroupContext().get().getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = dataMessage.getGroupContext().get().getGroupV1().get(); + final SignalServiceGroupContext groupContext = dataMessage.getGroupContext().get(); + if (groupContext.getGroupV1().isPresent()) { + SignalServiceGroup groupInfo = groupContext.getGroupV1().get(); this.groupInfo = new JsonGroupInfo(groupInfo); - } else if (dataMessage.getGroupContext().get().getGroupV2().isPresent()) { - SignalServiceGroupV2 groupInfo = dataMessage.getGroupContext().get().getGroupV2().get(); + } else if (groupContext.getGroupV2().isPresent()) { + SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get(); this.groupInfo = new JsonGroupInfo(groupInfo); + } else { + this.groupInfo = null; } + } else { + this.groupInfo = null; } - if (dataMessage.getBody().isPresent()) { - this.message = dataMessage.getBody().get(); - } + this.message = dataMessage.getBody().orNull(); this.expiresInSeconds = dataMessage.getExpiresInSeconds(); - if (dataMessage.getReaction().isPresent()) { - this.reaction = new JsonReaction(dataMessage.getReaction().get(), m); - } - if (dataMessage.getQuote().isPresent()) { - this.quote = new JsonQuote(dataMessage.getQuote().get(), m); - } + this.reaction = dataMessage.getReaction().isPresent() + ? new JsonReaction(dataMessage.getReaction().get(), m) + : null; + this.quote = dataMessage.getQuote().isPresent() ? new JsonQuote(dataMessage.getQuote().get(), m) : null; if (dataMessage.getMentions().isPresent()) { this.mentions = dataMessage.getMentions() .get() @@ -61,15 +88,14 @@ class JsonDataMessage { } else { this.attachments = List.of(); } - if (dataMessage.getSticker().isPresent()) { - this.sticker = new JsonSticker(dataMessage.getSticker().get()); - } + this.sticker = dataMessage.getSticker().isPresent() ? new JsonSticker(dataMessage.getSticker().get()) : null; } public JsonDataMessage(Signal.MessageReceived messageReceived) { timestamp = messageReceived.getTimestamp(); message = messageReceived.getMessage(); groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; + expiresInSeconds = null; reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; @@ -81,6 +107,7 @@ class JsonDataMessage { timestamp = messageReceived.getTimestamp(); message = messageReceived.getMessage(); groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; + expiresInSeconds = null; reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; diff --git a/src/main/java/org/asamk/signal/json/JsonError.java b/src/main/java/org/asamk/signal/json/JsonError.java index 29d85c8b..d8b3e5f5 100644 --- a/src/main/java/org/asamk/signal/json/JsonError.java +++ b/src/main/java/org/asamk/signal/json/JsonError.java @@ -1,8 +1,11 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonProperty; + public class JsonError { - String message; + @JsonProperty + final String message; public JsonError(Throwable exception) { this.message = exception.getMessage(); diff --git a/src/main/java/org/asamk/signal/json/JsonGroupInfo.java b/src/main/java/org/asamk/signal/json/JsonGroupInfo.java index bc3e7e0e..d79941ed 100644 --- a/src/main/java/org/asamk/signal/json/JsonGroupInfo.java +++ b/src/main/java/org/asamk/signal/json/JsonGroupInfo.java @@ -1,41 +1,59 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.asamk.signal.manager.groups.GroupUtils; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import java.util.ArrayList; import java.util.Base64; import java.util.List; +import java.util.stream.Collectors; class JsonGroupInfo { - String groupId; - List members; - String name; - String type; + @JsonProperty + final String groupId; + + @JsonProperty + final String type; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final String name; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List members; JsonGroupInfo(SignalServiceGroup groupInfo) { this.groupId = Base64.getEncoder().encodeToString(groupInfo.getGroupId()); - if (groupInfo.getMembers().isPresent()) { - this.members = new ArrayList<>(groupInfo.getMembers().get().size()); - for (SignalServiceAddress address : groupInfo.getMembers().get()) { - this.members.add(address.getLegacyIdentifier()); - } - } - if (groupInfo.getName().isPresent()) { - this.name = groupInfo.getName().get(); - } this.type = groupInfo.getType().toString(); + this.name = groupInfo.getName().orNull(); + if (groupInfo.getMembers().isPresent()) { + this.members = groupInfo.getMembers() + .get() + .stream() + .map(SignalServiceAddress::getLegacyIdentifier) + .collect(Collectors.toList()); + } else { + this.members = null; + } } JsonGroupInfo(SignalServiceGroupV2 groupInfo) { this.groupId = GroupUtils.getGroupIdV2(groupInfo.getMasterKey()).toBase64(); this.type = groupInfo.hasSignedGroupChange() ? "UPDATE" : "DELIVER"; + this.members = null; + this.name = null; } JsonGroupInfo(byte[] groupId) { this.groupId = Base64.getEncoder().encodeToString(groupId); + this.type = "DELIVER"; + this.members = null; + this.name = null; } } diff --git a/src/main/java/org/asamk/signal/json/JsonMention.java b/src/main/java/org/asamk/signal/json/JsonMention.java index 302128ed..b6243041 100644 --- a/src/main/java/org/asamk/signal/json/JsonMention.java +++ b/src/main/java/org/asamk/signal/json/JsonMention.java @@ -1,14 +1,21 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.push.SignalServiceAddress; public class JsonMention { - String name; - int start; - int length; + @JsonProperty + final String name; + + @JsonProperty + final int start; + + @JsonProperty + final int length; JsonMention(SignalServiceDataMessage.Mention mention, Manager m) { this.name = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)) diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index 787f62e2..2a87d129 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -1,5 +1,8 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.asamk.Signal; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceContent; @@ -10,61 +13,99 @@ import java.util.List; public class JsonMessageEnvelope { - String source; - int sourceDevice; - String relay; - long timestamp; - JsonDataMessage dataMessage; - JsonSyncMessage syncMessage; - JsonCallMessage callMessage; - JsonReceiptMessage receiptMessage; + @JsonProperty + final String source; + + @JsonProperty + final Integer sourceDevice; + + @JsonProperty + final String relay; + + @JsonProperty + final long timestamp; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonDataMessage dataMessage; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonSyncMessage syncMessage; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonCallMessage callMessage; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonReceiptMessage receiptMessage; public JsonMessageEnvelope(SignalServiceEnvelope envelope, SignalServiceContent content, Manager m) { if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { SignalServiceAddress source = envelope.getSourceAddress(); this.source = source.getLegacyIdentifier(); - this.relay = source.getRelay().isPresent() ? source.getRelay().get() : null; + this.sourceDevice = envelope.getSourceDevice(); + this.relay = source.getRelay().orNull(); + } else if (envelope.isUnidentifiedSender() && content != null) { + this.source = content.getSender().getLegacyIdentifier(); + this.sourceDevice = content.getSenderDevice(); + this.relay = null; + } else { + this.source = null; + this.sourceDevice = null; + this.relay = null; } - this.sourceDevice = envelope.getSourceDevice(); this.timestamp = envelope.getTimestamp(); if (envelope.isReceipt()) { this.receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp)); + } else if (content != null && content.getReceiptMessage().isPresent()) { + this.receiptMessage = new JsonReceiptMessage(content.getReceiptMessage().get()); + } else { + this.receiptMessage = null; } - if (content != null) { - if (envelope.isUnidentifiedSender()) { - this.source = content.getSender().getLegacyIdentifier(); - this.sourceDevice = content.getSenderDevice(); - } - if (content.getDataMessage().isPresent()) { - this.dataMessage = new JsonDataMessage(content.getDataMessage().get(), m); - } - if (content.getSyncMessage().isPresent()) { - this.syncMessage = new JsonSyncMessage(content.getSyncMessage().get(), m); - } - if (content.getCallMessage().isPresent()) { - this.callMessage = new JsonCallMessage(content.getCallMessage().get()); - } - if (content.getReceiptMessage().isPresent()) { - this.receiptMessage = new JsonReceiptMessage(content.getReceiptMessage().get()); - } - } + + this.dataMessage = content != null && content.getDataMessage().isPresent() + ? new JsonDataMessage(content.getDataMessage().get(), m) + : null; + this.syncMessage = content != null && content.getSyncMessage().isPresent() + ? new JsonSyncMessage(content.getSyncMessage().get(), m) + : null; + this.callMessage = content != null && content.getCallMessage().isPresent() + ? new JsonCallMessage(content.getCallMessage().get()) + : null; } public JsonMessageEnvelope(Signal.MessageReceived messageReceived) { source = messageReceived.getSender(); + sourceDevice = null; + relay = null; timestamp = messageReceived.getTimestamp(); + receiptMessage = null; dataMessage = new JsonDataMessage(messageReceived); + syncMessage = null; + callMessage = null; } public JsonMessageEnvelope(Signal.ReceiptReceived receiptReceived) { source = receiptReceived.getSender(); + sourceDevice = null; + relay = null; timestamp = receiptReceived.getTimestamp(); receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp)); + dataMessage = null; + syncMessage = null; + callMessage = null; } public JsonMessageEnvelope(Signal.SyncMessageReceived messageReceived) { source = messageReceived.getSource(); + sourceDevice = null; + relay = null; timestamp = messageReceived.getTimestamp(); + receiptMessage = null; + dataMessage = null; syncMessage = new JsonSyncMessage(messageReceived); + callMessage = null; } } diff --git a/src/main/java/org/asamk/signal/json/JsonQuote.java b/src/main/java/org/asamk/signal/json/JsonQuote.java index 10cd0bf4..8165bcfa 100644 --- a/src/main/java/org/asamk/signal/json/JsonQuote.java +++ b/src/main/java/org/asamk/signal/json/JsonQuote.java @@ -1,5 +1,8 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; @@ -9,12 +12,21 @@ import java.util.stream.Collectors; public class JsonQuote { - long id; - String author; - String text; + @JsonProperty + final long id; - List mentions; - List attachments; + @JsonProperty + final String author; + + @JsonProperty + final String text; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List mentions; + + @JsonProperty + final List attachments; JsonQuote(SignalServiceDataMessage.Quote quote, Manager m) { this.id = quote.getId(); @@ -26,6 +38,8 @@ public class JsonQuote { .stream() .map(quotedMention -> new JsonMention(quotedMention, m)) .collect(Collectors.toList()); + } else { + this.mentions = null; } if (quote.getAttachments().size() > 0) { diff --git a/src/main/java/org/asamk/signal/json/JsonQuotedAttachment.java b/src/main/java/org/asamk/signal/json/JsonQuotedAttachment.java index bcbbe2a5..f3f809dc 100644 --- a/src/main/java/org/asamk/signal/json/JsonQuotedAttachment.java +++ b/src/main/java/org/asamk/signal/json/JsonQuotedAttachment.java @@ -1,12 +1,21 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; public class JsonQuotedAttachment { - String contentType; - String filename; - JsonAttachment thumbnail; + @JsonProperty + final String contentType; + + @JsonProperty + final String filename; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonAttachment thumbnail; JsonQuotedAttachment(SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment) { contentType = quotedAttachment.getContentType(); diff --git a/src/main/java/org/asamk/signal/json/JsonReaction.java b/src/main/java/org/asamk/signal/json/JsonReaction.java index 5e978fe0..2fa15b48 100644 --- a/src/main/java/org/asamk/signal/json/JsonReaction.java +++ b/src/main/java/org/asamk/signal/json/JsonReaction.java @@ -1,14 +1,23 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction; public class JsonReaction { - String emoji; - String targetAuthor; - long targetSentTimestamp; - boolean isRemove; + @JsonProperty + final String emoji; + + @JsonProperty + final String targetAuthor; + + @JsonProperty + final long targetSentTimestamp; + + @JsonProperty + final boolean isRemove; JsonReaction(Reaction reaction, Manager m) { this.emoji = reaction.getEmoji(); diff --git a/src/main/java/org/asamk/signal/json/JsonReceiptMessage.java b/src/main/java/org/asamk/signal/json/JsonReceiptMessage.java index ccd5960b..e32009e1 100644 --- a/src/main/java/org/asamk/signal/json/JsonReceiptMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonReceiptMessage.java @@ -1,25 +1,29 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import java.util.List; class JsonReceiptMessage { - long when; - boolean isDelivery; - boolean isRead; - List timestamps; + @JsonProperty + final long when; + + @JsonProperty + final boolean isDelivery; + + @JsonProperty + final boolean isRead; + + @JsonProperty + final List timestamps; JsonReceiptMessage(SignalServiceReceiptMessage receiptMessage) { - this.when = receiptMessage.getWhen(); - if (receiptMessage.isDeliveryReceipt()) { - this.isDelivery = true; - } - if (receiptMessage.isReadReceipt()) { - this.isRead = true; - } + this.isDelivery = receiptMessage.isDeliveryReceipt(); + this.isRead = receiptMessage.isReadReceipt(); this.timestamps = receiptMessage.getTimestamps(); } diff --git a/src/main/java/org/asamk/signal/json/JsonSticker.java b/src/main/java/org/asamk/signal/json/JsonSticker.java index ea7a5815..e56ddf3f 100644 --- a/src/main/java/org/asamk/signal/json/JsonSticker.java +++ b/src/main/java/org/asamk/signal/json/JsonSticker.java @@ -1,19 +1,25 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import java.util.Base64; public class JsonSticker { - String packId; - String packKey; - int stickerId; + @JsonProperty + final String packId; + + @JsonProperty + final String packKey; + + @JsonProperty + final int stickerId; public JsonSticker(SignalServiceDataMessage.Sticker sticker) { this.packId = Base64.getEncoder().encodeToString(sticker.getPackId()); this.packKey = Base64.getEncoder().encodeToString(sticker.getPackKey()); this.stickerId = sticker.getStickerId(); - // TODO also download sticker image ?? } } diff --git a/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java index 7ea75bbd..d93351f1 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncDataMessage.java @@ -1,18 +1,23 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.asamk.Signal; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; class JsonSyncDataMessage extends JsonDataMessage { - String destination; + @JsonProperty + final String destination; JsonSyncDataMessage(SentTranscriptMessage transcriptMessage, Manager m) { super(transcriptMessage.getMessage(), m); - if (transcriptMessage.getDestination().isPresent()) { - this.destination = transcriptMessage.getDestination().get().getLegacyIdentifier(); - } + + this.destination = transcriptMessage.getDestination() + .transform(SignalServiceAddress::getLegacyIdentifier) + .orNull(); } JsonSyncDataMessage(Signal.SyncMessageReceived messageReceived) { diff --git a/src/main/java/org/asamk/signal/json/JsonSyncMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncMessage.java index f29bc02e..11ecbbc8 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncMessage.java @@ -1,13 +1,16 @@ package org.asamk.signal.json; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + import org.asamk.Signal; import org.asamk.signal.manager.Manager; -import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import java.util.ArrayList; +import java.util.Base64; import java.util.List; +import java.util.stream.Collectors; enum JsonSyncMessageType { CONTACTS_SYNC, @@ -17,23 +20,57 @@ enum JsonSyncMessageType { class JsonSyncMessage { - JsonSyncDataMessage sentMessage; - List blockedNumbers; - List readMessages; - JsonSyncMessageType type; + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonSyncDataMessage sentMessage; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List blockedNumbers; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List blockedGroupIds; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List readMessages; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonSyncMessageType type; JsonSyncMessage(SignalServiceSyncMessage syncMessage, Manager m) { - if (syncMessage.getSent().isPresent()) { - this.sentMessage = new JsonSyncDataMessage(syncMessage.getSent().get(), m); - } + this.sentMessage = syncMessage.getSent().isPresent() + ? new JsonSyncDataMessage(syncMessage.getSent().get(), m) + : null; if (syncMessage.getBlockedList().isPresent()) { - this.blockedNumbers = new ArrayList<>(syncMessage.getBlockedList().get().getAddresses().size()); - for (SignalServiceAddress address : syncMessage.getBlockedList().get().getAddresses()) { - this.blockedNumbers.add(address.getLegacyIdentifier()); - } + final Base64.Encoder base64 = Base64.getEncoder(); + this.blockedNumbers = syncMessage.getBlockedList() + .get() + .getAddresses() + .stream() + .map(SignalServiceAddress::getLegacyIdentifier) + .collect(Collectors.toList()); + this.blockedGroupIds = syncMessage.getBlockedList() + .get() + .getGroupIds() + .stream() + .map(base64::encodeToString) + .collect(Collectors.toList()); + } else { + this.blockedNumbers = null; + this.blockedGroupIds = null; } if (syncMessage.getRead().isPresent()) { - this.readMessages = syncMessage.getRead().get(); + this.readMessages = syncMessage.getRead() + .get() + .stream() + .map(message -> new JsonSyncReadMessage(message.getSender().getLegacyIdentifier(), + message.getTimestamp())) + .collect(Collectors.toList()); + } else { + this.readMessages = null; } if (syncMessage.getContacts().isPresent()) { @@ -42,10 +79,16 @@ class JsonSyncMessage { this.type = JsonSyncMessageType.GROUPS_SYNC; } else if (syncMessage.getRequest().isPresent()) { this.type = JsonSyncMessageType.REQUEST_SYNC; + } else { + this.type = null; } } JsonSyncMessage(Signal.SyncMessageReceived messageReceived) { - sentMessage = new JsonSyncDataMessage(messageReceived); + this.sentMessage = new JsonSyncDataMessage(messageReceived); + this.blockedNumbers = null; + this.blockedGroupIds = null; + this.readMessages = null; + this.type = null; } } diff --git a/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java new file mode 100644 index 00000000..d65b0672 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonSyncReadMessage.java @@ -0,0 +1,17 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +class JsonSyncReadMessage { + + @JsonProperty + final String sender; + + @JsonProperty + final long timestamp; + + public JsonSyncReadMessage(final String sender, final long timestamp) { + this.sender = sender; + this.timestamp = timestamp; + } +} From 5a2c4b8dfdbda71d6932998c48c9ffa633bb227c Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Jan 2021 11:29:11 +0100 Subject: [PATCH 055/124] Add viewOnce boolean to json data message --- src/main/java/org/asamk/signal/json/JsonDataMessage.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index e755acad..4636d808 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -24,6 +24,10 @@ class JsonDataMessage { @JsonProperty final Integer expiresInSeconds; + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final Boolean viewOnce; + @JsonProperty @JsonInclude(JsonInclude.Include.NON_NULL) final JsonReaction reaction; @@ -66,6 +70,7 @@ class JsonDataMessage { } this.message = dataMessage.getBody().orNull(); this.expiresInSeconds = dataMessage.getExpiresInSeconds(); + this.viewOnce = dataMessage.isViewOnce(); this.reaction = dataMessage.getReaction().isPresent() ? new JsonReaction(dataMessage.getReaction().get(), m) : null; @@ -96,6 +101,7 @@ class JsonDataMessage { message = messageReceived.getMessage(); groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; expiresInSeconds = null; + viewOnce = null; reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; @@ -108,6 +114,7 @@ class JsonDataMessage { message = messageReceived.getMessage(); groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; expiresInSeconds = null; + viewOnce = null; reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; From 3b29add396aea6f2174c56895f4d5875c9af2b49 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Jan 2021 11:43:39 +0100 Subject: [PATCH 056/124] Add typing message to json output Fixes #423 --- .../signal/json/JsonMessageEnvelope.java | 10 +++++++ .../asamk/signal/json/JsonTypingMessage.java | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/org/asamk/signal/json/JsonTypingMessage.java diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index 2a87d129..256b9751 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -41,6 +41,10 @@ public class JsonMessageEnvelope { @JsonInclude(JsonInclude.Include.NON_NULL) final JsonReceiptMessage receiptMessage; + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonTypingMessage typingMessage; + public JsonMessageEnvelope(SignalServiceEnvelope envelope, SignalServiceContent content, Manager m) { if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { SignalServiceAddress source = envelope.getSourceAddress(); @@ -64,6 +68,9 @@ public class JsonMessageEnvelope { } else { this.receiptMessage = null; } + this.typingMessage = content != null && content.getTypingMessage().isPresent() + ? new JsonTypingMessage(content.getTypingMessage().get()) + : null; this.dataMessage = content != null && content.getDataMessage().isPresent() ? new JsonDataMessage(content.getDataMessage().get(), m) @@ -85,6 +92,7 @@ public class JsonMessageEnvelope { dataMessage = new JsonDataMessage(messageReceived); syncMessage = null; callMessage = null; + typingMessage = null; } public JsonMessageEnvelope(Signal.ReceiptReceived receiptReceived) { @@ -96,6 +104,7 @@ public class JsonMessageEnvelope { dataMessage = null; syncMessage = null; callMessage = null; + typingMessage = null; } public JsonMessageEnvelope(Signal.SyncMessageReceived messageReceived) { @@ -107,5 +116,6 @@ public class JsonMessageEnvelope { dataMessage = null; syncMessage = new JsonSyncMessage(messageReceived); callMessage = null; + typingMessage = null; } } diff --git a/src/main/java/org/asamk/signal/json/JsonTypingMessage.java b/src/main/java/org/asamk/signal/json/JsonTypingMessage.java new file mode 100644 index 00000000..18548ce3 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonTypingMessage.java @@ -0,0 +1,28 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; + +import java.util.Base64; + +class JsonTypingMessage { + + @JsonProperty + final String action; + + @JsonProperty + final long timestamp; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final String groupId; + + JsonTypingMessage(SignalServiceTypingMessage typingMessage) { + this.action = typingMessage.getAction().name(); + this.timestamp = typingMessage.getTimestamp(); + final Base64.Encoder encoder = Base64.getEncoder(); + this.groupId = typingMessage.getGroupId().transform(encoder::encodeToString).orNull(); + } +} From a28ad7195ceb3970ffe2f3b169aab994948e3aba Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Jan 2021 11:57:46 +0100 Subject: [PATCH 057/124] Add remote delete info to json output Fixes #424 --- .../org/asamk/signal/json/JsonDataMessage.java | 8 ++++++++ .../org/asamk/signal/json/JsonRemoteDelete.java | 15 +++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/main/java/org/asamk/signal/json/JsonRemoteDelete.java diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 4636d808..1c927b40 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -48,6 +48,10 @@ class JsonDataMessage { @JsonInclude(JsonInclude.Include.NON_NULL) final JsonSticker sticker; + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final JsonRemoteDelete remoteDelete; + @JsonProperty @JsonInclude(JsonInclude.Include.NON_NULL) final JsonGroupInfo groupInfo; @@ -84,6 +88,8 @@ class JsonDataMessage { } else { this.mentions = List.of(); } + remoteDelete = dataMessage.getRemoteDelete().isPresent() ? new JsonRemoteDelete(dataMessage.getRemoteDelete() + .get()) : null; if (dataMessage.getAttachments().isPresent()) { this.attachments = dataMessage.getAttachments() .get() @@ -102,6 +108,7 @@ class JsonDataMessage { groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; expiresInSeconds = null; viewOnce = null; + remoteDelete = null; reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; @@ -115,6 +122,7 @@ class JsonDataMessage { groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; expiresInSeconds = null; viewOnce = null; + remoteDelete = null; reaction = null; // TODO Replace these 4 with the proper commands quote = null; mentions = null; diff --git a/src/main/java/org/asamk/signal/json/JsonRemoteDelete.java b/src/main/java/org/asamk/signal/json/JsonRemoteDelete.java new file mode 100644 index 00000000..a498a0e5 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonRemoteDelete.java @@ -0,0 +1,15 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; + +class JsonRemoteDelete { + + @JsonProperty + final long timestamp; + + JsonRemoteDelete(SignalServiceDataMessage.RemoteDelete remoteDelete) { + this.timestamp = remoteDelete.getTargetSentTimestamp(); + } +} From c440520ea6351f3937b2f8bba60c96d16b76e891 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Jan 2021 12:36:11 +0100 Subject: [PATCH 058/124] Ignore relay in json output if null --- src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index 256b9751..4944a74d 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -20,6 +20,7 @@ public class JsonMessageEnvelope { final Integer sourceDevice; @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) final String relay; @JsonProperty From a80e26896a9da67bd580d0a43be078035a7e6d57 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Jan 2021 13:14:12 +0100 Subject: [PATCH 059/124] Bump version --- CHANGELOG.md | 15 ++++++++++++++- build.gradle | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 466efd58..5370a283 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,31 @@ # Changelog ## [Unreleased] + +## [0.7.3] - 2021-01-17 ### Added -- `--verbose` flag to increase log level +- `getUserStatus` command to check if a user is registered on Signal (Thanks @Atomic-Bean) +- Global `--verbose` flag to increase log level +- Global `--output=json` flag, currently supported by `receive`, `daemon`, `getUserStatus`, `listGroups` - `--note-to-self` flag for `send` command to send a note to linked devices +- More info for received messages in json output: stickers, viewOnce, typing, remoteDelete ### Changed +- signal-cli can now be used without the username `-u` flag + For daemon command all local users will be exposed as dbus objects. + If only one local user exists, all other commands will use that user, + otherwise a user has to be specified. - Messages sent to self number will be sent as normal Signal messages again, to send a sync message, use the new `--note-to-self` flag +- Ignore messages with group context sent by non group member +- Profile key is sent along with all direct messages +- In json output unnecessary fields that are null are now omitted ### Fixed - Disable registration lock before removing the PIN - Fix PIN hash version to match the official clients. If you had previously set a PIN you need to set it again to be able to unlock the registration lock later. +- Issue with saving account file after linking ## [0.7.2] - 2020-12-31 ### Added diff --git a/build.gradle b/build.gradle index a6805093..8c5a7655 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ targetCompatibility = JavaVersion.VERSION_11 mainClassName = 'org.asamk.signal.Main' -version = '0.7.2' +version = '0.7.3' compileJava.options.encoding = 'UTF-8' From 70690fef36589a0526b23c128f409697ef086966 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Jan 2021 16:52:10 +0100 Subject: [PATCH 060/124] Use getRegisteredUsers instead of getContacts for updating v1 group Fixes #437 --- src/main/java/org/asamk/signal/manager/Manager.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 7d2681ae..ec427ce3 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -121,7 +121,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; -import org.whispersystems.signalservice.api.push.ContactTokenDetails; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; import org.whispersystems.signalservice.api.util.InvalidNumberException; @@ -773,12 +772,10 @@ public class Manager implements Closeable { newE164Members.add(member.getNumber().get()); } - final List contacts = accountManager.getContacts(newE164Members); - if (contacts.size() != newE164Members.size()) { + final Map registeredUsers = getRegisteredUsers(newE164Members); + if (registeredUsers.size() != newE164Members.size()) { // Some of the new members are not registered on Signal - for (ContactTokenDetails contact : contacts) { - newE164Members.remove(contact.getNumber()); - } + newE164Members.removeAll(registeredUsers.keySet()); throw new IOException("Failed to add members " + String.join(", ", newE164Members) + " to group: Not registered on Signal"); From 804ad34a0828d7b841f3fecd9af8f2bd948d31d3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 17 Jan 2021 17:38:33 +0100 Subject: [PATCH 061/124] Send and handle fetch profile sync message --- .../org/asamk/signal/manager/Manager.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index ec427ce3..457a6e54 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -363,6 +363,11 @@ public class Manager implements Closeable { avatarStore.deleteProfileAvatar(getSelfAddress()); } } + + try { + sendSyncMessage(SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE)); + } catch (UntrustedIdentityException ignored) { + } } public void unregister() throws IOException { @@ -493,6 +498,12 @@ public class Manager implements Closeable { private SignalProfile getRecipientProfile( SignalServiceAddress address + ) { + return getRecipientProfile(address, false); + } + + private SignalProfile getRecipientProfile( + SignalServiceAddress address, boolean force ) { SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address); if (profileEntry == null) { @@ -501,7 +512,9 @@ public class Manager implements Closeable { long now = new Date().getTime(); // Profiles are cached for 24h before retrieving them again if (!profileEntry.isRequestPending() && ( - profileEntry.getProfile() == null || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000 + force + || profileEntry.getProfile() == null + || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000 )) { profileEntry.setRequestPending(true); final SignalServiceProfile encryptedProfile; @@ -1992,6 +2005,14 @@ public class Manager implements Closeable { account.getStickerStore().updateSticker(sticker); } } + if (syncMessage.getFetchType().isPresent()) { + switch (syncMessage.getFetchType().get()) { + case LOCAL_PROFILE: + getRecipientProfile(getSelfAddress(), true); + case STORAGE_MANIFEST: + // TODO + } + } if (syncMessage.getConfiguration().isPresent()) { // TODO } From f4ed9a01b728272f2a3ba1790ec784fc4f4d7796 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 18 Jan 2021 20:28:25 +0100 Subject: [PATCH 062/124] If contact name is not set, print profile name instead --- .../asamk/signal/ReceiveMessageHandler.java | 25 +++++++++---------- .../org/asamk/signal/manager/Manager.java | 17 +++++++++++-- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 6ce21acd..0bb2e012 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -3,7 +3,6 @@ package org.asamk.signal; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupUtils; -import org.asamk.signal.manager.storage.contacts.ContactInfo; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.Util; @@ -53,9 +52,9 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { SignalServiceAddress source = envelope.getSourceAddress(); - ContactInfo sourceContact = m.getContact(source.getLegacyIdentifier()); + String name = m.getContactOrProfileName(source.getLegacyIdentifier()); System.out.println(String.format("Envelope from: %s (device: %d)", - (sourceContact == null ? "" : "“" + sourceContact.name + "” ") + source.getLegacyIdentifier(), + (name == null ? "" : "“" + name + "” ") + source.getLegacyIdentifier(), envelope.getSourceDevice())); if (source.getRelay().isPresent()) { System.out.println("Relayed by: " + source.getRelay().get()); @@ -98,10 +97,9 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (content == null) { System.out.println("Failed to decrypt message."); } else { - ContactInfo sourceContact = m.getContact(content.getSender().getLegacyIdentifier()); + String senderName = m.getContactOrProfileName(content.getSender().getLegacyIdentifier()); System.out.println(String.format("Sender: %s (device: %d)", - (sourceContact == null ? "" : "“" + sourceContact.name + "” ") + content.getSender() - .getLegacyIdentifier(), + (senderName == null ? "" : "“" + senderName + "” ") + content.getSender().getLegacyIdentifier(), content.getSenderDevice())); if (content.getDataMessage().isPresent()) { SignalServiceDataMessage message = content.getDataMessage().get(); @@ -127,10 +125,11 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (syncMessage.getRead().isPresent()) { System.out.println("Received sync read messages list"); for (ReadMessage rm : syncMessage.getRead().get()) { - ContactInfo fromContact = m.getContact(rm.getSender().getLegacyIdentifier()); + String name = m.getContactOrProfileName(rm.getSender().getLegacyIdentifier()); System.out.println("From: " - + (fromContact == null ? "" : "“" + fromContact.name + "” ") - + rm.getSender().getLegacyIdentifier() + + (name == null ? "" : "“" + name + "” ") + + rm.getSender() + .getLegacyIdentifier() + " Message timestamp: " + DateUtils.formatTimestamp(rm.getTimestamp())); } @@ -159,13 +158,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { String to; if (sentTranscriptMessage.getDestination().isPresent()) { String dest = sentTranscriptMessage.getDestination().get().getLegacyIdentifier(); - ContactInfo destContact = m.getContact(dest); - to = (destContact == null ? "" : "“" + destContact.name + "” ") + dest; + String name = m.getContactOrProfileName(dest); + to = (name == null ? "" : "“" + name + "” ") + dest; } else if (sentTranscriptMessage.getRecipients().size() > 0) { StringBuilder toBuilder = new StringBuilder(); for (SignalServiceAddress dest : sentTranscriptMessage.getRecipients()) { - ContactInfo destContact = m.getContact(dest.getLegacyIdentifier()); - toBuilder.append(destContact == null ? "" : "“" + destContact.name + "” ") + String name = m.getContactOrProfileName(dest.getLegacyIdentifier()); + toBuilder.append(name == null ? "" : "“" + name + "” ") .append(dest.getLegacyIdentifier()) .append(" "); } diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 457a6e54..3642d6db 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -137,6 +137,7 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.Hex; +import org.whispersystems.signalservice.internal.util.Util; import java.io.Closeable; import java.io.File; @@ -2304,8 +2305,20 @@ public class Manager implements Closeable { return account.getContactStore().getContacts(); } - public ContactInfo getContact(String number) { - return account.getContactStore().getContact(Utils.getSignalServiceAddressFromIdentifier(number)); + public String getContactOrProfileName(String number) { + final SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(number); + + final ContactInfo contact = account.getContactStore().getContact(address); + if (contact != null && !Util.isEmpty(contact.name)) { + return contact.name; + } + + final SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address); + if (profileEntry != null && profileEntry.getProfile() != null) { + return profileEntry.getProfile().getName(); + } + + return null; } public GroupInfo getGroup(GroupId groupId) { From 80e15ad54eff0d0552bea1db45208a6b80c19e16 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 18 Jan 2021 20:29:01 +0100 Subject: [PATCH 063/124] Only allow setting PIN by master device --- src/main/java/org/asamk/signal/manager/Manager.java | 3 +++ .../java/org/asamk/signal/manager/storage/SignalAccount.java | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 3642d6db..b8a0f57b 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -415,6 +415,9 @@ public class Manager implements Closeable { } public void setRegistrationLockPin(Optional pin) throws IOException, UnauthenticatedResponseException { + if (!account.isMasterDevice()) { + throw new RuntimeException("Only master device can set a PIN"); + } if (pin.isPresent()) { final MasterKey masterKey = account.getPinMasterKey() != null ? account.getPinMasterKey() diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index a6e243a8..cb6f7461 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -505,6 +505,10 @@ public class SignalAccount implements Closeable { this.deviceId = deviceId; } + public boolean isMasterDevice() { + return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID; + } + public String getPassword() { return password; } From c3c1802b4d02f9b1932a9893676b51489baae83f Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 18 Jan 2021 20:33:04 +0100 Subject: [PATCH 064/124] Store storage key from keys sync message --- .../org/asamk/signal/manager/Manager.java | 21 +++++++++++++++++++ .../signal/manager/storage/SignalAccount.java | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index b8a0f57b..96dbe212 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -114,6 +114,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; +import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; @@ -123,6 +124,7 @@ import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; +import org.whispersystems.signalservice.api.storage.StorageKey; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.SleepTimer; @@ -1167,6 +1169,18 @@ public class Manager implements Closeable { } } + void requestSyncKeys() throws IOException { + SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + .setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS) + .build(); + SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + try { + sendSyncMessage(message); + } catch (UntrustedIdentityException e) { + throw new AssertionError(e); + } + } + private byte[] getSenderCertificate() { // TODO support UUID capable sender certificates // byte[] certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy(); @@ -2017,6 +2031,13 @@ public class Manager implements Closeable { // TODO } } + if (syncMessage.getKeys().isPresent()) { + final KeysMessage keysMessage = syncMessage.getKeys().get(); + if (keysMessage.getStorageService().isPresent()) { + final StorageKey storageKey = keysMessage.getStorageService().get(); + account.setStorageKey(storageKey); + } + } if (syncMessage.getConfiguration().isPresent()) { // TODO } diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index cb6f7461..c1aaa788 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -41,6 +41,7 @@ import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.api.storage.StorageKey; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -71,6 +72,7 @@ public class SignalAccount implements Closeable { private String password; private String registrationLockPin; private MasterKey pinMasterKey; + private StorageKey storageKey; private String signalingKey; private ProfileKey profileKey; private int preKeyIdOffset; @@ -265,6 +267,10 @@ public class SignalAccount implements Closeable { pinMasterKey = pinMasterKeyNode == null || pinMasterKeyNode.isNull() ? null : new MasterKey(Base64.getDecoder().decode(pinMasterKeyNode.asText())); + JsonNode storageKeyNode = rootNode.get("storageKey"); + storageKey = storageKeyNode == null || storageKeyNode.isNull() + ? null + : new StorageKey(Base64.getDecoder().decode(storageKeyNode.asText())); if (rootNode.has("signalingKey")) { signalingKey = Utils.getNotNullNode(rootNode, "signalingKey").asText(); } @@ -398,6 +404,8 @@ public class SignalAccount implements Closeable { .put("registrationLockPin", registrationLockPin) .put("pinMasterKey", pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize())) + .put("storageKey", + storageKey == null ? null : Base64.getEncoder().encodeToString(storageKey.serialize())) .put("signalingKey", signalingKey) .put("preKeyIdOffset", preKeyIdOffset) .put("nextSignedPreKeyId", nextSignedPreKeyId) @@ -533,6 +541,17 @@ public class SignalAccount implements Closeable { this.pinMasterKey = pinMasterKey; } + public StorageKey getStorageKey() { + if (pinMasterKey != null) { + return pinMasterKey.deriveStorageServiceKey(); + } + return storageKey; + } + + public void setStorageKey(final StorageKey storageKey) { + this.storageKey = storageKey; + } + public String getSignalingKey() { return signalingKey; } From eae516a9a72adde85edb37056d1ca1eca983fb5d Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 19 Jan 2021 16:54:44 +0100 Subject: [PATCH 065/124] Fix issue with incorrectly saving signalingKey Fixes #442, #447 --- .../signal/manager/storage/SignalAccount.java | 54 +++++++++---------- .../storage/groups/JsonGroupStore.java | 2 +- .../protocol/JsonIdentityKeyStore.java | 9 ++-- .../org/asamk/signal/manager/util/Utils.java | 2 +- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index c1aaa788..78e30db8 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -244,50 +244,50 @@ public class SignalAccount implements Closeable { rootNode = jsonProcessor.readTree(Channels.newInputStream(fileChannel)); } - JsonNode uuidNode = rootNode.get("uuid"); - if (uuidNode != null && !uuidNode.isNull()) { + if (rootNode.hasNonNull("uuid")) { try { - uuid = UUID.fromString(uuidNode.asText()); + uuid = UUID.fromString(rootNode.get("uuid").asText()); } catch (IllegalArgumentException e) { throw new IOException("Config file contains an invalid uuid, needs to be a valid UUID", e); } } - JsonNode node = rootNode.get("deviceId"); - if (node != null) { - deviceId = node.asInt(); + if (rootNode.hasNonNull("deviceId")) { + deviceId = rootNode.get("deviceId").asInt(); } - if (rootNode.has("isMultiDevice")) { - isMultiDevice = Utils.getNotNullNode(rootNode, "isMultiDevice").asBoolean(); + if (rootNode.hasNonNull("isMultiDevice")) { + isMultiDevice = rootNode.get("isMultiDevice").asBoolean(); } username = Utils.getNotNullNode(rootNode, "username").asText(); password = Utils.getNotNullNode(rootNode, "password").asText(); - JsonNode pinNode = rootNode.get("registrationLockPin"); - registrationLockPin = pinNode == null || pinNode.isNull() ? null : pinNode.asText(); - JsonNode pinMasterKeyNode = rootNode.get("pinMasterKey"); - pinMasterKey = pinMasterKeyNode == null || pinMasterKeyNode.isNull() - ? null - : new MasterKey(Base64.getDecoder().decode(pinMasterKeyNode.asText())); - JsonNode storageKeyNode = rootNode.get("storageKey"); - storageKey = storageKeyNode == null || storageKeyNode.isNull() - ? null - : new StorageKey(Base64.getDecoder().decode(storageKeyNode.asText())); - if (rootNode.has("signalingKey")) { - signalingKey = Utils.getNotNullNode(rootNode, "signalingKey").asText(); + if (rootNode.hasNonNull("registrationLockPin")) { + registrationLockPin = rootNode.get("registrationLockPin").asText(); } - if (rootNode.has("preKeyIdOffset")) { - preKeyIdOffset = Utils.getNotNullNode(rootNode, "preKeyIdOffset").asInt(0); + if (rootNode.hasNonNull("pinMasterKey")) { + pinMasterKey = new MasterKey(Base64.getDecoder().decode(rootNode.get("pinMasterKey").asText())); + } + if (rootNode.hasNonNull("storageKey")) { + storageKey = new StorageKey(Base64.getDecoder().decode(rootNode.get("storageKey").asText())); + } + if (rootNode.hasNonNull("signalingKey")) { + signalingKey = rootNode.get("signalingKey").asText(); + if (signalingKey.equals("null")) { + // Workaround for load bug in older versions + signalingKey = null; + } + } + if (rootNode.hasNonNull("preKeyIdOffset")) { + preKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(0); } else { preKeyIdOffset = 0; } - if (rootNode.has("nextSignedPreKeyId")) { - nextSignedPreKeyId = Utils.getNotNullNode(rootNode, "nextSignedPreKeyId").asInt(); + if (rootNode.hasNonNull("nextSignedPreKeyId")) { + nextSignedPreKeyId = rootNode.get("nextSignedPreKeyId").asInt(); } else { nextSignedPreKeyId = 0; } - if (rootNode.has("profileKey")) { + if (rootNode.hasNonNull("profileKey")) { try { - profileKey = new ProfileKey(Base64.getDecoder() - .decode(Utils.getNotNullNode(rootNode, "profileKey").asText())); + profileKey = new ProfileKey(Base64.getDecoder().decode(rootNode.get("profileKey").asText())); } catch (InvalidInputException e) { throw new IOException( "Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes", diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java index 8d0ae630..f7292596 100644 --- a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java @@ -183,7 +183,7 @@ public class JsonGroupStore { JsonNode node = jsonParser.getCodec().readTree(jsonParser); for (JsonNode n : node) { GroupInfo g; - if (n.has("masterKey")) { + if (n.hasNonNull("masterKey")) { // a v2 group GroupIdV2 groupId = GroupIdV2.fromBase64(n.get("groupId").asText()); try { diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index 06db5560..28d64cbf 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -219,10 +219,11 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { try { IdentityKey id = new IdentityKey(Base64.getDecoder() .decode(trustedKey.get("identityKey").asText()), 0); - TrustLevel trustLevel = trustedKey.has("trustLevel") ? TrustLevel.fromInt(trustedKey.get( - "trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED; - Date added = trustedKey.has("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp") - .asLong()) : new Date(); + TrustLevel trustLevel = trustedKey.hasNonNull("trustLevel") + ? TrustLevel.fromInt(trustedKey.get("trustLevel").asInt()) + : TrustLevel.TRUSTED_UNVERIFIED; + Date added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get( + "addedTimestamp").asLong()) : new Date(); keyStore.saveIdentity(serviceAddress, id, trustLevel, added); } catch (InvalidKeyException e) { logger.warn("Error while decoding key for {}: {}", trustedKeyName, e.getMessage()); diff --git a/src/main/java/org/asamk/signal/manager/util/Utils.java b/src/main/java/org/asamk/signal/manager/util/Utils.java index 65f2811b..fb38d01a 100644 --- a/src/main/java/org/asamk/signal/manager/util/Utils.java +++ b/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -84,7 +84,7 @@ public class Utils { public static JsonNode getNotNullNode(JsonNode parent, String name) throws InvalidObjectException { JsonNode node = parent.get(name); - if (node == null) { + if (node == null || node.isNull()) { throw new InvalidObjectException(String.format("Incorrect file format: expected parameter %s not found ", name)); } From 6f4b17168399346a54ed46303f76ab5975e82c26 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 19 Jan 2021 17:04:02 +0100 Subject: [PATCH 066/124] Request storage key after linking --- src/main/java/org/asamk/signal/manager/ProvisioningManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 24f4dd06..535cecc6 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -133,6 +133,7 @@ public class ProvisioningManager { m.requestSyncContacts(); m.requestSyncBlocked(); m.requestSyncConfiguration(); + m.requestSyncKeys(); } catch (Exception e) { logger.error("Failed to request sync messages from linked device."); throw e; From d50db1d6719efba7509d10db49308208d2e5d61a Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 19 Jan 2021 17:31:10 +0100 Subject: [PATCH 067/124] Bump version --- CHANGELOG.md | 9 +++++++++ build.gradle | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5370a283..9092711d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] +## [0.7.4] - 2021-01-19 +### Changed +- Notify linked devices after profile has been updated + +### Fixes +- After registering a new account, receiving messages didn't work + You may have to register and verify again to fix the issue. +- Creating v1 groups works again + ## [0.7.3] - 2021-01-17 ### Added - `getUserStatus` command to check if a user is registered on Signal (Thanks @Atomic-Bean) diff --git a/build.gradle b/build.gradle index 8c5a7655..1fa56c1e 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ targetCompatibility = JavaVersion.VERSION_11 mainClassName = 'org.asamk.signal.Main' -version = '0.7.3' +version = '0.7.4' compileJava.options.encoding = 'UTF-8' From cb596c15f14cdcaf1dd98ea678b8023d5d54537e Mon Sep 17 00:00:00 2001 From: Benjamin Schmid Date: Thu, 21 Jan 2021 18:44:31 +0100 Subject: [PATCH 068/124] doc(README): put links to manpage & wiki first (#459) - Directly provide links to the complete man page below usage - Extend explanation of `USERNAME` --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 48ad7250..d12f2742 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,9 @@ You can find further instructions on the Wiki: ## Usage -Important: The USERNAME (your phone number) must include the country calling code, i.e. the number must start with a "+" sign. (See [Wikipedia](https://en.wikipedia.org/wiki/List_of_country_calling_codes) for a list of all country codes.) +For a complete usage overview please read the [man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc) and the [wiki](https://github.com/AsamK/signal-cli/wiki). + +Important: The USERNAME is your phone number in international format and must include the country calling code. Hence it should start with a "+" sign. (See [Wikipedia](https://en.wikipedia.org/wiki/List_of_country_calling_codes) for a list of all country codes.) * Register a number (with SMS verification) @@ -47,7 +49,6 @@ Important: The USERNAME (your phone number) must include the country calling cod signal-cli -u USERNAME receive -For more information read the [man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc) and the [wiki](https://github.com/AsamK/signal-cli/wiki). ## Storage From a7094475369a56afe1845a35cd1d20b166f00f33 Mon Sep 17 00:00:00 2001 From: Michael Kebe Date: Sat, 23 Jan 2021 11:11:50 +0100 Subject: [PATCH 069/124] Added GraalVM configuration to create a native binary (#452) * Initial GraalVM configuration * graalvm reflection changes for v0.7.4 * graalvm removed unneeded options for native-image * updated gradle plugin org.mikeneck.graalvm-native-image to 1.1.0 * moved from gradle graalvm-native-image plugin to native-build.sh * added missing graalvm reflection config for receive * refactored native-build.sh to a gradle task buildNativeBinary --- README.md | 14 + build.gradle | 36 + graalvm-config-dir/reflect-config.json | 1529 +++++++++++++++++++++++ graalvm-config-dir/resource-config.json | 11 + 4 files changed, 1590 insertions(+) create mode 100644 graalvm-config-dir/reflect-config.json create mode 100644 graalvm-config-dir/resource-config.json diff --git a/README.md b/README.md index d12f2742..26e9bc95 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,20 @@ dependencies. If you have a recent gradle version installed, you can replace `./ ./gradlew distTar +### Building a native binary with GraalVM + +It is possible to build a native binary with [GraalVM](https://www.graalvm.org). + +1. [Install GraalVM and setup the enviroment](https://www.graalvm.org/docs/getting-started/#install-graalvm) +2. [Install prerequisites](https://www.graalvm.org/reference-manual/native-image/#prerequisites) +2. Execute Gradle: + + ./gradle nativeImage + + The binary is available at + + build/native-image/signal-cli + ## Troubleshooting If you use a version of the Oracle JRE and get an InvalidKeyException you need to enable unlimited strength crypto. See https://stackoverflow.com/questions/6481627/java-security-illegal-key-size-or-default-parameters for instructions. diff --git a/build.gradle b/build.gradle index 1fa56c1e..d3ae9667 100644 --- a/build.gradle +++ b/build.gradle @@ -74,3 +74,39 @@ task checkLibVersions { } } } + +task buildNativeBinary { + dependsOn("build") + doLast { + def graalVMHome = System.getenv("GRAALVM_HOME") + if (!graalVMHome) { + throw new GradleException('Required GRAALVM_HOME environment variable not set.') + } + def nativeBinaryOutputPath = "$buildDir/native-image" + def nativeBinaryName = "signal-cli" + + mkdir nativeBinaryOutputPath + + exec { + workingDir "." + commandLine "$graalVMHome/bin/native-image", + "-H:Path=$nativeBinaryOutputPath", + "-H:Name=$nativeBinaryName", + "-H:JNIConfigurationFiles=", + "-H:DynamicProxyConfigurationFiles=", + "-H:ReflectionConfigurationFiles=", + "-H:ResourceConfigurationFiles=", + "--no-fallback", + "--allow-incomplete-classpath", + "--report-unsupported-elements-at-runtime", + "--enable-url-protocols=http,https", + "--enable-https", + "--enable-all-security-services", + "-H:ResourceConfigurationFiles=graalvm-config-dir/resource-config.json", + "-H:ReflectionConfigurationFiles=graalvm-config-dir/reflect-config.json", + "-cp", + sourceSets.main.runtimeClasspath.asPath, + project.mainClassName + } + } +} diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json new file mode 100644 index 00000000..fdcaa2ba --- /dev/null +++ b/graalvm-config-dir/reflect-config.json @@ -0,0 +1,1529 @@ +[ +{ + "name":"byte[]" +}, +{ + "name":"char[]" +}, +{ + "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.AESCipher$General", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.DHParameters", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.HmacCore$HmacSHA256", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.TlsKeyMaterialGenerator", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.TlsMasterSecretGenerator", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"com.sun.crypto.provider.TlsPrfGenerator$V12", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"java.io.Serializable", + "allDeclaredMethods":true +}, +{ + "name":"java.lang.Comparable", + "allDeclaredMethods":true +}, +{ + "name":"java.lang.Double", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"java.lang.Integer", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"java.lang.Number", + "allDeclaredFields":true, + "allDeclaredMethods":true +}, +{ + "name":"java.nio.Buffer", + "fields":[{"name":"address", "allowUnsafeAccess":true}] +}, +{ + "name":"java.security.KeyStoreSpi" +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.security.cert.PKIXRevocationChecker" +}, +{ + "name":"java.security.interfaces.ECPrivateKey" +}, +{ + "name":"java.security.interfaces.ECPublicKey" +}, +{ + "name":"java.security.interfaces.RSAPrivateKey" +}, +{ + "name":"java.security.interfaces.RSAPublicKey" +}, +{ + "name":"java.util.ArrayList", + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"java.util.Locale", + "methods":[{"name":"getUnicodeLocaleType","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"org.asamk.signal.manager.storage.contacts.ContactInfo", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.manager.storage.contacts.JsonContactsStore", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.manager.storage.groups.GroupInfo", + "allDeclaredFields":true, + "allDeclaredMethods":true +}, +{ + "name":"org.asamk.signal.manager.storage.groups.GroupInfoV1", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.manager.storage.groups.GroupInfoV1$MembersDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.groups.GroupInfoV1$MembersSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.groups.JsonGroupStore", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true, + "fields":[ + {"name":"groups", "allowWrite":true} + ], + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.manager.storage.groups.JsonGroupStore$GroupsDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.groups.JsonGroupStore$GroupsSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.profiles.ProfileStore", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true, + "fields":[ + {"name":"profiles", "allowWrite":true} + ] +}, +{ + "name":"org.asamk.signal.manager.storage.profiles.ProfileStore$ProfileStoreDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.profiles.ProfileStore$ProfileStoreSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.profiles.SignalProfile", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.manager.storage.profiles.SignalProfile$Capabilities", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonIdentityKeyStore$JsonIdentityKeyStoreDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonIdentityKeyStore$JsonIdentityKeyStoreSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonPreKeyStore$JsonPreKeyStoreDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonPreKeyStore$JsonPreKeyStoreSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonSessionStore$JsonSessionStoreDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonSessionStore$JsonSessionStoreSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonSignalProtocolStore", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonSignedPreKeyStore$JsonSignedPreKeyStoreDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonSignedPreKeyStore$JsonSignedPreKeyStoreSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.RecipientStore", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true, + "fields":[ + {"name":"addresses", "allowWrite":true} + ] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.RecipientStore$RecipientStoreDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.RecipientStore$RecipientStoreSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.stickers.StickerStore", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true, + "fields":[ + {"name":"stickers", "allowWrite":true} + ] +}, +{ + "name":"org.asamk.signal.manager.storage.stickers.StickerStore$StickersDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.manager.storage.stickers.StickerStore$StickersSerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.util.SecurityProvider$DefaultRandom", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.DSTU4145$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.ECGOST$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.EdEC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.ElGamal$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.GM$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.GOST$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.IES$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.edec.SignatureSpi$Ed25519", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.edec.SignatureSpi$Ed448", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Blake2b$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Blake2s$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.DSTU7564$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Haraka$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Keccak$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.MD5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD160$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.RIPEMD320$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA224$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA384$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA512$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SM3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Skein$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Tiger$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.digest.Whirlpool$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.BC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.BCFKS$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi$Std", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.AES$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ARIA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.CAST5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.CAST6$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Camellia$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.ChaCha$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DES$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.DSTU7624$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.GOST28147$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.GOST3412_2015$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Grain128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Grainv1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.HC128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Noekeon$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.OpenSSLPBKDF$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF1$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Poly1305$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC5$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.RC6$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Rijndael$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SCRYPT$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SEED$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SM4$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Salsa20$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Serpent$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Shacal2$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SipHash$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.SipHash128$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Skipjack$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.TEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.TLSKDF$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.VMPC$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.VMPCKSA3$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.XSalsa20$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.XTEA$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.signal.libsignal.metadata.SignalProtos$SenderCertificate", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"certificate_", "allowUnsafeAccess":true}, + {"name":"signature_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.libsignal.metadata.SignalProtos$SenderCertificate$Certificate", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"expires_", "allowUnsafeAccess":true}, + {"name":"identityKey_", "allowUnsafeAccess":true}, + {"name":"senderDevice_", "allowUnsafeAccess":true}, + {"name":"senderE164_", "allowUnsafeAccess":true}, + {"name":"senderUuid_", "allowUnsafeAccess":true}, + {"name":"signer_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.libsignal.metadata.SignalProtos$ServerCertificate", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"certificate_", "allowUnsafeAccess":true}, + {"name":"signature_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.libsignal.metadata.SignalProtos$ServerCertificate$Certificate", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"id_", "allowUnsafeAccess":true}, + {"name":"key_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.libsignal.metadata.SignalProtos$UnidentifiedSenderMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"encryptedMessage_", "allowUnsafeAccess":true}, + {"name":"encryptedStatic_", "allowUnsafeAccess":true}, + {"name":"ephemeralPublic_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.libsignal.metadata.SignalProtos$UnidentifiedSenderMessage$Message", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"content_", "allowUnsafeAccess":true}, + {"name":"senderCertificate_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.AccessControl", + "fields":[ + {"name":"addFromInviteLink_", "allowUnsafeAccess":true}, + {"name":"attributes_", "allowUnsafeAccess":true}, + {"name":"members_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.Group", + "fields":[ + {"name":"accessControl_", "allowUnsafeAccess":true}, + {"name":"avatar_", "allowUnsafeAccess":true}, + {"name":"disappearingMessagesTimer_", "allowUnsafeAccess":true}, + {"name":"inviteLinkPassword_", "allowUnsafeAccess":true}, + {"name":"members_", "allowUnsafeAccess":true}, + {"name":"pendingMembers_", "allowUnsafeAccess":true}, + {"name":"publicKey_", "allowUnsafeAccess":true}, + {"name":"requestingMembers_", "allowUnsafeAccess":true}, + {"name":"revision_", "allowUnsafeAccess":true}, + {"name":"title_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupAttributeBlob", + "fields":[ + {"name":"contentCase_", "allowUnsafeAccess":true}, + {"name":"content_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.Member", + "fields":[ + {"name":"joinedAtRevision_", "allowUnsafeAccess":true}, + {"name":"presentation_", "allowUnsafeAccess":true}, + {"name":"profileKey_", "allowUnsafeAccess":true}, + {"name":"role_", "allowUnsafeAccess":true}, + {"name":"userId_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.PendingMember", + "fields":[ + {"name":"addedByUserId_", "allowUnsafeAccess":true}, + {"name":"member_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.RequestingMember", + "fields":[ + {"name":"presentation_", "allowUnsafeAccess":true}, + {"name":"profileKey_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true}, + {"name":"userId_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.local.DecryptedGroup", + "fields":[ + {"name":"accessControl_", "allowUnsafeAccess":true}, + {"name":"avatar_", "allowUnsafeAccess":true}, + {"name":"disappearingMessagesTimer_", "allowUnsafeAccess":true}, + {"name":"inviteLinkPassword_", "allowUnsafeAccess":true}, + {"name":"members_", "allowUnsafeAccess":true}, + {"name":"pendingMembers_", "allowUnsafeAccess":true}, + {"name":"requestingMembers_", "allowUnsafeAccess":true}, + {"name":"revision_", "allowUnsafeAccess":true}, + {"name":"title_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.local.DecryptedMember", + "fields":[ + {"name":"joinedAtRevision_", "allowUnsafeAccess":true}, + {"name":"profileKey_", "allowUnsafeAccess":true}, + {"name":"role_", "allowUnsafeAccess":true}, + {"name":"uuid_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.local.DecryptedPendingMember", + "fields":[ + {"name":"addedByUuid_", "allowUnsafeAccess":true}, + {"name":"role_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true}, + {"name":"uuidCipherText_", "allowUnsafeAccess":true}, + {"name":"uuid_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.local.DecryptedRequestingMember", + "fields":[ + {"name":"profileKey_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true}, + {"name":"uuid_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.local.DecryptedTimer", + "fields":[{"name":"duration_", "allowUnsafeAccess":true}] +}, +{ + "name":"org.whispersystems.curve25519.OpportunisticCurve25519Provider", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.libsignal.fingerprint.FingerprintProtos$CombinedFingerprints", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"localFingerprint_", "allowUnsafeAccess":true}, + {"name":"remoteFingerprint_", "allowUnsafeAccess":true}, + {"name":"version_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.fingerprint.FingerprintProtos$LogicalFingerprint", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"content_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.protocol.SignalProtos$PreKeySignalMessage", + "fields":[ + {"name":"baseKey_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"identityKey_", "allowUnsafeAccess":true}, + {"name":"message_", "allowUnsafeAccess":true}, + {"name":"preKeyId_", "allowUnsafeAccess":true}, + {"name":"registrationId_", "allowUnsafeAccess":true}, + {"name":"signedPreKeyId_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.protocol.SignalProtos$SignalMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"ciphertext_", "allowUnsafeAccess":true}, + {"name":"counter_", "allowUnsafeAccess":true}, + {"name":"previousCounter_", "allowUnsafeAccess":true}, + {"name":"ratchetKey_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.IdentityKeyStore", + "allDeclaredMethods":true +}, +{ + "name":"org.whispersystems.libsignal.state.PreKeyStore", + "allDeclaredMethods":true +}, +{ + "name":"org.whispersystems.libsignal.state.SessionStore", + "allDeclaredMethods":true +}, +{ + "name":"org.whispersystems.libsignal.state.SignalProtocolStore", + "allDeclaredMethods":true +}, +{ + "name":"org.whispersystems.libsignal.state.SignedPreKeyStore", + "allDeclaredMethods":true +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$IdentityKeyPairStructure", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"privateKey_", "allowUnsafeAccess":true}, + {"name":"publicKey_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$PreKeyRecordStructure", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"id_", "allowUnsafeAccess":true}, + {"name":"privateKey_", "allowUnsafeAccess":true}, + {"name":"publicKey_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$RecordStructure", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"currentSession_", "allowUnsafeAccess":true}, + {"name":"previousSessions_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure", + "fields":[ + {"name":"aliceBaseKey_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"localIdentityPublic_", "allowUnsafeAccess":true}, + {"name":"localRegistrationId_", "allowUnsafeAccess":true}, + {"name":"needsRefresh_", "allowUnsafeAccess":true}, + {"name":"pendingKeyExchange_", "allowUnsafeAccess":true}, + {"name":"pendingPreKey_", "allowUnsafeAccess":true}, + {"name":"previousCounter_", "allowUnsafeAccess":true}, + {"name":"receiverChains_", "allowUnsafeAccess":true}, + {"name":"remoteIdentityPublic_", "allowUnsafeAccess":true}, + {"name":"remoteRegistrationId_", "allowUnsafeAccess":true}, + {"name":"rootKey_", "allowUnsafeAccess":true}, + {"name":"senderChain_", "allowUnsafeAccess":true}, + {"name":"sessionVersion_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure$Chain", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"chainKey_", "allowUnsafeAccess":true}, + {"name":"messageKeys_", "allowUnsafeAccess":true}, + {"name":"senderRatchetKeyPrivate_", "allowUnsafeAccess":true}, + {"name":"senderRatchetKey_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure$Chain$ChainKey", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"index_", "allowUnsafeAccess":true}, + {"name":"key_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure$Chain$MessageKey", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"cipherKey_", "allowUnsafeAccess":true}, + {"name":"index_", "allowUnsafeAccess":true}, + {"name":"iv_", "allowUnsafeAccess":true}, + {"name":"macKey_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure$PendingPreKey", + "fields":[ + {"name":"baseKey_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"preKeyId_", "allowUnsafeAccess":true}, + {"name":"signedPreKeyId_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.StorageProtos$SignedPreKeyRecordStructure", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"id_", "allowUnsafeAccess":true}, + {"name":"privateKey_", "allowUnsafeAccess":true}, + {"name":"publicKey_", "allowUnsafeAccess":true}, + {"name":"signature_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.api.account.AccountAttributes", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.api.account.AccountAttributes$Capabilities", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.api.groupsv2.CredentialResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.api.groupsv2.TemporalCredential", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.api.groupsv2.TemporalCredential[]" +}, +{ + "name":"org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.api.profiles.SignalServiceProfile", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.api.profiles.SignalServiceProfile$Capabilities", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity$ByteArraySerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.contacts.crypto.SignatureBodyEntity", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.KeyBackupRequest", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.KeyBackupResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationRequest", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.TokenResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.keybackup.protos.DeleteRequest", + "fields":[ + {"name":"backupId_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"serviceId_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.keybackup.protos.Request", + "fields":[ + {"name":"backup_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"delete_", "allowUnsafeAccess":true}, + {"name":"restore_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.AuthCredentials", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.ConfirmCodeMessage", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.DeviceCode", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.DeviceId", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.DeviceInfoList", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.OutgoingPushMessage", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.OutgoingPushMessageList", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.PreKeyEntity", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.PreKeyEntity$ECPublicKeySerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.PreKeyState", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.PreKeyStatus", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionEnvelope", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"body_", "allowUnsafeAccess":true}, + {"name":"publicKey_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"identityKeyPrivate_", "allowUnsafeAccess":true}, + {"name":"identityKeyPublic_", "allowUnsafeAccess":true}, + {"name":"number_", "allowUnsafeAccess":true}, + {"name":"profileKey_", "allowUnsafeAccess":true}, + {"name":"provisioningCode_", "allowUnsafeAccess":true}, + {"name":"provisioningVersion_", "allowUnsafeAccess":true}, + {"name":"readReceipts_", "allowUnsafeAccess":true}, + {"name":"userAgent_", "allowUnsafeAccess":true}, + {"name":"uuid_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisioningUuid", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"uuid_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SendMessageResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SenderCertificate", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SenderCertificate$ByteArrayDesieralizer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$AttachmentPointer", + "fields":[ + {"name":"attachmentIdentifierCase_", "allowUnsafeAccess":true}, + {"name":"attachmentIdentifier_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"blurHash_", "allowUnsafeAccess":true}, + {"name":"caption_", "allowUnsafeAccess":true}, + {"name":"cdnNumber_", "allowUnsafeAccess":true}, + {"name":"contentType_", "allowUnsafeAccess":true}, + {"name":"digest_", "allowUnsafeAccess":true}, + {"name":"fileName_", "allowUnsafeAccess":true}, + {"name":"flags_", "allowUnsafeAccess":true}, + {"name":"height_", "allowUnsafeAccess":true}, + {"name":"key_", "allowUnsafeAccess":true}, + {"name":"size_", "allowUnsafeAccess":true}, + {"name":"thumbnail_", "allowUnsafeAccess":true}, + {"name":"uploadTimestamp_", "allowUnsafeAccess":true}, + {"name":"width_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Content", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"callMessage_", "allowUnsafeAccess":true}, + {"name":"dataMessage_", "allowUnsafeAccess":true}, + {"name":"nullMessage_", "allowUnsafeAccess":true}, + {"name":"receiptMessage_", "allowUnsafeAccess":true}, + {"name":"syncMessage_", "allowUnsafeAccess":true}, + {"name":"typingMessage_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage", + "fields":[ + {"name":"attachments_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"bodyRanges_", "allowUnsafeAccess":true}, + {"name":"body_", "allowUnsafeAccess":true}, + {"name":"contact_", "allowUnsafeAccess":true}, + {"name":"delete_", "allowUnsafeAccess":true}, + {"name":"expireTimer_", "allowUnsafeAccess":true}, + {"name":"flags_", "allowUnsafeAccess":true}, + {"name":"groupV2_", "allowUnsafeAccess":true}, + {"name":"group_", "allowUnsafeAccess":true}, + {"name":"isViewOnce_", "allowUnsafeAccess":true}, + {"name":"preview_", "allowUnsafeAccess":true}, + {"name":"profileKey_", "allowUnsafeAccess":true}, + {"name":"quote_", "allowUnsafeAccess":true}, + {"name":"reaction_", "allowUnsafeAccess":true}, + {"name":"requiredProtocolVersion_", "allowUnsafeAccess":true}, + {"name":"sticker_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$BodyRange", + "fields":[ + {"name":"associatedValueCase_", "allowUnsafeAccess":true}, + {"name":"associatedValue_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"length_", "allowUnsafeAccess":true}, + {"name":"start_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact", + "fields":[ + {"name":"address_", "allowUnsafeAccess":true}, + {"name":"avatar_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"email_", "allowUnsafeAccess":true}, + {"name":"name_", "allowUnsafeAccess":true}, + {"name":"number_", "allowUnsafeAccess":true}, + {"name":"organization_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Preview", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"date_", "allowUnsafeAccess":true}, + {"name":"description_", "allowUnsafeAccess":true}, + {"name":"image_", "allowUnsafeAccess":true}, + {"name":"title_", "allowUnsafeAccess":true}, + {"name":"url_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Envelope", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"content_", "allowUnsafeAccess":true}, + {"name":"legacyMessage_", "allowUnsafeAccess":true}, + {"name":"relay_", "allowUnsafeAccess":true}, + {"name":"serverGuid_", "allowUnsafeAccess":true}, + {"name":"serverTimestamp_", "allowUnsafeAccess":true}, + {"name":"sourceDevice_", "allowUnsafeAccess":true}, + {"name":"sourceE164_", "allowUnsafeAccess":true}, + {"name":"sourceUuid_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$GroupContextV2", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"groupChange_", "allowUnsafeAccess":true}, + {"name":"masterKey_", "allowUnsafeAccess":true}, + {"name":"revision_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ReceiptMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"blocked_", "allowUnsafeAccess":true}, + {"name":"configuration_", "allowUnsafeAccess":true}, + {"name":"contacts_", "allowUnsafeAccess":true}, + {"name":"fetchLatest_", "allowUnsafeAccess":true}, + {"name":"groups_", "allowUnsafeAccess":true}, + {"name":"keys_", "allowUnsafeAccess":true}, + {"name":"messageRequestResponse_", "allowUnsafeAccess":true}, + {"name":"padding_", "allowUnsafeAccess":true}, + {"name":"read_", "allowUnsafeAccess":true}, + {"name":"request_", "allowUnsafeAccess":true}, + {"name":"sent_", "allowUnsafeAccess":true}, + {"name":"stickerPackOperation_", "allowUnsafeAccess":true}, + {"name":"verified_", "allowUnsafeAccess":true}, + {"name":"viewOnceOpen_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Read", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"senderE164_", "allowUnsafeAccess":true}, + {"name":"senderUuid_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"destinationE164_", "allowUnsafeAccess":true}, + {"name":"destinationUuid_", "allowUnsafeAccess":true}, + {"name":"expirationStartTimestamp_", "allowUnsafeAccess":true}, + {"name":"isRecipientUpdate_", "allowUnsafeAccess":true}, + {"name":"message_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true}, + {"name":"unidentifiedStatus_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent$UnidentifiedDeliveryStatus", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"destinationE164_", "allowUnsafeAccess":true}, + {"name":"destinationUuid_", "allowUnsafeAccess":true}, + {"name":"unidentified_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$StickerPackOperation", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"packId_", "allowUnsafeAccess":true}, + {"name":"packKey_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TypingMessage", + "fields":[ + {"name":"action_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"groupId_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.VerifyAccountResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.serialize.protos.AddressProto", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"e164_", "allowUnsafeAccess":true}, + {"name":"relay_", "allowUnsafeAccess":true}, + {"name":"uuid_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.serialize.protos.MetadataProto", + "fields":[ + {"name":"address_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"needsReceipt_", "allowUnsafeAccess":true}, + {"name":"senderDevice_", "allowUnsafeAccess":true}, + {"name":"serverDeliveredTimestamp_", "allowUnsafeAccess":true}, + {"name":"serverReceivedTimestamp_", "allowUnsafeAccess":true}, + {"name":"timestamp_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"dataCase_", "allowUnsafeAccess":true}, + {"name":"data_", "allowUnsafeAccess":true}, + {"name":"localAddress_", "allowUnsafeAccess":true}, + {"name":"metadata_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.util.JsonUtil$IdentityKeySerializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.util.JsonUtil$UuidDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"request_", "allowUnsafeAccess":true}, + {"name":"response_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketRequestMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"body_", "allowUnsafeAccess":true}, + {"name":"headers_", "allowUnsafeAccess":true}, + {"name":"id_", "allowUnsafeAccess":true}, + {"name":"path_", "allowUnsafeAccess":true}, + {"name":"verb_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.websocket.WebSocketProtos$WebSocketResponseMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"body_", "allowUnsafeAccess":true}, + {"name":"headers_", "allowUnsafeAccess":true}, + {"name":"id_", "allowUnsafeAccess":true}, + {"name":"message_", "allowUnsafeAccess":true}, + {"name":"status_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"sun.misc.Unsafe", + "allDeclaredFields":true, + "methods":[ + {"name":"arrayBaseOffset","parameterTypes":["java.lang.Class"] }, + {"name":"arrayIndexScale","parameterTypes":["java.lang.Class"] }, + {"name":"copyMemory","parameterTypes":["long","long","long"] }, + {"name":"copyMemory","parameterTypes":["java.lang.Object","long","java.lang.Object","long","long"] }, + {"name":"getBoolean","parameterTypes":["java.lang.Object","long"] }, + {"name":"getByte","parameterTypes":["long"] }, + {"name":"getByte","parameterTypes":["java.lang.Object","long"] }, + {"name":"getDouble","parameterTypes":["java.lang.Object","long"] }, + {"name":"getFloat","parameterTypes":["java.lang.Object","long"] }, + {"name":"getInt","parameterTypes":["long"] }, + {"name":"getInt","parameterTypes":["java.lang.Object","long"] }, + {"name":"getLong","parameterTypes":["long"] }, + {"name":"getLong","parameterTypes":["java.lang.Object","long"] }, + {"name":"getObject","parameterTypes":["java.lang.Object","long"] }, + {"name":"objectFieldOffset","parameterTypes":["java.lang.reflect.Field"] }, + {"name":"putBoolean","parameterTypes":["java.lang.Object","long","boolean"] }, + {"name":"putByte","parameterTypes":["long","byte"] }, + {"name":"putByte","parameterTypes":["java.lang.Object","long","byte"] }, + {"name":"putDouble","parameterTypes":["java.lang.Object","long","double"] }, + {"name":"putFloat","parameterTypes":["java.lang.Object","long","float"] }, + {"name":"putInt","parameterTypes":["long","int"] }, + {"name":"putInt","parameterTypes":["java.lang.Object","long","int"] }, + {"name":"putLong","parameterTypes":["long","long"] }, + {"name":"putLong","parameterTypes":["java.lang.Object","long","long"] }, + {"name":"putObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] } + ] +}, +{ + "name":"sun.security.provider.DSA$SHA224withDSA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.JavaKeyStore$DualFormatJKS", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.JavaKeyStore$JKS", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA2$SHA224", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA2$SHA256", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA5$SHA384", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SHA5$SHA512", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.SecureRandom", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.provider.certpath.PKIXCertPathValidator", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAKeyFactory$Legacy", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSAPSSSignature", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSASignature$SHA224withRSA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSASignature$SHA256withRSA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.rsa.RSASignature$SHA512withRSA", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.SSLContextImpl$TLSContext", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"sun.security.x509.AuthorityKeyIdentifierExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.BasicConstraintsExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.CRLDistributionPointsExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.KeyUsageExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectAlternativeNameExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +}, +{ + "name":"sun.security.x509.SubjectKeyIdentifierExtension", + "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] +} +] diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json new file mode 100644 index 00000000..402798af --- /dev/null +++ b/graalvm-config-dir/resource-config.json @@ -0,0 +1,11 @@ +{ + "resources":{ + "includes":[ + {"pattern":"com/google/i18n/phonenumbers/data/.*"}, + {"pattern":"libzkgroup.so"}, + {"pattern":"org/asamk/signal/manager/ias.store"}, + {"pattern":"org/asamk/signal/manager/whisper.store"}, + {"pattern":"org/slf4j/impl/StaticLoggerBinder.class"} + ]}, + "bundles":[{"name":"net.sourceforge.argparse4j.internal.ArgumentParserImpl"}] +} From de3b077a6f21b1eb4cf3fb3776b17e64871ccf8f Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 22 Jan 2021 18:23:42 +0100 Subject: [PATCH 070/124] Update libsignal --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d3ae9667..f3f76412 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ repositories { } dependencies { - implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_16' + implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_17' implementation 'org.bouncycastle:bcprov-jdk15on:1.68' implementation 'net.sourceforge.argparse4j:argparse4j:0.8.1' implementation 'com.github.hypfvieh:dbus-java:3.2.4' From b4311c7b76ba3bc757f9823f589fdd0da722f328 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Jan 2021 00:00:07 +0100 Subject: [PATCH 071/124] Add more config generated by agent --- README.md | 11 +- build.gradle | 14 +- graalvm-config-dir/jni-config.json | 13 + graalvm-config-dir/proxy-config.json | 3 + graalvm-config-dir/reflect-config.json | 476 ++++++++++++++++++++++++- 5 files changed, 488 insertions(+), 29 deletions(-) create mode 100644 graalvm-config-dir/jni-config.json create mode 100644 graalvm-config-dir/proxy-config.json diff --git a/README.md b/README.md index 26e9bc95..1b797b7c 100644 --- a/README.md +++ b/README.md @@ -83,19 +83,18 @@ dependencies. If you have a recent gradle version installed, you can replace `./ ./gradlew distTar -### Building a native binary with GraalVM +### Building a native binary with GraalVM (EXPERIMENTAL) It is possible to build a native binary with [GraalVM](https://www.graalvm.org). +This is still experimental and will not work in all situations. 1. [Install GraalVM and setup the enviroment](https://www.graalvm.org/docs/getting-started/#install-graalvm) 2. [Install prerequisites](https://www.graalvm.org/reference-manual/native-image/#prerequisites) -2. Execute Gradle: +3. Execute Gradle: - ./gradle nativeImage + ./gradlew assembleNativeImage - The binary is available at - - build/native-image/signal-cli + The binary is available at *build/native-image/signal-cli* ## Troubleshooting If you use a version of the Oracle JRE and get an InvalidKeyException you need to enable unlimited strength crypto. See https://stackoverflow.com/questions/6481627/java-security-illegal-key-size-or-default-parameters for instructions. diff --git a/build.gradle b/build.gradle index f3f76412..764ad99e 100644 --- a/build.gradle +++ b/build.gradle @@ -75,8 +75,8 @@ task checkLibVersions { } } -task buildNativeBinary { - dependsOn("build") +task assembleNativeImage { + dependsOn("assemble") doLast { def graalVMHome = System.getenv("GRAALVM_HOME") if (!graalVMHome) { @@ -92,18 +92,16 @@ task buildNativeBinary { commandLine "$graalVMHome/bin/native-image", "-H:Path=$nativeBinaryOutputPath", "-H:Name=$nativeBinaryName", - "-H:JNIConfigurationFiles=", - "-H:DynamicProxyConfigurationFiles=", - "-H:ReflectionConfigurationFiles=", - "-H:ResourceConfigurationFiles=", + "-H:JNIConfigurationFiles=graalvm-config-dir/jni-config.json", + "-H:DynamicProxyConfigurationFiles=graalvm-config-dir/proxy-config.json", + "-H:ResourceConfigurationFiles=graalvm-config-dir/resource-config.json", + "-H:ReflectionConfigurationFiles=graalvm-config-dir/reflect-config.json", "--no-fallback", "--allow-incomplete-classpath", "--report-unsupported-elements-at-runtime", "--enable-url-protocols=http,https", "--enable-https", "--enable-all-security-services", - "-H:ResourceConfigurationFiles=graalvm-config-dir/resource-config.json", - "-H:ReflectionConfigurationFiles=graalvm-config-dir/reflect-config.json", "-cp", sourceSets.main.runtimeClasspath.asPath, project.mainClassName diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json new file mode 100644 index 00000000..724387de --- /dev/null +++ b/graalvm-config-dir/jni-config.json @@ -0,0 +1,13 @@ +[ +{ + "name":"java.lang.ClassLoader", + "methods":[{"name":"getPlatformClassLoader","parameterTypes":[] }] +}, +{ + "name":"java.lang.NoSuchMethodError" +}, +{ + "name":"java.lang.UnsatisfiedLinkError", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +} +] diff --git a/graalvm-config-dir/proxy-config.json b/graalvm-config-dir/proxy-config.json new file mode 100644 index 00000000..883065a9 --- /dev/null +++ b/graalvm-config-dir/proxy-config.json @@ -0,0 +1,3 @@ +[ + ["org.freedesktop.DBus"] +] diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index fdcaa2ba..ca62ca59 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -1,6 +1,8 @@ [ { - "name":"byte[]" + "name":"byte[]", + "allDeclaredMethods":true, + "allPublicMethods":true }, { "name":"char[]" @@ -9,6 +11,49 @@ "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.google.protobuf.AbstractProtobufList", + "allDeclaredFields":true, + "allDeclaredMethods":true +}, +{ + "name":"com.google.protobuf.Internal$LongList", + "allDeclaredMethods":true +}, +{ + "name":"com.google.protobuf.Internal$ProtobufList", + "allDeclaredMethods":true +}, +{ + "name":"com.google.protobuf.LongArrayList", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"com.google.protobuf.PrimitiveNonBoxingCollection", + "allDeclaredMethods":true +}, +{ + "name":"com.kenai.jffi.Invoker", + "methods":[ + {"name":"invokeI6","parameterTypes":["com.kenai.jffi.CallContext","long","int","int","int","int","int","int"] }, + {"name":"invokeL6","parameterTypes":["com.kenai.jffi.CallContext","long","long","long","long","long","long","long"] }, + {"name":"invokeN6","parameterTypes":["com.kenai.jffi.CallContext","long","long","long","long","long","long","long"] } + ] +}, +{ + "name":"com.kenai.jffi.Version", + "fields":[ + {"name":"MAJOR"}, + {"name":"MICRO"}, + {"name":"MINOR"} + ] +}, +{ + "name":"com.kenai.jffi.internal.StubLoader", + "methods":[{"name":"isLoaded","parameterTypes":[] }] +}, { "name":"com.sun.crypto.provider.AESCipher$General", "methods":[{"name":"","parameterTypes":[] }] @@ -33,10 +78,26 @@ "name":"com.sun.crypto.provider.TlsPrfGenerator$V12", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"int", + "allDeclaredMethods":true, + "allPublicMethods":true +}, +{ + "name":"int[]", + "allDeclaredMethods":true, + "allPublicMethods":true +}, { "name":"java.io.Serializable", "allDeclaredMethods":true }, +{ + "name":"java.lang.Boolean", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"java.lang.Comparable", "allDeclaredMethods":true @@ -51,15 +112,39 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"java.lang.Iterable", + "allDeclaredMethods":true +}, +{ + "name":"java.lang.Long", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"java.lang.Number", "allDeclaredFields":true, "allDeclaredMethods":true }, +{ + "name":"java.lang.String", + "allPublicMethods":true +}, +{ + "name":"java.lang.reflect.Method", + "methods":[{"name":"isDefault","parameterTypes":[] }] +}, { "name":"java.nio.Buffer", + "allDeclaredMethods":true, "fields":[{"name":"address", "allowUnsafeAccess":true}] }, +{ + "name":"java.nio.ByteBuffer", + "allDeclaredMethods":true, + "allPublicMethods":true +}, { "name":"java.security.KeyStoreSpi" }, @@ -81,15 +166,184 @@ { "name":"java.security.interfaces.RSAPublicKey" }, +{ + "name":"java.util.AbstractCollection", + "allDeclaredFields":true, + "allDeclaredMethods":true +}, +{ + "name":"java.util.AbstractList", + "allDeclaredFields":true, + "allDeclaredMethods":true +}, { "name":"java.util.ArrayList", "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"java.util.Collection", + "allDeclaredMethods":true +}, +{ + "name":"java.util.LinkedHashMap", + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"java.util.List", + "allDeclaredMethods":true +}, { "name":"java.util.Locale", "methods":[{"name":"getUnicodeLocaleType","parameterTypes":["java.lang.String"] }] }, +{ + "name":"java.util.RandomAccess", + "allDeclaredMethods":true +}, +{ + "name":"java.util.UUID", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"jnr.constants.platform.linux.ProtocolFamily" +}, +{ + "name":"jnr.constants.platform.linux.Shutdown" +}, +{ + "name":"jnr.constants.platform.linux.Sock" +}, +{ + "name":"jnr.constants.platform.linux.SocketLevel" +}, +{ + "name":"jnr.constants.platform.linux.SocketOption" +}, +{ + "name":"jnr.enxio.channels.Native$LibC", + "allPublicMethods":true +}, +{ + "name":"jnr.enxio.channels.Native$LibC$jnr$ffi$1", + "methods":[{"name":"","parameterTypes":["jnr.ffi.Runtime","jnr.ffi.provider.jffi.NativeLibrary","java.lang.Object[]"] }] +}, +{ + "name":"jnr.ffi.Pointer", + "allDeclaredMethods":true, + "allPublicMethods":true +}, +{ + "name":"jnr.ffi.StructLayout$gid_t", + "methods":[{"name":"","parameterTypes":["jnr.ffi.StructLayout"] }] +}, +{ + "name":"jnr.ffi.byref.IntByReference", + "allPublicMethods":true +}, +{ + "name":"jnr.ffi.provider.converters.ByReferenceParameterConverter", + "methods":[{"name":"nativeType","parameterTypes":[] }] +}, +{ + "name":"jnr.ffi.provider.converters.ByReferenceParameterConverter$Out", + "allPublicMethods":true +}, +{ + "name":"jnr.ffi.provider.converters.StringResultConverter", + "allPublicMethods":true +}, +{ + "name":"jnr.ffi.provider.converters.StructByReferenceToNativeConverter", + "allPublicMethods":true +}, +{ + "name":"jnr.ffi.provider.jffi.BufferParameterStrategy", + "methods":[{"name":"address","parameterTypes":["java.nio.Buffer"] }] +}, +{ + "name":"jnr.ffi.provider.jffi.PointerParameterStrategy", + "methods":[{"name":"address","parameterTypes":["jnr.ffi.Pointer"] }] +}, +{ + "name":"jnr.ffi.provider.jffi.Provider", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"jnr.ffi.provider.jffi.platform.x86_64.linux.TypeAliases", + "fields":[{"name":"ALIASES"}] +}, +{ + "name":"jnr.posix.Timeval", + "allPublicMethods":true +}, +{ + "name":"jnr.unixsocket.Native$LibC", + "allPublicMethods":true +}, +{ + "name":"jnr.unixsocket.Native$LibC$jnr$ffi$0", + "methods":[{"name":"","parameterTypes":["jnr.ffi.Runtime","jnr.ffi.provider.jffi.NativeLibrary","java.lang.Object[]"] }] +}, +{ + "name":"jnr.unixsocket.SockAddrUnix", + "allPublicMethods":true +}, +{ + "name":"long", + "allDeclaredMethods":true, + "allPublicMethods":true +}, +{ + "name":"long[]" +}, +{ + "name":"org.asamk.Signal", + "allDeclaredMethods":true, + "allDeclaredClasses":true +}, +{ + "name":"org.asamk.Signal$MessageReceived", + "allDeclaredConstructors":true, + "allPublicConstructors":true +}, +{ + "name":"org.asamk.Signal$ReceiptReceived", + "allDeclaredConstructors":true, + "allPublicConstructors":true +}, +{ + "name":"org.asamk.Signal$SyncMessageReceived", + "allDeclaredConstructors":true, + "allPublicConstructors":true +}, +{ + "name":"org.asamk.signal.json.JsonDataMessage", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.json.JsonMessageEnvelope", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.json.JsonReaction", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.json.JsonReceiptMessage", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.contacts.ContactInfo", "allDeclaredFields":true, @@ -113,6 +367,12 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.manager.storage.groups.GroupInfoV1$JsonSignalServiceAddress", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.groups.GroupInfoV1$MembersDeserializer", "methods":[{"name":"","parameterTypes":[] }] @@ -126,10 +386,7 @@ "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "fields":[ - {"name":"groups", "allowWrite":true} - ], - "allDeclaredConstructors":true + "fields":[{"name":"groups", "allowWrite":true}] }, { "name":"org.asamk.signal.manager.storage.groups.JsonGroupStore$GroupsDeserializer", @@ -144,9 +401,7 @@ "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "fields":[ - {"name":"profiles", "allowWrite":true} - ] + "fields":[{"name":"profiles", "allowWrite":true}] }, { "name":"org.asamk.signal.manager.storage.profiles.ProfileStore$ProfileStoreDeserializer", @@ -211,9 +466,7 @@ "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "fields":[ - {"name":"addresses", "allowWrite":true} - ] + "fields":[{"name":"addresses", "allowWrite":true}] }, { "name":"org.asamk.signal.manager.storage.protocol.RecipientStore$RecipientStoreDeserializer", @@ -228,9 +481,7 @@ "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, - "fields":[ - {"name":"stickers", "allowWrite":true} - ] + "fields":[{"name":"stickers", "allowWrite":true}] }, { "name":"org.asamk.signal.manager.storage.stickers.StickerStore$StickersDeserializer", @@ -368,6 +619,10 @@ "name":"org.bouncycastle.jcajce.provider.digest.SHA224$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.bouncycastle.jcajce.provider.digest.SHA256$Digest", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.bouncycastle.jcajce.provider.digest.SHA256$Mappings", "methods":[{"name":"","parameterTypes":[] }] @@ -604,6 +859,23 @@ "name":"org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.freedesktop.DBus$NameAcquired", + "allDeclaredConstructors":true +}, +{ + "name":"org.freedesktop.dbus.interfaces.Introspectable", + "allDeclaredMethods":true, + "allDeclaredClasses":true +}, +{ + "name":"org.freedesktop.dbus.interfaces.Peer", + "allDeclaredMethods":true, + "allDeclaredClasses":true +}, +{ + "name":"org.objectweb.asm.util.TraceMethodVisitor" +}, { "name":"org.signal.libsignal.metadata.SignalProtos$SenderCertificate", "fields":[ @@ -965,6 +1237,10 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity$ByteArrayDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.api.push.SignedPreKeyEntity$ByteArraySerializer", "methods":[{"name":"","parameterTypes":[] }] @@ -975,6 +1251,18 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.DiscoveryRequest", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.DiscoveryResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.KeyBackupRequest", "allDeclaredFields":true, @@ -987,6 +1275,18 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.MultiRemoteAttestationResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.contacts.entities.QueryEnvelope", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationRequest", "allDeclaredFields":true, @@ -1022,6 +1322,12 @@ {"name":"restore_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.AttachmentV2UploadAttributes", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.AuthCredentials", "allDeclaredFields":true, @@ -1052,6 +1358,12 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.internal.push.MismatchedDevices", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.OutgoingPushMessage", "allDeclaredFields":true, @@ -1070,10 +1382,26 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.internal.push.PreKeyEntity$ECPublicKeyDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyEntity$ECPublicKeySerializer", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.whispersystems.signalservice.internal.push.PreKeyResponse", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.whispersystems.signalservice.internal.push.PreKeyResponseItem", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.PreKeyState", "allDeclaredFields":true, @@ -1086,6 +1414,12 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.internal.push.ProvisioningMessage", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.ProvisioningProtos$ProvisionEnvelope", "fields":[ @@ -1153,6 +1487,31 @@ {"name":"width_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ContactDetails", + "fields":[ + {"name":"archived_", "allowUnsafeAccess":true}, + {"name":"avatar_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"blocked_", "allowUnsafeAccess":true}, + {"name":"color_", "allowUnsafeAccess":true}, + {"name":"expireTimer_", "allowUnsafeAccess":true}, + {"name":"inboxPosition_", "allowUnsafeAccess":true}, + {"name":"name_", "allowUnsafeAccess":true}, + {"name":"number_", "allowUnsafeAccess":true}, + {"name":"profileKey_", "allowUnsafeAccess":true}, + {"name":"uuid_", "allowUnsafeAccess":true}, + {"name":"verified_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ContactDetails$Avatar", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"contentType_", "allowUnsafeAccess":true}, + {"name":"length_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Content", "fields":[ @@ -1221,6 +1580,39 @@ {"name":"url_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Quote", + "fields":[ + {"name":"attachments_", "allowUnsafeAccess":true}, + {"name":"authorE164_", "allowUnsafeAccess":true}, + {"name":"authorUuid_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"bodyRanges_", "allowUnsafeAccess":true}, + {"name":"id_", "allowUnsafeAccess":true}, + {"name":"text_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Reaction", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"emoji_", "allowUnsafeAccess":true}, + {"name":"remove_", "allowUnsafeAccess":true}, + {"name":"targetAuthorUuid_", "allowUnsafeAccess":true}, + {"name":"targetSentTimestamp_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Sticker", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"data_", "allowUnsafeAccess":true}, + {"name":"emoji_", "allowUnsafeAccess":true}, + {"name":"packId_", "allowUnsafeAccess":true}, + {"name":"packKey_", "allowUnsafeAccess":true}, + {"name":"stickerId_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Envelope", "fields":[ @@ -1274,6 +1666,28 @@ {"name":"viewOnceOpen_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Contacts", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"blob_", "allowUnsafeAccess":true}, + {"name":"complete_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$FetchLatest", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Keys", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"storageService_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Read", "fields":[ @@ -1283,6 +1697,13 @@ {"name":"timestamp_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Request", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Sent", "fields":[ @@ -1323,6 +1744,17 @@ {"name":"timestamp_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$Verified", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"destinationE164_", "allowUnsafeAccess":true}, + {"name":"destinationUuid_", "allowUnsafeAccess":true}, + {"name":"identityKey_", "allowUnsafeAccess":true}, + {"name":"nullMessage_", "allowUnsafeAccess":true}, + {"name":"state_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.VerifyAccountResponse", "allDeclaredFields":true, @@ -1360,6 +1792,10 @@ {"name":"metadata_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.util.JsonUtil$IdentityKeyDeserializer", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"org.whispersystems.signalservice.internal.util.JsonUtil$IdentityKeySerializer", "methods":[{"name":"","parameterTypes":[] }] @@ -1403,31 +1839,41 @@ "name":"sun.misc.Unsafe", "allDeclaredFields":true, "methods":[ + {"name":"allocateMemory","parameterTypes":["long"] }, {"name":"arrayBaseOffset","parameterTypes":["java.lang.Class"] }, {"name":"arrayIndexScale","parameterTypes":["java.lang.Class"] }, {"name":"copyMemory","parameterTypes":["long","long","long"] }, {"name":"copyMemory","parameterTypes":["java.lang.Object","long","java.lang.Object","long","long"] }, + {"name":"freeMemory","parameterTypes":["long"] }, + {"name":"getAddress","parameterTypes":["long"] }, {"name":"getBoolean","parameterTypes":["java.lang.Object","long"] }, {"name":"getByte","parameterTypes":["long"] }, {"name":"getByte","parameterTypes":["java.lang.Object","long"] }, + {"name":"getDouble","parameterTypes":["long"] }, {"name":"getDouble","parameterTypes":["java.lang.Object","long"] }, + {"name":"getFloat","parameterTypes":["long"] }, {"name":"getFloat","parameterTypes":["java.lang.Object","long"] }, {"name":"getInt","parameterTypes":["long"] }, {"name":"getInt","parameterTypes":["java.lang.Object","long"] }, {"name":"getLong","parameterTypes":["long"] }, {"name":"getLong","parameterTypes":["java.lang.Object","long"] }, {"name":"getObject","parameterTypes":["java.lang.Object","long"] }, + {"name":"getShort","parameterTypes":["long"] }, {"name":"objectFieldOffset","parameterTypes":["java.lang.reflect.Field"] }, + {"name":"putAddress","parameterTypes":["long","long"] }, {"name":"putBoolean","parameterTypes":["java.lang.Object","long","boolean"] }, {"name":"putByte","parameterTypes":["long","byte"] }, {"name":"putByte","parameterTypes":["java.lang.Object","long","byte"] }, + {"name":"putDouble","parameterTypes":["long","double"] }, {"name":"putDouble","parameterTypes":["java.lang.Object","long","double"] }, + {"name":"putFloat","parameterTypes":["long","float"] }, {"name":"putFloat","parameterTypes":["java.lang.Object","long","float"] }, {"name":"putInt","parameterTypes":["long","int"] }, {"name":"putInt","parameterTypes":["java.lang.Object","long","int"] }, {"name":"putLong","parameterTypes":["long","long"] }, {"name":"putLong","parameterTypes":["java.lang.Object","long","long"] }, - {"name":"putObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] } + {"name":"putObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] }, + {"name":"putShort","parameterTypes":["long","short"] } ] }, { From 644aacf59516dd1ecafc58878d287557f20dc112 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 21 Dec 2020 10:25:36 +0100 Subject: [PATCH 072/124] Update libsignal --- CHANGELOG.md | 2 + build.gradle | 3 +- .../asamk/signal/ReceiveMessageHandler.java | 8 +++ .../org/asamk/signal/manager/Manager.java | 32 +++++++--- .../signal/manager/ProvisioningManager.java | 1 + .../signal/manager/RegistrationManager.java | 2 +- .../asamk/signal/manager/ServiceConfig.java | 3 +- .../signal/manager/helper/PinHelper.java | 3 +- .../protocol/JsonIdentityKeyStore.java | 64 +++++++++---------- 9 files changed, 72 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9092711d..ef4d240b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +**Attention**: For all functionality an additional native library is now required: [libsignal-client](https://github.com/signalapp/libsignal-client/). +See https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal for more information. ## [0.7.4] - 2021-01-19 ### Changed diff --git a/build.gradle b/build.gradle index 764ad99e..9eaf7b8a 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,8 @@ repositories { } dependencies { - implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_17' + implementation 'com.google.protobuf:protobuf-javalite:3.10.0' + implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_18' implementation 'org.bouncycastle:bcprov-jdk15on:1.68' implementation 'net.sourceforge.argparse4j:argparse4j:0.8.1' implementation 'com.github.hypfvieh:dbus-java:3.2.4' diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 0bb2e012..da1be271 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -318,6 +318,9 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (receiptMessage.isReadReceipt()) { System.out.println(" - Is read receipt"); } + if (receiptMessage.isViewedReceipt()) { + System.out.println(" - Is viewed receipt"); + } System.out.println(" - Timestamps:"); for (long timestamp : receiptMessage.getTimestamps()) { System.out.println(" " + DateUtils.formatTimestamp(timestamp)); @@ -397,6 +400,11 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { System.out.println(" Has signed group change: " + groupInfo.hasSignedGroupChange()); } } + if (message.getGroupCallUpdate().isPresent()) { + final SignalServiceDataMessage.GroupCallUpdate groupCallUpdate = message.getGroupCallUpdate().get(); + System.out.println("Group call update:"); + System.out.println(" - Era id: " + groupCallUpdate.getEraId()); + } if (message.getPreviews().isPresent()) { final List previews = message.getPreviews().get(); System.out.println("Previews:"); diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/src/main/java/org/asamk/signal/manager/Manager.java index 96dbe212..c4f32460 100644 --- a/src/main/java/org/asamk/signal/manager/Manager.java +++ b/src/main/java/org/asamk/signal/manager/Manager.java @@ -219,6 +219,7 @@ public class Manager implements Closeable { account.getDeviceId()), userAgent, groupsV2Operations, + ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); this.groupsV2Api = accountManager.getGroupsV2Api(); final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); @@ -234,7 +235,8 @@ public class Manager implements Closeable { userAgent, null, timer, - clientZkProfileOperations); + clientZkProfileOperations, + ServiceConfig.AUTOMATIC_NETWORK_RETRY); this.account.setResolver(this::resolveSignalServiceAddress); @@ -352,10 +354,19 @@ public class Manager implements Closeable { * if it's Optional.absent(), the avatar will be removed */ public void setProfile(String name, Optional avatar) throws IOException { + // TODO + String about = null; + String aboutEmoji = null; + try (final StreamDetails streamDetails = avatar == null ? avatarStore.retrieveProfileAvatar(getSelfAddress()) : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) { - accountManager.setVersionedProfile(account.getUuid(), account.getProfileKey(), name, streamDetails); + accountManager.setVersionedProfile(account.getUuid(), + account.getProfileKey(), + name, + about, + aboutEmoji, + streamDetails); } if (avatar != null) { @@ -378,6 +389,7 @@ public class Manager implements Closeable { // If this is the master device, other users can't send messages to this number anymore. // If this is a linked device, other users can still send messages, but this device doesn't receive them anymore. accountManager.setGcmId(Optional.absent()); + accountManager.deleteAccount(); account.setRegistered(false); account.save(); @@ -499,7 +511,8 @@ public class Manager implements Closeable { Optional.absent(), clientZkProfileOperations, executor, - ServiceConfig.MAX_ENVELOPE_SIZE); + ServiceConfig.MAX_ENVELOPE_SIZE, + ServiceConfig.AUTOMATIC_NETWORK_RETRY); } private SignalProfile getRecipientProfile( @@ -1250,7 +1263,7 @@ public class Manager implements Closeable { private Map getRegisteredUsers(final Set numbersMissingUuid) throws IOException { try { return accountManager.getRegisteredUsers(getIasKeyStore(), numbersMissingUuid, CDS_MRENCLAVE); - } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException e) { + } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) { throw new IOException(e); } } @@ -1391,10 +1404,13 @@ public class Manager implements Closeable { if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) { org.whispersystems.libsignal.UntrustedIdentityException identityException = (org.whispersystems.libsignal.UntrustedIdentityException) e .getCause(); - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(identityException.getName()), - identityException.getUntrustedIdentity(), - TrustLevel.UNTRUSTED); + final IdentityKey untrustedIdentity = identityException.getUntrustedIdentity(); + if (untrustedIdentity != null) { + account.getSignalProtocolStore() + .saveIdentity(resolveSignalServiceAddress(identityException.getName()), + untrustedIdentity, + TrustLevel.UNTRUSTED); + } throw identityException; } throw new AssertionError(e); diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 535cecc6..0c26cf77 100644 --- a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -70,6 +70,7 @@ public class ProvisioningManager { new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, + ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); } diff --git a/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/src/main/java/org/asamk/signal/manager/RegistrationManager.java index d6bf78b1..506948ba 100644 --- a/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -70,7 +70,7 @@ public class RegistrationManager implements Closeable { account.getUsername(), account.getPassword(), account.getSignalingKey(), - SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, timer); + SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); this.pinHelper = new PinHelper(keyBackupService); } diff --git a/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/src/main/java/org/asamk/signal/manager/ServiceConfig.java index b6d4f4fd..c68e5773 100644 --- a/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ b/src/main/java/org/asamk/signal/manager/ServiceConfig.java @@ -36,8 +36,9 @@ public class ServiceConfig { final static int PREKEY_MINIMUM_COUNT = 20; final static int PREKEY_BATCH_SIZE = 100; final static int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024; - final static int MAX_ENVELOPE_SIZE = 0; + final static long MAX_ENVELOPE_SIZE = 0; final static long AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE = 10 * 1024 * 1024; + final static boolean AUTOMATIC_NETWORK_RETRY = true; final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; diff --git a/src/main/java/org/asamk/signal/manager/helper/PinHelper.java b/src/main/java/org/asamk/signal/manager/helper/PinHelper.java index b4fa04c4..090b20b6 100644 --- a/src/main/java/org/asamk/signal/manager/helper/PinHelper.java +++ b/src/main/java/org/asamk/signal/manager/helper/PinHelper.java @@ -1,6 +1,7 @@ package org.asamk.signal.manager.helper; import org.asamk.signal.manager.util.PinHashing; +import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.signalservice.api.KbsPinData; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; @@ -82,7 +83,7 @@ public class PinHelper { throw new AssertionError("Null not expected"); } return kbsData; - } catch (UnauthenticatedResponseException e) { + } catch (UnauthenticatedResponseException | InvalidKeyException e) { throw new IOException(e); } } diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index 28d64cbf..9f01b719 100644 --- a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java +++ b/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -194,47 +194,43 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); - try { - int localRegistrationId = node.get("registrationId").asInt(); - IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.getDecoder() - .decode(node.get("identityKey").asText())); + int localRegistrationId = node.get("registrationId").asInt(); + IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.getDecoder() + .decode(node.get("identityKey").asText())); - JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId); + JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId); - JsonNode trustedKeysNode = node.get("trustedKeys"); - if (trustedKeysNode.isArray()) { - for (JsonNode trustedKey : trustedKeysNode) { - String trustedKeyName = trustedKey.hasNonNull("name") ? trustedKey.get("name").asText() : null; + JsonNode trustedKeysNode = node.get("trustedKeys"); + if (trustedKeysNode.isArray()) { + for (JsonNode trustedKey : trustedKeysNode) { + String trustedKeyName = trustedKey.hasNonNull("name") ? trustedKey.get("name").asText() : null; - if (UuidUtil.isUuid(trustedKeyName)) { - // Ignore identities that were incorrectly created with UUIDs as name - continue; - } + if (UuidUtil.isUuid(trustedKeyName)) { + // Ignore identities that were incorrectly created with UUIDs as name + continue; + } - UUID uuid = trustedKey.hasNonNull("uuid") ? UuidUtil.parseOrNull(trustedKey.get("uuid") - .asText()) : null; - final SignalServiceAddress serviceAddress = uuid == null - ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName) - : new SignalServiceAddress(uuid, trustedKeyName); - try { - IdentityKey id = new IdentityKey(Base64.getDecoder() - .decode(trustedKey.get("identityKey").asText()), 0); - TrustLevel trustLevel = trustedKey.hasNonNull("trustLevel") - ? TrustLevel.fromInt(trustedKey.get("trustLevel").asInt()) - : TrustLevel.TRUSTED_UNVERIFIED; - Date added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get( - "addedTimestamp").asLong()) : new Date(); - keyStore.saveIdentity(serviceAddress, id, trustLevel, added); - } catch (InvalidKeyException e) { - logger.warn("Error while decoding key for {}: {}", trustedKeyName, e.getMessage()); - } + UUID uuid = trustedKey.hasNonNull("uuid") + ? UuidUtil.parseOrNull(trustedKey.get("uuid").asText()) + : null; + final SignalServiceAddress serviceAddress = uuid == null + ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName) + : new SignalServiceAddress(uuid, trustedKeyName); + try { + IdentityKey id = new IdentityKey(Base64.getDecoder() + .decode(trustedKey.get("identityKey").asText()), 0); + TrustLevel trustLevel = trustedKey.hasNonNull("trustLevel") ? TrustLevel.fromInt(trustedKey.get( + "trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED; + Date added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp") + .asLong()) : new Date(); + keyStore.saveIdentity(serviceAddress, id, trustLevel, added); + } catch (InvalidKeyException e) { + logger.warn("Error while decoding key for {}: {}", trustedKeyName, e.getMessage()); } } - - return keyStore; - } catch (InvalidKeyException e) { - throw new IOException(e); } + + return keyStore; } } From 383e7db360622aa139e5143a855417d3138da932 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Jan 2021 13:00:12 +0100 Subject: [PATCH 073/124] Update gradle wrapper --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 12d38de6..da9702f9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 69149b16c139bd1cc28a695832560c288eb94f2e Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Jan 2021 13:36:39 +0100 Subject: [PATCH 074/124] Prepare build.gradle for kotlin migration --- build.gradle | 80 +++++++++++++++++++++++++------------------------ settings.gradle | 19 +----------- 2 files changed, 42 insertions(+), 57 deletions(-) diff --git a/build.gradle b/build.gradle index 9eaf7b8a..f3475343 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,17 @@ -apply plugin: 'java' -apply plugin: 'application' -apply plugin: 'eclipse' +plugins { + id "java" + id "application" + id "eclipse" +} sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 -mainClassName = 'org.asamk.signal.Main' +mainClassName = "org.asamk.signal.Main" -version = '0.7.4' +version = "0.7.4" -compileJava.options.encoding = 'UTF-8' +compileJava.options.encoding = "UTF-8" repositories { mavenLocal() @@ -17,20 +19,20 @@ repositories { } dependencies { - implementation 'com.google.protobuf:protobuf-javalite:3.10.0' - implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_18' - implementation 'org.bouncycastle:bcprov-jdk15on:1.68' - implementation 'net.sourceforge.argparse4j:argparse4j:0.8.1' - implementation 'com.github.hypfvieh:dbus-java:3.2.4' - implementation 'org.slf4j:slf4j-simple:1.7.30' + implementation("com.google.protobuf:protobuf-javalite:3.10.0") + implementation("com.github.turasa:signal-service-java:2.15.3_unofficial_18") + implementation("org.bouncycastle:bcprov-jdk15on:1.68") + implementation("net.sourceforge.argparse4j:argparse4j:0.8.1") + implementation("com.github.hypfvieh:dbus-java:3.2.4") + implementation("org.slf4j:slf4j-simple:1.7.30") } jar { manifest { attributes( - 'Implementation-Title': project.name, - 'Implementation-Version': project.version, - 'Main-Class': project.mainClassName, + "Implementation-Title": project.name, + "Implementation-Version": project.version, + "Main-Class": project.mainClassName, ) } } @@ -39,7 +41,7 @@ run { if (project.hasProperty("appArgs")) { // allow passing command-line arguments to the main application e.g.: // $ gradle run -PappArgs="['-u', '+...', 'daemon', '--json']" - args Eval.me(appArgs) + args(Eval.me(appArgs)) } } @@ -54,19 +56,19 @@ task checkLibVersions { def version = dependency.version if (!checked[dependency]) { def group = dependency.group - def path = group.replace('.', '/') + def path = group.replace(".", "/") def name = dependency.name def url = "https://repo1.maven.org/maven2/$path/$name/maven-metadata.xml" try { def metadata = new XmlSlurper().parseText(url.toURL().text) def newest = metadata.versioning.latest; if ("$version" != "$newest") { - println "UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}" + println("UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}") } } catch (FileNotFoundException e) { - logger.debug "Unable to download $url: $e.message" + logger.debug("Unable to download $url: $e.message") } catch (org.xml.sax.SAXParseException e) { - logger.debug "Unable to parse $url: $e.message" + logger.debug("Unable to parse $url: $e.message") } checked[dependency] = true } @@ -81,31 +83,31 @@ task assembleNativeImage { doLast { def graalVMHome = System.getenv("GRAALVM_HOME") if (!graalVMHome) { - throw new GradleException('Required GRAALVM_HOME environment variable not set.') + throw new GradleException("Required GRAALVM_HOME environment variable not set.") } def nativeBinaryOutputPath = "$buildDir/native-image" def nativeBinaryName = "signal-cli" - mkdir nativeBinaryOutputPath + mkdir(nativeBinaryOutputPath) exec { - workingDir "." - commandLine "$graalVMHome/bin/native-image", - "-H:Path=$nativeBinaryOutputPath", - "-H:Name=$nativeBinaryName", - "-H:JNIConfigurationFiles=graalvm-config-dir/jni-config.json", - "-H:DynamicProxyConfigurationFiles=graalvm-config-dir/proxy-config.json", - "-H:ResourceConfigurationFiles=graalvm-config-dir/resource-config.json", - "-H:ReflectionConfigurationFiles=graalvm-config-dir/reflect-config.json", - "--no-fallback", - "--allow-incomplete-classpath", - "--report-unsupported-elements-at-runtime", - "--enable-url-protocols=http,https", - "--enable-https", - "--enable-all-security-services", - "-cp", - sourceSets.main.runtimeClasspath.asPath, - project.mainClassName + workingDir = "." + commandLine("$graalVMHome/bin/native-image", + "-H:Path=$nativeBinaryOutputPath", + "-H:Name=$nativeBinaryName", + "-H:JNIConfigurationFiles=graalvm-config-dir/jni-config.json", + "-H:DynamicProxyConfigurationFiles=graalvm-config-dir/proxy-config.json", + "-H:ResourceConfigurationFiles=graalvm-config-dir/resource-config.json", + "-H:ReflectionConfigurationFiles=graalvm-config-dir/reflect-config.json", + "--no-fallback", + "--allow-incomplete-classpath", + "--report-unsupported-elements-at-runtime", + "--enable-url-protocols=http,https", + "--enable-https", + "--enable-all-security-services", + "-cp", + sourceSets.main.runtimeClasspath.asPath, + project.mainClassName) } } } diff --git a/settings.gradle b/settings.gradle index 9f877185..e837999a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,18 +1 @@ -/* - * This settings file was auto generated by the Gradle buildInit task - * - * The settings file is used to specify which projects to include in your build. - * In a single project build this file can be empty or even removed. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user guide at http://gradle.org/docs/2.2.1/userguide/multi_project_builds.html - */ - -/* -// To declare projects as part of a multi-project build use the 'include' method -include 'shared' -include 'api' -include 'services:webservice' -*/ - -rootProject.name = 'signal-cli' +rootProject.name = "signal-cli" From 4adb11dada29ac6ca2d12270fd7e617007ff9bf3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Jan 2021 15:55:41 +0100 Subject: [PATCH 075/124] Convert gradle scripts to kotlin --- build.gradle | 113 ---------------------- build.gradle.kts | 129 +++++++++++++++++++++++++ settings.gradle => settings.gradle.kts | 0 3 files changed, 129 insertions(+), 113 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts rename settings.gradle => settings.gradle.kts (100%) diff --git a/build.gradle b/build.gradle deleted file mode 100644 index f3475343..00000000 --- a/build.gradle +++ /dev/null @@ -1,113 +0,0 @@ -plugins { - id "java" - id "application" - id "eclipse" -} - -sourceCompatibility = JavaVersion.VERSION_11 -targetCompatibility = JavaVersion.VERSION_11 - -mainClassName = "org.asamk.signal.Main" - -version = "0.7.4" - -compileJava.options.encoding = "UTF-8" - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - implementation("com.google.protobuf:protobuf-javalite:3.10.0") - implementation("com.github.turasa:signal-service-java:2.15.3_unofficial_18") - implementation("org.bouncycastle:bcprov-jdk15on:1.68") - implementation("net.sourceforge.argparse4j:argparse4j:0.8.1") - implementation("com.github.hypfvieh:dbus-java:3.2.4") - implementation("org.slf4j:slf4j-simple:1.7.30") -} - -jar { - manifest { - attributes( - "Implementation-Title": project.name, - "Implementation-Version": project.version, - "Main-Class": project.mainClassName, - ) - } -} - -run { - if (project.hasProperty("appArgs")) { - // allow passing command-line arguments to the main application e.g.: - // $ gradle run -PappArgs="['-u', '+...', 'daemon', '--json']" - args(Eval.me(appArgs)) - } -} - -// Find any 3rd party libraries which have released new versions -// to the central Maven repo since we last upgraded. -task checkLibVersions { - doLast { - def checked = [:] - allprojects { - configurations.each { configuration -> - configuration.allDependencies.each { dependency -> - def version = dependency.version - if (!checked[dependency]) { - def group = dependency.group - def path = group.replace(".", "/") - def name = dependency.name - def url = "https://repo1.maven.org/maven2/$path/$name/maven-metadata.xml" - try { - def metadata = new XmlSlurper().parseText(url.toURL().text) - def newest = metadata.versioning.latest; - if ("$version" != "$newest") { - println("UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}") - } - } catch (FileNotFoundException e) { - logger.debug("Unable to download $url: $e.message") - } catch (org.xml.sax.SAXParseException e) { - logger.debug("Unable to parse $url: $e.message") - } - checked[dependency] = true - } - } - } - } - } -} - -task assembleNativeImage { - dependsOn("assemble") - doLast { - def graalVMHome = System.getenv("GRAALVM_HOME") - if (!graalVMHome) { - throw new GradleException("Required GRAALVM_HOME environment variable not set.") - } - def nativeBinaryOutputPath = "$buildDir/native-image" - def nativeBinaryName = "signal-cli" - - mkdir(nativeBinaryOutputPath) - - exec { - workingDir = "." - commandLine("$graalVMHome/bin/native-image", - "-H:Path=$nativeBinaryOutputPath", - "-H:Name=$nativeBinaryName", - "-H:JNIConfigurationFiles=graalvm-config-dir/jni-config.json", - "-H:DynamicProxyConfigurationFiles=graalvm-config-dir/proxy-config.json", - "-H:ResourceConfigurationFiles=graalvm-config-dir/resource-config.json", - "-H:ReflectionConfigurationFiles=graalvm-config-dir/reflect-config.json", - "--no-fallback", - "--allow-incomplete-classpath", - "--report-unsupported-elements-at-runtime", - "--enable-url-protocols=http,https", - "--enable-https", - "--enable-all-security-services", - "-cp", - sourceSets.main.runtimeClasspath.asPath, - project.mainClassName) - } - } -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..b2bd53e4 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,129 @@ +plugins { + java + application + eclipse +} + +version = "0.7.4" + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +application { + mainClass.set("org.asamk.signal.Main") +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation("com.google.protobuf:protobuf-javalite:3.10.0") + implementation("com.github.turasa:signal-service-java:2.15.3_unofficial_18") + implementation("org.bouncycastle:bcprov-jdk15on:1.68") + implementation("net.sourceforge.argparse4j:argparse4j:0.8.1") + implementation("com.github.hypfvieh:dbus-java:3.2.4") + implementation("org.slf4j:slf4j-simple:1.7.30") +} + +configurations { + implementation { + resolutionStrategy.failOnVersionConflict() + } +} + +tasks.withType { + options.encoding = "UTF-8" +} + +tasks.withType { + manifest { + attributes( + "Implementation-Title" to project.name, + "Implementation-Version" to project.version, + "Main-Class" to application.mainClass.get() + ) + } +} + +tasks.withType { + val appArgs: String? by project + if (appArgs != null) { + // allow passing command-line arguments to the main application e.g.: + // $ gradle run -PappArgs="['-u', '+...', 'daemon', '--json']" + args = groovy.util.Eval.me(appArgs) as MutableList + } +} + +// Find any 3rd party libraries which have released new versions +// to the central Maven repo since we last upgraded. +val checkLibVersions by tasks.registering { + doLast { + val checked = kotlin.collections.HashSet() + allprojects { + configurations.forEach { configuration -> + configuration.allDependencies.forEach { dependency -> + val version = dependency.version + if (!checked.contains(dependency)) { + val group = dependency.group + val path = group?.replace(".", "/") ?: "" + val name = dependency.name + val metaDataUrl = "https://repo1.maven.org/maven2/$path/$name/maven-metadata.xml" + try { + val url = org.codehaus.groovy.runtime.ResourceGroovyMethods.toURL(metaDataUrl) + val metaDataText = org.codehaus.groovy.runtime.ResourceGroovyMethods.getText(url) + val metadata = groovy.util.XmlSlurper().parseText(metaDataText) + val newest = (metadata.getProperty("versioning") as groovy.util.slurpersupport.GPathResult).getProperty("latest") + if (version != newest.toString()) { + println("UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}") + } + } catch (e: Throwable) { + logger.debug("Unable to download or parse $metaDataUrl: $e.message") + } + checked.add(dependency) + } + } + } + } + } +} + +val assembleNativeImage by tasks.registering { + dependsOn("assemble") + + var graalVMHome = "" + doFirst { + graalVMHome = System.getenv("GRAALVM_HOME") + ?: throw GradleException("Required GRAALVM_HOME environment variable not set.") + } + + doLast { + val nativeBinaryOutputPath = "$buildDir/native-image" + val nativeBinaryName = "signal-cli" + + mkdir(nativeBinaryOutputPath) + + exec { + workingDir = File(".") + commandLine("$graalVMHome/bin/native-image", + "-H:Path=$nativeBinaryOutputPath", + "-H:Name=$nativeBinaryName", + "-H:JNIConfigurationFiles=graalvm-config-dir/jni-config.json", + "-H:DynamicProxyConfigurationFiles=graalvm-config-dir/proxy-config.json", + "-H:ResourceConfigurationFiles=graalvm-config-dir/resource-config.json", + "-H:ReflectionConfigurationFiles=graalvm-config-dir/reflect-config.json", + "--no-fallback", + "--allow-incomplete-classpath", + "--report-unsupported-elements-at-runtime", + "--enable-url-protocols=http,https", + "--enable-https", + "--enable-all-security-services", + "-cp", + sourceSets.main.get().runtimeClasspath.asPath, + application.mainClass.get()) + } + } +} diff --git a/settings.gradle b/settings.gradle.kts similarity index 100% rename from settings.gradle rename to settings.gradle.kts From c72aeed8bba4d5ca873b36b4edb2b8eda9c24ec7 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Jan 2021 16:57:36 +0100 Subject: [PATCH 076/124] Extract lib module --- build.gradle.kts | 3 +- lib/build.gradle.kts | 30 ++++++++++++++++++ .../manager/AttachmentInvalidException.java | 0 .../asamk/signal/manager/AttachmentStore.java | 0 .../org/asamk/signal/manager/AvatarStore.java | 0 .../asamk/signal/manager/DeviceLinkInfo.java | 0 .../asamk/signal/manager/HandleAction.java | 0 .../asamk/signal/manager/IasTrustStore.java | 0 .../asamk/signal/manager/JsonStickerPack.java | 0 .../asamk/signal/manager/LibSignalLogger.java | 0 .../org/asamk/signal/manager/Manager.java | 0 .../manager/NotRegisteredException.java | 0 .../org/asamk/signal/manager/PathConfig.java | 0 .../signal/manager/ProvisioningManager.java | 0 .../signal/manager/RegistrationManager.java | 0 .../asamk/signal/manager/ServiceConfig.java | 0 .../manager/StickerPackInvalidException.java | 0 .../org/asamk/signal/manager/TrustLevel.java | 0 .../signal/manager/UserAlreadyExists.java | 0 .../signal/manager/WhisperTrustStore.java | 0 .../asamk/signal/manager/groups/GroupId.java | 0 .../groups/GroupIdFormatException.java | 0 .../signal/manager/groups/GroupIdV1.java | 0 .../signal/manager/groups/GroupIdV2.java | 0 .../manager/groups/GroupInviteLinkUrl.java | 0 .../manager/groups/GroupLinkPassword.java | 0 .../groups/GroupNotFoundException.java | 0 .../signal/manager/groups/GroupUtils.java | 0 .../groups/NotAGroupMemberException.java | 0 .../helper/GroupAuthorizationProvider.java | 0 .../signal/manager/helper/GroupHelper.java | 0 .../manager/helper/MessagePipeProvider.java | 0 .../helper/MessageReceiverProvider.java | 0 .../signal/manager/helper/PinHelper.java | 0 .../signal/manager/helper/ProfileHelper.java | 0 .../helper/ProfileKeyCredentialProvider.java | 0 .../manager/helper/ProfileKeyProvider.java | 0 .../manager/helper/ProfileProvider.java | 0 .../manager/helper/SelfAddressProvider.java | 0 .../helper/SelfProfileKeyProvider.java | 0 .../helper/UnidentifiedAccessHelper.java | 0 .../helper/UnidentifiedAccessProvider.java | 0 ...tifiedAccessSenderCertificateProvider.java | 0 .../signal/manager/storage/SignalAccount.java | 0 .../manager/storage/contacts/ContactInfo.java | 0 .../storage/contacts/JsonContactsStore.java | 0 .../manager/storage/groups/GroupInfo.java | 0 .../manager/storage/groups/GroupInfoV1.java | 0 .../manager/storage/groups/GroupInfoV2.java | 0 .../storage/groups/JsonGroupStore.java | 2 +- .../storage/messageCache/CachedMessage.java | 0 .../storage/messageCache/MessageCache.java | 0 .../storage/profiles/ProfileStore.java | 0 .../storage/profiles/SignalProfile.java | 0 .../storage/profiles/SignalProfileEntry.java | 0 .../storage/protocol/IdentityInfo.java | 0 .../protocol/JsonIdentityKeyStore.java | 0 .../storage/protocol/JsonPreKeyStore.java | 0 .../storage/protocol/JsonSessionStore.java | 0 .../protocol/JsonSignalProtocolStore.java | 0 .../protocol/JsonSignedPreKeyStore.java | 0 .../storage/protocol/RecipientStore.java | 0 .../manager/storage/protocol/SessionInfo.java | 0 .../SignalServiceAddressResolver.java | 0 .../manager/storage/stickers/Sticker.java | 0 .../storage/stickers/StickerStore.java | 0 .../threads/LegacyJsonThreadStore.java | 0 .../manager/storage/threads/ThreadInfo.java | 0 .../signal/manager/util/AttachmentUtils.java | 0 .../asamk/signal/manager/util/IOUtils.java | 0 .../asamk/signal/manager/util/KeyUtils.java | 8 +++-- .../manager/util/MessageCacheUtils.java | 0 .../asamk/signal/manager/util/PinHashing.java | 0 .../signal/manager/util/ProfileUtils.java | 0 .../signal/manager/util/StickerUtils.java | 0 .../org/asamk/signal/manager/util/Utils.java | 0 .../org/asamk/signal/manager/ias.store | Bin .../org/asamk/signal/manager/whisper.store | Bin settings.gradle.kts | 1 + 79 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 lib/build.gradle.kts rename {src => lib/src}/main/java/org/asamk/signal/manager/AttachmentInvalidException.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/AttachmentStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/AvatarStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/DeviceLinkInfo.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/HandleAction.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/IasTrustStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/JsonStickerPack.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/LibSignalLogger.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/Manager.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/NotRegisteredException.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/PathConfig.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/ProvisioningManager.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/RegistrationManager.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/ServiceConfig.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/StickerPackInvalidException.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/TrustLevel.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/UserAlreadyExists.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/WhisperTrustStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/GroupId.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/GroupIdFormatException.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/GroupIdV1.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/GroupIdV2.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/GroupNotFoundException.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/GroupUtils.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/groups/NotAGroupMemberException.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/GroupAuthorizationProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/GroupHelper.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/MessagePipeProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/PinHelper.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/ProfileHelper.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/ProfileKeyCredentialProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/ProfileKeyProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/ProfileProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/SelfAddressProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/SelfProfileKeyProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessSenderCertificateProvider.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/SignalAccount.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/contacts/ContactInfo.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java (99%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/profiles/SignalProfileEntry.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/SessionInfo.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/protocol/SignalServiceAddressResolver.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/stickers/Sticker.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/util/AttachmentUtils.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/util/IOUtils.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/util/KeyUtils.java (93%) rename {src => lib/src}/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/util/PinHashing.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/util/ProfileUtils.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/util/StickerUtils.java (100%) rename {src => lib/src}/main/java/org/asamk/signal/manager/util/Utils.java (100%) rename {src => lib/src}/main/resources/org/asamk/signal/manager/ias.store (100%) rename {src => lib/src}/main/resources/org/asamk/signal/manager/whisper.store (100%) diff --git a/build.gradle.kts b/build.gradle.kts index b2bd53e4..8a3a8aae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,12 +21,11 @@ repositories { } dependencies { - implementation("com.google.protobuf:protobuf-javalite:3.10.0") - implementation("com.github.turasa:signal-service-java:2.15.3_unofficial_18") implementation("org.bouncycastle:bcprov-jdk15on:1.68") implementation("net.sourceforge.argparse4j:argparse4j:0.8.1") implementation("com.github.hypfvieh:dbus-java:3.2.4") implementation("org.slf4j:slf4j-simple:1.7.30") + implementation(project(":lib")) } configurations { diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts new file mode 100644 index 00000000..5eb6c9b4 --- /dev/null +++ b/lib/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + `java-library` +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + api("com.github.turasa:signal-service-java:2.15.3_unofficial_18") + implementation("com.google.protobuf:protobuf-javalite:3.10.0") + implementation("org.bouncycastle:bcprov-jdk15on:1.68") + implementation("org.slf4j:slf4j-api:1.7.30") +} + +configurations { + implementation { + resolutionStrategy.failOnVersionConflict() + } +} + +tasks.withType { + options.encoding = "UTF-8" +} diff --git a/src/main/java/org/asamk/signal/manager/AttachmentInvalidException.java b/lib/src/main/java/org/asamk/signal/manager/AttachmentInvalidException.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/AttachmentInvalidException.java rename to lib/src/main/java/org/asamk/signal/manager/AttachmentInvalidException.java diff --git a/src/main/java/org/asamk/signal/manager/AttachmentStore.java b/lib/src/main/java/org/asamk/signal/manager/AttachmentStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/AttachmentStore.java rename to lib/src/main/java/org/asamk/signal/manager/AttachmentStore.java diff --git a/src/main/java/org/asamk/signal/manager/AvatarStore.java b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/AvatarStore.java rename to lib/src/main/java/org/asamk/signal/manager/AvatarStore.java diff --git a/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java b/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java rename to lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java diff --git a/src/main/java/org/asamk/signal/manager/HandleAction.java b/lib/src/main/java/org/asamk/signal/manager/HandleAction.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/HandleAction.java rename to lib/src/main/java/org/asamk/signal/manager/HandleAction.java diff --git a/src/main/java/org/asamk/signal/manager/IasTrustStore.java b/lib/src/main/java/org/asamk/signal/manager/IasTrustStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/IasTrustStore.java rename to lib/src/main/java/org/asamk/signal/manager/IasTrustStore.java diff --git a/src/main/java/org/asamk/signal/manager/JsonStickerPack.java b/lib/src/main/java/org/asamk/signal/manager/JsonStickerPack.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/JsonStickerPack.java rename to lib/src/main/java/org/asamk/signal/manager/JsonStickerPack.java diff --git a/src/main/java/org/asamk/signal/manager/LibSignalLogger.java b/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/LibSignalLogger.java rename to lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java diff --git a/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/Manager.java rename to lib/src/main/java/org/asamk/signal/manager/Manager.java diff --git a/src/main/java/org/asamk/signal/manager/NotRegisteredException.java b/lib/src/main/java/org/asamk/signal/manager/NotRegisteredException.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/NotRegisteredException.java rename to lib/src/main/java/org/asamk/signal/manager/NotRegisteredException.java diff --git a/src/main/java/org/asamk/signal/manager/PathConfig.java b/lib/src/main/java/org/asamk/signal/manager/PathConfig.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/PathConfig.java rename to lib/src/main/java/org/asamk/signal/manager/PathConfig.java diff --git a/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/ProvisioningManager.java rename to lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java diff --git a/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/RegistrationManager.java rename to lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java diff --git a/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/ServiceConfig.java rename to lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java diff --git a/src/main/java/org/asamk/signal/manager/StickerPackInvalidException.java b/lib/src/main/java/org/asamk/signal/manager/StickerPackInvalidException.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/StickerPackInvalidException.java rename to lib/src/main/java/org/asamk/signal/manager/StickerPackInvalidException.java diff --git a/src/main/java/org/asamk/signal/manager/TrustLevel.java b/lib/src/main/java/org/asamk/signal/manager/TrustLevel.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/TrustLevel.java rename to lib/src/main/java/org/asamk/signal/manager/TrustLevel.java diff --git a/src/main/java/org/asamk/signal/manager/UserAlreadyExists.java b/lib/src/main/java/org/asamk/signal/manager/UserAlreadyExists.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/UserAlreadyExists.java rename to lib/src/main/java/org/asamk/signal/manager/UserAlreadyExists.java diff --git a/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java b/lib/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/WhisperTrustStore.java rename to lib/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupId.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/GroupId.java rename to lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupIdFormatException.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupIdFormatException.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/GroupIdFormatException.java rename to lib/src/main/java/org/asamk/signal/manager/groups/GroupIdFormatException.java diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java rename to lib/src/main/java/org/asamk/signal/manager/groups/GroupIdV1.java diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupIdV2.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupIdV2.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/GroupIdV2.java rename to lib/src/main/java/org/asamk/signal/manager/groups/GroupIdV2.java diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java rename to lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java rename to lib/src/main/java/org/asamk/signal/manager/groups/GroupLinkPassword.java diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupNotFoundException.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupNotFoundException.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/GroupNotFoundException.java rename to lib/src/main/java/org/asamk/signal/manager/groups/GroupNotFoundException.java diff --git a/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/GroupUtils.java rename to lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java diff --git a/src/main/java/org/asamk/signal/manager/groups/NotAGroupMemberException.java b/lib/src/main/java/org/asamk/signal/manager/groups/NotAGroupMemberException.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/groups/NotAGroupMemberException.java rename to lib/src/main/java/org/asamk/signal/manager/groups/NotAGroupMemberException.java diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupAuthorizationProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupAuthorizationProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/GroupAuthorizationProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/GroupAuthorizationProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/GroupHelper.java rename to lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java diff --git a/src/main/java/org/asamk/signal/manager/helper/MessagePipeProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/MessagePipeProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/MessagePipeProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/MessagePipeProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/MessageReceiverProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/PinHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/PinHelper.java rename to lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java diff --git a/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java rename to lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java diff --git a/src/main/java/org/asamk/signal/manager/helper/ProfileKeyCredentialProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileKeyCredentialProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/ProfileKeyCredentialProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/ProfileKeyCredentialProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/ProfileKeyProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileKeyProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/ProfileKeyProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/ProfileKeyProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/ProfileProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/ProfileProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/ProfileProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/SelfAddressProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/SelfAddressProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/SelfAddressProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/SelfAddressProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/SelfProfileKeyProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/SelfProfileKeyProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/SelfProfileKeyProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/SelfProfileKeyProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java rename to lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java diff --git a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessProvider.java diff --git a/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessSenderCertificateProvider.java b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessSenderCertificateProvider.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessSenderCertificateProvider.java rename to lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessSenderCertificateProvider.java diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/SignalAccount.java rename to lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java diff --git a/src/main/java/org/asamk/signal/manager/storage/contacts/ContactInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/contacts/ContactInfo.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/contacts/ContactInfo.java rename to lib/src/main/java/org/asamk/signal/manager/storage/contacts/ContactInfo.java diff --git a/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java rename to lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java rename to lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java rename to lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java diff --git a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java similarity index 99% rename from src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java index f7292596..52b789c7 100644 --- a/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java @@ -17,12 +17,12 @@ import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.util.IOUtils; -import org.asamk.signal.util.Hex; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.groups.GroupMasterKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.whispersystems.libsignal.util.Hex; import java.io.File; import java.io.FileInputStream; diff --git a/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java b/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java rename to lib/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java diff --git a/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java b/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java rename to lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java diff --git a/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java rename to lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java diff --git a/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfileEntry.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfileEntry.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfileEntry.java rename to lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfileEntry.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/SessionInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SessionInfo.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/SessionInfo.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/SessionInfo.java diff --git a/src/main/java/org/asamk/signal/manager/storage/protocol/SignalServiceAddressResolver.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalServiceAddressResolver.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/protocol/SignalServiceAddressResolver.java rename to lib/src/main/java/org/asamk/signal/manager/storage/protocol/SignalServiceAddressResolver.java diff --git a/src/main/java/org/asamk/signal/manager/storage/stickers/Sticker.java b/lib/src/main/java/org/asamk/signal/manager/storage/stickers/Sticker.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/stickers/Sticker.java rename to lib/src/main/java/org/asamk/signal/manager/storage/stickers/Sticker.java diff --git a/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java rename to lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java diff --git a/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java rename to lib/src/main/java/org/asamk/signal/manager/storage/threads/ThreadInfo.java diff --git a/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java rename to lib/src/main/java/org/asamk/signal/manager/util/AttachmentUtils.java diff --git a/src/main/java/org/asamk/signal/manager/util/IOUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/IOUtils.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/util/IOUtils.java rename to lib/src/main/java/org/asamk/signal/manager/util/IOUtils.java diff --git a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java similarity index 93% rename from src/main/java/org/asamk/signal/manager/util/KeyUtils.java rename to lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java index 3285e65a..b5567b57 100644 --- a/src/main/java/org/asamk/signal/manager/util/KeyUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -1,6 +1,5 @@ package org.asamk.signal.manager.util; -import org.asamk.signal.util.RandomUtils; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.IdentityKey; @@ -14,12 +13,15 @@ import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Medium; import org.whispersystems.signalservice.api.kbs.MasterKey; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Base64; import java.util.List; public class KeyUtils { + private static final SecureRandom secureRandom = new SecureRandom(); + private KeyUtils() { } @@ -77,7 +79,7 @@ public class KeyUtils { } public static MasterKey createMasterKey() { - return MasterKey.createNew(RandomUtils.getSecureRandom()); + return MasterKey.createNew(secureRandom); } private static String getSecret(int size) { @@ -87,7 +89,7 @@ public class KeyUtils { public static byte[] getSecretBytes(int size) { byte[] secret = new byte[size]; - RandomUtils.getSecureRandom().nextBytes(secret); + secureRandom.nextBytes(secret); return secret; } } diff --git a/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java rename to lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java diff --git a/src/main/java/org/asamk/signal/manager/util/PinHashing.java b/lib/src/main/java/org/asamk/signal/manager/util/PinHashing.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/util/PinHashing.java rename to lib/src/main/java/org/asamk/signal/manager/util/PinHashing.java diff --git a/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/util/ProfileUtils.java rename to lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java diff --git a/src/main/java/org/asamk/signal/manager/util/StickerUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/util/StickerUtils.java rename to lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java diff --git a/src/main/java/org/asamk/signal/manager/util/Utils.java b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java similarity index 100% rename from src/main/java/org/asamk/signal/manager/util/Utils.java rename to lib/src/main/java/org/asamk/signal/manager/util/Utils.java diff --git a/src/main/resources/org/asamk/signal/manager/ias.store b/lib/src/main/resources/org/asamk/signal/manager/ias.store similarity index 100% rename from src/main/resources/org/asamk/signal/manager/ias.store rename to lib/src/main/resources/org/asamk/signal/manager/ias.store diff --git a/src/main/resources/org/asamk/signal/manager/whisper.store b/lib/src/main/resources/org/asamk/signal/manager/whisper.store similarity index 100% rename from src/main/resources/org/asamk/signal/manager/whisper.store rename to lib/src/main/resources/org/asamk/signal/manager/whisper.store diff --git a/settings.gradle.kts b/settings.gradle.kts index e837999a..eb34b67b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1,2 @@ rootProject.name = "signal-cli" +include("lib") From 4eaec8359423df351a2c978095419e65e8dbac49 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Jan 2021 17:59:50 +0100 Subject: [PATCH 077/124] Extract checkLibVersions task to gradle plugin --- build.gradle.kts | 34 +--------------- buildSrc/build.gradle.kts | 16 ++++++++ .../src/main/kotlin/CheckLibVersionsPlugin.kt | 39 +++++++++++++++++++ lib/build.gradle.kts | 1 + 4 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt diff --git a/build.gradle.kts b/build.gradle.kts index 8a3a8aae..052e3c22 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ plugins { java application eclipse + `check-lib-versions` } version = "0.7.4" @@ -57,39 +58,6 @@ tasks.withType { } } -// Find any 3rd party libraries which have released new versions -// to the central Maven repo since we last upgraded. -val checkLibVersions by tasks.registering { - doLast { - val checked = kotlin.collections.HashSet() - allprojects { - configurations.forEach { configuration -> - configuration.allDependencies.forEach { dependency -> - val version = dependency.version - if (!checked.contains(dependency)) { - val group = dependency.group - val path = group?.replace(".", "/") ?: "" - val name = dependency.name - val metaDataUrl = "https://repo1.maven.org/maven2/$path/$name/maven-metadata.xml" - try { - val url = org.codehaus.groovy.runtime.ResourceGroovyMethods.toURL(metaDataUrl) - val metaDataText = org.codehaus.groovy.runtime.ResourceGroovyMethods.getText(url) - val metadata = groovy.util.XmlSlurper().parseText(metaDataText) - val newest = (metadata.getProperty("versioning") as groovy.util.slurpersupport.GPathResult).getProperty("latest") - if (version != newest.toString()) { - println("UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}") - } - } catch (e: Throwable) { - logger.debug("Unable to download or parse $metaDataUrl: $e.message") - } - checked.add(dependency) - } - } - } - } - } -} - val assembleNativeImage by tasks.registering { dependsOn("assemble") diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..1d88ae7d --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + `kotlin-dsl` +} + +repositories { + jcenter() +} + +gradlePlugin { + plugins { + register("check-lib-versions") { + id = "check-lib-versions" + implementationClass = "CheckLibVersionsPlugin" + } + } +} diff --git a/buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt b/buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt new file mode 100644 index 00000000..701bd6e3 --- /dev/null +++ b/buildSrc/src/main/kotlin/CheckLibVersionsPlugin.kt @@ -0,0 +1,39 @@ +import groovy.util.XmlSlurper +import groovy.util.slurpersupport.GPathResult +import org.codehaus.groovy.runtime.ResourceGroovyMethods +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.artifacts.Dependency + +class CheckLibVersionsPlugin : Plugin { + override fun apply(project: Project) { + project.task("checkLibVersions") { + description = "Find any 3rd party libraries which have released new versions to the central Maven repo since we last upgraded." + doLast { + project.configurations.flatMap { it.allDependencies } + .toSet() + .forEach { checkDependency(it) } + } + } + } + + private fun Task.checkDependency(dependency: Dependency) { + val version = dependency.version + val group = dependency.group + val path = group?.replace(".", "/") ?: "" + val name = dependency.name + val metaDataUrl = "https://repo1.maven.org/maven2/$path/$name/maven-metadata.xml" + try { + val url = ResourceGroovyMethods.toURL(metaDataUrl) + val metaDataText = ResourceGroovyMethods.getText(url) + val metadata = XmlSlurper().parseText(metaDataText) + val newest = (metadata.getProperty("versioning") as GPathResult).getProperty("latest") + if (version != newest.toString()) { + println("UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}") + } + } catch (e: Throwable) { + logger.debug("Unable to download or parse $metaDataUrl: $e.message") + } + } +} diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 5eb6c9b4..c5d46c4f 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,5 +1,6 @@ plugins { `java-library` + `check-lib-versions` } java { From 7d802fb8c5152c5fc089052e5915c7f588057e53 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 23 Jan 2021 23:06:58 +0100 Subject: [PATCH 078/124] Refactor ServiceConfig and add sandbox config --- .../org/asamk/signal/manager/Manager.java | 50 ++++--- .../signal/manager/ProvisioningManager.java | 30 +++- .../signal/manager/RegistrationManager.java | 50 +++++-- .../asamk/signal/manager/ServiceConfig.java | 141 ------------------ .../manager/{ => config}/IasTrustStore.java | 2 +- .../manager/config/KeyBackupConfig.java | 26 ++++ .../signal/manager/config/LiveConfig.java | 82 ++++++++++ .../signal/manager/config/SandboxConfig.java | 82 ++++++++++ .../signal/manager/config/ServiceConfig.java | 85 +++++++++++ .../manager/config/ServiceEnvironment.java | 6 + .../config/ServiceEnvironmentConfig.java | 43 ++++++ .../{ => config}/WhisperTrustStore.java | 2 +- .../signal/manager/{ => config}/ias.store | Bin .../signal/manager/{ => config}/whisper.store | Bin src/main/java/org/asamk/signal/App.java | 37 +++-- 15 files changed, 433 insertions(+), 203 deletions(-) delete mode 100644 lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java rename lib/src/main/java/org/asamk/signal/manager/{ => config}/IasTrustStore.java (89%) create mode 100644 lib/src/main/java/org/asamk/signal/manager/config/KeyBackupConfig.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironment.java create mode 100644 lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java rename lib/src/main/java/org/asamk/signal/manager/{ => config}/WhisperTrustStore.java (90%) rename lib/src/main/resources/org/asamk/signal/manager/{ => config}/ias.store (100%) rename lib/src/main/resources/org/asamk/signal/manager/{ => config}/whisper.store (100%) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index c4f32460..11ee137d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -16,6 +16,9 @@ */ package org.asamk.signal.manager; +import org.asamk.signal.manager.config.ServiceConfig; +import org.asamk.signal.manager.config.ServiceEnvironment; +import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.groups.GroupIdV2; @@ -131,7 +134,6 @@ import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.contacts.crypto.Quote; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; @@ -169,17 +171,15 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; -import static org.asamk.signal.manager.ServiceConfig.CDS_MRENCLAVE; -import static org.asamk.signal.manager.ServiceConfig.capabilities; -import static org.asamk.signal.manager.ServiceConfig.getIasKeyStore; +import static org.asamk.signal.manager.config.ServiceConfig.capabilities; public class Manager implements Closeable { private final static Logger logger = LoggerFactory.getLogger(Manager.class); - private final CertificateValidator certificateValidator = new CertificateValidator(ServiceConfig.getUnidentifiedSenderTrustRoot()); + private final CertificateValidator certificateValidator; - private final SignalServiceConfiguration serviceConfiguration; + private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; private SignalAccount account; @@ -202,16 +202,17 @@ public class Manager implements Closeable { Manager( SignalAccount account, PathConfig pathConfig, - SignalServiceConfiguration serviceConfiguration, + ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent ) { this.account = account; - this.serviceConfiguration = serviceConfiguration; + this.serviceEnvironmentConfig = serviceEnvironmentConfig; + this.certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot()); this.userAgent = userAgent; this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create( - serviceConfiguration)) : null; + serviceEnvironmentConfig.getSignalServiceConfiguration())) : null; final SleepTimer timer = new UptimeSleepTimer(); - this.accountManager = new SignalServiceAccountManager(serviceConfiguration, + this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider(account.getUuid(), account.getUsername(), account.getPassword(), @@ -222,11 +223,18 @@ public class Manager implements Closeable { ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); this.groupsV2Api = accountManager.getGroupsV2Api(); - final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); + final KeyBackupService keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), + serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), + serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), + serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), + 10); + this.pinHelper = new PinHelper(keyBackupService); - this.clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(serviceConfiguration) - .getProfileOperations() : null; - this.messageReceiver = new SignalServiceMessageReceiver(serviceConfiguration, + this.clientZkProfileOperations = capabilities.isGv2() + ? ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()) + .getProfileOperations() + : null; + this.messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(), account.getUuid(), account.getUsername(), account.getPassword(), @@ -275,7 +283,7 @@ public class Manager implements Closeable { } public static Manager init( - String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent + String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent ) throws IOException, NotRegisteredException { PathConfig pathConfig = PathConfig.createDefault(settingsPath); @@ -289,7 +297,11 @@ public class Manager implements Closeable { throw new NotRegisteredException(); } - return new Manager(account, pathConfig, serviceConfiguration, userAgent); + final ServiceEnvironmentConfig serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig( + serviceEnvironment, + userAgent); + + return new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent); } public static List getAllLocalUsernames(File settingsPath) { @@ -498,7 +510,7 @@ public class Manager implements Closeable { private SignalServiceMessageSender createMessageSender() { final ExecutorService executor = null; - return new SignalServiceMessageSender(serviceConfiguration, + return new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(), account.getUuid(), account.getUsername(), account.getPassword(), @@ -1262,7 +1274,9 @@ public class Manager implements Closeable { private Map getRegisteredUsers(final Set numbersMissingUuid) throws IOException { try { - return accountManager.getRegisteredUsers(getIasKeyStore(), numbersMissingUuid, CDS_MRENCLAVE); + return accountManager.getRegisteredUsers(ServiceConfig.getIasKeyStore(), + numbersMissingUuid, + serviceEnvironmentConfig.getCdsMrenclave()); } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) { throw new IOException(e); } diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 0c26cf77..c01ceabf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -16,6 +16,9 @@ */ package org.asamk.signal.manager; +import org.asamk.signal.manager.config.ServiceConfig; +import org.asamk.signal.manager.config.ServiceEnvironment; +import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; import org.signal.zkgroup.InvalidInputException; @@ -31,7 +34,6 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import java.io.File; @@ -43,7 +45,7 @@ public class ProvisioningManager { private final static Logger logger = LoggerFactory.getLogger(ProvisioningManager.class); private final PathConfig pathConfig; - private final SignalServiceConfiguration serviceConfiguration; + private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; private final SignalServiceAccountManager accountManager; @@ -51,9 +53,9 @@ public class ProvisioningManager { private final int registrationId; private final String password; - public ProvisioningManager(File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent) { - this.pathConfig = PathConfig.createDefault(settingsPath); - this.serviceConfiguration = serviceConfiguration; + ProvisioningManager(PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent) { + this.pathConfig = pathConfig; + this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; identityKey = KeyUtils.generateIdentityKeyPair(); @@ -62,11 +64,11 @@ public class ProvisioningManager { final SleepTimer timer = new UptimeSleepTimer(); GroupsV2Operations groupsV2Operations; try { - groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceConfiguration)); + groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())); } catch (Throwable ignored) { groupsV2Operations = null; } - accountManager = new SignalServiceAccountManager(serviceConfiguration, + accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, @@ -74,6 +76,18 @@ public class ProvisioningManager { timer); } + public static ProvisioningManager init( + File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent + ) { + PathConfig pathConfig = PathConfig.createDefault(settingsPath); + + final ServiceEnvironmentConfig serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig( + serviceEnvironment, + userAgent); + + return new ProvisioningManager(pathConfig, serviceConfiguration, userAgent); + } + public String getDeviceLinkUri() throws TimeoutException, IOException { String deviceUuid = accountManager.getNewDeviceUuid(); @@ -120,7 +134,7 @@ public class ProvisioningManager { profileKey)) { account.save(); - try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) { + try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { try { m.refreshPreKeys(); diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 506948ba..914a5fcf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -16,6 +16,9 @@ */ package org.asamk.signal.manager; +import org.asamk.signal.manager.config.ServiceConfig; +import org.asamk.signal.manager.config.ServiceEnvironment; +import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.helper.PinHelper; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; @@ -28,11 +31,12 @@ import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.SignalServiceAccountManager; +import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; +import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.SleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UuidUtil; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; @@ -46,7 +50,7 @@ public class RegistrationManager implements Closeable { private SignalAccount account; private final PathConfig pathConfig; - private final SignalServiceConfiguration serviceConfiguration; + private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; private final SignalServiceAccountManager accountManager; @@ -55,31 +59,49 @@ public class RegistrationManager implements Closeable { public RegistrationManager( SignalAccount account, PathConfig pathConfig, - SignalServiceConfiguration serviceConfiguration, + ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent ) { this.account = account; this.pathConfig = pathConfig; - this.serviceConfiguration = serviceConfiguration; + this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; final SleepTimer timer = new UptimeSleepTimer(); - this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider( - // Using empty UUID, because registering doesn't work otherwise - null, - account.getUsername(), - account.getPassword(), - account.getSignalingKey(), - SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); - final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager); + GroupsV2Operations groupsV2Operations; + try { + groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())); + } catch (Throwable ignored) { + groupsV2Operations = null; + } + this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), + new DynamicCredentialsProvider( + // Using empty UUID, because registering doesn't work otherwise + null, + account.getUsername(), + account.getPassword(), + account.getSignalingKey(), + SignalServiceAddress.DEFAULT_DEVICE_ID), + userAgent, + groupsV2Operations, + ServiceConfig.AUTOMATIC_NETWORK_RETRY, + timer); + final KeyBackupService keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), + serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), + serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), + serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), + 10); this.pinHelper = new PinHelper(keyBackupService); } public static RegistrationManager init( - String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent + String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent ) throws IOException { PathConfig pathConfig = PathConfig.createDefault(settingsPath); + final ServiceEnvironmentConfig serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig( + serviceEnvironment, + userAgent); if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair(); int registrationId = KeyHelper.generateRegistrationId(false); @@ -159,7 +181,7 @@ public class RegistrationManager implements Closeable { account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(), TrustLevel.TRUSTED_VERIFIED); - try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) { + try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { m.refreshPreKeys(); diff --git a/lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java deleted file mode 100644 index c68e5773..00000000 --- a/lib/src/main/java/org/asamk/signal/manager/ServiceConfig.java +++ /dev/null @@ -1,141 +0,0 @@ -package org.asamk.signal.manager; - -import org.bouncycastle.util.encoders.Hex; -import org.signal.zkgroup.ServerPublicParams; -import org.whispersystems.libsignal.InvalidKeyException; -import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECPublicKey; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.KeyBackupService; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.account.AccountAttributes; -import org.whispersystems.signalservice.api.push.TrustStore; -import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; -import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl; -import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; -import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl; -import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl; - -import java.io.IOException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.util.Base64; -import java.util.List; -import java.util.Map; - -import okhttp3.Dns; -import okhttp3.Interceptor; - -public class ServiceConfig { - - final static byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() - .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); - final static int PREKEY_MINIMUM_COUNT = 20; - final static int PREKEY_BATCH_SIZE = 100; - final static int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024; - final static long MAX_ENVELOPE_SIZE = 0; - final static long AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE = 10 * 1024 * 1024; - final static boolean AUTOMATIC_NETWORK_RETRY = true; - - final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; - - final static String KEY_BACKUP_ENCLAVE_NAME = "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"; - final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( - "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"); - final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; - - private final static String URL = "https://textsecure-service.whispersystems.org"; - private final static String CDN_URL = "https://cdn.signal.org"; - private final static String CDN2_URL = "https://cdn2.signal.org"; - private final static String SIGNAL_CONTACT_DISCOVERY_URL = "https://api.directory.signal.org"; - private final static String SIGNAL_KEY_BACKUP_URL = "https://api.backup.signal.org"; - private final static String STORAGE_URL = "https://storage.signal.org"; - private final static TrustStore TRUST_STORE = new WhisperTrustStore(); - private final static TrustStore IAS_TRUST_STORE = new IasTrustStore(); - - private final static Optional dns = Optional.absent(); - - private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() - .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0="); - - static final AccountAttributes.Capabilities capabilities; - - static { - boolean zkGroupAvailable; - try { - new ServerPublicParams(zkGroupServerPublicParams); - zkGroupAvailable = true; - } catch (Throwable ignored) { - zkGroupAvailable = false; - } - capabilities = new AccountAttributes.Capabilities(false, zkGroupAvailable, false, zkGroupAvailable); - } - - public static SignalServiceConfiguration createDefaultServiceConfiguration(String userAgent) { - final Interceptor userAgentInterceptor = chain -> chain.proceed(chain.request() - .newBuilder() - .header("User-Agent", userAgent) - .build()); - - final List interceptors = List.of(userAgentInterceptor); - - return new SignalServiceConfiguration(new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)}, - makeSignalCdnUrlMapFor(new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)}, - new SignalCdnUrl[]{new SignalCdnUrl(CDN2_URL, TRUST_STORE)}), - new SignalContactDiscoveryUrl[]{new SignalContactDiscoveryUrl(SIGNAL_CONTACT_DISCOVERY_URL, - TRUST_STORE)}, - new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)}, - new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)}, - interceptors, - dns, - zkGroupServerPublicParams); - } - - public static AccountAttributes.Capabilities getCapabilities() { - return capabilities; - } - - static KeyStore getIasKeyStore() { - try { - TrustStore contactTrustStore = IAS_TRUST_STORE; - - KeyStore keyStore = KeyStore.getInstance("BKS"); - keyStore.load(contactTrustStore.getKeyStoreInputStream(), - contactTrustStore.getKeyStorePassword().toCharArray()); - - return keyStore; - } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) { - throw new AssertionError(e); - } - } - - static KeyBackupService createKeyBackupService(SignalServiceAccountManager accountManager) { - KeyStore keyStore = ServiceConfig.getIasKeyStore(); - - return accountManager.getKeyBackupService(keyStore, - ServiceConfig.KEY_BACKUP_ENCLAVE_NAME, - ServiceConfig.KEY_BACKUP_SERVICE_ID, - ServiceConfig.KEY_BACKUP_MRENCLAVE, - 10); - } - - static ECPublicKey getUnidentifiedSenderTrustRoot() { - try { - return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0); - } catch (InvalidKeyException e) { - throw new AssertionError(e); - } - } - - private static Map makeSignalCdnUrlMapFor( - SignalCdnUrl[] cdn0Urls, SignalCdnUrl[] cdn2Urls - ) { - return Map.of(0, cdn0Urls, 2, cdn2Urls); - } - - private ServiceConfig() { - } -} diff --git a/lib/src/main/java/org/asamk/signal/manager/IasTrustStore.java b/lib/src/main/java/org/asamk/signal/manager/config/IasTrustStore.java similarity index 89% rename from lib/src/main/java/org/asamk/signal/manager/IasTrustStore.java rename to lib/src/main/java/org/asamk/signal/manager/config/IasTrustStore.java index f9bbb0b3..4d70809b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/IasTrustStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/IasTrustStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.config; import org.whispersystems.signalservice.api.push.TrustStore; diff --git a/lib/src/main/java/org/asamk/signal/manager/config/KeyBackupConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/KeyBackupConfig.java new file mode 100644 index 00000000..60173c1d --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/KeyBackupConfig.java @@ -0,0 +1,26 @@ +package org.asamk.signal.manager.config; + +public class KeyBackupConfig { + + private final String enclaveName; + private final byte[] serviceId; + private final String mrenclave; + + public KeyBackupConfig(final String enclaveName, final byte[] serviceId, final String mrenclave) { + this.enclaveName = enclaveName; + this.serviceId = serviceId; + this.mrenclave = mrenclave; + } + + public String getEnclaveName() { + return enclaveName; + } + + public byte[] getServiceId() { + return serviceId; + } + + public String getMrenclave() { + return mrenclave; + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java new file mode 100644 index 00000000..80df8bbb --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -0,0 +1,82 @@ +package org.asamk.signal.manager.config; + +import org.bouncycastle.util.encoders.Hex; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.push.TrustStore; +import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; +import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl; +import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl; +import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; +import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl; +import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl; + +import java.util.Base64; +import java.util.List; +import java.util.Map; + +import okhttp3.Dns; +import okhttp3.Interceptor; + +class LiveConfig { + + private final static byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() + .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); + private final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; + + private final static String KEY_BACKUP_ENCLAVE_NAME = "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"; + private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( + "fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe"); + private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; + + private final static String URL = "https://textsecure-service.whispersystems.org"; + private final static String CDN_URL = "https://cdn.signal.org"; + private final static String CDN2_URL = "https://cdn2.signal.org"; + private final static String SIGNAL_CONTACT_DISCOVERY_URL = "https://api.directory.signal.org"; + private final static String SIGNAL_KEY_BACKUP_URL = "https://api.backup.signal.org"; + private final static String STORAGE_URL = "https://storage.signal.org"; + private final static TrustStore TRUST_STORE = new WhisperTrustStore(); + + private final static Optional dns = Optional.absent(); + + private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() + .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0="); + + static SignalServiceConfiguration createDefaultServiceConfiguration( + final List interceptors + ) { + return new SignalServiceConfiguration(new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)}, + Map.of(0, + new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)}, + 2, + new SignalCdnUrl[]{new SignalCdnUrl(CDN2_URL, TRUST_STORE)}), + new SignalContactDiscoveryUrl[]{new SignalContactDiscoveryUrl(SIGNAL_CONTACT_DISCOVERY_URL, + TRUST_STORE)}, + new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)}, + new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)}, + interceptors, + dns, + zkGroupServerPublicParams); + } + + static ECPublicKey getUnidentifiedSenderTrustRoot() { + try { + return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + static KeyBackupConfig createKeyBackupConfig() { + return new KeyBackupConfig(KEY_BACKUP_ENCLAVE_NAME, KEY_BACKUP_SERVICE_ID, KEY_BACKUP_MRENCLAVE); + } + + static String getCdsMrenclave() { + return CDS_MRENCLAVE; + } + + private LiveConfig() { + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java new file mode 100644 index 00000000..edb180ac --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java @@ -0,0 +1,82 @@ +package org.asamk.signal.manager.config; + +import org.bouncycastle.util.encoders.Hex; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.push.TrustStore; +import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; +import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl; +import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl; +import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; +import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl; +import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl; + +import java.util.Base64; +import java.util.List; +import java.util.Map; + +import okhttp3.Dns; +import okhttp3.Interceptor; + +class SandboxConfig { + + private final static byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() + .decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"); + private final static String CDS_MRENCLAVE = "c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15"; + + private final static String KEY_BACKUP_ENCLAVE_NAME = "823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9"; + private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( + "038c40bbbacdc873caa81ac793bb75afde6dfe436a99ab1f15e3f0cbb7434ced"); + private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; + + private final static String URL = "https://textsecure-service-staging.whispersystems.org"; + private final static String CDN_URL = "https://cdn-staging.signal.org"; + private final static String CDN2_URL = "https://cdn2-staging.signal.org"; + private final static String SIGNAL_CONTACT_DISCOVERY_URL = "https://api-staging.directory.signal.org"; + private final static String SIGNAL_KEY_BACKUP_URL = "https://api-staging.backup.signal.org"; + private final static String STORAGE_URL = "https://storage-staging.signal.org"; + private final static TrustStore TRUST_STORE = new WhisperTrustStore(); + + private final static Optional dns = Optional.absent(); + + private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() + .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdls="); + + static SignalServiceConfiguration createDefaultServiceConfiguration( + final List interceptors + ) { + return new SignalServiceConfiguration(new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)}, + Map.of(0, + new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)}, + 2, + new SignalCdnUrl[]{new SignalCdnUrl(CDN2_URL, TRUST_STORE)}), + new SignalContactDiscoveryUrl[]{new SignalContactDiscoveryUrl(SIGNAL_CONTACT_DISCOVERY_URL, + TRUST_STORE)}, + new SignalKeyBackupServiceUrl[]{new SignalKeyBackupServiceUrl(SIGNAL_KEY_BACKUP_URL, TRUST_STORE)}, + new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)}, + interceptors, + dns, + zkGroupServerPublicParams); + } + + static ECPublicKey getUnidentifiedSenderTrustRoot() { + try { + return Curve.decodePoint(UNIDENTIFIED_SENDER_TRUST_ROOT, 0); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + static KeyBackupConfig createKeyBackupConfig() { + return new KeyBackupConfig(KEY_BACKUP_ENCLAVE_NAME, KEY_BACKUP_SERVICE_ID, KEY_BACKUP_MRENCLAVE); + } + + static String getCdsMrenclave() { + return CDS_MRENCLAVE; + } + + private SandboxConfig() { + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java new file mode 100644 index 00000000..7a54edd1 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -0,0 +1,85 @@ +package org.asamk.signal.manager.config; + +import org.signal.zkgroup.internal.Native; +import org.whispersystems.signalservice.api.account.AccountAttributes; +import org.whispersystems.signalservice.api.push.TrustStore; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.List; + +import okhttp3.Interceptor; + +public class ServiceConfig { + + public final static int PREKEY_MINIMUM_COUNT = 20; + public final static int PREKEY_BATCH_SIZE = 100; + public final static int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024; + public final static long MAX_ENVELOPE_SIZE = 0; + public final static long AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE = 10 * 1024 * 1024; + public final static boolean AUTOMATIC_NETWORK_RETRY = true; + + private final static KeyStore iasKeyStore; + + public static final AccountAttributes.Capabilities capabilities; + + static { + boolean zkGroupAvailable; + try { + Native.serverPublicParamsCheckValidContentsJNI(new byte[]{}); + zkGroupAvailable = true; + } catch (Throwable ignored) { + zkGroupAvailable = false; + } + capabilities = new AccountAttributes.Capabilities(false, zkGroupAvailable, false, zkGroupAvailable); + + try { + TrustStore contactTrustStore = new IasTrustStore(); + + KeyStore keyStore = KeyStore.getInstance("BKS"); + keyStore.load(contactTrustStore.getKeyStoreInputStream(), + contactTrustStore.getKeyStorePassword().toCharArray()); + + iasKeyStore = keyStore; + } catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + } + + public static AccountAttributes.Capabilities getCapabilities() { + return capabilities; + } + + public static KeyStore getIasKeyStore() { + return iasKeyStore; + } + + public static ServiceEnvironmentConfig getServiceEnvironmentConfig( + ServiceEnvironment serviceEnvironment, String userAgent + ) { + final Interceptor userAgentInterceptor = chain -> chain.proceed(chain.request() + .newBuilder() + .header("User-Agent", userAgent) + .build()); + + final List interceptors = List.of(userAgentInterceptor); + + switch (serviceEnvironment) { + case LIVE: + return new ServiceEnvironmentConfig(LiveConfig.createDefaultServiceConfiguration(interceptors), + LiveConfig.getUnidentifiedSenderTrustRoot(), + LiveConfig.createKeyBackupConfig(), + LiveConfig.getCdsMrenclave()); + case SANDBOX: + return new ServiceEnvironmentConfig(SandboxConfig.createDefaultServiceConfiguration(interceptors), + SandboxConfig.getUnidentifiedSenderTrustRoot(), + SandboxConfig.createKeyBackupConfig(), + SandboxConfig.getCdsMrenclave()); + default: + throw new IllegalArgumentException("Unsupported environment"); + } + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironment.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironment.java new file mode 100644 index 00000000..142a2dd4 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironment.java @@ -0,0 +1,6 @@ +package org.asamk.signal.manager.config; + +public enum ServiceEnvironment { + LIVE, + SANDBOX, +} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java new file mode 100644 index 00000000..e64472a0 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java @@ -0,0 +1,43 @@ +package org.asamk.signal.manager.config; + +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; + +public class ServiceEnvironmentConfig { + + private final SignalServiceConfiguration signalServiceConfiguration; + + private final ECPublicKey unidentifiedSenderTrustRoot; + + private final KeyBackupConfig keyBackupConfig; + + private final String cdsMrenclave; + + public ServiceEnvironmentConfig( + final SignalServiceConfiguration signalServiceConfiguration, + final ECPublicKey unidentifiedSenderTrustRoot, + final KeyBackupConfig keyBackupConfig, + final String cdsMrenclave + ) { + this.signalServiceConfiguration = signalServiceConfiguration; + this.unidentifiedSenderTrustRoot = unidentifiedSenderTrustRoot; + this.keyBackupConfig = keyBackupConfig; + this.cdsMrenclave = cdsMrenclave; + } + + public SignalServiceConfiguration getSignalServiceConfiguration() { + return signalServiceConfiguration; + } + + public ECPublicKey getUnidentifiedSenderTrustRoot() { + return unidentifiedSenderTrustRoot; + } + + public KeyBackupConfig getKeyBackupConfig() { + return keyBackupConfig; + } + + public String getCdsMrenclave() { + return cdsMrenclave; + } +} diff --git a/lib/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java b/lib/src/main/java/org/asamk/signal/manager/config/WhisperTrustStore.java similarity index 90% rename from lib/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java rename to lib/src/main/java/org/asamk/signal/manager/config/WhisperTrustStore.java index 185ab599..7add1d79 100644 --- a/lib/src/main/java/org/asamk/signal/manager/WhisperTrustStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/WhisperTrustStore.java @@ -1,4 +1,4 @@ -package org.asamk.signal.manager; +package org.asamk.signal.manager.config; import org.whispersystems.signalservice.api.push.TrustStore; diff --git a/lib/src/main/resources/org/asamk/signal/manager/ias.store b/lib/src/main/resources/org/asamk/signal/manager/config/ias.store similarity index 100% rename from lib/src/main/resources/org/asamk/signal/manager/ias.store rename to lib/src/main/resources/org/asamk/signal/manager/config/ias.store diff --git a/lib/src/main/resources/org/asamk/signal/manager/whisper.store b/lib/src/main/resources/org/asamk/signal/manager/config/whisper.store similarity index 100% rename from lib/src/main/resources/org/asamk/signal/manager/whisper.store rename to lib/src/main/resources/org/asamk/signal/manager/config/whisper.store diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 6dc3d799..e35285c3 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -21,14 +21,14 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.RegistrationManager; -import org.asamk.signal.manager.ServiceConfig; +import org.asamk.signal.manager.config.ServiceConfig; +import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.util.IOUtils; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; -import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import java.io.File; import java.io.IOException; @@ -114,8 +114,7 @@ public class App { dataPath = getDefaultDataPath(); } - final SignalServiceConfiguration serviceConfiguration = ServiceConfig.createDefaultServiceConfiguration( - BaseConfig.USER_AGENT); + final ServiceEnvironment serviceEnvironment = ServiceEnvironment.LIVE; if (!ServiceConfig.getCapabilities().isGv2()) { logger.warn("WARNING: Support for new group V2 is disabled," @@ -128,7 +127,7 @@ public class App { return 1; } - return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceConfiguration); + return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment); } if (username == null) { @@ -139,7 +138,7 @@ public class App { } if (command instanceof MultiLocalCommand) { - return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceConfiguration, usernames); + return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceEnvironment, usernames); } if (usernames.size() > 1) { @@ -154,7 +153,7 @@ public class App { } if (command instanceof RegistrationCommand) { - return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceConfiguration); + return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment); } if (!(command instanceof LocalCommand)) { @@ -162,15 +161,13 @@ public class App { return 1; } - return handleLocalCommand((LocalCommand) command, username, dataPath, serviceConfiguration); + return handleLocalCommand((LocalCommand) command, username, dataPath, serviceEnvironment); } private int handleProvisioningCommand( - final ProvisioningCommand command, - final File dataPath, - final SignalServiceConfiguration serviceConfiguration + final ProvisioningCommand command, final File dataPath, final ServiceEnvironment serviceEnvironment ) { - ProvisioningManager pm = new ProvisioningManager(dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + ProvisioningManager pm = ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); return command.handleCommand(ns, pm); } @@ -178,11 +175,11 @@ public class App { final RegistrationCommand command, final String username, final File dataPath, - final SignalServiceConfiguration serviceConfiguration + final ServiceEnvironment serviceEnvironment ) { final RegistrationManager manager; try { - manager = RegistrationManager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + manager = RegistrationManager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (Throwable e) { logger.error("Error loading or creating state file: {}", e.getMessage()); return 2; @@ -199,9 +196,9 @@ public class App { final LocalCommand command, final String username, final File dataPath, - final SignalServiceConfiguration serviceConfiguration + final ServiceEnvironment serviceEnvironment ) { - try (Manager m = loadManager(username, dataPath, serviceConfiguration)) { + try (Manager m = loadManager(username, dataPath, serviceEnvironment)) { if (m == null) { return 2; } @@ -216,11 +213,11 @@ public class App { private int handleMultiLocalCommand( final MultiLocalCommand command, final File dataPath, - final SignalServiceConfiguration serviceConfiguration, + final ServiceEnvironment serviceEnvironment, final List usernames ) { final List managers = usernames.stream() - .map(u -> loadManager(u, dataPath, serviceConfiguration)) + .map(u -> loadManager(u, dataPath, serviceEnvironment)) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -237,11 +234,11 @@ public class App { } private Manager loadManager( - final String username, final File dataPath, final SignalServiceConfiguration serviceConfiguration + final String username, final File dataPath, final ServiceEnvironment serviceEnvironment ) { Manager manager; try { - manager = Manager.init(username, dataPath, serviceConfiguration, BaseConfig.USER_AGENT); + manager = Manager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (NotRegisteredException e) { logger.error("User " + username + " is not registered."); return null; From a811d1a05a28d332705f30528db882a1031e8257 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 24 Jan 2021 00:02:07 +0100 Subject: [PATCH 079/124] Add support for about and aboutEmoji for profiles --- .../org/asamk/signal/manager/Manager.java | 35 ++++++++++++++----- .../storage/profiles/SignalProfile.java | 29 ++++++++++++++- .../signal/manager/util/ProfileUtils.java | 25 ++++++++----- .../signal/commands/UpdateProfileCommand.java | 12 ++++--- 4 files changed, 79 insertions(+), 22 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 11ee137d..be31493d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -362,22 +362,33 @@ public class Manager implements Closeable { } /** - * @param avatar if avatar is null the image from the local avatar store is used (if present), - * if it's Optional.absent(), the avatar will be removed + * @param name if null, the previous name will be kept + * @param about if null, the previous about text will be kept + * @param aboutEmoji if null, the previous about emoji will be kept + * @param avatar if avatar is null the image from the local avatar store is used (if present), + * if it's Optional.absent(), the avatar will be removed */ - public void setProfile(String name, Optional avatar) throws IOException { - // TODO - String about = null; - String aboutEmoji = null; + public void setProfile(String name, String about, String aboutEmoji, Optional avatar) throws IOException { + SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(getSelfAddress()); + SignalProfile profile = profileEntry == null ? null : profileEntry.getProfile(); + SignalProfile newProfile = new SignalProfile(profile == null ? null : profile.getIdentityKey(), + name != null ? name : profile == null || profile.getName() == null ? "" : profile.getName(), + about != null ? about : profile == null || profile.getAbout() == null ? "" : profile.getAbout(), + aboutEmoji != null + ? aboutEmoji + : profile == null || profile.getAboutEmoji() == null ? "" : profile.getAboutEmoji(), + profile == null ? null : profile.getUnidentifiedAccess(), + account.isUnrestrictedUnidentifiedAccess(), + profile == null ? null : profile.getCapabilities()); try (final StreamDetails streamDetails = avatar == null ? avatarStore.retrieveProfileAvatar(getSelfAddress()) : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) { accountManager.setVersionedProfile(account.getUuid(), account.getProfileKey(), - name, - about, - aboutEmoji, + newProfile.getName(), + newProfile.getAbout(), + newProfile.getAboutEmoji(), streamDetails); } @@ -389,6 +400,12 @@ public class Manager implements Closeable { avatarStore.deleteProfileAvatar(getSelfAddress()); } } + account.getProfileStore() + .updateProfile(getSelfAddress(), + account.getProfileKey(), + System.currentTimeMillis(), + newProfile, + profileEntry == null ? null : profileEntry.getProfileKeyCredential()); try { sendSyncMessage(SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE)); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java index 1ec2eeaa..6a761c29 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java @@ -13,6 +13,12 @@ public class SignalProfile { @JsonProperty private final String name; + @JsonProperty + private final String about; + + @JsonProperty + private final String aboutEmoji; + @JsonProperty private final String unidentifiedAccess; @@ -25,12 +31,16 @@ public class SignalProfile { public SignalProfile( final String identityKey, final String name, + final String about, + final String aboutEmoji, final String unidentifiedAccess, final boolean unrestrictedUnidentifiedAccess, final SignalServiceProfile.Capabilities capabilities ) { this.identityKey = identityKey; this.name = name; + this.about = about; + this.aboutEmoji = aboutEmoji; this.unidentifiedAccess = unidentifiedAccess; this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess; this.capabilities = new Capabilities(); @@ -42,12 +52,16 @@ public class SignalProfile { public SignalProfile( @JsonProperty("identityKey") final String identityKey, @JsonProperty("name") final String name, + @JsonProperty("about") final String about, + @JsonProperty("aboutEmoji") final String aboutEmoji, @JsonProperty("unidentifiedAccess") final String unidentifiedAccess, @JsonProperty("unrestrictedUnidentifiedAccess") final boolean unrestrictedUnidentifiedAccess, @JsonProperty("capabilities") final Capabilities capabilities ) { this.identityKey = identityKey; this.name = name; + this.about = about; + this.aboutEmoji = aboutEmoji; this.unidentifiedAccess = unidentifiedAccess; this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess; this.capabilities = capabilities; @@ -61,6 +75,14 @@ public class SignalProfile { return name; } + public String getAbout() { + return about; + } + + public String getAboutEmoji() { + return aboutEmoji; + } + public String getUnidentifiedAccess() { return unidentifiedAccess; } @@ -82,7 +104,12 @@ public class SignalProfile { + ", name='" + name + '\'' - + ", avatarFile=" + + ", about='" + + about + + '\'' + + ", aboutEmoji='" + + aboutEmoji + + '\'' + ", unidentifiedAccess='" + unidentifiedAccess + '\'' diff --git a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java index b91e864a..11362444 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java @@ -15,14 +15,9 @@ public class ProfileUtils { ) { ProfileCipher profileCipher = new ProfileCipher(profileKey); try { - String name; - try { - name = encryptedProfile.getName() == null - ? null - : new String(profileCipher.decryptName(Base64.getDecoder().decode(encryptedProfile.getName()))); - } catch (IllegalArgumentException e) { - name = null; - } + String name = decryptName(encryptedProfile.getName(), profileCipher); + String about = decryptName(encryptedProfile.getAbout(), profileCipher); + String aboutEmoji = decryptName(encryptedProfile.getAboutEmoji(), profileCipher); String unidentifiedAccess; try { unidentifiedAccess = encryptedProfile.getUnidentifiedAccess() == null @@ -35,6 +30,8 @@ public class ProfileUtils { } return new SignalProfile(encryptedProfile.getIdentityKey(), name, + about, + aboutEmoji, unidentifiedAccess, encryptedProfile.isUnrestrictedUnidentifiedAccess(), encryptedProfile.getCapabilities()); @@ -42,4 +39,16 @@ public class ProfileUtils { return null; } } + + private static String decryptName( + final String encryptedName, final ProfileCipher profileCipher + ) throws InvalidCiphertextException { + try { + return encryptedName == null + ? null + : new String(profileCipher.decryptName(Base64.getDecoder().decode(encryptedName))); + } catch (IllegalArgumentException e) { + return null; + } + } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index c2ff2e5e..35190fe8 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -15,18 +15,22 @@ public class UpdateProfileCommand implements LocalCommand { @Override public void attachToSubparser(final Subparser subparser) { + subparser.addArgument("--name").help("New profile name"); + subparser.addArgument("--about").help("New profile about text"); + subparser.addArgument("--about-emoji").help("New profile about emoji"); + final MutuallyExclusiveGroup avatarOptions = subparser.addMutuallyExclusiveGroup(); avatarOptions.addArgument("--avatar").help("Path to new profile avatar"); avatarOptions.addArgument("--remove-avatar").action(Arguments.storeTrue()); - subparser.addArgument("--name").required(true).help("New profile name"); - - subparser.help("Set a name and avatar image for the user profile"); + subparser.help("Set a name, about and avatar image for the user profile"); } @Override public int handleCommand(final Namespace ns, final Manager m) { String name = ns.getString("name"); + String about = ns.getString("about"); + String aboutEmoji = ns.getString("about_emoji"); String avatarPath = ns.getString("avatar"); boolean removeAvatar = ns.getBoolean("remove_avatar"); @@ -34,7 +38,7 @@ public class UpdateProfileCommand implements LocalCommand { Optional avatarFile = removeAvatar ? Optional.absent() : avatarPath == null ? null : Optional.of(new File(avatarPath)); - m.setProfile(name, avatarFile); + m.setProfile(name, about, aboutEmoji, avatarFile); } catch (IOException e) { System.err.println("UpdateAccount error: " + e.getMessage()); return 3; From c276055302235feeff73d7dfd142b54dd378bad4 Mon Sep 17 00:00:00 2001 From: Michael Kebe Date: Wed, 27 Jan 2021 16:35:29 +0100 Subject: [PATCH 080/124] GraalVM configuration update (#477) * graalvm configuration update removed unnecessary classes from reflect added libsignal_jni.so to resource-config.json graalvm configuration changed location of ias.store and whisper.store * graalvm configuration fixes receiving messages * graalvm config setPin * graalvm config quitGroup * graalvm config trust * graalvm config more receive failed to decrypt error * graalvm config --output=json * graalvm config JsonTypingMessage --- graalvm-config-dir/jni-config.json | 74 ++++ graalvm-config-dir/reflect-config.json | 357 +++++++++---------- graalvm-config-dir/resource-config.json | 11 +- graalvm-config-dir/serialization-config.json | 2 + 4 files changed, 243 insertions(+), 201 deletions(-) create mode 100644 graalvm-config-dir/serialization-config.json diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index 724387de..abb23704 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -3,11 +3,85 @@ "name":"java.lang.ClassLoader", "methods":[{"name":"getPlatformClassLoader","parameterTypes":[] }] }, +{ + "name":"java.lang.IllegalStateException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"java.lang.NoSuchMethodError" }, { "name":"java.lang.UnsatisfiedLinkError", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"org.asamk.signal.manager.storage.protocol.JsonSignalProtocolStore", + "methods":[ + {"name":"getIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress"] }, + {"name":"getIdentityKeyPair","parameterTypes":[] }, + {"name":"getLocalRegistrationId","parameterTypes":[] }, + {"name":"isTrustedIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.IdentityKey","org.whispersystems.libsignal.state.IdentityKeyStore$Direction"] }, + {"name":"loadSession","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress"] }, + {"name":"loadSignedPreKey","parameterTypes":["int"] }, + {"name":"saveIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.IdentityKey"] }, + {"name":"storeSession","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.state.SessionRecord"] } + ] +}, +{ + "name":"org.whispersystems.libsignal.IdentityKey", + "methods":[ + {"name":"","parameterTypes":["byte[]"] }, + {"name":"serialize","parameterTypes":[] } + ] +}, +{ + "name":"org.whispersystems.libsignal.IdentityKeyPair", + "methods":[{"name":"serialize","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.libsignal.InvalidMessageException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"org.whispersystems.libsignal.SignalProtocolAddress", + "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }] +}, +{ + "name":"org.whispersystems.libsignal.protocol.PreKeySignalMessage", + "methods":[{"name":"","parameterTypes":["long"] }] +}, +{ + "name":"org.whispersystems.libsignal.protocol.SignalMessage", + "methods":[{"name":"","parameterTypes":["long"] }] +}, +{ + "name":"org.whispersystems.libsignal.state.IdentityKeyStore" +}, +{ + "name":"org.whispersystems.libsignal.state.IdentityKeyStore$Direction", + "fields":[ + {"name":"RECEIVING"}, + {"name":"SENDING"} + ] +}, +{ + "name":"org.whispersystems.libsignal.state.PreKeyStore" +}, +{ + "name":"org.whispersystems.libsignal.state.SessionRecord", + "methods":[ + {"name":"","parameterTypes":["byte[]"] }, + {"name":"nativeHandle","parameterTypes":[] } + ] +}, +{ + "name":"org.whispersystems.libsignal.state.SessionStore" +}, +{ + "name":"org.whispersystems.libsignal.state.SignedPreKeyRecord", + "methods":[{"name":"nativeHandle","parameterTypes":[] }] +}, +{ + "name":"org.whispersystems.libsignal.state.SignedPreKeyStore" } ] diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index ca62ca59..e2d5ad03 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -227,10 +227,6 @@ "name":"jnr.enxio.channels.Native$LibC", "allPublicMethods":true }, -{ - "name":"jnr.enxio.channels.Native$LibC$jnr$ffi$1", - "methods":[{"name":"","parameterTypes":["jnr.ffi.Runtime","jnr.ffi.provider.jffi.NativeLibrary","java.lang.Object[]"] }] -}, { "name":"jnr.ffi.Pointer", "allDeclaredMethods":true, @@ -284,10 +280,6 @@ "name":"jnr.unixsocket.Native$LibC", "allPublicMethods":true }, -{ - "name":"jnr.unixsocket.Native$LibC$jnr$ffi$0", - "methods":[{"name":"","parameterTypes":["jnr.ffi.Runtime","jnr.ffi.provider.jffi.NativeLibrary","java.lang.Object[]"] }] -}, { "name":"jnr.unixsocket.SockAddrUnix", "allPublicMethods":true @@ -326,6 +318,12 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.json.JsonError", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonMessageEnvelope", "allDeclaredFields":true, @@ -344,6 +342,12 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.json.JsonTypingMessage", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.manager.storage.contacts.ContactInfo", "allDeclaredFields":true, @@ -876,60 +880,6 @@ { "name":"org.objectweb.asm.util.TraceMethodVisitor" }, -{ - "name":"org.signal.libsignal.metadata.SignalProtos$SenderCertificate", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"certificate_", "allowUnsafeAccess":true}, - {"name":"signature_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.signal.libsignal.metadata.SignalProtos$SenderCertificate$Certificate", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"expires_", "allowUnsafeAccess":true}, - {"name":"identityKey_", "allowUnsafeAccess":true}, - {"name":"senderDevice_", "allowUnsafeAccess":true}, - {"name":"senderE164_", "allowUnsafeAccess":true}, - {"name":"senderUuid_", "allowUnsafeAccess":true}, - {"name":"signer_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.signal.libsignal.metadata.SignalProtos$ServerCertificate", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"certificate_", "allowUnsafeAccess":true}, - {"name":"signature_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.signal.libsignal.metadata.SignalProtos$ServerCertificate$Certificate", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"key_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.signal.libsignal.metadata.SignalProtos$UnidentifiedSenderMessage", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"encryptedMessage_", "allowUnsafeAccess":true}, - {"name":"encryptedStatic_", "allowUnsafeAccess":true}, - {"name":"ephemeralPublic_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.signal.libsignal.metadata.SignalProtos$UnidentifiedSenderMessage$Message", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"content_", "allowUnsafeAccess":true}, - {"name":"senderCertificate_", "allowUnsafeAccess":true}, - {"name":"type_", "allowUnsafeAccess":true} - ] -}, { "name":"org.signal.storageservice.protos.groups.AccessControl", "fields":[ @@ -960,6 +910,87 @@ {"name":"content_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange", + "fields":[ + {"name":"actions_", "allowUnsafeAccess":true}, + {"name":"changeEpoch_", "allowUnsafeAccess":true}, + {"name":"serverSignature_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions", + "fields":[ + {"name":"addMembers_", "allowUnsafeAccess":true}, + {"name":"addPendingMembers_", "allowUnsafeAccess":true}, + {"name":"addRequestingMembers_", "allowUnsafeAccess":true}, + {"name":"deleteMembers_", "allowUnsafeAccess":true}, + {"name":"deletePendingMembers_", "allowUnsafeAccess":true}, + {"name":"deleteRequestingMembers_", "allowUnsafeAccess":true}, + {"name":"modifyAddFromInviteLinkAccess_", "allowUnsafeAccess":true}, + {"name":"modifyAttributesAccess_", "allowUnsafeAccess":true}, + {"name":"modifyAvatar_", "allowUnsafeAccess":true}, + {"name":"modifyDisappearingMessagesTimer_", "allowUnsafeAccess":true}, + {"name":"modifyInviteLinkPassword_", "allowUnsafeAccess":true}, + {"name":"modifyMemberAccess_", "allowUnsafeAccess":true}, + {"name":"modifyMemberProfileKeys_", "allowUnsafeAccess":true}, + {"name":"modifyMemberRoles_", "allowUnsafeAccess":true}, + {"name":"modifyTitle_", "allowUnsafeAccess":true}, + {"name":"promotePendingMembers_", "allowUnsafeAccess":true}, + {"name":"promoteRequestingMembers_", "allowUnsafeAccess":true}, + {"name":"revision_", "allowUnsafeAccess":true}, + {"name":"sourceUuid_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddMemberAction", + "fields":[ + {"name":"added_", "allowUnsafeAccess":true}, + {"name":"joinFromInviteLink_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddPendingMemberAction", + "fields":[{"name":"added_", "allowUnsafeAccess":true}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddRequestingMemberAction", + "fields":[{"name":"added_", "allowUnsafeAccess":true}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteMemberAction", + "fields":[{"name":"deletedUserId_", "allowUnsafeAccess":true}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeletePendingMemberAction", + "fields":[{"name":"deletedUserId_", "allowUnsafeAccess":true}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteRequestingMemberAction", + "fields":[{"name":"deletedUserId_", "allowUnsafeAccess":true}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberProfileKeyAction", + "fields":[{"name":"presentation_", "allowUnsafeAccess":true}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberRoleAction", + "fields":[ + {"name":"role_", "allowUnsafeAccess":true}, + {"name":"userId_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingMemberAction", + "fields":[{"name":"presentation_", "allowUnsafeAccess":true}] +}, +{ + "name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromoteRequestingMemberAction", + "fields":[ + {"name":"role_", "allowUnsafeAccess":true}, + {"name":"userId_", "allowUnsafeAccess":true} + ] +}, { "name":"org.signal.storageservice.protos.groups.Member", "fields":[ @@ -1001,6 +1032,30 @@ {"name":"title_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.signal.storageservice.protos.groups.local.DecryptedGroupChange", + "fields":[ + {"name":"deleteMembers_", "allowUnsafeAccess":true}, + {"name":"deletePendingMembers_", "allowUnsafeAccess":true}, + {"name":"deleteRequestingMembers_", "allowUnsafeAccess":true}, + {"name":"editor_", "allowUnsafeAccess":true}, + {"name":"modifiedProfileKeys_", "allowUnsafeAccess":true}, + {"name":"modifyMemberRoles_", "allowUnsafeAccess":true}, + {"name":"newAttributeAccess_", "allowUnsafeAccess":true}, + {"name":"newAvatar_", "allowUnsafeAccess":true}, + {"name":"newInviteLinkAccess_", "allowUnsafeAccess":true}, + {"name":"newInviteLinkPassword_", "allowUnsafeAccess":true}, + {"name":"newMemberAccess_", "allowUnsafeAccess":true}, + {"name":"newMembers_", "allowUnsafeAccess":true}, + {"name":"newPendingMembers_", "allowUnsafeAccess":true}, + {"name":"newRequestingMembers_", "allowUnsafeAccess":true}, + {"name":"newTimer_", "allowUnsafeAccess":true}, + {"name":"newTitle_", "allowUnsafeAccess":true}, + {"name":"promotePendingMembers_", "allowUnsafeAccess":true}, + {"name":"promoteRequestingMembers_", "allowUnsafeAccess":true}, + {"name":"revision_", "allowUnsafeAccess":true} + ] +}, { "name":"org.signal.storageservice.protos.groups.local.DecryptedMember", "fields":[ @@ -1032,48 +1087,6 @@ "name":"org.signal.storageservice.protos.groups.local.DecryptedTimer", "fields":[{"name":"duration_", "allowUnsafeAccess":true}] }, -{ - "name":"org.whispersystems.curve25519.OpportunisticCurve25519Provider", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"org.whispersystems.libsignal.fingerprint.FingerprintProtos$CombinedFingerprints", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"localFingerprint_", "allowUnsafeAccess":true}, - {"name":"remoteFingerprint_", "allowUnsafeAccess":true}, - {"name":"version_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.fingerprint.FingerprintProtos$LogicalFingerprint", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"content_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.protocol.SignalProtos$PreKeySignalMessage", - "fields":[ - {"name":"baseKey_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"identityKey_", "allowUnsafeAccess":true}, - {"name":"message_", "allowUnsafeAccess":true}, - {"name":"preKeyId_", "allowUnsafeAccess":true}, - {"name":"registrationId_", "allowUnsafeAccess":true}, - {"name":"signedPreKeyId_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.protocol.SignalProtos$SignalMessage", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"ciphertext_", "allowUnsafeAccess":true}, - {"name":"counter_", "allowUnsafeAccess":true}, - {"name":"previousCounter_", "allowUnsafeAccess":true}, - {"name":"ratchetKey_", "allowUnsafeAccess":true} - ] -}, { "name":"org.whispersystems.libsignal.state.IdentityKeyStore", "allDeclaredMethods":true @@ -1094,98 +1107,6 @@ "name":"org.whispersystems.libsignal.state.SignedPreKeyStore", "allDeclaredMethods":true }, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$IdentityKeyPairStructure", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"privateKey_", "allowUnsafeAccess":true}, - {"name":"publicKey_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$PreKeyRecordStructure", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"privateKey_", "allowUnsafeAccess":true}, - {"name":"publicKey_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$RecordStructure", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"currentSession_", "allowUnsafeAccess":true}, - {"name":"previousSessions_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure", - "fields":[ - {"name":"aliceBaseKey_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"localIdentityPublic_", "allowUnsafeAccess":true}, - {"name":"localRegistrationId_", "allowUnsafeAccess":true}, - {"name":"needsRefresh_", "allowUnsafeAccess":true}, - {"name":"pendingKeyExchange_", "allowUnsafeAccess":true}, - {"name":"pendingPreKey_", "allowUnsafeAccess":true}, - {"name":"previousCounter_", "allowUnsafeAccess":true}, - {"name":"receiverChains_", "allowUnsafeAccess":true}, - {"name":"remoteIdentityPublic_", "allowUnsafeAccess":true}, - {"name":"remoteRegistrationId_", "allowUnsafeAccess":true}, - {"name":"rootKey_", "allowUnsafeAccess":true}, - {"name":"senderChain_", "allowUnsafeAccess":true}, - {"name":"sessionVersion_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure$Chain", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"chainKey_", "allowUnsafeAccess":true}, - {"name":"messageKeys_", "allowUnsafeAccess":true}, - {"name":"senderRatchetKeyPrivate_", "allowUnsafeAccess":true}, - {"name":"senderRatchetKey_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure$Chain$ChainKey", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"index_", "allowUnsafeAccess":true}, - {"name":"key_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure$Chain$MessageKey", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"cipherKey_", "allowUnsafeAccess":true}, - {"name":"index_", "allowUnsafeAccess":true}, - {"name":"iv_", "allowUnsafeAccess":true}, - {"name":"macKey_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$SessionStructure$PendingPreKey", - "fields":[ - {"name":"baseKey_", "allowUnsafeAccess":true}, - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"preKeyId_", "allowUnsafeAccess":true}, - {"name":"signedPreKeyId_", "allowUnsafeAccess":true} - ] -}, -{ - "name":"org.whispersystems.libsignal.state.StorageProtos$SignedPreKeyRecordStructure", - "fields":[ - {"name":"bitField0_", "allowUnsafeAccess":true}, - {"name":"id_", "allowUnsafeAccess":true}, - {"name":"privateKey_", "allowUnsafeAccess":true}, - {"name":"publicKey_", "allowUnsafeAccess":true}, - {"name":"signature_", "allowUnsafeAccess":true}, - {"name":"timestamp_", "allowUnsafeAccess":true} - ] -}, { "name":"org.whispersystems.signalservice.api.account.AccountAttributes", "allDeclaredFields":true, @@ -1305,6 +1226,27 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.whispersystems.signalservice.internal.keybackup.protos.BackupRequest", + "fields":[ + {"name":"backupId_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"data_", "allowUnsafeAccess":true}, + {"name":"pin_", "allowUnsafeAccess":true}, + {"name":"serviceId_", "allowUnsafeAccess":true}, + {"name":"token_", "allowUnsafeAccess":true}, + {"name":"tries_", "allowUnsafeAccess":true}, + {"name":"validFrom_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.keybackup.protos.BackupResponse", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"status_", "allowUnsafeAccess":true}, + {"name":"token_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.keybackup.protos.DeleteRequest", "fields":[ @@ -1322,6 +1264,15 @@ {"name":"restore_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.keybackup.protos.Response", + "fields":[ + {"name":"backup_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"delete_", "allowUnsafeAccess":true}, + {"name":"restore_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.AttachmentV2UploadAttributes", "allDeclaredFields":true, @@ -1450,6 +1401,12 @@ {"name":"uuid_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.PushServiceSocket$RegistrationLockV2", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.whispersystems.signalservice.internal.push.SendMessageResponse", "allDeclaredFields":true, @@ -1535,6 +1492,7 @@ {"name":"delete_", "allowUnsafeAccess":true}, {"name":"expireTimer_", "allowUnsafeAccess":true}, {"name":"flags_", "allowUnsafeAccess":true}, + {"name":"groupCallUpdate_", "allowUnsafeAccess":true}, {"name":"groupV2_", "allowUnsafeAccess":true}, {"name":"group_", "allowUnsafeAccess":true}, {"name":"isViewOnce_", "allowUnsafeAccess":true}, @@ -1638,6 +1596,13 @@ {"name":"revision_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$NullMessage", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"padding_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$ReceiptMessage", "fields":[ diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index 402798af..fcacac15 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -1,11 +1,12 @@ { "resources":{ "includes":[ - {"pattern":"com/google/i18n/phonenumbers/data/.*"}, - {"pattern":"libzkgroup.so"}, - {"pattern":"org/asamk/signal/manager/ias.store"}, - {"pattern":"org/asamk/signal/manager/whisper.store"}, - {"pattern":"org/slf4j/impl/StaticLoggerBinder.class"} + {"pattern":"com/google/i18n/phonenumbers/data/.*"}, + {"pattern":"\\Qlibsignal_jni.so\\E"}, + {"pattern":"\\Qlibzkgroup.so\\E"}, + {"pattern":"\\Qorg/asamk/signal/manager/config/ias.store\\E"}, + {"pattern":"\\Qorg/asamk/signal/manager/config/whisper.store\\E"}, + {"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"} ]}, "bundles":[{"name":"net.sourceforge.argparse4j.internal.ArgumentParserImpl"}] } diff --git a/graalvm-config-dir/serialization-config.json b/graalvm-config-dir/serialization-config.json new file mode 100644 index 00000000..0d4f101c --- /dev/null +++ b/graalvm-config-dir/serialization-config.json @@ -0,0 +1,2 @@ +[ +] From 23df85ff909bd1bc8088c429ffd63ea3af6591c6 Mon Sep 17 00:00:00 2001 From: Dom Date: Wed, 3 Feb 2021 19:40:12 +0100 Subject: [PATCH 081/124] Proposal for the Manager class to manage an ExecutorService for all SignalServiceMessageSender instances (#482) * Modified the Manager class to provide an ExecuterService for the SignalServiceMessageSender. * removed unnesessary comment * Changed the ExecutorService to a unbounded newCachedThreadPool Co-authored-by: Cutkh --- lib/src/main/java/org/asamk/signal/manager/Manager.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index be31493d..cb28c45b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -167,6 +167,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; @@ -189,6 +190,8 @@ public class Manager implements Closeable { private final SignalServiceMessageReceiver messageReceiver; private final ClientZkProfileOperations clientZkProfileOperations; + private final ExecutorService executor = Executors.newCachedThreadPool(); + private SignalServiceMessagePipe messagePipe = null; private SignalServiceMessagePipe unidentifiedMessagePipe = null; @@ -526,7 +529,6 @@ public class Manager implements Closeable { } private SignalServiceMessageSender createMessageSender() { - final ExecutorService executor = null; return new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(), account.getUuid(), account.getUsername(), @@ -2533,6 +2535,8 @@ public class Manager implements Closeable { } void close(boolean closeAccount) throws IOException { + executor.shutdown(); + if (messagePipe != null) { messagePipe.shutdown(); messagePipe = null; From 07972175881d5e749e1e4efff92ac7d0211ff241 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 7 Feb 2021 18:47:21 +0100 Subject: [PATCH 082/124] Print additional call message content --- .../java/org/asamk/signal/ReceiveMessageHandler.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index da1be271..07dbfd1d 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -21,6 +21,7 @@ import org.whispersystems.signalservice.api.messages.calls.BusyMessage; import org.whispersystems.signalservice.api.messages.calls.HangupMessage; import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; import org.whispersystems.signalservice.api.messages.calls.OfferMessage; +import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage; @@ -282,6 +283,10 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (content.getCallMessage().isPresent()) { System.out.println("Received a call message"); SignalServiceCallMessage callMessage = content.getCallMessage().get(); + if (callMessage.getDestinationDeviceId().isPresent()) { + final Integer deviceId = callMessage.getDestinationDeviceId().get(); + System.out.println("Destination device id: " + deviceId); + } if (callMessage.getAnswerMessage().isPresent()) { AnswerMessage answerMessage = callMessage.getAnswerMessage().get(); System.out.println("Answer message: " + answerMessage.getId() + ": " + answerMessage.getSdp()); @@ -307,6 +312,10 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { OfferMessage offerMessage = callMessage.getOfferMessage().get(); System.out.println("Offer message: " + offerMessage.getId() + ": " + offerMessage.getSdp()); } + if (callMessage.getOpaqueMessage().isPresent()) { + final OpaqueMessage opaqueMessage = callMessage.getOpaqueMessage().get(); + System.out.println("Opaque message: size " + opaqueMessage.getOpaque().length); + } } if (content.getReceiptMessage().isPresent()) { System.out.println("Received a receipt message"); From 630803c3d1b217253b66e6b3be6107702be43849 Mon Sep 17 00:00:00 2001 From: Michael Kebe Date: Tue, 9 Feb 2021 17:39:36 +0100 Subject: [PATCH 083/124] GraalVM: added reflection config (#507) * GraalVM: added reflection config for delete messages * GraalVM: added reflection config for PreKey exchange * GraalVM: added reflection config for DuplicateMessageException * GraalVM: added reflection config for JsonRemoteDelete * GraalVM: added reflection config for JsonAttachment * GraalVM: added reflection config for JsonSticker and Contacts --- graalvm-config-dir/jni-config.json | 10 ++++ graalvm-config-dir/reflect-config.json | 78 +++++++++++++++++++++++++ graalvm-config-dir/resource-config.json | 5 +- 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/graalvm-config-dir/jni-config.json b/graalvm-config-dir/jni-config.json index abb23704..849f5e32 100644 --- a/graalvm-config-dir/jni-config.json +++ b/graalvm-config-dir/jni-config.json @@ -21,12 +21,18 @@ {"name":"getIdentityKeyPair","parameterTypes":[] }, {"name":"getLocalRegistrationId","parameterTypes":[] }, {"name":"isTrustedIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.IdentityKey","org.whispersystems.libsignal.state.IdentityKeyStore$Direction"] }, + {"name":"loadPreKey","parameterTypes":["int"] }, {"name":"loadSession","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress"] }, {"name":"loadSignedPreKey","parameterTypes":["int"] }, + {"name":"removePreKey","parameterTypes":["int"] }, {"name":"saveIdentity","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.IdentityKey"] }, {"name":"storeSession","parameterTypes":["org.whispersystems.libsignal.SignalProtocolAddress","org.whispersystems.libsignal.state.SessionRecord"] } ] }, +{ + "name":"org.whispersystems.libsignal.DuplicateMessageException", + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"org.whispersystems.libsignal.IdentityKey", "methods":[ @@ -64,6 +70,10 @@ {"name":"SENDING"} ] }, +{ + "name":"org.whispersystems.libsignal.state.PreKeyRecord", + "methods":[{"name":"nativeHandle","parameterTypes":[] }] +}, { "name":"org.whispersystems.libsignal.state.PreKeyStore" }, diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index e2d5ad03..17a761a0 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -312,6 +312,12 @@ "allDeclaredConstructors":true, "allPublicConstructors":true }, +{ + "name":"org.asamk.signal.json.JsonAttachment", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonDataMessage", "allDeclaredFields":true, @@ -342,6 +348,18 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"org.asamk.signal.json.JsonRemoteDelete", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, +{ + "name":"org.asamk.signal.json.JsonSticker", + "allDeclaredFields":true, + "allDeclaredMethods":true, + "allDeclaredConstructors":true +}, { "name":"org.asamk.signal.json.JsonTypingMessage", "allDeclaredFields":true, @@ -1527,6 +1545,66 @@ {"name":"organization_", "allowUnsafeAccess":true} ] }, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Avatar", + "fields":[ + {"name":"avatar_", "allowUnsafeAccess":true}, + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"isProfile_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Email", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"label_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true}, + {"name":"value_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Name", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"displayName_", "allowUnsafeAccess":true}, + {"name":"familyName_", "allowUnsafeAccess":true}, + {"name":"givenName_", "allowUnsafeAccess":true}, + {"name":"middleName_", "allowUnsafeAccess":true}, + {"name":"prefix_", "allowUnsafeAccess":true}, + {"name":"suffix_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$Phone", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"label_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true}, + {"name":"value_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Contact$PostalAddress", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"city_", "allowUnsafeAccess":true}, + {"name":"country_", "allowUnsafeAccess":true}, + {"name":"label_", "allowUnsafeAccess":true}, + {"name":"neighborhood_", "allowUnsafeAccess":true}, + {"name":"pobox_", "allowUnsafeAccess":true}, + {"name":"postcode_", "allowUnsafeAccess":true}, + {"name":"region_", "allowUnsafeAccess":true}, + {"name":"street_", "allowUnsafeAccess":true}, + {"name":"type_", "allowUnsafeAccess":true} + ] +}, +{ + "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Delete", + "fields":[ + {"name":"bitField0_", "allowUnsafeAccess":true}, + {"name":"targetSentTimestamp_", "allowUnsafeAccess":true} + ] +}, { "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$DataMessage$Preview", "fields":[ diff --git a/graalvm-config-dir/resource-config.json b/graalvm-config-dir/resource-config.json index fcacac15..9922e29f 100644 --- a/graalvm-config-dir/resource-config.json +++ b/graalvm-config-dir/resource-config.json @@ -1,12 +1,13 @@ { "resources":{ "includes":[ - {"pattern":"com/google/i18n/phonenumbers/data/.*"}, + {"pattern":"\\Qcom/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DE\\E"}, {"pattern":"\\Qlibsignal_jni.so\\E"}, {"pattern":"\\Qlibzkgroup.so\\E"}, {"pattern":"\\Qorg/asamk/signal/manager/config/ias.store\\E"}, {"pattern":"\\Qorg/asamk/signal/manager/config/whisper.store\\E"}, - {"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"} + {"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"}, + {"pattern":"com/google/i18n/phonenumbers/data/.*"} ]}, "bundles":[{"name":"net.sourceforge.argparse4j.internal.ArgumentParserImpl"}] } From 40fc71258160812b56f9dc0b7de511ef764e2633 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 12 Feb 2021 22:31:33 +0100 Subject: [PATCH 084/124] Update signal-service-java --- lib/build.gradle.kts | 2 +- .../org/asamk/signal/manager/Manager.java | 8 +----- .../signal/manager/ProvisioningManager.java | 5 +--- .../signal/manager/RegistrationManager.java | 12 +++------ .../signal/manager/config/LiveConfig.java | 3 +++ .../signal/manager/config/SandboxConfig.java | 3 +++ .../signal/manager/storage/SignalAccount.java | 19 -------------- .../storage/protocol/JsonSessionStore.java | 25 +++++++++++++++++-- .../protocol/JsonSignalProtocolStore.java | 13 ++++++++-- 9 files changed, 46 insertions(+), 44 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index c5d46c4f..b9dfbc21 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -14,7 +14,7 @@ repositories { } dependencies { - api("com.github.turasa:signal-service-java:2.15.3_unofficial_18") + api("com.github.turasa:signal-service-java:2.15.3_unofficial_19") implementation("com.google.protobuf:protobuf-javalite:3.10.0") implementation("org.bouncycastle:bcprov-jdk15on:1.68") implementation("org.slf4j:slf4j-api:1.7.30") diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index cb28c45b..9661b12c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -76,7 +76,6 @@ import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidMessageException; -import org.whispersystems.libsignal.InvalidVersionException; import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; @@ -219,7 +218,6 @@ public class Manager implements Closeable { new DynamicCredentialsProvider(account.getUuid(), account.getUsername(), account.getPassword(), - account.getSignalingKey(), account.getDeviceId()), userAgent, groupsV2Operations, @@ -242,7 +240,6 @@ public class Manager implements Closeable { account.getUsername(), account.getPassword(), account.getDeviceId(), - account.getSignalingKey(), userAgent, null, timer, @@ -352,7 +349,7 @@ public class Manager implements Closeable { } public void updateAccountAttributes() throws IOException { - accountManager.setAccountAttributes(account.getSignalingKey(), + accountManager.setAccountAttributes(null, account.getSignalProtocolStore().getLocalRegistrationId(), true, // set legacy pin only if no KBS master key is set @@ -1747,9 +1744,6 @@ public class Manager implements Closeable { } catch (TimeoutException e) { if (returnOnTimeout) return; continue; - } catch (InvalidVersionException e) { - logger.warn("Error while receiving messages, ignoring: {}", e.getMessage()); - continue; } if (envelope.hasSource()) { diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index c01ceabf..f48ade7c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -69,7 +69,7 @@ public class ProvisioningManager { groupsV2Operations = null; } accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), - new DynamicCredentialsProvider(null, null, password, null, SignalServiceAddress.DEFAULT_DEVICE_ID), + new DynamicCredentialsProvider(null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY, @@ -95,10 +95,8 @@ public class ProvisioningManager { } public String finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists { - String signalingKey = KeyUtils.createSignalingKey(); SignalServiceAccountManager.NewDeviceRegistrationReturn ret = accountManager.finishNewDeviceRegistration( identityKey, - signalingKey, false, true, registrationId, @@ -130,7 +128,6 @@ public class ProvisioningManager { ret.getDeviceId(), ret.getIdentity(), registrationId, - signalingKey, profileKey)) { account.save(); diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 914a5fcf..baff2047 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -77,11 +77,7 @@ public class RegistrationManager implements Closeable { this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), new DynamicCredentialsProvider( // Using empty UUID, because registering doesn't work otherwise - null, - account.getUsername(), - account.getPassword(), - account.getSignalingKey(), - SignalServiceAddress.DEFAULT_DEVICE_ID), + null, account.getUsername(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY, @@ -142,9 +138,6 @@ public class RegistrationManager implements Closeable { String verificationCode, String pin ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { verificationCode = verificationCode.replace("-", ""); - if (account.getSignalingKey() == null) { - account.setSignalingKey(KeyUtils.createSignalingKey()); - } VerifyAccountResponse response; try { response = verifyAccountWithCode(verificationCode, pin, null); @@ -176,6 +169,7 @@ public class RegistrationManager implements Closeable { account.setRegistered(true); account.setUuid(UuidUtil.parseOrNull(response.getUuid())); account.setRegistrationLockPin(pin); + account.getSignalProtocolStore().archiveAllSessions(); account.getSignalProtocolStore() .saveIdentity(account.getSelfAddress(), account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(), @@ -195,7 +189,7 @@ public class RegistrationManager implements Closeable { final String verificationCode, final String legacyPin, final String registrationLock ) throws IOException { return accountManager.verifyAccountWithCode(verificationCode, - account.getSignalingKey(), + null, account.getSignalProtocolStore().getLocalRegistrationId(), true, legacyPin, diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index 80df8bbb..4298547d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -9,6 +9,7 @@ import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl; import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl; +import org.whispersystems.signalservice.internal.configuration.SignalProxy; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl; import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl; @@ -40,6 +41,7 @@ class LiveConfig { private final static TrustStore TRUST_STORE = new WhisperTrustStore(); private final static Optional dns = Optional.absent(); + private final static Optional proxy = Optional.absent(); private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() .decode("AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0="); @@ -58,6 +60,7 @@ class LiveConfig { new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)}, interceptors, dns, + proxy, zkGroupServerPublicParams); } diff --git a/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java index edb180ac..f5adb54d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java @@ -9,6 +9,7 @@ import org.whispersystems.signalservice.api.push.TrustStore; import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl; import org.whispersystems.signalservice.internal.configuration.SignalContactDiscoveryUrl; import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl; +import org.whispersystems.signalservice.internal.configuration.SignalProxy; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl; import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl; @@ -40,6 +41,7 @@ class SandboxConfig { private final static TrustStore TRUST_STORE = new WhisperTrustStore(); private final static Optional dns = Optional.absent(); + private final static Optional proxy = Optional.absent(); private final static byte[] zkGroupServerPublicParams = Base64.getDecoder() .decode("ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdls="); @@ -58,6 +60,7 @@ class SandboxConfig { new SignalStorageUrl[]{new SignalStorageUrl(STORAGE_URL, TRUST_STORE)}, interceptors, dns, + proxy, zkGroupServerPublicParams); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index 78e30db8..bac42733 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -73,7 +73,6 @@ public class SignalAccount implements Closeable { private String registrationLockPin; private MasterKey pinMasterKey; private StorageKey storageKey; - private String signalingKey; private ProfileKey profileKey; private int preKeyIdOffset; private int nextSignedPreKeyId; @@ -153,7 +152,6 @@ public class SignalAccount implements Closeable { int deviceId, IdentityKeyPair identityKey, int registrationId, - String signalingKey, ProfileKey profileKey ) throws IOException { IOUtils.createPrivateDirectories(dataPath); @@ -170,7 +168,6 @@ public class SignalAccount implements Closeable { account.password = password; account.profileKey = profileKey; account.deviceId = deviceId; - account.signalingKey = signalingKey; account.signalProtocolStore = new JsonSignalProtocolStore(identityKey, registrationId); account.groupStore = new JsonGroupStore(getGroupCachePath(dataPath, username)); account.contactStore = new JsonContactsStore(); @@ -268,13 +265,6 @@ public class SignalAccount implements Closeable { if (rootNode.hasNonNull("storageKey")) { storageKey = new StorageKey(Base64.getDecoder().decode(rootNode.get("storageKey").asText())); } - if (rootNode.hasNonNull("signalingKey")) { - signalingKey = rootNode.get("signalingKey").asText(); - if (signalingKey.equals("null")) { - // Workaround for load bug in older versions - signalingKey = null; - } - } if (rootNode.hasNonNull("preKeyIdOffset")) { preKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(0); } else { @@ -406,7 +396,6 @@ public class SignalAccount implements Closeable { pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize())) .put("storageKey", storageKey == null ? null : Base64.getEncoder().encodeToString(storageKey.serialize())) - .put("signalingKey", signalingKey) .put("preKeyIdOffset", preKeyIdOffset) .put("nextSignedPreKeyId", nextSignedPreKeyId) .put("profileKey", Base64.getEncoder().encodeToString(profileKey.serialize())) @@ -552,14 +541,6 @@ public class SignalAccount implements Closeable { this.storageKey = storageKey; } - public String getSignalingKey() { - return signalingKey; - } - - public void setSignalingKey(final String signalingKey) { - this.signalingKey = signalingKey; - } - public ProfileKey getProfileKey() { return profileKey; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java index de84c895..81a8adb2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java @@ -13,7 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SessionStore; +import org.whispersystems.signalservice.api.SignalServiceSessionStore; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.UuidUtil; @@ -24,7 +24,7 @@ import java.util.LinkedList; import java.util.List; import java.util.UUID; -class JsonSessionStore implements SessionStore { +class JsonSessionStore implements SignalServiceSessionStore { private final static Logger logger = LoggerFactory.getLogger(JsonSessionStore.class); @@ -127,6 +127,27 @@ class JsonSessionStore implements SessionStore { sessions.removeIf(info -> info.address.matches(serviceAddress)); } + @Override + public void archiveSession(final SignalProtocolAddress address) { + final SessionRecord sessionRecord = loadSession(address); + if (sessionRecord == null) { + return; + } + sessionRecord.archiveCurrentState(); + storeSession(address, sessionRecord); + } + + public void archiveAllSessions() { + for (SessionInfo info : sessions) { + try { + final SessionRecord sessionRecord = new SessionRecord(info.sessionRecord); + sessionRecord.archiveCurrentState(); + info.sessionRecord = sessionRecord.serialize(); + } catch (IOException ignored) { + } + } + } + public static class JsonSessionStoreDeserializer extends JsonDeserializer { @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java index 41a63013..5a863b6e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java @@ -11,13 +11,13 @@ import org.whispersystems.libsignal.InvalidKeyIdException; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.libsignal.state.SignalProtocolStore; import org.whispersystems.libsignal.state.SignedPreKeyRecord; +import org.whispersystems.signalservice.api.SignalServiceProtocolStore; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.List; -public class JsonSignalProtocolStore implements SignalProtocolStore { +public class JsonSignalProtocolStore implements SignalServiceProtocolStore { @JsonProperty("preKeys") @JsonDeserialize(using = JsonPreKeyStore.JsonPreKeyStoreDeserializer.class) @@ -171,6 +171,15 @@ public class JsonSignalProtocolStore implements SignalProtocolStore { sessionStore.deleteAllSessions(serviceAddress); } + @Override + public void archiveSession(final SignalProtocolAddress address) { + sessionStore.archiveSession(address); + } + + public void archiveAllSessions() { + sessionStore.archiveAllSessions(); + } + @Override public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); From 3ed776e4ccd7d5420568d67e94b0a808fc2cec35 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 13 Feb 2021 15:16:54 +0100 Subject: [PATCH 085/124] Only attempt to delete profile avatar if it exists Fixes #486 --- lib/src/main/java/org/asamk/signal/manager/AvatarStore.java | 4 +++- .../java/org/asamk/signal/commands/UpdateProfileCommand.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java index b7244ce2..712e982c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java @@ -64,7 +64,9 @@ public class AvatarStore { } private void deleteAvatar(final File avatarFile) throws IOException { - Files.delete(avatarFile.toPath()); + if (avatarFile.exists()) { + Files.delete(avatarFile.toPath()); + } } private File getGroupAvatarFile(GroupId groupId) { diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index 35190fe8..5455f523 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -40,7 +40,7 @@ public class UpdateProfileCommand implements LocalCommand { : avatarPath == null ? null : Optional.of(new File(avatarPath)); m.setProfile(name, about, aboutEmoji, avatarFile); } catch (IOException e) { - System.err.println("UpdateAccount error: " + e.getMessage()); + System.err.println("Update profile error: " + e.getMessage()); return 3; } From 0dc33e18923b1e5770850cb5a803dae1b510e80a Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 13 Feb 2021 18:40:27 +0100 Subject: [PATCH 086/124] Add more information for CaptchaRequiredException --- .../org/asamk/signal/commands/RegisterCommand.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index da652f7d..f7736018 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -22,13 +22,21 @@ public class RegisterCommand implements RegistrationCommand { @Override public int handleCommand(final Namespace ns, final RegistrationManager m) { + final boolean voiceVerification = ns.getBoolean("voice"); + final String captcha = ns.getString("captcha"); + try { - final boolean voiceVerification = ns.getBoolean("voice"); - final String captcha = ns.getString("captcha"); m.register(voiceVerification, captcha); return 0; } catch (CaptchaRequiredException e) { - System.err.println("Captcha invalid or required for verification (" + e.getMessage() + ")"); + if (captcha == null) { + System.err.println("Captcha required for verification, use --captcha CAPTCHA"); + System.err.println("To get the token, go to https://signalcaptchas.org/registration/generate.html"); + System.err.println("Check the developer tools (F12) console for a failed redirect to signalcaptcha://"); + System.err.println("Everything after signalcaptcha:// is the captcha token."); + } else { + System.err.println("Invalid captcha given."); + } return 1; } catch (IOException e) { System.err.println("Request verify error: " + e.getMessage()); From 9eeba88bd05bfcf58632f1951dc52df58e58cc6d Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 14 Feb 2021 10:40:35 +0100 Subject: [PATCH 087/124] Add more information for libsignal-client native dependency --- README.md | 7 ++++++- .../org/asamk/signal/manager/config/ServiceConfig.java | 9 +++++++++ src/main/java/org/asamk/signal/App.java | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b797b7c..a3900649 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,12 @@ signal-cli is primarily intended to be used on servers to notify admins of impor ## Installation -You can [build signal-cli](#building) yourself, or use the [provided binary files](https://github.com/AsamK/signal-cli/releases/latest), which should work on Linux, macOS and Windows. For Arch Linux there is also a [package in AUR](https://aur.archlinux.org/packages/signal-cli/) and there is a [FreeBSD port](https://www.freshports.org/net-im/signal-cli) available as well. You need to have at least JRE 11 installed, to run signal-cli. +You can [build signal-cli](#building) yourself, or use the [provided binary files](https://github.com/AsamK/signal-cli/releases/latest), which should work on Linux, macOS and Windows. For Arch Linux there is also a [package in AUR](https://aur.archlinux.org/packages/signal-cli/) and there is a [FreeBSD port](https://www.freshports.org/net-im/signal-cli) available as well. + +System requirements: +- at least Java Runtime Environment (JRE) 11 +- native libraries: libzkgroup, libsignal-client + Those are bundled for x86_64 Linux, for other systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) ### Install system-wide on Linux See [latest version](https://github.com/AsamK/signal-cli/releases). diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java index 7a54edd1..0b83f53e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -49,6 +49,15 @@ public class ServiceConfig { } } + public static boolean isSignalClientAvailable() { + try { + org.signal.client.internal.Native.DisplayableFingerprint_Format(new byte[30], new byte[30]); + return true; + } catch (UnsatisfiedLinkError ignored) { + return false; + } + } + public static AccountAttributes.Capabilities getCapabilities() { return capabilities; } diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index e35285c3..90efc20f 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -121,6 +121,11 @@ public class App { + " because the required native library dependency is missing: libzkgroup"); } + if (!ServiceConfig.isSignalClientAvailable()) { + logger.error("Missing required native library dependency: libsignal-client"); + return 1; + } + if (command instanceof ProvisioningCommand) { if (username != null) { System.err.println("You cannot specify a username (phone number) when linking"); From 129a846cc1d539ea4208e6255a7f2ab37fca7c30 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 14 Feb 2021 11:01:06 +0100 Subject: [PATCH 088/124] Bump version --- CHANGELOG.md | 13 +++++++++++-- build.gradle.kts | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef4d240b..6585c78a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,23 @@ # Changelog ## [Unreleased] -**Attention**: For all functionality an additional native library is now required: [libsignal-client](https://github.com/signalapp/libsignal-client/). + +## [0.8.0] - 2021-02-14 +**Attention**: For all signal protocol functionality an additional native library is now required: [libsignal-client](https://github.com/signalapp/libsignal-client/). See https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal for more information. +### Added +- Experimental support for building a GraalVM native image +- Support for setting profile about text and emoji + +### Fixed +- Incorrect error message when removing a non-existent profile avatar + ## [0.7.4] - 2021-01-19 ### Changed - Notify linked devices after profile has been updated -### Fixes +### Fixed - After registering a new account, receiving messages didn't work You may have to register and verify again to fix the issue. - Creating v1 groups works again diff --git a/build.gradle.kts b/build.gradle.kts index 052e3c22..5d1fab6a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { `check-lib-versions` } -version = "0.7.4" +version = "0.8.0" java { sourceCompatibility = JavaVersion.VERSION_11 From d0ce3c1543543c157069772a3e4991c9b5566cbb Mon Sep 17 00:00:00 2001 From: Adimarantis <74186638+bublath@users.noreply.github.com> Date: Fri, 19 Feb 2021 17:54:59 +0100 Subject: [PATCH 089/124] Add updateProfile to Dbus (#522) * Added updateProfile on Dbus * added updateProfile on Dbus (fix) Co-authored-by: Adimarantis --- src/main/java/org/asamk/Signal.java | 2 ++ .../org/asamk/signal/dbus/DbusSignalImpl.java | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 7f5d54d6..1c7d9f1c 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -51,6 +51,8 @@ public interface Signal extends DBusInterface { boolean isRegistered(); + void updateProfile(String name,String about,String aboutEmoji,String avatarPath,boolean removeAvatar) throws Error.Failure; + class MessageReceived extends DBusSignal { private final long timestamp; diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index a2f6cace..4f23d461 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -13,6 +13,8 @@ import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.InvalidNumberException; +import org.whispersystems.libsignal.util.guava.Optional; +import org.asamk.signal.manager.storage.contacts.ContactInfo; import java.io.File; import java.io.IOException; @@ -248,4 +250,20 @@ public class DbusSignalImpl implements Signal { public boolean isRegistered() { return true; } + + @Override + public void updateProfile(final String name,final String about,final String aboutEmoji, String avatarPath, final boolean removeAvatar) { + try { + if (avatarPath.isEmpty()) { + avatarPath = null; + } + Optional avatarFile = removeAvatar + ? Optional.absent() + : avatarPath == null ? null : Optional.of(new File(avatarPath)); + m.setProfile(name, about, aboutEmoji, avatarFile); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } + } + } From 9f3276d7e359cf681a42c675ad376977d5957395 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 19 Feb 2021 17:56:43 +0100 Subject: [PATCH 090/124] Reformat code --- src/main/java/org/asamk/Signal.java | 4 +++- .../java/org/asamk/signal/dbus/DbusSignalImpl.java | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 1c7d9f1c..3bd1c0d7 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -51,7 +51,9 @@ public interface Signal extends DBusInterface { boolean isRegistered(); - void updateProfile(String name,String about,String aboutEmoji,String avatarPath,boolean removeAvatar) throws Error.Failure; + void updateProfile( + String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar + ) throws Error.Failure; class MessageReceived extends DBusSignal { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 4f23d461..4af7156d 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -10,11 +10,10 @@ import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.util.ErrorUtils; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.whispersystems.libsignal.util.Pair; +import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.libsignal.util.guava.Optional; -import org.asamk.signal.manager.storage.contacts.ContactInfo; import java.io.File; import java.io.IOException; @@ -252,9 +251,15 @@ public class DbusSignalImpl implements Signal { } @Override - public void updateProfile(final String name,final String about,final String aboutEmoji, String avatarPath, final boolean removeAvatar) { + public void updateProfile( + final String name, + final String about, + final String aboutEmoji, + String avatarPath, + final boolean removeAvatar + ) { try { - if (avatarPath.isEmpty()) { + if (avatarPath.isEmpty()) { avatarPath = null; } Optional avatarFile = removeAvatar From 237abe431bb77436ef7a23d32339804fff592cc7 Mon Sep 17 00:00:00 2001 From: Atomic-Bean <75401809+Atomic-Bean@users.noreply.github.com> Date: Sat, 20 Feb 2021 03:33:15 +1030 Subject: [PATCH 091/124] Output "SharedContacts" field from a SignalDataMessage (#529) * Initial version of SharedContacts from data message. Need to change location of avatar downloaded and fix plain text mode * Made empty strings for json null and fixed plaintext output * Removed old comments, simplified if-statement and added a 'leadingSpaces' field to the print attachments/mentions functions * Added AsamK's changes --- .../org/asamk/signal/manager/Manager.java | 16 +- .../asamk/signal/ReceiveMessageHandler.java | 153 +++++++++++++++--- .../asamk/signal/json/JsonContactAddress.java | 48 ++++++ .../asamk/signal/json/JsonContactAvatar.java | 19 +++ .../asamk/signal/json/JsonContactEmail.java | 24 +++ .../asamk/signal/json/JsonContactName.java | 36 +++++ .../asamk/signal/json/JsonContactPhone.java | 24 +++ .../asamk/signal/json/JsonDataMessage.java | 20 ++- .../asamk/signal/json/JsonSharedContact.java | 75 +++++++++ src/main/java/org/asamk/signal/util/Util.java | 9 ++ 10 files changed, 399 insertions(+), 25 deletions(-) create mode 100644 src/main/java/org/asamk/signal/json/JsonContactAddress.java create mode 100644 src/main/java/org/asamk/signal/json/JsonContactAvatar.java create mode 100644 src/main/java/org/asamk/signal/json/JsonContactEmail.java create mode 100644 src/main/java/org/asamk/signal/json/JsonContactName.java create mode 100644 src/main/java/org/asamk/signal/json/JsonContactPhone.java create mode 100644 src/main/java/org/asamk/signal/json/JsonSharedContact.java diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 9661b12c..80ff4e71 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -122,6 +122,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptM import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -1553,9 +1554,18 @@ public class Manager implements Closeable { } } } - if (message.getAttachments().isPresent() && !ignoreAttachments) { - for (SignalServiceAttachment attachment : message.getAttachments().get()) { - downloadAttachment(attachment); + if (!ignoreAttachments) { + if (message.getAttachments().isPresent()) { + for (SignalServiceAttachment attachment : message.getAttachments().get()) { + downloadAttachment(attachment); + } + } + if (message.getSharedContacts().isPresent()) { + for (SharedContact contact : message.getSharedContacts().get()) { + if (contact.getAvatar().isPresent()) { + downloadAttachment(contact.getAvatar().get().getAttachment()); + } + } } } if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 07dbfd1d..9ab752cf 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -117,11 +117,11 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } else { System.out.println("Received sync contacts"); } - printAttachment(contactsMessage.getContactsStream()); + printAttachment(contactsMessage.getContactsStream(), 0); } if (syncMessage.getGroups().isPresent()) { System.out.println("Received sync groups"); - printAttachment(syncMessage.getGroups().get()); + printAttachment(syncMessage.getGroups().get(), 0); } if (syncMessage.getRead().isPresent()) { System.out.println("Received sync read messages list"); @@ -393,7 +393,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } if (groupInfo.getAvatar().isPresent()) { System.out.println(" Avatar:"); - printAttachment(groupInfo.getAvatar().get()); + printAttachment(groupInfo.getAvatar().get(), 2); } } else if (groupContext.getGroupV2().isPresent()) { final SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get(); @@ -421,7 +421,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { System.out.println(" - Title: " + preview.getTitle()); System.out.println(" - Url: " + preview.getUrl()); if (preview.getImage().isPresent()) { - printAttachment(preview.getImage().get()); + printAttachment(preview.getImage().get(), 1); } } } @@ -429,8 +429,106 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { final List sharedContacts = message.getSharedContacts().get(); System.out.println("Contacts:"); for (SharedContact contact : sharedContacts) { - System.out.println(" - Name: " + contact.getName()); - // TODO show or store rest of the contact info + System.out.println(" - Name:"); + SharedContact.Name name = contact.getName(); + if (name.getDisplay().isPresent() && !name.getDisplay().get().isBlank()) { + System.out.println(" - Display name: " + name.getDisplay().get()); + } + if (name.getGiven().isPresent() && !name.getGiven().get().isBlank()) { + System.out.println(" - First name: " + name.getGiven().get()); + } + if (name.getMiddle().isPresent() && !name.getMiddle().get().isBlank()) { + System.out.println(" - Middle name: " + name.getMiddle().get()); + } + if (name.getFamily().isPresent() && !name.getFamily().get().isBlank()) { + System.out.println(" - Family name: " + name.getFamily().get()); + } + if (name.getPrefix().isPresent() && !name.getPrefix().get().isBlank()) { + System.out.println(" - Prefix name: " + name.getPrefix().get()); + } + if (name.getSuffix().isPresent() && !name.getSuffix().get().isBlank()) { + System.out.println(" - Suffix name: " + name.getSuffix().get()); + } + + if (contact.getAvatar().isPresent()) { + SharedContact.Avatar avatar = contact.getAvatar().get(); + System.out.println(" - Avatar:"); + printAttachment(avatar.getAttachment(), 3); + if (avatar.isProfile()) { + System.out.println(" - Is profile"); + } else { + System.out.println(" - Is not a profile"); + } + } + + if (contact.getPhone().isPresent()) { + System.out.println(" - Phone details:"); + for (SharedContact.Phone phone : contact.getPhone().get()) { + System.out.println(" - Phone:"); + if (phone.getValue() != null) { + System.out.println(" - Number: " + phone.getValue()); + } + if (phone.getType() != null) { + System.out.println(" - Type: " + phone.getType()); + } + if (phone.getLabel().isPresent() && !phone.getLabel().get().isBlank()) { + System.out.println(" - Label: " + phone.getLabel().get()); + } + } + } + + if (contact.getEmail().isPresent()) { + System.out.println(" - Email details:"); + for (SharedContact.Email email : contact.getEmail().get()) { + System.out.println(" - Email:"); + if (email.getValue() != null) { + System.out.println(" - Value: " + email.getValue()); + } + if (email.getType() != null) { + System.out.println(" - Type: " + email.getType()); + } + if (email.getLabel().isPresent() && !email.getLabel().get().isBlank()) { + System.out.println(" - Label: " + email.getLabel().get()); + } + } + } + + if (contact.getAddress().isPresent()) { + System.out.println(" - Address details:"); + for (SharedContact.PostalAddress address : contact.getAddress().get()) { + System.out.println(" - Address:"); + if (address.getType() != null) { + System.out.println(" - Type: " + address.getType()); + } + if (address.getLabel().isPresent() && !address.getLabel().get().isBlank()) { + System.out.println(" - Label: " + address.getLabel().get()); + } + if (address.getStreet().isPresent() && !address.getStreet().get().isBlank()) { + System.out.println(" - Street: " + address.getStreet().get()); + } + if (address.getPobox().isPresent() && !address.getPobox().get().isBlank()) { + System.out.println(" - Pobox: " + address.getPobox().get()); + } + if (address.getNeighborhood().isPresent() && !address.getNeighborhood().get().isBlank()) { + System.out.println(" - Neighbourhood: " + address.getNeighborhood().get()); + } + if (address.getCity().isPresent() && !address.getCity().get().isBlank()) { + System.out.println(" - City: " + address.getCity().get()); + } + if (address.getRegion().isPresent() && !address.getRegion().get().isBlank()) { + System.out.println(" - Region: " + address.getRegion().get()); + } + if (address.getPostcode().isPresent() && !address.getPostcode().get().isBlank()) { + System.out.println(" - Postcode: " + address.getPostcode().get()); + } + if (address.getCountry().isPresent() && !address.getCountry().get().isBlank()) { + System.out.println(" - Country: " + address.getCountry().get()); + } + } + } + + System.out.println(" - Organisation: " + + (contact.getOrganization().isPresent() ? contact.getOrganization().get() : "-")); } } if (message.getSticker().isPresent()) { @@ -472,7 +570,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (quote.getMentions() != null && quote.getMentions().size() > 0) { System.out.println(" Mentions: "); for (SignalServiceDataMessage.Mention mention : quote.getMentions()) { - printMention(mention, m); + printMention(mention, m, 1); } } if (quote.getAttachments().size() > 0) { @@ -482,7 +580,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { System.out.println(" Type: " + attachment.getContentType()); System.out.println(" Thumbnail:"); if (attachment.getThumbnail() != null) { - printAttachment(attachment.getThumbnail()); + printAttachment(attachment.getThumbnail(), 3); } } } @@ -495,45 +593,60 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (message.getMentions().isPresent()) { System.out.println("Mentions: "); for (SignalServiceDataMessage.Mention mention : message.getMentions().get()) { - printMention(mention, m); + printMention(mention, m, 0); } } if (message.getAttachments().isPresent()) { System.out.println("Attachments: "); for (SignalServiceAttachment attachment : message.getAttachments().get()) { - printAttachment(attachment); + printAttachment(attachment, 0); } } } - private void printMention(SignalServiceDataMessage.Mention mention, Manager m) { - System.out.println("- " + m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)) + /** + * Prints the Signal mention information + * + * @param mention is the Signal mention to print + * @param m is the Manager. used to resolve UUIDs into phone numbers if possible + * @param leadingSpaces is the number of spaces you want the message to be indented by + */ + private void printMention(SignalServiceDataMessage.Mention mention, Manager m, int leadingSpaces) { + String spaces = " ".repeat(leadingSpaces); + System.out.println(spaces + "- " + m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)) .getLegacyIdentifier() + ": " + mention.getStart() + " (length: " + mention.getLength() + ")"); } - private void printAttachment(SignalServiceAttachment attachment) { - System.out.println("- " + attachment.getContentType() + " (" + (attachment.isPointer() ? "Pointer" : "") + ( + /** + * Prints the Signal attachment information + * + * @param attachment is the Signal attachment to print + * @param leadingSpaces is the number of spaces you want the message to be indented by + */ + private void printAttachment(SignalServiceAttachment attachment, int leadingSpaces) { + String spaces = " ".repeat(leadingSpaces); + System.out.println(spaces + "- " + attachment.getContentType() + " (" + (attachment.isPointer() ? "Pointer" : "") + ( attachment.isStream() ? "Stream" : "" ) + ")"); if (attachment.isPointer()) { final SignalServiceAttachmentPointer pointer = attachment.asPointer(); - System.out.println(" Id: " + pointer.getRemoteId() + " Key length: " + pointer.getKey().length); - System.out.println(" Filename: " + ( + System.out.println(spaces + " Id: " + pointer.getRemoteId() + " Key length: " + pointer.getKey().length); + System.out.println(spaces + " Filename: " + ( pointer.getFileName().isPresent() ? pointer.getFileName().get() : "-" )); - System.out.println(" Size: " + ( + System.out.println(spaces + " Size: " + ( pointer.getSize().isPresent() ? pointer.getSize().get() + " bytes" : "" ) + ( pointer.getPreview().isPresent() ? " (Preview is available: " + pointer.getPreview().get().length + " bytes)" : "" )); - System.out.println(" Voice note: " + (pointer.getVoiceNote() ? "yes" : "no")); - System.out.println(" Dimensions: " + pointer.getWidth() + "x" + pointer.getHeight()); + System.out.println(spaces + " Voice note: " + (pointer.getVoiceNote() ? "yes" : "no")); + System.out.println(spaces + " Dimensions: " + pointer.getWidth() + "x" + pointer.getHeight()); File file = m.getAttachmentFile(pointer.getRemoteId()); if (file.exists()) { - System.out.println(" Stored plaintext in: " + file); + System.out.println(spaces + " Stored plaintext in: " + file); } } } diff --git a/src/main/java/org/asamk/signal/json/JsonContactAddress.java b/src/main/java/org/asamk/signal/json/JsonContactAddress.java new file mode 100644 index 00000000..712dd4f3 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactAddress.java @@ -0,0 +1,48 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.asamk.signal.util.Util; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactAddress { + + @JsonProperty + private final SharedContact.PostalAddress.Type type; + + @JsonProperty + private final String label; + + @JsonProperty + private final String street; + + @JsonProperty + private final String pobox; + + @JsonProperty + private final String neighborhood; + + @JsonProperty + private final String city; + + @JsonProperty + private final String region; + + @JsonProperty + private final String postcode; + + @JsonProperty + private final String country; + + public JsonContactAddress(SharedContact.PostalAddress address) { + type = address.getType(); + label = Util.getStringIfNotBlank(address.getLabel()); + street = Util.getStringIfNotBlank(address.getStreet()); + pobox = Util.getStringIfNotBlank(address.getPobox()); + neighborhood = Util.getStringIfNotBlank(address.getNeighborhood()); + city = Util.getStringIfNotBlank(address.getCity()); + region = Util.getStringIfNotBlank(address.getRegion()); + postcode = Util.getStringIfNotBlank(address.getPostcode()); + country = Util.getStringIfNotBlank(address.getCountry()); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonContactAvatar.java b/src/main/java/org/asamk/signal/json/JsonContactAvatar.java new file mode 100644 index 00000000..3ed55f6f --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactAvatar.java @@ -0,0 +1,19 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactAvatar { + + @JsonProperty + private final JsonAttachment attachment; + + @JsonProperty + private final boolean isProfile; + + public JsonContactAvatar(SharedContact.Avatar avatar) { + attachment = new JsonAttachment(avatar.getAttachment()); + isProfile = avatar.isProfile(); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonContactEmail.java b/src/main/java/org/asamk/signal/json/JsonContactEmail.java new file mode 100644 index 00000000..070cfb72 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactEmail.java @@ -0,0 +1,24 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.asamk.signal.util.Util; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactEmail { + + @JsonProperty + private final String value; + + @JsonProperty + private final SharedContact.Email.Type type; + + @JsonProperty + private final String label; + + public JsonContactEmail(SharedContact.Email email) { + value = email.getValue(); + type = email.getType(); + label = Util.getStringIfNotBlank(email.getLabel()); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonContactName.java b/src/main/java/org/asamk/signal/json/JsonContactName.java new file mode 100644 index 00000000..9da27825 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactName.java @@ -0,0 +1,36 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.asamk.signal.util.Util; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactName { + + @JsonProperty + private final String display; + + @JsonProperty + private final String given; + + @JsonProperty + private final String family; + + @JsonProperty + private final String prefix; + + @JsonProperty + private final String suffix; + + @JsonProperty + private final String middle; + + public JsonContactName(SharedContact.Name name) { + display = Util.getStringIfNotBlank(name.getDisplay()); + given = Util.getStringIfNotBlank(name.getGiven()); + family = Util.getStringIfNotBlank(name.getFamily()); + prefix = Util.getStringIfNotBlank(name.getPrefix()); + suffix = Util.getStringIfNotBlank(name.getSuffix()); + middle = Util.getStringIfNotBlank(name.getMiddle()); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonContactPhone.java b/src/main/java/org/asamk/signal/json/JsonContactPhone.java new file mode 100644 index 00000000..fce75843 --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContactPhone.java @@ -0,0 +1,24 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.asamk.signal.util.Util; +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +public class JsonContactPhone { + + @JsonProperty + private final String value; + + @JsonProperty + private final SharedContact.Phone.Type type; + + @JsonProperty + private final String label; + + public JsonContactPhone(SharedContact.Phone phone) { + value = phone.getValue(); + type = phone.getType(); + label = Util.getStringIfNotBlank(phone.getLabel()); + } +} diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 1c927b40..57facc99 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -52,6 +52,10 @@ class JsonDataMessage { @JsonInclude(JsonInclude.Include.NON_NULL) final JsonRemoteDelete remoteDelete; + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List contacts; + @JsonProperty @JsonInclude(JsonInclude.Include.NON_NULL) final JsonGroupInfo groupInfo; @@ -100,6 +104,16 @@ class JsonDataMessage { this.attachments = List.of(); } this.sticker = dataMessage.getSticker().isPresent() ? new JsonSticker(dataMessage.getSticker().get()) : null; + + if (dataMessage.getSharedContacts().isPresent()) { + this.contacts = dataMessage.getSharedContacts() + .get() + .stream() + .map(JsonSharedContact::new) + .collect(Collectors.toList()); + } else { + this.contacts = List.of(); + } } public JsonDataMessage(Signal.MessageReceived messageReceived) { @@ -109,10 +123,11 @@ class JsonDataMessage { expiresInSeconds = null; viewOnce = null; remoteDelete = null; - reaction = null; // TODO Replace these 4 with the proper commands + reaction = null; // TODO Replace these 5 with the proper commands quote = null; mentions = null; sticker = null; + contacts = null; attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); } @@ -123,10 +138,11 @@ class JsonDataMessage { expiresInSeconds = null; viewOnce = null; remoteDelete = null; - reaction = null; // TODO Replace these 4 with the proper commands + reaction = null; // TODO Replace these 5 with the proper commands quote = null; mentions = null; sticker = null; + contacts = null; attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); } } diff --git a/src/main/java/org/asamk/signal/json/JsonSharedContact.java b/src/main/java/org/asamk/signal/json/JsonSharedContact.java new file mode 100644 index 00000000..b1b7718c --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonSharedContact.java @@ -0,0 +1,75 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.whispersystems.signalservice.api.messages.shared.SharedContact; + +import java.util.List; +import java.util.stream.Collectors; + +public class JsonSharedContact { + + @JsonProperty + final JsonContactName name; + + @JsonProperty + final JsonContactAvatar avatar; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List phone; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List email; + + @JsonProperty + @JsonInclude(JsonInclude.Include.NON_NULL) + final List address; + + @JsonProperty + final String organization; + + + public JsonSharedContact(SharedContact contact) { + name = new JsonContactName(contact.getName()); + if (contact.getAvatar().isPresent()) { + avatar = new JsonContactAvatar(contact.getAvatar().get()); + } else { + avatar = null; + } + + if (contact.getPhone().isPresent()) { + phone = contact.getPhone() + .get() + .stream() + .map(JsonContactPhone::new) + .collect(Collectors.toList()); + } else { + phone = null; + } + + if (contact.getEmail().isPresent()) { + email = contact.getEmail() + .get() + .stream() + .map(JsonContactEmail::new) + .collect(Collectors.toList()); + } else { + email = null; + } + + if (contact.getAddress().isPresent()) { + address = contact.getAddress() + .get() + .stream() + .map(JsonContactAddress::new) + .collect(Collectors.toList()); + } else { + address = null; + } + + organization = contact.getOrganization().orNull(); + } +} diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index 92bfae7b..e798d537 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -2,12 +2,21 @@ package org.asamk.signal.util; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.whispersystems.libsignal.util.guava.Optional; public class Util { private Util() { } + public static String getStringIfNotBlank(Optional value) { + String string = value.orNull(); + if (string == null || string.isBlank()) { + return null; + } + return string; + } + public static String formatSafetyNumber(String digits) { final int partCount = 12; int partSize = digits.length() / partCount; From 6c33a89f82ffac98454c6f854e1c8877e3941f6a Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 19 Feb 2021 18:04:08 +0100 Subject: [PATCH 092/124] Reformat code --- .../asamk/signal/ReceiveMessageHandler.java | 19 +++++++++++-------- .../asamk/signal/json/JsonSharedContact.java | 19 +++---------------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 9ab752cf..c3bede8b 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -527,8 +527,9 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } - System.out.println(" - Organisation: " + - (contact.getOrganization().isPresent() ? contact.getOrganization().get() : "-")); + System.out.println(" - Organisation: " + ( + contact.getOrganization().isPresent() ? contact.getOrganization().get() : "-" + )); } } if (message.getSticker().isPresent()) { @@ -608,25 +609,27 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { /** * Prints the Signal mention information * - * @param mention is the Signal mention to print - * @param m is the Manager. used to resolve UUIDs into phone numbers if possible + * @param mention is the Signal mention to print + * @param m is the Manager. used to resolve UUIDs into phone numbers if possible * @param leadingSpaces is the number of spaces you want the message to be indented by */ private void printMention(SignalServiceDataMessage.Mention mention, Manager m, int leadingSpaces) { String spaces = " ".repeat(leadingSpaces); - System.out.println(spaces + "- " + m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)) - .getLegacyIdentifier() + ": " + mention.getStart() + " (length: " + mention.getLength() + ")"); + System.out.println(spaces + "- " + m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), + null)).getLegacyIdentifier() + ": " + mention.getStart() + " (length: " + mention.getLength() + ")"); } /** * Prints the Signal attachment information * - * @param attachment is the Signal attachment to print + * @param attachment is the Signal attachment to print * @param leadingSpaces is the number of spaces you want the message to be indented by */ private void printAttachment(SignalServiceAttachment attachment, int leadingSpaces) { String spaces = " ".repeat(leadingSpaces); - System.out.println(spaces + "- " + attachment.getContentType() + " (" + (attachment.isPointer() ? "Pointer" : "") + ( + System.out.println(spaces + "- " + attachment.getContentType() + " (" + ( + attachment.isPointer() ? "Pointer" : "" + ) + ( attachment.isStream() ? "Stream" : "" ) + ")"); if (attachment.isPointer()) { diff --git a/src/main/java/org/asamk/signal/json/JsonSharedContact.java b/src/main/java/org/asamk/signal/json/JsonSharedContact.java index b1b7718c..ea15b574 100644 --- a/src/main/java/org/asamk/signal/json/JsonSharedContact.java +++ b/src/main/java/org/asamk/signal/json/JsonSharedContact.java @@ -31,7 +31,6 @@ public class JsonSharedContact { @JsonProperty final String organization; - public JsonSharedContact(SharedContact contact) { name = new JsonContactName(contact.getName()); if (contact.getAvatar().isPresent()) { @@ -41,31 +40,19 @@ public class JsonSharedContact { } if (contact.getPhone().isPresent()) { - phone = contact.getPhone() - .get() - .stream() - .map(JsonContactPhone::new) - .collect(Collectors.toList()); + phone = contact.getPhone().get().stream().map(JsonContactPhone::new).collect(Collectors.toList()); } else { phone = null; } if (contact.getEmail().isPresent()) { - email = contact.getEmail() - .get() - .stream() - .map(JsonContactEmail::new) - .collect(Collectors.toList()); + email = contact.getEmail().get().stream().map(JsonContactEmail::new).collect(Collectors.toList()); } else { email = null; } if (contact.getAddress().isPresent()) { - address = contact.getAddress() - .get() - .stream() - .map(JsonContactAddress::new) - .collect(Collectors.toList()); + address = contact.getAddress().get().stream().map(JsonContactAddress::new).collect(Collectors.toList()); } else { address = null; } From 03c30519b11e9772eecdc74f89d350bdecd20361 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 20 Feb 2021 19:22:36 +0100 Subject: [PATCH 093/124] Refactor ReceiveMessageHandler Introduce PlainTextWriter to improve indentation handling. --- .../asamk/signal/manager/groups/GroupId.java | 2 +- .../java/org/asamk/signal/JsonWriter.java | 6 +- .../org/asamk/signal/PlainTextWriter.java | 23 + .../org/asamk/signal/PlainTextWriterImpl.java | 67 ++ .../asamk/signal/ReceiveMessageHandler.java | 1048 +++++++++-------- .../java/org/asamk/signal/util/DateUtils.java | 2 +- 6 files changed, 641 insertions(+), 507 deletions(-) create mode 100644 src/main/java/org/asamk/signal/PlainTextWriter.java create mode 100644 src/main/java/org/asamk/signal/PlainTextWriterImpl.java diff --git a/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java index f56e5d38..5c5d639c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java +++ b/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java @@ -33,7 +33,7 @@ public abstract class GroupId { } } - public GroupId(final byte[] id) { + protected GroupId(final byte[] id) { this.id = id; } diff --git a/src/main/java/org/asamk/signal/JsonWriter.java b/src/main/java/org/asamk/signal/JsonWriter.java index 26a20a1f..8aed4487 100644 --- a/src/main/java/org/asamk/signal/JsonWriter.java +++ b/src/main/java/org/asamk/signal/JsonWriter.java @@ -6,18 +6,20 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.Writer; import java.nio.charset.StandardCharsets; public class JsonWriter { - private final OutputStreamWriter writer; + private final Writer writer; private final ObjectMapper objectMapper; public JsonWriter(final OutputStream writer) { - this.writer = new OutputStreamWriter(writer, StandardCharsets.UTF_8); + this.writer = new BufferedWriter(new OutputStreamWriter(writer, StandardCharsets.UTF_8)); objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY); diff --git a/src/main/java/org/asamk/signal/PlainTextWriter.java b/src/main/java/org/asamk/signal/PlainTextWriter.java new file mode 100644 index 00000000..91a4dbba --- /dev/null +++ b/src/main/java/org/asamk/signal/PlainTextWriter.java @@ -0,0 +1,23 @@ +package org.asamk.signal; + +import java.io.IOException; + +public interface PlainTextWriter { + + void println(String format, Object... args) throws IOException; + + PlainTextWriter indentedWriter(); + + default void println() throws IOException { + println(""); + } + + default void indent(final WriterConsumer subWriter) throws IOException { + subWriter.consume(indentedWriter()); + } + + interface WriterConsumer { + + void consume(PlainTextWriter writer) throws IOException; + } +} diff --git a/src/main/java/org/asamk/signal/PlainTextWriterImpl.java b/src/main/java/org/asamk/signal/PlainTextWriterImpl.java new file mode 100644 index 00000000..19874d55 --- /dev/null +++ b/src/main/java/org/asamk/signal/PlainTextWriterImpl.java @@ -0,0 +1,67 @@ +package org.asamk.signal; + +import org.slf4j.helpers.MessageFormatter; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; + +public final class PlainTextWriterImpl implements PlainTextWriter { + + private final Writer writer; + + private PlainTextWriter indentedWriter; + + public PlainTextWriterImpl(final OutputStream outputStream) { + this.writer = new BufferedWriter(new OutputStreamWriter(outputStream)); + } + + @Override + public void println(String format, Object... args) throws IOException { + final String message = MessageFormatter.arrayFormat(format, args).getMessage(); + + writer.write(message); + writer.write(System.lineSeparator()); + writer.flush(); + } + + @Override + public PlainTextWriter indentedWriter() { + if (indentedWriter == null) { + indentedWriter = new IndentedPlainTextWriter(this, writer); + } + return indentedWriter; + } + + private static final class IndentedPlainTextWriter implements PlainTextWriter { + + private final static int INDENTATION = 2; + + private final String spaces = " ".repeat(INDENTATION); + private final PlainTextWriter plainTextWriter; + private final Writer writer; + + private PlainTextWriter indentedWriter; + + private IndentedPlainTextWriter(final PlainTextWriter plainTextWriter, final Writer writer) { + this.plainTextWriter = plainTextWriter; + this.writer = writer; + } + + @Override + public void println(final String format, final Object... args) throws IOException { + writer.write(spaces); + plainTextWriter.println(format, args); + } + + @Override + public PlainTextWriter indentedWriter() { + if (indentedWriter == null) { + indentedWriter = new IndentedPlainTextWriter(this, writer); + } + return indentedWriter; + } + } +} diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index c3bede8b..6e9e1a94 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -6,6 +6,8 @@ import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.Util; +import org.slf4j.helpers.MessageFormatter; +import org.whispersystems.libsignal.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import org.whispersystems.signalservice.api.messages.SignalServiceContent; @@ -38,8 +40,10 @@ import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.File; +import java.io.IOException; import java.util.Base64; import java.util.List; +import java.util.stream.Collectors; public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { @@ -51,606 +55,644 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { @Override public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { - if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { + try { + printMessage(envelope, content, exception); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void printMessage( + SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception + ) throws IOException { + PlainTextWriter writer = new PlainTextWriterImpl(System.out); + + if (envelope.hasSource()) { SignalServiceAddress source = envelope.getSourceAddress(); - String name = m.getContactOrProfileName(source.getLegacyIdentifier()); - System.out.println(String.format("Envelope from: %s (device: %d)", - (name == null ? "" : "“" + name + "” ") + source.getLegacyIdentifier(), - envelope.getSourceDevice())); + writer.println("Envelope from: {} (device: {})", formatContact(source), envelope.getSourceDevice()); if (source.getRelay().isPresent()) { - System.out.println("Relayed by: " + source.getRelay().get()); + writer.println("Relayed by: {}", source.getRelay().get()); } } else { - System.out.println("Envelope from: unknown source"); + writer.println("Envelope from: unknown source"); } - System.out.println("Timestamp: " + DateUtils.formatTimestamp(envelope.getTimestamp())); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(envelope.getTimestamp())); if (envelope.isUnidentifiedSender()) { - System.out.println("Sent by unidentified/sealed sender"); + writer.println("Sent by unidentified/sealed sender"); } if (envelope.isReceipt()) { - System.out.println("Got receipt."); + writer.println("Got receipt."); } else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) { if (exception != null) { - if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) { - org.whispersystems.libsignal.UntrustedIdentityException e = (org.whispersystems.libsignal.UntrustedIdentityException) exception; - System.out.println( + if (exception instanceof UntrustedIdentityException) { + UntrustedIdentityException e = (UntrustedIdentityException) exception; + writer.println( "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message."); - System.out.println("Use 'signal-cli -u " - + m.getUsername() - + " listIdentities -n " - + e.getName() - + "', verify the key and run 'signal-cli -u " - + m.getUsername() - + " trust -v \"FINGER_PRINT\" " - + e.getName() - + "' to mark it as trusted"); - System.out.println("If you don't care about security, use 'signal-cli -u " - + m.getUsername() - + " trust -a " - + e.getName() - + "' to trust it without verification"); + writer.println( + "Use 'signal-cli -u {0} listIdentities -n {1}', verify the key and run 'signal-cli -u {0} trust -v \"FINGER_PRINT\" {1}' to mark it as trusted", + m.getUsername(), + e.getName()); + writer.println( + "If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification", + m.getUsername(), + e.getName()); } else { - System.out.println("Exception: " + exception.getMessage() + " (" + exception.getClass() - .getSimpleName() + ")"); + writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName()); } } if (content == null) { - System.out.println("Failed to decrypt message."); + writer.println("Failed to decrypt message."); } else { - String senderName = m.getContactOrProfileName(content.getSender().getLegacyIdentifier()); - System.out.println(String.format("Sender: %s (device: %d)", - (senderName == null ? "" : "“" + senderName + "” ") + content.getSender().getLegacyIdentifier(), - content.getSenderDevice())); + writer.println("Sender: {} (device: {})", + formatContact(content.getSender()), + content.getSenderDevice()); + writer.println("Server timestamps: received: {} delivered: {}", + DateUtils.formatTimestamp(content.getServerReceivedTimestamp()), + DateUtils.formatTimestamp(content.getServerDeliveredTimestamp())); + if (content.getDataMessage().isPresent()) { SignalServiceDataMessage message = content.getDataMessage().get(); - handleSignalServiceDataMessage(message); + printDataMessage(writer, message); } if (content.getSyncMessage().isPresent()) { - System.out.println("Received a sync message"); + writer.println("Received a sync message"); SignalServiceSyncMessage syncMessage = content.getSyncMessage().get(); - - if (syncMessage.getContacts().isPresent()) { - final ContactsMessage contactsMessage = syncMessage.getContacts().get(); - if (contactsMessage.isComplete()) { - System.out.println("Received complete sync contacts"); - } else { - System.out.println("Received sync contacts"); - } - printAttachment(contactsMessage.getContactsStream(), 0); - } - if (syncMessage.getGroups().isPresent()) { - System.out.println("Received sync groups"); - printAttachment(syncMessage.getGroups().get(), 0); - } - if (syncMessage.getRead().isPresent()) { - System.out.println("Received sync read messages list"); - for (ReadMessage rm : syncMessage.getRead().get()) { - String name = m.getContactOrProfileName(rm.getSender().getLegacyIdentifier()); - System.out.println("From: " - + (name == null ? "" : "“" + name + "” ") - + rm.getSender() - .getLegacyIdentifier() - + " Message timestamp: " - + DateUtils.formatTimestamp(rm.getTimestamp())); - } - } - if (syncMessage.getRequest().isPresent()) { - System.out.println("Received sync request"); - if (syncMessage.getRequest().get().isContactsRequest()) { - System.out.println(" - contacts request"); - } - if (syncMessage.getRequest().get().isGroupsRequest()) { - System.out.println(" - groups request"); - } - if (syncMessage.getRequest().get().isBlockedListRequest()) { - System.out.println(" - blocked list request"); - } - if (syncMessage.getRequest().get().isConfigurationRequest()) { - System.out.println(" - configuration request"); - } - if (syncMessage.getRequest().get().isKeysRequest()) { - System.out.println(" - keys request"); - } - } - if (syncMessage.getSent().isPresent()) { - System.out.println("Received sync sent message"); - final SentTranscriptMessage sentTranscriptMessage = syncMessage.getSent().get(); - String to; - if (sentTranscriptMessage.getDestination().isPresent()) { - String dest = sentTranscriptMessage.getDestination().get().getLegacyIdentifier(); - String name = m.getContactOrProfileName(dest); - to = (name == null ? "" : "“" + name + "” ") + dest; - } else if (sentTranscriptMessage.getRecipients().size() > 0) { - StringBuilder toBuilder = new StringBuilder(); - for (SignalServiceAddress dest : sentTranscriptMessage.getRecipients()) { - String name = m.getContactOrProfileName(dest.getLegacyIdentifier()); - toBuilder.append(name == null ? "" : "“" + name + "” ") - .append(dest.getLegacyIdentifier()) - .append(" "); - } - to = toBuilder.toString(); - } else { - to = "Unknown"; - } - System.out.println("To: " + to + " , Message timestamp: " + DateUtils.formatTimestamp( - sentTranscriptMessage.getTimestamp())); - if (sentTranscriptMessage.getExpirationStartTimestamp() > 0) { - System.out.println("Expiration started at: " + DateUtils.formatTimestamp( - sentTranscriptMessage.getExpirationStartTimestamp())); - } - SignalServiceDataMessage message = sentTranscriptMessage.getMessage(); - handleSignalServiceDataMessage(message); - } - if (syncMessage.getBlockedList().isPresent()) { - System.out.println("Received sync message with block list"); - System.out.println("Blocked numbers:"); - final BlockedListMessage blockedList = syncMessage.getBlockedList().get(); - for (SignalServiceAddress address : blockedList.getAddresses()) { - System.out.println(" - " + address.getLegacyIdentifier()); - } - } - if (syncMessage.getVerified().isPresent()) { - System.out.println("Received sync message with verified identities:"); - final VerifiedMessage verifiedMessage = syncMessage.getVerified().get(); - System.out.println(" - " - + verifiedMessage.getDestination() - + ": " - + verifiedMessage.getVerified()); - String safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(verifiedMessage.getDestination(), - verifiedMessage.getIdentityKey())); - System.out.println(" " + safetyNumber); - } - if (syncMessage.getConfiguration().isPresent()) { - System.out.println("Received sync message with configuration:"); - final ConfigurationMessage configurationMessage = syncMessage.getConfiguration().get(); - if (configurationMessage.getReadReceipts().isPresent()) { - System.out.println(" - Read receipts: " + ( - configurationMessage.getReadReceipts().get() ? "enabled" : "disabled" - )); - } - if (configurationMessage.getLinkPreviews().isPresent()) { - System.out.println(" - Link previews: " + ( - configurationMessage.getLinkPreviews().get() ? "enabled" : "disabled" - )); - } - if (configurationMessage.getTypingIndicators().isPresent()) { - System.out.println(" - Typing indicators: " + ( - configurationMessage.getTypingIndicators().get() ? "enabled" : "disabled" - )); - } - if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) { - System.out.println(" - Unidentified Delivery Indicators: " + ( - configurationMessage.getUnidentifiedDeliveryIndicators().get() - ? "enabled" - : "disabled" - )); - } - } - if (syncMessage.getFetchType().isPresent()) { - final SignalServiceSyncMessage.FetchType fetchType = syncMessage.getFetchType().get(); - System.out.println("Received sync message with fetch type: " + fetchType.toString()); - } - if (syncMessage.getViewOnceOpen().isPresent()) { - final ViewOnceOpenMessage viewOnceOpenMessage = syncMessage.getViewOnceOpen().get(); - System.out.println("Received sync message with view once open message:"); - System.out.println(" - Sender:" + viewOnceOpenMessage.getSender().getLegacyIdentifier()); - System.out.println(" - Timestamp:" + viewOnceOpenMessage.getTimestamp()); - } - if (syncMessage.getStickerPackOperations().isPresent()) { - final List stickerPackOperationMessages = syncMessage.getStickerPackOperations() - .get(); - System.out.println("Received sync message with sticker pack operations:"); - for (StickerPackOperationMessage m : stickerPackOperationMessages) { - System.out.println(" - " + m.getType().toString()); - if (m.getPackId().isPresent()) { - System.out.println(" packId: " + Base64.getEncoder() - .encodeToString(m.getPackId().get())); - } - if (m.getPackKey().isPresent()) { - System.out.println(" packKey: " + Base64.getEncoder() - .encodeToString(m.getPackKey().get())); - } - } - } - if (syncMessage.getMessageRequestResponse().isPresent()) { - final MessageRequestResponseMessage requestResponseMessage = syncMessage.getMessageRequestResponse() - .get(); - System.out.println("Received message request response:"); - System.out.println(" Type: " + requestResponseMessage.getType()); - if (requestResponseMessage.getGroupId().isPresent()) { - System.out.println(" Group id: " + Base64.getEncoder() - .encodeToString(requestResponseMessage.getGroupId().get())); - } - if (requestResponseMessage.getPerson().isPresent()) { - System.out.println(" Person: " + requestResponseMessage.getPerson() - .get() - .getLegacyIdentifier()); - } - } - if (syncMessage.getKeys().isPresent()) { - final KeysMessage keysMessage = syncMessage.getKeys().get(); - System.out.println("Received sync message with keys:"); - if (keysMessage.getStorageService().isPresent()) { - System.out.println(" With storage key length: " + keysMessage.getStorageService() - .get() - .serialize().length); - } else { - System.out.println(" With empty storage key"); - } - } + printSyncMessage(writer, syncMessage); } + if (content.getCallMessage().isPresent()) { - System.out.println("Received a call message"); + writer.println("Received a call message"); SignalServiceCallMessage callMessage = content.getCallMessage().get(); - if (callMessage.getDestinationDeviceId().isPresent()) { - final Integer deviceId = callMessage.getDestinationDeviceId().get(); - System.out.println("Destination device id: " + deviceId); - } - if (callMessage.getAnswerMessage().isPresent()) { - AnswerMessage answerMessage = callMessage.getAnswerMessage().get(); - System.out.println("Answer message: " + answerMessage.getId() + ": " + answerMessage.getSdp()); - } - if (callMessage.getBusyMessage().isPresent()) { - BusyMessage busyMessage = callMessage.getBusyMessage().get(); - System.out.println("Busy message: " + busyMessage.getId()); - } - if (callMessage.getHangupMessage().isPresent()) { - HangupMessage hangupMessage = callMessage.getHangupMessage().get(); - System.out.println("Hangup message: " + hangupMessage.getId()); - } - if (callMessage.getIceUpdateMessages().isPresent()) { - List iceUpdateMessages = callMessage.getIceUpdateMessages().get(); - for (IceUpdateMessage iceUpdateMessage : iceUpdateMessages) { - System.out.println("Ice update message: " - + iceUpdateMessage.getId() - + ", sdp: " - + iceUpdateMessage.getSdp()); - } - } - if (callMessage.getOfferMessage().isPresent()) { - OfferMessage offerMessage = callMessage.getOfferMessage().get(); - System.out.println("Offer message: " + offerMessage.getId() + ": " + offerMessage.getSdp()); - } - if (callMessage.getOpaqueMessage().isPresent()) { - final OpaqueMessage opaqueMessage = callMessage.getOpaqueMessage().get(); - System.out.println("Opaque message: size " + opaqueMessage.getOpaque().length); - } + printCallMessage(writer.indentedWriter(), callMessage); } if (content.getReceiptMessage().isPresent()) { - System.out.println("Received a receipt message"); + writer.println("Received a receipt message"); SignalServiceReceiptMessage receiptMessage = content.getReceiptMessage().get(); - System.out.println(" - When: " + DateUtils.formatTimestamp(receiptMessage.getWhen())); - if (receiptMessage.isDeliveryReceipt()) { - System.out.println(" - Is delivery receipt"); - } - if (receiptMessage.isReadReceipt()) { - System.out.println(" - Is read receipt"); - } - if (receiptMessage.isViewedReceipt()) { - System.out.println(" - Is viewed receipt"); - } - System.out.println(" - Timestamps:"); - for (long timestamp : receiptMessage.getTimestamps()) { - System.out.println(" " + DateUtils.formatTimestamp(timestamp)); - } + printReceiptMessage(writer.indentedWriter(), receiptMessage); } if (content.getTypingMessage().isPresent()) { - System.out.println("Received a typing message"); + writer.println("Received a typing message"); SignalServiceTypingMessage typingMessage = content.getTypingMessage().get(); - System.out.println(" - Action: " + typingMessage.getAction()); - System.out.println(" - Timestamp: " + DateUtils.formatTimestamp(typingMessage.getTimestamp())); - if (typingMessage.getGroupId().isPresent()) { - System.out.println(" - Group Info:"); - final GroupId groupId = GroupId.unknownVersion(typingMessage.getGroupId().get()); - System.out.println(" Id: " + groupId.toBase64()); - GroupInfo group = m.getGroup(groupId); - if (group != null) { - System.out.println(" Name: " + group.getTitle()); - } else { - System.out.println(" Name: "); - } - } + printTypingMessage(writer.indentedWriter(), typingMessage); } } } else { - System.out.println("Unknown message received."); + writer.println("Unknown message received."); } - System.out.println(); + writer.println(); } - private void handleSignalServiceDataMessage(SignalServiceDataMessage message) { - System.out.println("Message timestamp: " + DateUtils.formatTimestamp(message.getTimestamp())); + private void printDataMessage( + PlainTextWriter writer, SignalServiceDataMessage message + ) throws IOException { + writer.println("Message timestamp: {}", DateUtils.formatTimestamp(message.getTimestamp())); if (message.isViewOnce()) { - System.out.println("=VIEW ONCE="); + writer.println("=VIEW ONCE="); } if (message.getBody().isPresent()) { - System.out.println("Body: " + message.getBody().get()); + writer.println("Body: {}", message.getBody().get()); } if (message.getGroupContext().isPresent()) { - System.out.println("Group info:"); + writer.println("Group info:"); final SignalServiceGroupContext groupContext = message.getGroupContext().get(); - final GroupId groupId = GroupUtils.getGroupId(groupContext); - if (groupContext.getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = groupContext.getGroupV1().get(); - System.out.println(" Id: " + groupId.toBase64()); - if (groupInfo.getType() == SignalServiceGroup.Type.UPDATE && groupInfo.getName().isPresent()) { - System.out.println(" Name: " + groupInfo.getName().get()); - } else { - GroupInfo group = m.getGroup(groupId); - if (group != null) { - System.out.println(" Name: " + group.getTitle()); - } else { - System.out.println(" Name: "); - } - } - System.out.println(" Type: " + groupInfo.getType()); - if (groupInfo.getMembers().isPresent()) { - for (SignalServiceAddress member : groupInfo.getMembers().get()) { - System.out.println(" Member: " + member.getLegacyIdentifier()); - } - } - if (groupInfo.getAvatar().isPresent()) { - System.out.println(" Avatar:"); - printAttachment(groupInfo.getAvatar().get(), 2); - } - } else if (groupContext.getGroupV2().isPresent()) { - final SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get(); - System.out.println(" Id: " + groupId.toBase64()); - GroupInfo group = m.getGroup(groupId); - if (group != null) { - System.out.println(" Name: " + group.getTitle()); - } else { - System.out.println(" Name: "); - } - System.out.println(" Revision: " + groupInfo.getRevision()); - System.out.println(" Master key length: " + groupInfo.getMasterKey().serialize().length); - System.out.println(" Has signed group change: " + groupInfo.hasSignedGroupChange()); - } + printGroupContext(writer.indentedWriter(), groupContext); } if (message.getGroupCallUpdate().isPresent()) { + writer.println("Group call update:"); final SignalServiceDataMessage.GroupCallUpdate groupCallUpdate = message.getGroupCallUpdate().get(); - System.out.println("Group call update:"); - System.out.println(" - Era id: " + groupCallUpdate.getEraId()); + writer.indentedWriter().println("Era id: {}", groupCallUpdate.getEraId()); } if (message.getPreviews().isPresent()) { + writer.println("Previews:"); final List previews = message.getPreviews().get(); - System.out.println("Previews:"); for (SignalServiceDataMessage.Preview preview : previews) { - System.out.println(" - Title: " + preview.getTitle()); - System.out.println(" - Url: " + preview.getUrl()); - if (preview.getImage().isPresent()) { - printAttachment(preview.getImage().get(), 1); - } + writer.println("- Preview"); + printPreview(writer.indentedWriter(), preview); } } if (message.getSharedContacts().isPresent()) { final List sharedContacts = message.getSharedContacts().get(); - System.out.println("Contacts:"); + writer.println("Contacts:"); for (SharedContact contact : sharedContacts) { - System.out.println(" - Name:"); - SharedContact.Name name = contact.getName(); - if (name.getDisplay().isPresent() && !name.getDisplay().get().isBlank()) { - System.out.println(" - Display name: " + name.getDisplay().get()); - } - if (name.getGiven().isPresent() && !name.getGiven().get().isBlank()) { - System.out.println(" - First name: " + name.getGiven().get()); - } - if (name.getMiddle().isPresent() && !name.getMiddle().get().isBlank()) { - System.out.println(" - Middle name: " + name.getMiddle().get()); - } - if (name.getFamily().isPresent() && !name.getFamily().get().isBlank()) { - System.out.println(" - Family name: " + name.getFamily().get()); - } - if (name.getPrefix().isPresent() && !name.getPrefix().get().isBlank()) { - System.out.println(" - Prefix name: " + name.getPrefix().get()); - } - if (name.getSuffix().isPresent() && !name.getSuffix().get().isBlank()) { - System.out.println(" - Suffix name: " + name.getSuffix().get()); - } - - if (contact.getAvatar().isPresent()) { - SharedContact.Avatar avatar = contact.getAvatar().get(); - System.out.println(" - Avatar:"); - printAttachment(avatar.getAttachment(), 3); - if (avatar.isProfile()) { - System.out.println(" - Is profile"); - } else { - System.out.println(" - Is not a profile"); - } - } - - if (contact.getPhone().isPresent()) { - System.out.println(" - Phone details:"); - for (SharedContact.Phone phone : contact.getPhone().get()) { - System.out.println(" - Phone:"); - if (phone.getValue() != null) { - System.out.println(" - Number: " + phone.getValue()); - } - if (phone.getType() != null) { - System.out.println(" - Type: " + phone.getType()); - } - if (phone.getLabel().isPresent() && !phone.getLabel().get().isBlank()) { - System.out.println(" - Label: " + phone.getLabel().get()); - } - } - } - - if (contact.getEmail().isPresent()) { - System.out.println(" - Email details:"); - for (SharedContact.Email email : contact.getEmail().get()) { - System.out.println(" - Email:"); - if (email.getValue() != null) { - System.out.println(" - Value: " + email.getValue()); - } - if (email.getType() != null) { - System.out.println(" - Type: " + email.getType()); - } - if (email.getLabel().isPresent() && !email.getLabel().get().isBlank()) { - System.out.println(" - Label: " + email.getLabel().get()); - } - } - } - - if (contact.getAddress().isPresent()) { - System.out.println(" - Address details:"); - for (SharedContact.PostalAddress address : contact.getAddress().get()) { - System.out.println(" - Address:"); - if (address.getType() != null) { - System.out.println(" - Type: " + address.getType()); - } - if (address.getLabel().isPresent() && !address.getLabel().get().isBlank()) { - System.out.println(" - Label: " + address.getLabel().get()); - } - if (address.getStreet().isPresent() && !address.getStreet().get().isBlank()) { - System.out.println(" - Street: " + address.getStreet().get()); - } - if (address.getPobox().isPresent() && !address.getPobox().get().isBlank()) { - System.out.println(" - Pobox: " + address.getPobox().get()); - } - if (address.getNeighborhood().isPresent() && !address.getNeighborhood().get().isBlank()) { - System.out.println(" - Neighbourhood: " + address.getNeighborhood().get()); - } - if (address.getCity().isPresent() && !address.getCity().get().isBlank()) { - System.out.println(" - City: " + address.getCity().get()); - } - if (address.getRegion().isPresent() && !address.getRegion().get().isBlank()) { - System.out.println(" - Region: " + address.getRegion().get()); - } - if (address.getPostcode().isPresent() && !address.getPostcode().get().isBlank()) { - System.out.println(" - Postcode: " + address.getPostcode().get()); - } - if (address.getCountry().isPresent() && !address.getCountry().get().isBlank()) { - System.out.println(" - Country: " + address.getCountry().get()); - } - } - } - - System.out.println(" - Organisation: " + ( - contact.getOrganization().isPresent() ? contact.getOrganization().get() : "-" - )); + writer.println("- Contact:"); + printSharedContact(writer.indentedWriter(), contact); } } if (message.getSticker().isPresent()) { final SignalServiceDataMessage.Sticker sticker = message.getSticker().get(); - System.out.println("Sticker:"); - System.out.println(" - Pack id: " + Base64.getEncoder().encodeToString(sticker.getPackId())); - System.out.println(" - Pack key: " + Base64.getEncoder().encodeToString(sticker.getPackKey())); - System.out.println(" - Sticker id: " + sticker.getStickerId()); - // TODO also download sticker image ?? + writer.println("Sticker:"); + printSticker(writer.indentedWriter(), sticker); } if (message.isEndSession()) { - System.out.println("Is end session"); + writer.println("Is end session"); } if (message.isExpirationUpdate()) { - System.out.println("Is Expiration update: " + message.isExpirationUpdate()); + writer.println("Is Expiration update: {}", message.isExpirationUpdate()); } if (message.getExpiresInSeconds() > 0) { - System.out.println("Expires in: " + message.getExpiresInSeconds() + " seconds"); + writer.println("Expires in: {} seconds", message.getExpiresInSeconds()); } if (message.getProfileKey().isPresent()) { - System.out.println("Profile key update, key length:" + message.getProfileKey().get().length); + writer.println("Profile key update, key length: {}", message.getProfileKey().get().length); } - if (message.getReaction().isPresent()) { + writer.println("Reaction:"); final SignalServiceDataMessage.Reaction reaction = message.getReaction().get(); - System.out.println("Reaction:"); - System.out.println(" - Emoji: " + reaction.getEmoji()); - System.out.println(" - Target author: " + m.resolveSignalServiceAddress(reaction.getTargetAuthor()) - .getLegacyIdentifier()); - System.out.println(" - Target timestamp: " + reaction.getTargetSentTimestamp()); - System.out.println(" - Is remove: " + reaction.isRemove()); + printReaction(writer.indentedWriter(), reaction); } - if (message.getQuote().isPresent()) { + writer.println("Quote:"); SignalServiceDataMessage.Quote quote = message.getQuote().get(); - System.out.println("Quote: (" + quote.getId() + ")"); - System.out.println(" Author: " + m.resolveSignalServiceAddress(quote.getAuthor()).getLegacyIdentifier()); - System.out.println(" Text: " + quote.getText()); - if (quote.getMentions() != null && quote.getMentions().size() > 0) { - System.out.println(" Mentions: "); - for (SignalServiceDataMessage.Mention mention : quote.getMentions()) { - printMention(mention, m, 1); - } - } - if (quote.getAttachments().size() > 0) { - System.out.println(" Attachments: "); - for (SignalServiceDataMessage.Quote.QuotedAttachment attachment : quote.getAttachments()) { - System.out.println(" - Filename: " + attachment.getFileName()); - System.out.println(" Type: " + attachment.getContentType()); - System.out.println(" Thumbnail:"); - if (attachment.getThumbnail() != null) { - printAttachment(attachment.getThumbnail(), 3); - } - } - } + printQuote(writer.indentedWriter(), quote); } - if (message.getRemoteDelete().isPresent()) { final SignalServiceDataMessage.RemoteDelete remoteDelete = message.getRemoteDelete().get(); - System.out.println("Remote delete message: timestamp = " + remoteDelete.getTargetSentTimestamp()); + writer.println("Remote delete message: timestamp = {}", remoteDelete.getTargetSentTimestamp()); } if (message.getMentions().isPresent()) { - System.out.println("Mentions: "); + writer.println("Mentions:"); for (SignalServiceDataMessage.Mention mention : message.getMentions().get()) { - printMention(mention, m, 0); + printMention(writer, mention); } } - if (message.getAttachments().isPresent()) { - System.out.println("Attachments: "); + writer.println("Attachments:"); for (SignalServiceAttachment attachment : message.getAttachments().get()) { - printAttachment(attachment, 0); + writer.println("- Attachment:"); + printAttachment(writer.indentedWriter(), attachment); } } } - /** - * Prints the Signal mention information - * - * @param mention is the Signal mention to print - * @param m is the Manager. used to resolve UUIDs into phone numbers if possible - * @param leadingSpaces is the number of spaces you want the message to be indented by - */ - private void printMention(SignalServiceDataMessage.Mention mention, Manager m, int leadingSpaces) { - String spaces = " ".repeat(leadingSpaces); - System.out.println(spaces + "- " + m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), - null)).getLegacyIdentifier() + ": " + mention.getStart() + " (length: " + mention.getLength() + ")"); + private void printTypingMessage( + final PlainTextWriter writer, final SignalServiceTypingMessage typingMessage + ) throws IOException { + writer.println("Action: {}", typingMessage.getAction()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.getTimestamp())); + if (typingMessage.getGroupId().isPresent()) { + writer.println("Group Info:"); + final GroupId groupId = GroupId.unknownVersion(typingMessage.getGroupId().get()); + printGroupInfo(writer.indentedWriter(), groupId); + } } - /** - * Prints the Signal attachment information - * - * @param attachment is the Signal attachment to print - * @param leadingSpaces is the number of spaces you want the message to be indented by - */ - private void printAttachment(SignalServiceAttachment attachment, int leadingSpaces) { - String spaces = " ".repeat(leadingSpaces); - System.out.println(spaces + "- " + attachment.getContentType() + " (" + ( - attachment.isPointer() ? "Pointer" : "" - ) + ( - attachment.isStream() ? "Stream" : "" - ) + ")"); + private void printReceiptMessage( + final PlainTextWriter writer, final SignalServiceReceiptMessage receiptMessage + ) throws IOException { + writer.println("When: {}", DateUtils.formatTimestamp(receiptMessage.getWhen())); + if (receiptMessage.isDeliveryReceipt()) { + writer.println("Is delivery receipt"); + } + if (receiptMessage.isReadReceipt()) { + writer.println("Is read receipt"); + } + if (receiptMessage.isViewedReceipt()) { + writer.println("Is viewed receipt"); + } + writer.println("Timestamps:"); + for (long timestamp : receiptMessage.getTimestamps()) { + writer.println("- {}", DateUtils.formatTimestamp(timestamp)); + } + } + + private void printCallMessage( + final PlainTextWriter writer, final SignalServiceCallMessage callMessage + ) throws IOException { + if (callMessage.getDestinationDeviceId().isPresent()) { + final Integer deviceId = callMessage.getDestinationDeviceId().get(); + writer.println("Destination device id: {}", deviceId); + } + if (callMessage.getAnswerMessage().isPresent()) { + AnswerMessage answerMessage = callMessage.getAnswerMessage().get(); + writer.println("Answer message: {}, sdp: {})", answerMessage.getId(), answerMessage.getSdp()); + } + if (callMessage.getBusyMessage().isPresent()) { + BusyMessage busyMessage = callMessage.getBusyMessage().get(); + writer.println("Busy message: {}", busyMessage.getId()); + } + if (callMessage.getHangupMessage().isPresent()) { + HangupMessage hangupMessage = callMessage.getHangupMessage().get(); + writer.println("Hangup message: {}", hangupMessage.getId()); + } + if (callMessage.getIceUpdateMessages().isPresent()) { + writer.println("Ice update messages:"); + List iceUpdateMessages = callMessage.getIceUpdateMessages().get(); + for (IceUpdateMessage iceUpdateMessage : iceUpdateMessages) { + writer.println("- {}, sdp: {}", iceUpdateMessage.getId(), iceUpdateMessage.getSdp()); + } + } + if (callMessage.getOfferMessage().isPresent()) { + OfferMessage offerMessage = callMessage.getOfferMessage().get(); + writer.println("Offer message: {}, sdp: {}", offerMessage.getId(), offerMessage.getSdp()); + } + if (callMessage.getOpaqueMessage().isPresent()) { + final OpaqueMessage opaqueMessage = callMessage.getOpaqueMessage().get(); + writer.println("Opaque message: size {}", opaqueMessage.getOpaque().length); + } + } + + private void printSyncMessage( + final PlainTextWriter writer, final SignalServiceSyncMessage syncMessage + ) throws IOException { + if (syncMessage.getContacts().isPresent()) { + final ContactsMessage contactsMessage = syncMessage.getContacts().get(); + String type = contactsMessage.isComplete() ? "complete" : "partial"; + writer.println("Received {} sync contacts:", type); + printAttachment(writer.indentedWriter(), contactsMessage.getContactsStream()); + } + if (syncMessage.getGroups().isPresent()) { + writer.println("Received sync groups:"); + printAttachment(writer.indentedWriter(), syncMessage.getGroups().get()); + } + if (syncMessage.getRead().isPresent()) { + writer.println("Received sync read messages list"); + for (ReadMessage rm : syncMessage.getRead().get()) { + writer.println("- From: {} Message timestamp: {}", + formatContact(rm.getSender()), + DateUtils.formatTimestamp(rm.getTimestamp())); + } + } + if (syncMessage.getRequest().isPresent()) { + String type; + if (syncMessage.getRequest().get().isContactsRequest()) { + type = "contacts"; + } else if (syncMessage.getRequest().get().isGroupsRequest()) { + type = "groups"; + } else if (syncMessage.getRequest().get().isBlockedListRequest()) { + type = "blocked list"; + } else if (syncMessage.getRequest().get().isConfigurationRequest()) { + type = "configuration"; + } else if (syncMessage.getRequest().get().isKeysRequest()) { + type = "keys"; + } else { + type = ""; + } + writer.println("Received sync request for: {}", type); + } + if (syncMessage.getSent().isPresent()) { + writer.println("Received sync sent message"); + final SentTranscriptMessage sentTranscriptMessage = syncMessage.getSent().get(); + String to; + if (sentTranscriptMessage.getDestination().isPresent()) { + to = formatContact(sentTranscriptMessage.getDestination().get()); + } else if (sentTranscriptMessage.getRecipients().size() > 0) { + to = sentTranscriptMessage.getRecipients() + .stream() + .map(this::formatContact) + .collect(Collectors.joining(", ")); + } else { + to = ""; + } + writer.indentedWriter().println("To: {}", to); + writer.indentedWriter() + .println("Timestamp: {}", DateUtils.formatTimestamp(sentTranscriptMessage.getTimestamp())); + if (sentTranscriptMessage.getExpirationStartTimestamp() > 0) { + writer.indentedWriter() + .println("Expiration started at: {}", + DateUtils.formatTimestamp(sentTranscriptMessage.getExpirationStartTimestamp())); + } + SignalServiceDataMessage message = sentTranscriptMessage.getMessage(); + printDataMessage(writer.indentedWriter(), message); + } + if (syncMessage.getBlockedList().isPresent()) { + writer.println("Received sync message with block list"); + writer.println("Blocked numbers:"); + final BlockedListMessage blockedList = syncMessage.getBlockedList().get(); + for (SignalServiceAddress address : blockedList.getAddresses()) { + writer.println("- {}", address.getLegacyIdentifier()); + } + } + if (syncMessage.getVerified().isPresent()) { + writer.println("Received sync message with verified identities:"); + final VerifiedMessage verifiedMessage = syncMessage.getVerified().get(); + writer.println("- {}: {}", formatContact(verifiedMessage.getDestination()), verifiedMessage.getVerified()); + String safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(verifiedMessage.getDestination(), + verifiedMessage.getIdentityKey())); + writer.indentedWriter().println(safetyNumber); + } + if (syncMessage.getConfiguration().isPresent()) { + writer.println("Received sync message with configuration:"); + final ConfigurationMessage configurationMessage = syncMessage.getConfiguration().get(); + if (configurationMessage.getReadReceipts().isPresent()) { + writer.println("- Read receipts: {}", + configurationMessage.getReadReceipts().get() ? "enabled" : "disabled"); + } + if (configurationMessage.getLinkPreviews().isPresent()) { + writer.println("- Link previews: {}", + configurationMessage.getLinkPreviews().get() ? "enabled" : "disabled"); + } + if (configurationMessage.getTypingIndicators().isPresent()) { + writer.println("- Typing indicators: {}", + configurationMessage.getTypingIndicators().get() ? "enabled" : "disabled"); + } + if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) { + writer.println("- Unidentified Delivery Indicators: {}", + configurationMessage.getUnidentifiedDeliveryIndicators().get() ? "enabled" : "disabled"); + } + } + if (syncMessage.getFetchType().isPresent()) { + final SignalServiceSyncMessage.FetchType fetchType = syncMessage.getFetchType().get(); + writer.println("Received sync message with fetch type: {}", fetchType); + } + if (syncMessage.getViewOnceOpen().isPresent()) { + final ViewOnceOpenMessage viewOnceOpenMessage = syncMessage.getViewOnceOpen().get(); + writer.println("Received sync message with view once open message:"); + writer.indentedWriter().println("Sender: {}", formatContact(viewOnceOpenMessage.getSender())); + writer.indentedWriter() + .println("Timestamp: {}", DateUtils.formatTimestamp(viewOnceOpenMessage.getTimestamp())); + } + if (syncMessage.getStickerPackOperations().isPresent()) { + final List stickerPackOperationMessages = syncMessage.getStickerPackOperations() + .get(); + writer.println("Received sync message with sticker pack operations:"); + for (StickerPackOperationMessage m : stickerPackOperationMessages) { + writer.println("- {}", m.getType().isPresent() ? m.getType().get() : ""); + if (m.getPackId().isPresent()) { + writer.indentedWriter() + .println("packId: {}", Base64.getEncoder().encodeToString(m.getPackId().get())); + } + if (m.getPackKey().isPresent()) { + writer.indentedWriter() + .println("packKey: {}", Base64.getEncoder().encodeToString(m.getPackKey().get())); + } + } + } + if (syncMessage.getMessageRequestResponse().isPresent()) { + final MessageRequestResponseMessage requestResponseMessage = syncMessage.getMessageRequestResponse().get(); + writer.println("Received message request response:"); + writer.indentedWriter().println("Type: {}", requestResponseMessage.getType()); + if (requestResponseMessage.getGroupId().isPresent()) { + writer.println("For group:"); + printGroupInfo(writer.indentedWriter(), + GroupId.unknownVersion(requestResponseMessage.getGroupId().get())); + } + if (requestResponseMessage.getPerson().isPresent()) { + writer.indentedWriter() + .println("For Person: {}", formatContact(requestResponseMessage.getPerson().get())); + } + } + if (syncMessage.getKeys().isPresent()) { + final KeysMessage keysMessage = syncMessage.getKeys().get(); + writer.println("Received sync message with keys:"); + if (keysMessage.getStorageService().isPresent()) { + writer.println("- storage key: length: {}", keysMessage.getStorageService().get().serialize().length); + } + } + } + + private void printPreview( + final PlainTextWriter writer, final SignalServiceDataMessage.Preview preview + ) throws IOException { + writer.println("Title: {}", preview.getTitle()); + writer.println("Description: {}", preview.getDescription()); + writer.println("Date: {}", DateUtils.formatTimestamp(preview.getDate())); + writer.println("Url: {}", preview.getUrl()); + if (preview.getImage().isPresent()) { + writer.println("Image:"); + printAttachment(writer.indentedWriter(), preview.getImage().get()); + } + } + + private void printSticker( + final PlainTextWriter writer, final SignalServiceDataMessage.Sticker sticker + ) throws IOException { + writer.println("Pack id: {}", Base64.getEncoder().encodeToString(sticker.getPackId())); + writer.println("Pack key: {}", Base64.getEncoder().encodeToString(sticker.getPackKey())); + writer.println("Sticker id: {}", sticker.getStickerId()); + writer.println("Image:"); + printAttachment(writer.indentedWriter(), sticker.getAttachment()); + } + + private void printReaction( + final PlainTextWriter writer, final SignalServiceDataMessage.Reaction reaction + ) throws IOException { + writer.println("Emoji: {}", reaction.getEmoji()); + writer.println("Target author: {}", formatContact(m.resolveSignalServiceAddress(reaction.getTargetAuthor()))); + writer.println("Target timestamp: {}", DateUtils.formatTimestamp(reaction.getTargetSentTimestamp())); + writer.println("Is remove: {}", reaction.isRemove()); + } + + private void printQuote( + final PlainTextWriter writer, final SignalServiceDataMessage.Quote quote + ) throws IOException { + writer.println("Id: {}", quote.getId()); + writer.println("Author: {}", m.resolveSignalServiceAddress(quote.getAuthor()).getLegacyIdentifier()); + writer.println("Text: {}", quote.getText()); + if (quote.getMentions() != null && quote.getMentions().size() > 0) { + writer.println("Mentions:"); + for (SignalServiceDataMessage.Mention mention : quote.getMentions()) { + printMention(writer, mention); + } + } + if (quote.getAttachments().size() > 0) { + writer.println("Attachments:"); + for (SignalServiceDataMessage.Quote.QuotedAttachment attachment : quote.getAttachments()) { + writer.println("- Filename: {}", attachment.getFileName()); + writer.indent(w -> { + w.println("Type: {}", attachment.getContentType()); + w.println("Thumbnail:"); + if (attachment.getThumbnail() != null) { + printAttachment(w, attachment.getThumbnail()); + } + }); + } + } + } + + private void printSharedContact(final PlainTextWriter writer, final SharedContact contact) throws IOException { + writer.println("Name:"); + SharedContact.Name name = contact.getName(); + writer.indent(w -> { + if (name.getDisplay().isPresent() && !name.getDisplay().get().isBlank()) { + w.println("Display name: {}", name.getDisplay().get()); + } + if (name.getGiven().isPresent() && !name.getGiven().get().isBlank()) { + w.println("First name: {}", name.getGiven().get()); + } + if (name.getMiddle().isPresent() && !name.getMiddle().get().isBlank()) { + w.println("Middle name: {}", name.getMiddle().get()); + } + if (name.getFamily().isPresent() && !name.getFamily().get().isBlank()) { + w.println("Family name: {}", name.getFamily().get()); + } + if (name.getPrefix().isPresent() && !name.getPrefix().get().isBlank()) { + w.println("Prefix name: {}", name.getPrefix().get()); + } + if (name.getSuffix().isPresent() && !name.getSuffix().get().isBlank()) { + w.println("Suffix name: {}", name.getSuffix().get()); + } + }); + + if (contact.getAvatar().isPresent()) { + SharedContact.Avatar avatar = contact.getAvatar().get(); + writer.println("Avatar: (profile: {})", avatar.isProfile()); + printAttachment(writer.indentedWriter(), avatar.getAttachment()); + } + + if (contact.getOrganization().isPresent()) { + writer.println("Organisation: {}", contact.getOrganization().get()); + } + + if (contact.getPhone().isPresent()) { + writer.println("Phone details:"); + for (SharedContact.Phone phone : contact.getPhone().get()) { + writer.println("- Phone:"); + writer.indent(w -> { + if (phone.getValue() != null) { + w.println("Number: {}", phone.getValue()); + } + if (phone.getType() != null) { + w.println("Type: {}", phone.getType()); + } + if (phone.getLabel().isPresent() && !phone.getLabel().get().isBlank()) { + w.println("Label: {}", phone.getLabel().get()); + } + }); + } + } + + if (contact.getEmail().isPresent()) { + writer.println("Email details:"); + for (SharedContact.Email email : contact.getEmail().get()) { + writer.println("- Email:"); + writer.indent(w -> { + if (email.getValue() != null) { + w.println("Address: {}", email.getValue()); + } + if (email.getType() != null) { + w.println("Type: {}", email.getType()); + } + if (email.getLabel().isPresent() && !email.getLabel().get().isBlank()) { + w.println("Label: {}", email.getLabel().get()); + } + }); + } + } + + if (contact.getAddress().isPresent()) { + writer.println("Address details:"); + for (SharedContact.PostalAddress address : contact.getAddress().get()) { + writer.println("- Address:"); + writer.indent(w -> { + if (address.getType() != null) { + w.println("Type: {}", address.getType()); + } + if (address.getLabel().isPresent() && !address.getLabel().get().isBlank()) { + w.println("Label: {}", address.getLabel().get()); + } + if (address.getStreet().isPresent() && !address.getStreet().get().isBlank()) { + w.println("Street: {}", address.getStreet().get()); + } + if (address.getPobox().isPresent() && !address.getPobox().get().isBlank()) { + w.println("Pobox: {}", address.getPobox().get()); + } + if (address.getNeighborhood().isPresent() && !address.getNeighborhood().get().isBlank()) { + w.println("Neighbourhood: {}", address.getNeighborhood().get()); + } + if (address.getCity().isPresent() && !address.getCity().get().isBlank()) { + w.println("City: {}", address.getCity().get()); + } + if (address.getRegion().isPresent() && !address.getRegion().get().isBlank()) { + w.println("Region: {}", address.getRegion().get()); + } + if (address.getPostcode().isPresent() && !address.getPostcode().get().isBlank()) { + w.println("Postcode: {}", address.getPostcode().get()); + } + if (address.getCountry().isPresent() && !address.getCountry().get().isBlank()) { + w.println("Country: {}", address.getCountry().get()); + } + }); + } + } + } + + private void printGroupContext( + final PlainTextWriter writer, final SignalServiceGroupContext groupContext + ) throws IOException { + final GroupId groupId = GroupUtils.getGroupId(groupContext); + if (groupContext.getGroupV1().isPresent()) { + SignalServiceGroup groupInfo = groupContext.getGroupV1().get(); + printGroupInfo(writer, groupId); + writer.println("Type: {}", groupInfo.getType()); + if (groupInfo.getMembers().isPresent()) { + writer.println("Members:"); + for (SignalServiceAddress member : groupInfo.getMembers().get()) { + writer.println("- {}", formatContact(member)); + } + } + if (groupInfo.getAvatar().isPresent()) { + writer.println("Avatar:"); + printAttachment(writer.indentedWriter(), groupInfo.getAvatar().get()); + } + } else if (groupContext.getGroupV2().isPresent()) { + final SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get(); + printGroupInfo(writer, groupId); + writer.println("Revision: {}", groupInfo.getRevision()); + writer.println("Master key length: {}", groupInfo.getMasterKey().serialize().length); + writer.println("Has signed group change: {}", groupInfo.hasSignedGroupChange()); + } + } + + private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) throws IOException { + writer.println("Id: {}", groupId.toBase64()); + + GroupInfo group = m.getGroup(groupId); + if (group != null) { + writer.println("Name: {}", group.getTitle()); + } else { + writer.println("Name: "); + } + } + + private void printMention( + PlainTextWriter writer, SignalServiceDataMessage.Mention mention + ) throws IOException { + final SignalServiceAddress address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), + null)); + writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength()); + } + + private void printAttachment(PlainTextWriter writer, SignalServiceAttachment attachment) throws IOException { + writer.println("Content-Type: {}", attachment.getContentType()); + writer.println("Type: {}", attachment.isPointer() ? "Pointer" : attachment.isStream() ? "Stream" : ""); if (attachment.isPointer()) { final SignalServiceAttachmentPointer pointer = attachment.asPointer(); - System.out.println(spaces + " Id: " + pointer.getRemoteId() + " Key length: " + pointer.getKey().length); - System.out.println(spaces + " Filename: " + ( - pointer.getFileName().isPresent() ? pointer.getFileName().get() : "-" - )); - System.out.println(spaces + " Size: " + ( - pointer.getSize().isPresent() ? pointer.getSize().get() + " bytes" : "" - ) + ( + writer.println("Id: {} Key length: {}", pointer.getRemoteId(), pointer.getKey().length); + if (pointer.getUploadTimestamp() > 0) { + writer.println("Upload timestamp: {}", DateUtils.formatTimestamp(pointer.getUploadTimestamp())); + } + if (pointer.getCaption().isPresent()) { + writer.println("Caption: {}", pointer.getCaption().get()); + } + if (pointer.getFileName().isPresent()) { + writer.println("Filename: {}", pointer.getFileName().get()); + } + writer.println("Size: {}{}", + pointer.getSize().isPresent() ? pointer.getSize().get() + " bytes" : "", pointer.getPreview().isPresent() ? " (Preview is available: " + pointer.getPreview().get().length - + " bytes)" : "" - )); - System.out.println(spaces + " Voice note: " + (pointer.getVoiceNote() ? "yes" : "no")); - System.out.println(spaces + " Dimensions: " + pointer.getWidth() + "x" + pointer.getHeight()); + + " bytes)" : ""); + writer.println("Voice note: {}", pointer.getVoiceNote() ? "yes" : "no"); + writer.println("Borderless: {}", pointer.isBorderless() ? "yes" : "no"); + if (pointer.getWidth() > 0 || pointer.getHeight() > 0) { + writer.println("Dimensions: {}x{}", pointer.getWidth(), pointer.getHeight()); + } File file = m.getAttachmentFile(pointer.getRemoteId()); if (file.exists()) { - System.out.println(spaces + " Stored plaintext in: " + file); + writer.println("Stored plaintext in: {}", file); } } } + + private String formatContact(SignalServiceAddress address) { + final String number = address.getLegacyIdentifier(); + String name = m.getContactOrProfileName(number); + if (name == null) { + return number; + } else { + return MessageFormatter.arrayFormat("“{}” {}", new Object[]{name, number}).getMessage(); + } + } } diff --git a/src/main/java/org/asamk/signal/util/DateUtils.java b/src/main/java/org/asamk/signal/util/DateUtils.java index c9b92529..f94261bf 100644 --- a/src/main/java/org/asamk/signal/util/DateUtils.java +++ b/src/main/java/org/asamk/signal/util/DateUtils.java @@ -14,7 +14,7 @@ public class DateUtils { public static String formatTimestamp(long timestamp) { Date date = new Date(timestamp); - final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); // Quoted "Z" to indicate UTC, no timezone offset + final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX"); // Quoted "Z" to indicate UTC, no timezone offset df.setTimeZone(tzUTC); return timestamp + " (" + df.format(date) + ")"; } From de273586b4106171a3940ce5ec2c2b3712430c8c Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 21 Feb 2021 15:01:41 +0100 Subject: [PATCH 094/124] Use var instead of explicit types --- .../org/asamk/signal/manager/AvatarStore.java | 2 +- .../asamk/signal/manager/DeviceLinkInfo.java | 24 +- .../asamk/signal/manager/HandleAction.java | 10 +- .../asamk/signal/manager/LibSignalLogger.java | 2 +- .../org/asamk/signal/manager/Manager.java | 566 ++++++++---------- .../signal/manager/ProvisioningManager.java | 23 +- .../signal/manager/RegistrationManager.java | 28 +- .../signal/manager/config/ServiceConfig.java | 4 +- .../asamk/signal/manager/groups/GroupId.java | 2 +- .../manager/groups/GroupInviteLinkUrl.java | 20 +- .../signal/manager/groups/GroupUtils.java | 11 +- .../signal/manager/helper/GroupHelper.java | 122 ++-- .../signal/manager/helper/PinHelper.java | 20 +- .../signal/manager/helper/ProfileHelper.java | 20 +- .../helper/UnidentifiedAccessHelper.java | 16 +- .../signal/manager/storage/SignalAccount.java | 73 +-- .../storage/contacts/JsonContactsStore.java | 6 +- .../manager/storage/groups/GroupInfo.java | 4 +- .../manager/storage/groups/GroupInfoV1.java | 10 +- .../storage/groups/JsonGroupStore.java | 36 +- .../storage/messageCache/MessageCache.java | 10 +- .../storage/profiles/ProfileStore.java | 35 +- .../protocol/JsonIdentityKeyStore.java | 49 +- .../storage/protocol/JsonPreKeyStore.java | 12 +- .../storage/protocol/JsonSessionStore.java | 49 +- .../protocol/JsonSignedPreKeyStore.java | 16 +- .../storage/protocol/RecipientStore.java | 15 +- .../storage/stickers/StickerStore.java | 15 +- .../threads/LegacyJsonThreadStore.java | 6 +- .../signal/manager/util/AttachmentUtils.java | 6 +- .../asamk/signal/manager/util/IOUtils.java | 11 +- .../asamk/signal/manager/util/KeyUtils.java | 24 +- .../manager/util/MessageCacheUtils.java | 24 +- .../asamk/signal/manager/util/PinHashing.java | 6 +- .../signal/manager/util/ProfileUtils.java | 8 +- .../signal/manager/util/StickerUtils.java | 19 +- .../org/asamk/signal/manager/util/Utils.java | 11 +- src/main/java/org/asamk/signal/App.java | 50 +- .../signal/JsonDbusReceiveMessageHandler.java | 25 +- .../signal/JsonReceiveMessageHandler.java | 3 +- src/main/java/org/asamk/signal/Main.java | 9 +- .../org/asamk/signal/PlainTextWriterImpl.java | 2 +- .../asamk/signal/ReceiveMessageHandler.java | 146 ++--- .../asamk/signal/commands/BlockCommand.java | 7 +- .../asamk/signal/commands/DaemonCommand.java | 24 +- .../signal/commands/GetUserStatusCommand.java | 9 +- .../signal/commands/JoinGroupCommand.java | 10 +- .../asamk/signal/commands/LinkCommand.java | 4 +- .../signal/commands/ListContactsCommand.java | 7 +- .../signal/commands/ListDevicesCommand.java | 6 +- .../signal/commands/ListGroupsCommand.java | 14 +- .../commands/ListIdentitiesCommand.java | 12 +- .../signal/commands/QuitGroupCommand.java | 8 +- .../asamk/signal/commands/ReceiveCommand.java | 28 +- .../signal/commands/RegisterCommand.java | 2 +- .../asamk/signal/commands/SendCommand.java | 19 +- .../signal/commands/SendReactionCommand.java | 11 +- .../asamk/signal/commands/SetPinCommand.java | 2 +- .../asamk/signal/commands/TrustCommand.java | 9 +- .../asamk/signal/commands/UnblockCommand.java | 7 +- .../signal/commands/UpdateContactCommand.java | 6 +- .../signal/commands/UpdateGroupCommand.java | 6 +- .../signal/commands/UpdateProfileCommand.java | 11 +- .../commands/UploadStickerPackCommand.java | 4 +- .../asamk/signal/commands/VerifyCommand.java | 4 +- .../org/asamk/signal/dbus/DbusSignalImpl.java | 41 +- .../org/asamk/signal/json/JsonAttachment.java | 6 +- .../asamk/signal/json/JsonDataMessage.java | 9 +- .../signal/json/JsonMessageEnvelope.java | 3 +- .../asamk/signal/json/JsonSyncMessage.java | 2 +- .../asamk/signal/json/JsonTypingMessage.java | 2 +- .../java/org/asamk/signal/util/DateUtils.java | 2 +- .../org/asamk/signal/util/ErrorUtils.java | 10 +- src/main/java/org/asamk/signal/util/Hex.java | 14 +- .../java/org/asamk/signal/util/IOUtils.java | 6 +- .../org/asamk/signal/util/RandomUtils.java | 2 +- src/main/java/org/asamk/signal/util/Util.java | 10 +- 77 files changed, 850 insertions(+), 1017 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java index 712e982c..de59f2af 100644 --- a/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/AvatarStore.java @@ -29,7 +29,7 @@ public class AvatarStore { } public StreamDetails retrieveGroupAvatar(GroupId groupId) throws IOException { - final File groupAvatarFile = getGroupAvatarFile(groupId); + final var groupAvatarFile = getGroupAvatarFile(groupId); return retrieveAvatar(groupAvatarFile); } diff --git a/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java b/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java index b8b27eaa..82d8a37f 100644 --- a/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java @@ -20,14 +20,14 @@ public class DeviceLinkInfo { final ECPublicKey deviceKey; public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws InvalidKeyException { - final String rawQuery = linkUri.getRawQuery(); + final var rawQuery = linkUri.getRawQuery(); if (isEmpty(rawQuery)) { throw new RuntimeException("Invalid device link uri"); } - Map query = getQueryMap(rawQuery); - String deviceIdentifier = query.get("uuid"); - String publicKeyEncoded = query.get("pub_key"); + var query = getQueryMap(rawQuery); + var deviceIdentifier = query.get("uuid"); + var publicKeyEncoded = query.get("pub_key"); if (isEmpty(deviceIdentifier) || isEmpty(publicKeyEncoded)) { throw new RuntimeException("Invalid device link uri"); @@ -39,18 +39,18 @@ public class DeviceLinkInfo { } catch (IllegalArgumentException e) { throw new RuntimeException("Invalid device link uri", e); } - ECPublicKey deviceKey = Curve.decodePoint(publicKeyBytes, 0); + var deviceKey = Curve.decodePoint(publicKeyBytes, 0); return new DeviceLinkInfo(deviceIdentifier, deviceKey); } private static Map getQueryMap(String query) { - String[] params = query.split("&"); - Map map = new HashMap<>(); - for (String param : params) { - final String[] paramParts = param.split("="); - String name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8); - String value = URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8); + var params = query.split("&"); + var map = new HashMap(); + for (var param : params) { + final var paramParts = param.split("="); + var name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8); + var value = URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8); map.put(name, value); } return map; @@ -62,7 +62,7 @@ public class DeviceLinkInfo { } public String createDeviceLinkUri() { - final String deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", ""); + final var deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", ""); return "tsdevice:/?uuid=" + URLEncoder.encode(deviceIdentifier, StandardCharsets.UTF_8) + "&pub_key=" diff --git a/lib/src/main/java/org/asamk/signal/manager/HandleAction.java b/lib/src/main/java/org/asamk/signal/manager/HandleAction.java index 8338e4e6..9d119c11 100644 --- a/lib/src/main/java/org/asamk/signal/manager/HandleAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/HandleAction.java @@ -29,7 +29,7 @@ class SendReceiptAction implements HandleAction { public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - final SendReceiptAction that = (SendReceiptAction) o; + final var that = (SendReceiptAction) o; return timestamp == that.timestamp && address.equals(that.address); } @@ -110,7 +110,7 @@ class SendGroupInfoRequestAction implements HandleAction { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - final SendGroupInfoRequestAction that = (SendGroupInfoRequestAction) o; + final var that = (SendGroupInfoRequestAction) o; if (!address.equals(that.address)) return false; return groupId.equals(that.groupId); @@ -118,7 +118,7 @@ class SendGroupInfoRequestAction implements HandleAction { @Override public int hashCode() { - int result = address.hashCode(); + var result = address.hashCode(); result = 31 * result + groupId.hashCode(); return result; } @@ -144,7 +144,7 @@ class SendGroupInfoAction implements HandleAction { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - final SendGroupInfoAction that = (SendGroupInfoAction) o; + final var that = (SendGroupInfoAction) o; if (!address.equals(that.address)) return false; return groupId.equals(that.groupId); @@ -152,7 +152,7 @@ class SendGroupInfoAction implements HandleAction { @Override public int hashCode() { - int result = address.hashCode(); + var result = address.hashCode(); result = 31 * result + groupId.hashCode(); return result; } diff --git a/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java b/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java index 9118846d..3be4d7e9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java +++ b/lib/src/main/java/org/asamk/signal/manager/LibSignalLogger.java @@ -18,7 +18,7 @@ public class LibSignalLogger implements SignalProtocolLogger { @Override public void log(final int priority, final String tag, final String message) { - final String logMessage = String.format("[%s]: %s", tag, message); + final var logMessage = String.format("[%s]: %s", tag, message); switch (priority) { case SignalProtocolLogger.VERBOSE: logger.trace(logMessage); diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 80ff4e71..caedf2f1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -21,7 +21,6 @@ import org.asamk.signal.manager.config.ServiceEnvironment; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdV1; -import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.GroupUtils; @@ -37,7 +36,6 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.GroupInfoV2; import org.asamk.signal.manager.storage.messageCache.CachedMessage; import org.asamk.signal.manager.storage.profiles.SignalProfile; -import org.asamk.signal.manager.storage.profiles.SignalProfileEntry; import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.manager.storage.stickers.Sticker; import org.asamk.signal.manager.util.AttachmentUtils; @@ -60,11 +58,8 @@ import org.signal.libsignal.metadata.SelfSendException; import org.signal.libsignal.metadata.certificate.CertificateValidator; import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.protos.groups.local.DecryptedGroup; -import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; -import org.signal.storageservice.protos.groups.local.DecryptedMember; import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.VerificationFailedException; -import org.signal.zkgroup.auth.AuthCredentialResponse; import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupSecretParams; import org.signal.zkgroup.profiles.ClientZkProfileOperations; @@ -81,20 +76,17 @@ import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceMessagePipe; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; -import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; @@ -106,7 +98,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifestUpload; import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact; @@ -116,22 +107,18 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; -import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage; import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; -import org.whispersystems.signalservice.api.storage.StorageKey; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.SleepTimer; -import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.internal.contacts.crypto.Quote; @@ -160,7 +147,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -225,7 +211,7 @@ public class Manager implements Closeable { ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); this.groupsV2Api = accountManager.getGroupsV2Api(); - final KeyBackupService keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), + final var keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), @@ -286,29 +272,27 @@ public class Manager implements Closeable { public static Manager init( String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent ) throws IOException, NotRegisteredException { - PathConfig pathConfig = PathConfig.createDefault(settingsPath); + var pathConfig = PathConfig.createDefault(settingsPath); if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { throw new NotRegisteredException(); } - SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username); + var account = SignalAccount.load(pathConfig.getDataPath(), username); if (!account.isRegistered()) { throw new NotRegisteredException(); } - final ServiceEnvironmentConfig serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig( - serviceEnvironment, - userAgent); + final var serviceEnvironmentConfig = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); return new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent); } public static List getAllLocalUsernames(File settingsPath) { - PathConfig pathConfig = PathConfig.createDefault(settingsPath); - final File dataPath = pathConfig.getDataPath(); - final File[] files = dataPath.listFiles(); + var pathConfig = PathConfig.createDefault(settingsPath); + final var dataPath = pathConfig.getDataPath(); + final var files = dataPath.listFiles(); if (files == null) { return List.of(); @@ -342,9 +326,9 @@ public class Manager implements Closeable { */ public Map areUsersRegistered(Set numbers) throws IOException { // Note "contactDetails" has no optionals. It only gives us info on users who are registered - Map contactDetails = getRegisteredUsers(numbers); + var contactDetails = getRegisteredUsers(numbers); - Set registeredUsers = contactDetails.keySet(); + var registeredUsers = contactDetails.keySet(); return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains)); } @@ -370,9 +354,9 @@ public class Manager implements Closeable { * if it's Optional.absent(), the avatar will be removed */ public void setProfile(String name, String about, String aboutEmoji, Optional avatar) throws IOException { - SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(getSelfAddress()); - SignalProfile profile = profileEntry == null ? null : profileEntry.getProfile(); - SignalProfile newProfile = new SignalProfile(profile == null ? null : profile.getIdentityKey(), + var profileEntry = account.getProfileStore().getProfileEntry(getSelfAddress()); + var profile = profileEntry == null ? null : profileEntry.getProfile(); + var newProfile = new SignalProfile(profile == null ? null : profile.getIdentityKey(), name != null ? name : profile == null || profile.getName() == null ? "" : profile.getName(), about != null ? about : profile == null || profile.getAbout() == null ? "" : profile.getAbout(), aboutEmoji != null @@ -382,7 +366,7 @@ public class Manager implements Closeable { account.isUnrestrictedUnidentifiedAccess(), profile == null ? null : profile.getCapabilities()); - try (final StreamDetails streamDetails = avatar == null + try (final var streamDetails = avatar == null ? avatarStore.retrieveProfileAvatar(getSelfAddress()) : avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) { accountManager.setVersionedProfile(account.getUuid(), @@ -426,7 +410,7 @@ public class Manager implements Closeable { } public List getLinkedDevices() throws IOException { - List devices = accountManager.getDevices(); + var devices = accountManager.getDevices(); account.setMultiDevice(devices.size() > 1); account.save(); return devices; @@ -434,20 +418,20 @@ public class Manager implements Closeable { public void removeLinkedDevices(int deviceId) throws IOException { accountManager.removeDevice(deviceId); - List devices = accountManager.getDevices(); + var devices = accountManager.getDevices(); account.setMultiDevice(devices.size() > 1); account.save(); } public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException { - DeviceLinkInfo info = DeviceLinkInfo.parseDeviceLinkUri(linkUri); + var info = DeviceLinkInfo.parseDeviceLinkUri(linkUri); addDevice(info.deviceIdentifier, info.deviceKey); } private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException { - IdentityKeyPair identityKeyPair = getIdentityKeyPair(); - String verificationCode = accountManager.getNewDeviceVerificationCode(); + var identityKeyPair = getIdentityKeyPair(); + var verificationCode = accountManager.getNewDeviceVerificationCode(); accountManager.addDevice(deviceIdentifier, deviceKey, @@ -463,7 +447,7 @@ public class Manager implements Closeable { throw new RuntimeException("Only master device can set a PIN"); } if (pin.isPresent()) { - final MasterKey masterKey = account.getPinMasterKey() != null + final var masterKey = account.getPinMasterKey() != null ? account.getPinMasterKey() : KeyUtils.createMasterKey(); @@ -485,17 +469,17 @@ public class Manager implements Closeable { } void refreshPreKeys() throws IOException { - List oneTimePreKeys = generatePreKeys(); - final IdentityKeyPair identityKeyPair = getIdentityKeyPair(); - SignedPreKeyRecord signedPreKeyRecord = generateSignedPreKey(identityKeyPair); + var oneTimePreKeys = generatePreKeys(); + final var identityKeyPair = getIdentityKeyPair(); + var signedPreKeyRecord = generateSignedPreKey(identityKeyPair); accountManager.setPreKeys(identityKeyPair.getPublicKey(), signedPreKeyRecord, oneTimePreKeys); } private List generatePreKeys() { - final int offset = account.getPreKeyIdOffset(); + final var offset = account.getPreKeyIdOffset(); - List records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE); + var records = KeyUtils.generatePreKeyRecords(offset, ServiceConfig.PREKEY_BATCH_SIZE); account.addPreKeys(records); account.save(); @@ -503,9 +487,9 @@ public class Manager implements Closeable { } private SignedPreKeyRecord generateSignedPreKey(IdentityKeyPair identityKeyPair) { - final int signedPreKeyId = account.getNextSignedPreKeyId(); + final var signedPreKeyId = account.getNextSignedPreKeyId(); - SignedPreKeyRecord record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId); + var record = KeyUtils.generateSignedPreKeyRecord(identityKeyPair, signedPreKeyId); account.addSignedPreKey(record); account.save(); @@ -553,11 +537,11 @@ public class Manager implements Closeable { private SignalProfile getRecipientProfile( SignalServiceAddress address, boolean force ) { - SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address); + var profileEntry = account.getProfileStore().getProfileEntry(address); if (profileEntry == null) { return null; } - long now = new Date().getTime(); + var now = new Date().getTime(); // Profiles are cached for 24h before retrieving them again if (!profileEntry.isRequestPending() && ( force @@ -576,8 +560,8 @@ public class Manager implements Closeable { profileEntry.setRequestPending(false); } - final ProfileKey profileKey = profileEntry.getProfileKey(); - final SignalProfile profile = decryptProfileAndDownloadAvatar(address, profileKey, encryptedProfile); + final var profileKey = profileEntry.getProfileKey(); + final var profile = decryptProfileAndDownloadAvatar(address, profileKey, encryptedProfile); account.getProfileStore() .updateProfile(address, profileKey, now, profile, profileEntry.getProfileKeyCredential()); return profile; @@ -586,7 +570,7 @@ public class Manager implements Closeable { } private ProfileKeyCredential getRecipientProfileKeyCredential(SignalServiceAddress address) { - SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address); + var profileEntry = account.getProfileStore().getProfileEntry(address); if (profileEntry == null) { return null; } @@ -600,9 +584,9 @@ public class Manager implements Closeable { return null; } - long now = new Date().getTime(); - final ProfileKeyCredential profileKeyCredential = profileAndCredential.getProfileKeyCredential().orNull(); - final SignalProfile profile = decryptProfileAndDownloadAvatar(address, + var now = new Date().getTime(); + final var profileKeyCredential = profileAndCredential.getProfileKeyCredential().orNull(); + final var profile = decryptProfileAndDownloadAvatar(address, profileEntry.getProfileKey(), profileAndCredential.getProfile()); account.getProfileStore() @@ -623,7 +607,7 @@ public class Manager implements Closeable { } private Optional createGroupAvatarAttachment(GroupId groupId) throws IOException { - final StreamDetails streamDetails = avatarStore.retrieveGroupAvatar(groupId); + final var streamDetails = avatarStore.retrieveGroupAvatar(groupId); if (streamDetails == null) { return Optional.absent(); } @@ -632,7 +616,7 @@ public class Manager implements Closeable { } private Optional createContactAvatarAttachment(SignalServiceAddress address) throws IOException { - final StreamDetails streamDetails = avatarStore.retrieveContactAvatar(address); + final var streamDetails = avatarStore.retrieveContactAvatar(address); if (streamDetails == null) { return Optional.absent(); } @@ -641,7 +625,7 @@ public class Manager implements Closeable { } private GroupInfo getGroupForSending(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { - GroupInfo g = getGroup(groupId); + var g = getGroup(groupId); if (g == null) { throw new GroupNotFoundException(groupId); } @@ -652,7 +636,7 @@ public class Manager implements Closeable { } private GroupInfo getGroupForUpdating(GroupId groupId) throws GroupNotFoundException, NotAGroupMemberException { - GroupInfo g = getGroup(groupId); + var g = getGroup(groupId); if (g == null) { throw new GroupNotFoundException(groupId); } @@ -669,8 +653,7 @@ public class Manager implements Closeable { public Pair> sendGroupMessage( String messageText, List attachments, GroupId groupId ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException { - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withBody(messageText); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments)); } @@ -681,12 +664,11 @@ public class Manager implements Closeable { public Pair> sendGroupMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, GroupId groupId ) throws IOException, InvalidNumberException, NotAGroupMemberException, GroupNotFoundException { - SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, + var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, canonicalizeAndResolveSignalServiceAddress(targetAuthor), targetSentTimestamp); - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withReaction(reaction); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); return sendGroupMessage(messageBuilder, groupId); } @@ -694,7 +676,7 @@ public class Manager implements Closeable { public Pair> sendGroupMessage( SignalServiceDataMessage.Builder messageBuilder, GroupId groupId ) throws IOException, GroupNotFoundException, NotAGroupMemberException { - final GroupInfo g = getGroupForSending(groupId); + final var g = getGroupForSending(groupId); GroupUtils.setGroupContext(messageBuilder, g); messageBuilder.withExpiration(g.getMessageExpirationTime()); @@ -705,18 +687,16 @@ public class Manager implements Closeable { public Pair> sendQuitGroupMessage(GroupId groupId) throws GroupNotFoundException, IOException, NotAGroupMemberException { SignalServiceDataMessage.Builder messageBuilder; - final GroupInfo g = getGroupForUpdating(groupId); + final var g = getGroupForUpdating(groupId); if (g instanceof GroupInfoV1) { - GroupInfoV1 groupInfoV1 = (GroupInfoV1) g; - SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT) - .withId(groupId.serialize()) - .build(); + var groupInfoV1 = (GroupInfoV1) g; + var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT).withId(groupId.serialize()).build(); messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group); groupInfoV1.removeMember(account.getSelfAddress()); account.getGroupStore().updateGroup(groupInfoV1); } else { - final GroupInfoV2 groupInfoV2 = (GroupInfoV2) g; - final Pair groupGroupChangePair = groupHelper.leaveGroup(groupInfoV2); + final var groupInfoV2 = (GroupInfoV2) g; + final var groupGroupChangePair = groupHelper.leaveGroup(groupInfoV2); groupInfoV2.setGroup(groupGroupChangePair.first()); messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray()); account.getGroupStore().updateGroup(groupInfoV2); @@ -741,11 +721,11 @@ public class Manager implements Closeable { SignalServiceDataMessage.Builder messageBuilder; if (groupId == null) { // Create new group - GroupInfoV2 gv2 = groupHelper.createGroupV2(name == null ? "" : name, + var gv2 = groupHelper.createGroupV2(name == null ? "" : name, members == null ? List.of() : members, avatarFile); if (gv2 == null) { - GroupInfoV1 gv1 = new GroupInfoV1(GroupIdV1.createRandom()); + var gv1 = new GroupInfoV1(GroupIdV1.createRandom()); gv1.addMembers(List.of(account.getSelfAddress())); updateGroupV1(gv1, name, members, avatarFile); messageBuilder = getGroupUpdateMessageBuilder(gv1); @@ -759,36 +739,33 @@ public class Manager implements Closeable { g = gv2; } } else { - GroupInfo group = getGroupForUpdating(groupId); + var group = getGroupForUpdating(groupId); if (group instanceof GroupInfoV2) { - final GroupInfoV2 groupInfoV2 = (GroupInfoV2) group; + final var groupInfoV2 = (GroupInfoV2) group; Pair> result = null; if (groupInfoV2.isPendingMember(getSelfAddress())) { - Pair groupGroupChangePair = groupHelper.acceptInvite(groupInfoV2); + var groupGroupChangePair = groupHelper.acceptInvite(groupInfoV2); result = sendUpdateGroupMessage(groupInfoV2, groupGroupChangePair.first(), groupGroupChangePair.second()); } if (members != null) { - final Set newMembers = new HashSet<>(members); + final var newMembers = new HashSet<>(members); newMembers.removeAll(group.getMembers() .stream() .map(this::resolveSignalServiceAddress) .collect(Collectors.toSet())); if (newMembers.size() > 0) { - Pair groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, - newMembers); + var groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, newMembers); result = sendUpdateGroupMessage(groupInfoV2, groupGroupChangePair.first(), groupGroupChangePair.second()); } } if (result == null || name != null || avatarFile != null) { - Pair groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, - name, - avatarFile); + var groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, name, avatarFile); if (avatarFile != null) { avatarStore.storeGroupAvatar(groupInfoV2.getGroupId(), outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream)); @@ -800,7 +777,7 @@ public class Manager implements Closeable { return new Pair<>(group.getGroupId(), result.second()); } else { - GroupInfoV1 gv1 = (GroupInfoV1) group; + var gv1 = (GroupInfoV1) group; updateGroupV1(gv1, name, members, avatarFile); messageBuilder = getGroupUpdateMessageBuilder(gv1); g = gv1; @@ -809,8 +786,7 @@ public class Manager implements Closeable { account.getGroupStore().updateGroup(g); - final Pair> result = sendMessage(messageBuilder, - g.getMembersIncludingPendingWithout(account.getSelfAddress())); + final var result = sendMessage(messageBuilder, g.getMembersIncludingPendingWithout(account.getSelfAddress())); return new Pair<>(g.getGroupId(), result.second()); } @@ -825,15 +801,15 @@ public class Manager implements Closeable { } if (members != null) { - final Set newE164Members = new HashSet<>(); - for (SignalServiceAddress member : members) { + final var newE164Members = new HashSet(); + for (var member : members) { if (g.isMember(member) || !member.getNumber().isPresent()) { continue; } newE164Members.add(member.getNumber().get()); } - final Map registeredUsers = getRegisteredUsers(newE164Members); + final var registeredUsers = getRegisteredUsers(newE164Members); if (registeredUsers.size() != newE164Members.size()) { // Some of the new members are not registered on Signal newE164Members.removeAll(registeredUsers.keySet()); @@ -860,12 +836,12 @@ public class Manager implements Closeable { private Pair> sendJoinGroupMessage( GroupInviteLinkUrl inviteLinkUrl ) throws IOException, GroupLinkNotActiveException { - final DecryptedGroupJoinInfo groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), + final var groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword()); - final GroupChange groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(), + final var groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo); - final GroupInfoV2 group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), + final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), groupJoinInfo.getRevision() + 1, groupChange.toByteArray()); @@ -874,7 +850,7 @@ public class Manager implements Closeable { return new Pair<>(group.getGroupId(), List.of()); } - final Pair> result = sendUpdateGroupMessage(group, group.getGroup(), groupChange); + final var result = sendUpdateGroupMessage(group, group.getGroup(), groupChange); return new Pair<>(group.getGroupId(), result.second()); } @@ -886,11 +862,11 @@ public class Manager implements Closeable { private GroupsV2AuthorizationString getGroupAuthForToday( final GroupSecretParams groupSecretParams ) throws IOException { - final int today = currentTimeDays(); + final var today = currentTimeDays(); // Returns credentials for the next 7 days - final HashMap credentials = groupsV2Api.getCredentials(today); + final var credentials = groupsV2Api.getCredentials(today); // TODO cache credentials until they expire - AuthCredentialResponse authCredentialResponse = credentials.get(today); + var authCredentialResponse = credentials.get(today); try { return groupsV2Api.getGroupsV2AuthorizationString(account.getUuid(), today, @@ -905,8 +881,7 @@ public class Manager implements Closeable { GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange ) throws IOException { group.setGroup(newDecryptedGroup); - final SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(group, - groupChange.toByteArray()); + final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChange.toByteArray()); account.getGroupStore().updateGroup(group); return sendMessage(messageBuilder, group.getMembersIncludingPendingWithout(account.getSelfAddress())); } @@ -915,7 +890,7 @@ public class Manager implements Closeable { GroupIdV1 groupId, SignalServiceAddress recipient ) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException { GroupInfoV1 g; - GroupInfo group = getGroupForSending(groupId); + var group = getGroupForSending(groupId); if (!(group instanceof GroupInfoV1)) { throw new RuntimeException("Received an invalid group request for a v2 group!"); } @@ -925,20 +900,20 @@ public class Manager implements Closeable { throw new NotAGroupMemberException(groupId, g.name); } - SignalServiceDataMessage.Builder messageBuilder = getGroupUpdateMessageBuilder(g); + var messageBuilder = getGroupUpdateMessageBuilder(g); // Send group message only to the recipient who requested it return sendMessage(messageBuilder, List.of(recipient)); } private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException { - SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.UPDATE) + var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.UPDATE) .withId(g.getGroupId().serialize()) .withName(g.name) .withMembers(new ArrayList<>(g.getMembers())); try { - final Optional attachment = createGroupAvatarAttachment(g.getGroupId()); + final var attachment = createGroupAvatarAttachment(g.getGroupId()); if (attachment.isPresent()) { group.withAvatar(attachment.get()); } @@ -952,7 +927,7 @@ public class Manager implements Closeable { } private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV2 g, byte[] signedGroupChange) { - SignalServiceGroupV2.Builder group = SignalServiceGroupV2.newBuilder(g.getMasterKey()) + var group = SignalServiceGroupV2.newBuilder(g.getMasterKey()) .withRevision(g.getGroup().getRevision()) .withSignedGroupChange(signedGroupChange); return SignalServiceDataMessage.newBuilder() @@ -963,11 +938,9 @@ public class Manager implements Closeable { Pair> sendGroupInfoRequest( GroupIdV1 groupId, SignalServiceAddress recipient ) throws IOException { - SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO) - .withId(groupId.serialize()); + var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.REQUEST_INFO).withId(groupId.serialize()); - SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .asGroupMessage(group.build()); + var messageBuilder = SignalServiceDataMessage.newBuilder().asGroupMessage(group.build()); // Send group info request message to the recipient who sent us a message with this groupId return sendMessage(messageBuilder, List.of(recipient)); @@ -976,7 +949,7 @@ public class Manager implements Closeable { void sendReceipt( SignalServiceAddress remoteAddress, long messageId ) throws IOException, UntrustedIdentityException { - SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, + var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, List.of(messageId), System.currentTimeMillis()); @@ -988,15 +961,14 @@ public class Manager implements Closeable { public Pair> sendMessage( String messageText, List attachments, List recipients ) throws IOException, AttachmentInvalidException, InvalidNumberException { - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withBody(messageText); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { - List attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments); + var attachmentStreams = AttachmentUtils.getSignalServiceAttachments(attachments); // Upload attachments here, so we only upload once even for multiple recipients - SignalServiceMessageSender messageSender = createMessageSender(); - List attachmentPointers = new ArrayList<>(attachmentStreams.size()); - for (SignalServiceAttachment attachment : attachmentStreams) { + var messageSender = createMessageSender(); + var attachmentPointers = new ArrayList(attachmentStreams.size()); + for (var attachment : attachmentStreams) { if (attachment.isStream()) { attachmentPointers.add(messageSender.uploadAttachment(attachment.asStream())); } else if (attachment.isPointer()) { @@ -1012,8 +984,7 @@ public class Manager implements Closeable { public Pair sendSelfMessage( String messageText, List attachments ) throws IOException, AttachmentInvalidException { - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withBody(messageText); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText); if (attachments != null) { messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments)); } @@ -1023,23 +994,22 @@ public class Manager implements Closeable { public Pair> sendMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List recipients ) throws IOException, InvalidNumberException { - SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, + var reaction = new SignalServiceDataMessage.Reaction(emoji, remove, canonicalizeAndResolveSignalServiceAddress(targetAuthor), targetSentTimestamp); - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .withReaction(reaction); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction); return sendMessage(messageBuilder, getSignalServiceAddresses(recipients)); } public Pair> sendEndSessionMessage(List recipients) throws IOException, InvalidNumberException { - SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage(); + var messageBuilder = SignalServiceDataMessage.newBuilder().asEndSessionMessage(); - final Collection signalServiceAddresses = getSignalServiceAddresses(recipients); + final var signalServiceAddresses = getSignalServiceAddresses(recipients); try { return sendMessage(messageBuilder, signalServiceAddresses); } catch (Exception e) { - for (SignalServiceAddress address : signalServiceAddresses) { + for (var address : signalServiceAddresses) { handleEndSession(address); } account.save(); @@ -1048,7 +1018,7 @@ public class Manager implements Closeable { } public String getContactName(String number) throws InvalidNumberException { - ContactInfo contact = account.getContactStore().getContact(canonicalizeAndResolveSignalServiceAddress(number)); + var contact = account.getContactStore().getContact(canonicalizeAndResolveSignalServiceAddress(number)); if (contact == null) { return ""; } else { @@ -1057,8 +1027,8 @@ public class Manager implements Closeable { } public void setContactName(String number, String name) throws InvalidNumberException { - final SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(number); - ContactInfo contact = account.getContactStore().getContact(address); + final var address = canonicalizeAndResolveSignalServiceAddress(number); + var contact = account.getContactStore().getContact(address); if (contact == null) { contact = new ContactInfo(address); } @@ -1072,7 +1042,7 @@ public class Manager implements Closeable { } private void setContactBlocked(SignalServiceAddress address, boolean blocked) { - ContactInfo contact = account.getContactStore().getContact(address); + var contact = account.getContactStore().getContact(address); if (contact == null) { contact = new ContactInfo(address); } @@ -1082,7 +1052,7 @@ public class Manager implements Closeable { } public void setGroupBlocked(final GroupId groupId, final boolean blocked) throws GroupNotFoundException { - GroupInfo group = getGroup(groupId); + var group = getGroup(groupId); if (group == null) { throw new GroupNotFoundException(groupId); } @@ -1096,7 +1066,7 @@ public class Manager implements Closeable { * Change the expiration timer for a contact */ public void setExpirationTimer(SignalServiceAddress address, int messageExpirationTimer) throws IOException { - ContactInfo contact = account.getContactStore().getContact(address); + var contact = account.getContactStore().getContact(address); contact.messageExpirationTime = messageExpirationTimer; account.getContactStore().updateContact(contact); sendExpirationTimerUpdate(address); @@ -1104,8 +1074,7 @@ public class Manager implements Closeable { } private void sendExpirationTimerUpdate(SignalServiceAddress address) throws IOException { - final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder() - .asExpirationUpdate(); + final var messageBuilder = SignalServiceDataMessage.newBuilder().asExpirationUpdate(); sendMessage(messageBuilder, List.of(address)); } @@ -1115,7 +1084,7 @@ public class Manager implements Closeable { public void setExpirationTimer( String number, int messageExpirationTimer ) throws IOException, InvalidNumberException { - SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(number); + var address = canonicalizeAndResolveSignalServiceAddress(number); setExpirationTimer(address, messageExpirationTimer); } @@ -1123,9 +1092,9 @@ public class Manager implements Closeable { * Change the expiration timer for a group */ public void setExpirationTimer(GroupId groupId, int messageExpirationTimer) { - GroupInfo g = getGroup(groupId); + var g = getGroup(groupId); if (g instanceof GroupInfoV1) { - GroupInfoV1 groupInfoV1 = (GroupInfoV1) g; + var groupInfoV1 = (GroupInfoV1) g; groupInfoV1.messageExpirationTime = messageExpirationTimer; account.getGroupStore().updateGroup(groupInfoV1); } else { @@ -1140,14 +1109,14 @@ public class Manager implements Closeable { * @return if successful, returns the URL to install the sticker pack in the signal app */ public String uploadStickerPack(File path) throws IOException, StickerPackInvalidException { - SignalServiceStickerManifestUpload manifest = StickerUtils.getSignalServiceStickerManifestUpload(path); + var manifest = StickerUtils.getSignalServiceStickerManifestUpload(path); - SignalServiceMessageSender messageSender = createMessageSender(); + var messageSender = createMessageSender(); - byte[] packKey = KeyUtils.createStickerUploadKey(); - String packId = messageSender.uploadStickerManifest(manifest, packKey); + var packKey = KeyUtils.createStickerUploadKey(); + var packId = messageSender.uploadStickerManifest(manifest, packKey); - Sticker sticker = new Sticker(Hex.fromStringCondensed(packId), packKey); + var sticker = new Sticker(Hex.fromStringCondensed(packId), packKey); account.getStickerStore().updateSticker(sticker); account.save(); @@ -1164,10 +1133,10 @@ public class Manager implements Closeable { } void requestSyncGroups() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1176,10 +1145,10 @@ public class Manager implements Closeable { } void requestSyncContacts() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1188,10 +1157,10 @@ public class Manager implements Closeable { } void requestSyncBlocked() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1200,10 +1169,10 @@ public class Manager implements Closeable { } void requestSyncConfiguration() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1212,10 +1181,10 @@ public class Manager implements Closeable { } void requestSyncKeys() throws IOException { - SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder() + var r = SignalServiceProtos.SyncMessage.Request.newBuilder() .setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS) .build(); - SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); + var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); try { sendSyncMessage(message); } catch (UntrustedIdentityException e) { @@ -1238,7 +1207,7 @@ public class Manager implements Closeable { } private void sendSyncMessage(SignalServiceSyncMessage message) throws IOException, UntrustedIdentityException { - SignalServiceMessageSender messageSender = createMessageSender(); + var messageSender = createMessageSender(); try { messageSender.sendMessage(message, unidentifiedAccessHelper.getAccessForSync()); } catch (UntrustedIdentityException e) { @@ -1251,11 +1220,11 @@ public class Manager implements Closeable { } private Collection getSignalServiceAddresses(Collection numbers) throws InvalidNumberException { - final Set signalServiceAddresses = new HashSet<>(numbers.size()); - final Set addressesMissingUuid = new HashSet<>(); + final var signalServiceAddresses = new HashSet(numbers.size()); + final var addressesMissingUuid = new HashSet(); - for (String number : numbers) { - final SignalServiceAddress resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number); + for (var number : numbers) { + final var resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number); if (resolvedAddress.getUuid().isPresent()) { signalServiceAddresses.add(resolvedAddress); } else { @@ -1263,7 +1232,7 @@ public class Manager implements Closeable { } } - final Set numbersMissingUuid = addressesMissingUuid.stream() + final var numbersMissingUuid = addressesMissingUuid.stream() .map(a -> a.getNumber().get()) .collect(Collectors.toSet()); Map registeredUsers; @@ -1274,11 +1243,10 @@ public class Manager implements Closeable { registeredUsers = Map.of(); } - for (SignalServiceAddress address : addressesMissingUuid) { - final String number = address.getNumber().get(); + for (var address : addressesMissingUuid) { + final var number = address.getNumber().get(); if (registeredUsers.containsKey(number)) { - final SignalServiceAddress newAddress = resolveSignalServiceAddress(new SignalServiceAddress( - registeredUsers.get(number), + final var newAddress = resolveSignalServiceAddress(new SignalServiceAddress(registeredUsers.get(number), number)); signalServiceAddresses.add(newAddress); } else { @@ -1303,7 +1271,7 @@ public class Manager implements Closeable { SignalServiceDataMessage.Builder messageBuilder, Collection recipients ) throws IOException { recipients = recipients.stream().map(this::resolveSignalServiceAddress).collect(Collectors.toSet()); - final long timestamp = System.currentTimeMillis(); + final var timestamp = System.currentTimeMillis(); messageBuilder.withTimestamp(timestamp); getOrCreateMessagePipe(); getOrCreateUnidentifiedMessagePipe(); @@ -1312,13 +1280,13 @@ public class Manager implements Closeable { message = messageBuilder.build(); if (message.getGroupContext().isPresent()) { try { - SignalServiceMessageSender messageSender = createMessageSender(); - final boolean isRecipientUpdate = false; - List result = messageSender.sendMessage(new ArrayList<>(recipients), + var messageSender = createMessageSender(); + final var isRecipientUpdate = false; + var result = messageSender.sendMessage(new ArrayList<>(recipients), unidentifiedAccessHelper.getAccessFor(recipients), isRecipientUpdate, message); - for (SendMessageResult r : result) { + for (var r : result) { if (r.getIdentityFailure() != null) { account.getSignalProtocolStore() .saveIdentity(r.getAddress(), @@ -1337,10 +1305,10 @@ public class Manager implements Closeable { } else { // Send to all individually, so sync messages are sent correctly messageBuilder.withProfileKey(account.getProfileKey().serialize()); - List results = new ArrayList<>(recipients.size()); - for (SignalServiceAddress address : recipients) { - final ContactInfo contact = account.getContactStore().getContact(address); - final int expirationTime = contact != null ? contact.messageExpirationTime : 0; + var results = new ArrayList(recipients.size()); + for (var address : recipients) { + final var contact = account.getContactStore().getContact(address); + final var expirationTime = contact != null ? contact.messageExpirationTime : 0; messageBuilder.withExpiration(expirationTime); message = messageBuilder.build(); results.add(sendMessage(address, message)); @@ -1349,7 +1317,7 @@ public class Manager implements Closeable { } } finally { if (message != null && message.isEndSession()) { - for (SignalServiceAddress recipient : recipients) { + for (var recipient : recipients) { handleEndSession(recipient); } } @@ -1360,19 +1328,19 @@ public class Manager implements Closeable { private Pair sendSelfMessage( SignalServiceDataMessage.Builder messageBuilder ) throws IOException { - final long timestamp = System.currentTimeMillis(); + final var timestamp = System.currentTimeMillis(); messageBuilder.withTimestamp(timestamp); getOrCreateMessagePipe(); getOrCreateUnidentifiedMessagePipe(); try { - final SignalServiceAddress address = getSelfAddress(); + final var address = getSelfAddress(); - final ContactInfo contact = account.getContactStore().getContact(address); - final int expirationTime = contact != null ? contact.messageExpirationTime : 0; + final var contact = account.getContactStore().getContact(address); + final var expirationTime = contact != null ? contact.messageExpirationTime : 0; messageBuilder.withExpiration(expirationTime); - SignalServiceDataMessage message = messageBuilder.build(); - final SendMessageResult result = sendSelfMessage(message); + var message = messageBuilder.build(); + final var result = sendSelfMessage(message); return new Pair<>(timestamp, result); } finally { account.save(); @@ -1380,21 +1348,21 @@ public class Manager implements Closeable { } private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException { - SignalServiceMessageSender messageSender = createMessageSender(); + var messageSender = createMessageSender(); - SignalServiceAddress recipient = account.getSelfAddress(); + var recipient = account.getSelfAddress(); - final Optional unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(recipient); - SentTranscriptMessage transcript = new SentTranscriptMessage(Optional.of(recipient), + final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(recipient); + var transcript = new SentTranscriptMessage(Optional.of(recipient), message.getTimestamp(), message, message.getExpiresInSeconds(), Map.of(recipient, unidentifiedAccess.isPresent()), false); - SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript); + var syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript); try { - long startTime = System.currentTimeMillis(); + var startTime = System.currentTimeMillis(); messageSender.sendMessage(syncMessage, unidentifiedAccess); return SendMessageResult.success(recipient, unidentifiedAccess.isPresent(), @@ -1412,7 +1380,7 @@ public class Manager implements Closeable { private SendMessageResult sendMessage( SignalServiceAddress address, SignalServiceDataMessage message ) throws IOException { - SignalServiceMessageSender messageSender = createMessageSender(); + var messageSender = createMessageSender(); try { return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(address), message); @@ -1426,16 +1394,15 @@ public class Manager implements Closeable { } private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException { - SignalServiceCipher cipher = new SignalServiceCipher(account.getSelfAddress(), + var cipher = new SignalServiceCipher(account.getSelfAddress(), account.getSignalProtocolStore(), certificateValidator); try { return cipher.decrypt(envelope); } catch (ProtocolUntrustedIdentityException e) { if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) { - org.whispersystems.libsignal.UntrustedIdentityException identityException = (org.whispersystems.libsignal.UntrustedIdentityException) e - .getCause(); - final IdentityKey untrustedIdentity = identityException.getUntrustedIdentity(); + var identityException = (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause(); + final var untrustedIdentity = identityException.getUntrustedIdentity(); if (untrustedIdentity != null) { account.getSignalProtocolStore() .saveIdentity(resolveSignalServiceAddress(identityException.getName()), @@ -1459,14 +1426,14 @@ public class Manager implements Closeable { SignalServiceAddress destination, boolean ignoreAttachments ) { - List actions = new ArrayList<>(); + var actions = new ArrayList(); if (message.getGroupContext().isPresent()) { if (message.getGroupContext().get().getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get(); - GroupIdV1 groupId = GroupId.v1(groupInfo.getGroupId()); - GroupInfo group = getGroup(groupId); + var groupInfo = message.getGroupContext().get().getGroupV1().get(); + var groupId = GroupId.v1(groupInfo.getGroupId()); + var group = getGroup(groupId); if (group == null || group instanceof GroupInfoV1) { - GroupInfoV1 groupV1 = (GroupInfoV1) group; + var groupV1 = (GroupInfoV1) group; switch (groupInfo.getType()) { case UPDATE: { if (groupV1 == null) { @@ -1474,7 +1441,7 @@ public class Manager implements Closeable { } if (groupInfo.getAvatar().isPresent()) { - SignalServiceAttachment avatar = groupInfo.getAvatar().get(); + var avatar = groupInfo.getAvatar().get(); downloadGroupAvatar(avatar, groupV1.getGroupId()); } @@ -1516,8 +1483,8 @@ public class Manager implements Closeable { } } if (message.getGroupContext().get().getGroupV2().isPresent()) { - final SignalServiceGroupV2 groupContext = message.getGroupContext().get().getGroupV2().get(); - final GroupMasterKey groupMasterKey = groupContext.getMasterKey(); + final var groupContext = message.getGroupContext().get().getGroupV2().get(); + final var groupMasterKey = groupContext.getMasterKey(); getOrMigrateGroup(groupMasterKey, groupContext.getRevision(), @@ -1525,15 +1492,15 @@ public class Manager implements Closeable { } } - final SignalServiceAddress conversationPartnerAddress = isSync ? destination : source; + final var conversationPartnerAddress = isSync ? destination : source; if (conversationPartnerAddress != null && message.isEndSession()) { handleEndSession(conversationPartnerAddress); } if (message.isExpirationUpdate() || message.getBody().isPresent()) { if (message.getGroupContext().isPresent()) { if (message.getGroupContext().get().getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get(); - GroupInfoV1 group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId())); + var groupInfo = message.getGroupContext().get().getGroupV1().get(); + var group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId())); if (group != null) { if (group.messageExpirationTime != message.getExpiresInSeconds()) { group.messageExpirationTime = message.getExpiresInSeconds(); @@ -1544,7 +1511,7 @@ public class Manager implements Closeable { // disappearing message timer already stored in the DecryptedGroup } } else if (conversationPartnerAddress != null) { - ContactInfo contact = account.getContactStore().getContact(conversationPartnerAddress); + var contact = account.getContactStore().getContact(conversationPartnerAddress); if (contact == null) { contact = new ContactInfo(conversationPartnerAddress); } @@ -1556,12 +1523,12 @@ public class Manager implements Closeable { } if (!ignoreAttachments) { if (message.getAttachments().isPresent()) { - for (SignalServiceAttachment attachment : message.getAttachments().get()) { + for (var attachment : message.getAttachments().get()) { downloadAttachment(attachment); } } if (message.getSharedContacts().isPresent()) { - for (SharedContact contact : message.getSharedContacts().get()) { + for (var contact : message.getSharedContacts().get()) { if (contact.getAvatar().isPresent()) { downloadAttachment(contact.getAvatar().get().getAttachment()); } @@ -1581,26 +1548,26 @@ public class Manager implements Closeable { this.account.getProfileStore().storeProfileKey(source, profileKey); } if (message.getPreviews().isPresent()) { - final List previews = message.getPreviews().get(); - for (SignalServiceDataMessage.Preview preview : previews) { + final var previews = message.getPreviews().get(); + for (var preview : previews) { if (preview.getImage().isPresent()) { downloadAttachment(preview.getImage().get()); } } } if (message.getQuote().isPresent()) { - final SignalServiceDataMessage.Quote quote = message.getQuote().get(); + final var quote = message.getQuote().get(); - for (SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment : quote.getAttachments()) { - final SignalServiceAttachment thumbnail = quotedAttachment.getThumbnail(); + for (var quotedAttachment : quote.getAttachments()) { + final var thumbnail = quotedAttachment.getThumbnail(); if (thumbnail != null) { downloadAttachment(thumbnail); } } } if (message.getSticker().isPresent()) { - final SignalServiceDataMessage.Sticker messageSticker = message.getSticker().get(); - Sticker sticker = account.getStickerStore().getSticker(messageSticker.getPackId()); + final var messageSticker = message.getSticker().get(); + var sticker = account.getStickerStore().getSticker(messageSticker.getPackId()); if (sticker == null) { sticker = new Sticker(messageSticker.getPackId(), messageSticker.getPackKey()); account.getStickerStore().updateSticker(sticker); @@ -1612,10 +1579,10 @@ public class Manager implements Closeable { private GroupInfoV2 getOrMigrateGroup( final GroupMasterKey groupMasterKey, final int revision, final byte[] signedGroupChange ) { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); - GroupIdV2 groupId = GroupUtils.getGroupIdV2(groupSecretParams); - GroupInfo groupInfo = getGroup(groupId); + var groupId = GroupUtils.getGroupIdV2(groupSecretParams); + var groupInfo = getGroup(groupId); final GroupInfoV2 groupInfoV2; if (groupInfo instanceof GroupInfoV1) { // Received a v2 group message for a v1 group, we need to locally migrate the group @@ -1642,7 +1609,7 @@ public class Manager implements Closeable { } if (group != null) { storeProfileKeysFromMembers(group); - final String avatar = group.getAvatar(); + final var avatar = group.getAvatar(); if (avatar != null && !avatar.isEmpty()) { downloadGroupAvatar(groupId, groupSecretParams, avatar); } @@ -1655,9 +1622,9 @@ public class Manager implements Closeable { } private void storeProfileKeysFromMembers(final DecryptedGroup group) { - for (DecryptedMember member : group.getMembersList()) { - final SignalServiceAddress address = resolveSignalServiceAddress(new SignalServiceAddress(UuidUtil.parseOrThrow( - member.getUuid().toByteArray()), null)); + for (var member : group.getMembersList()) { + final var address = resolveSignalServiceAddress(new SignalServiceAddress(UuidUtil.parseOrThrow(member.getUuid() + .toByteArray()), null)); try { account.getProfileStore() .storeProfileKey(address, new ProfileKey(member.getProfileKey().toByteArray())); @@ -1667,7 +1634,7 @@ public class Manager implements Closeable { } private void retryFailedReceivedMessages(ReceiveMessageHandler handler, boolean ignoreAttachments) { - for (CachedMessage cachedMessage : account.getMessageCache().getCachedMessages()) { + for (var cachedMessage : account.getMessageCache().getCachedMessages()) { retryFailedReceivedMessage(handler, ignoreAttachments, cachedMessage); } } @@ -1675,7 +1642,7 @@ public class Manager implements Closeable { private void retryFailedReceivedMessage( final ReceiveMessageHandler handler, final boolean ignoreAttachments, final CachedMessage cachedMessage ) { - SignalServiceEnvelope envelope = cachedMessage.loadEnvelope(); + var envelope = cachedMessage.loadEnvelope(); if (envelope == null) { return; } @@ -1690,8 +1657,8 @@ public class Manager implements Closeable { cachedMessage.delete(); return; } - List actions = handleMessage(envelope, content, ignoreAttachments); - for (HandleAction action : actions) { + var actions = handleMessage(envelope, content, ignoreAttachments); + for (var action : actions) { try { action.execute(this); } catch (Throwable e) { @@ -1715,9 +1682,9 @@ public class Manager implements Closeable { Set queuedActions = null; - final SignalServiceMessagePipe messagePipe = getOrCreateMessagePipe(); + final var messagePipe = getOrCreateMessagePipe(); - boolean hasCaughtUpWithOldMessages = false; + var hasCaughtUpWithOldMessages = false; while (true) { SignalServiceEnvelope envelope; @@ -1725,7 +1692,7 @@ public class Manager implements Closeable { Exception exception = null; final CachedMessage[] cachedMessage = {null}; try { - Optional result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> { + var result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> { // store message on disk, before acknowledging receipt to the server cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1); }); @@ -1736,7 +1703,7 @@ public class Manager implements Closeable { hasCaughtUpWithOldMessages = true; if (queuedActions != null) { - for (HandleAction action : queuedActions) { + for (var action : queuedActions) { try { action.execute(this); } catch (Throwable e) { @@ -1758,7 +1725,7 @@ public class Manager implements Closeable { if (envelope.hasSource()) { // Store uuid if we don't have it already - SignalServiceAddress source = envelope.getSourceAddress(); + var source = envelope.getSourceAddress(); resolveSignalServiceAddress(source); } if (!envelope.isReceipt()) { @@ -1767,9 +1734,9 @@ public class Manager implements Closeable { } catch (Exception e) { exception = e; } - List actions = handleMessage(envelope, content, ignoreAttachments); + var actions = handleMessage(envelope, content, ignoreAttachments); if (hasCaughtUpWithOldMessages) { - for (HandleAction action : actions) { + for (var action : actions) { try { action.execute(this); } catch (Throwable e) { @@ -1810,16 +1777,16 @@ public class Manager implements Closeable { } else { return false; } - ContactInfo sourceContact = account.getContactStore().getContact(source); + var sourceContact = account.getContactStore().getContact(source); if (sourceContact != null && sourceContact.blocked) { return true; } if (content != null && content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); + var message = content.getDataMessage().get(); if (message.getGroupContext().isPresent()) { - GroupId groupId = GroupUtils.getGroupId(message.getGroupContext().get()); - GroupInfo group = getGroup(groupId); + var groupId = GroupUtils.getGroupId(message.getGroupContext().get()); + var group = getGroup(groupId); if (group != null && group.isBlocked()) { return true; } @@ -1841,16 +1808,16 @@ public class Manager implements Closeable { } if (content != null && content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); + var message = content.getDataMessage().get(); if (message.getGroupContext().isPresent()) { if (message.getGroupContext().get().getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = message.getGroupContext().get().getGroupV1().get(); + var groupInfo = message.getGroupContext().get().getGroupV1().get(); if (groupInfo.getType() == SignalServiceGroup.Type.QUIT) { return false; } } - GroupId groupId = GroupUtils.getGroupId(message.getGroupContext().get()); - GroupInfo group = getGroup(groupId); + var groupId = GroupUtils.getGroupId(message.getGroupContext().get()); + var group = getGroup(groupId); if (group != null && !group.isMember(source)) { return true; } @@ -1862,7 +1829,7 @@ public class Manager implements Closeable { private List handleMessage( SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments ) { - List actions = new ArrayList<>(); + var actions = new ArrayList(); if (content != null) { final SignalServiceAddress sender; if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { @@ -1874,7 +1841,7 @@ public class Manager implements Closeable { resolveSignalServiceAddress(sender); if (content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); + var message = content.getDataMessage().get(); if (content.isNeedsReceipt()) { actions.add(new SendReceiptAction(sender, message.getTimestamp())); @@ -1888,10 +1855,10 @@ public class Manager implements Closeable { } if (content.getSyncMessage().isPresent()) { account.setMultiDevice(true); - SignalServiceSyncMessage syncMessage = content.getSyncMessage().get(); + var syncMessage = content.getSyncMessage().get(); if (syncMessage.getSent().isPresent()) { - SentTranscriptMessage message = syncMessage.getSent().get(); - final SignalServiceAddress destination = message.getDestination().orNull(); + var message = syncMessage.getSent().get(); + final var destination = message.getDestination().orNull(); actions.addAll(handleSignalServiceDataMessage(message.getMessage(), true, sender, @@ -1899,7 +1866,7 @@ public class Manager implements Closeable { ignoreAttachments)); } if (syncMessage.getRequest().isPresent()) { - RequestMessage rm = syncMessage.getRequest().get(); + var rm = syncMessage.getRequest().get(); if (rm.isContactsRequest()) { actions.add(SendSyncContactsAction.create()); } @@ -1915,14 +1882,12 @@ public class Manager implements Closeable { File tmpFile = null; try { tmpFile = IOUtils.createTempFile(); - final SignalServiceAttachment groupsMessage = syncMessage.getGroups().get(); - try (InputStream attachmentAsStream = retrieveAttachmentAsStream(groupsMessage.asPointer(), - tmpFile)) { - DeviceGroupsInputStream s = new DeviceGroupsInputStream(attachmentAsStream); + final var groupsMessage = syncMessage.getGroups().get(); + try (var attachmentAsStream = retrieveAttachmentAsStream(groupsMessage.asPointer(), tmpFile)) { + var s = new DeviceGroupsInputStream(attachmentAsStream); DeviceGroup g; while ((g = s.read()) != null) { - GroupInfoV1 syncGroup = account.getGroupStore() - .getOrCreateGroupV1(GroupId.v1(g.getId())); + var syncGroup = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(g.getId())); if (syncGroup != null) { if (g.getName().isPresent()) { syncGroup.name = g.getName().get(); @@ -1968,11 +1933,11 @@ public class Manager implements Closeable { } } if (syncMessage.getBlockedList().isPresent()) { - final BlockedListMessage blockedListMessage = syncMessage.getBlockedList().get(); - for (SignalServiceAddress address : blockedListMessage.getAddresses()) { + final var blockedListMessage = syncMessage.getBlockedList().get(); + for (var address : blockedListMessage.getAddresses()) { setContactBlocked(resolveSignalServiceAddress(address), true); } - for (GroupId groupId : blockedListMessage.getGroupIds() + for (var groupId : blockedListMessage.getGroupIds() .stream() .map(GroupId::unknownVersion) .collect(Collectors.toSet())) { @@ -1988,10 +1953,10 @@ public class Manager implements Closeable { File tmpFile = null; try { tmpFile = IOUtils.createTempFile(); - final ContactsMessage contactsMessage = syncMessage.getContacts().get(); - try (InputStream attachmentAsStream = retrieveAttachmentAsStream(contactsMessage.getContactsStream() + final var contactsMessage = syncMessage.getContacts().get(); + try (var attachmentAsStream = retrieveAttachmentAsStream(contactsMessage.getContactsStream() .asPointer(), tmpFile)) { - DeviceContactsInputStream s = new DeviceContactsInputStream(attachmentAsStream); + var s = new DeviceContactsInputStream(attachmentAsStream); if (contactsMessage.isComplete()) { account.getContactStore().clear(); } @@ -2000,8 +1965,8 @@ public class Manager implements Closeable { if (c.getAddress().matches(account.getSelfAddress()) && c.getProfileKey().isPresent()) { account.setProfileKey(c.getProfileKey().get()); } - final SignalServiceAddress address = resolveSignalServiceAddress(c.getAddress()); - ContactInfo contact = account.getContactStore().getContact(address); + final var address = resolveSignalServiceAddress(c.getAddress()); + var contact = account.getContactStore().getContact(address); if (contact == null) { contact = new ContactInfo(address); } @@ -2015,7 +1980,7 @@ public class Manager implements Closeable { account.getProfileStore().storeProfileKey(address, c.getProfileKey().get()); } if (c.getVerified().isPresent()) { - final VerifiedMessage verifiedMessage = c.getVerified().get(); + final var verifiedMessage = c.getVerified().get(); account.getSignalProtocolStore() .setIdentityTrustLevel(verifiedMessage.getDestination(), verifiedMessage.getIdentityKey(), @@ -2051,20 +2016,19 @@ public class Manager implements Closeable { } } if (syncMessage.getVerified().isPresent()) { - final VerifiedMessage verifiedMessage = syncMessage.getVerified().get(); + final var verifiedMessage = syncMessage.getVerified().get(); account.getSignalProtocolStore() .setIdentityTrustLevel(resolveSignalServiceAddress(verifiedMessage.getDestination()), verifiedMessage.getIdentityKey(), TrustLevel.fromVerifiedState(verifiedMessage.getVerified())); } if (syncMessage.getStickerPackOperations().isPresent()) { - final List stickerPackOperationMessages = syncMessage.getStickerPackOperations() - .get(); - for (StickerPackOperationMessage m : stickerPackOperationMessages) { + final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get(); + for (var m : stickerPackOperationMessages) { if (!m.getPackId().isPresent()) { continue; } - Sticker sticker = account.getStickerStore().getSticker(m.getPackId().get()); + var sticker = account.getStickerStore().getSticker(m.getPackId().get()); if (sticker == null) { if (!m.getPackKey().isPresent()) { continue; @@ -2085,9 +2049,9 @@ public class Manager implements Closeable { } } if (syncMessage.getKeys().isPresent()) { - final KeysMessage keysMessage = syncMessage.getKeys().get(); + final var keysMessage = syncMessage.getKeys().get(); if (keysMessage.getStorageService().isPresent()) { - final StorageKey storageKey = keysMessage.getStorageService().get(); + final var storageKey = keysMessage.getStorageService().get(); account.setStorageKey(storageKey); } } @@ -2144,9 +2108,9 @@ public class Manager implements Closeable { logger.warn("Invalid state, can't store an attachment stream."); } - SignalServiceAttachmentPointer pointer = attachment.asPointer(); + var pointer = attachment.asPointer(); if (pointer.getPreview().isPresent()) { - final byte[] preview = pointer.getPreview().get(); + final var preview = pointer.getPreview().get(); try { attachmentStore.storeAttachmentPreview(pointer.getRemoteId(), outputStream -> outputStream.write(preview, 0, preview.length)); @@ -2166,15 +2130,15 @@ public class Manager implements Closeable { private void retrieveGroupV2Avatar( GroupSecretParams groupSecretParams, String cdnKey, OutputStream outputStream ) throws IOException { - GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + var groupOperations = groupsV2Operations.forGroup(groupSecretParams); - File tmpFile = IOUtils.createTempFile(); + var tmpFile = IOUtils.createTempFile(); try (InputStream input = messageReceiver.retrieveGroupsV2ProfileAvatar(cdnKey, tmpFile, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { - byte[] encryptedData = IOUtils.readFully(input); + var encryptedData = IOUtils.readFully(input); - byte[] decryptedData = groupOperations.decryptAvatar(encryptedData); + var decryptedData = groupOperations.decryptAvatar(encryptedData); outputStream.write(decryptedData); } finally { try { @@ -2190,8 +2154,8 @@ public class Manager implements Closeable { private void retrieveProfileAvatar( String avatarPath, ProfileKey profileKey, OutputStream outputStream ) throws IOException { - File tmpFile = IOUtils.createTempFile(); - try (InputStream input = messageReceiver.retrieveProfileAvatar(avatarPath, + var tmpFile = IOUtils.createTempFile(); + try (var input = messageReceiver.retrieveProfileAvatar(avatarPath, tmpFile, profileKey, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) { @@ -2212,10 +2176,10 @@ public class Manager implements Closeable { final SignalServiceAttachment attachment, final OutputStream outputStream ) throws IOException { if (attachment.isPointer()) { - SignalServiceAttachmentPointer pointer = attachment.asPointer(); + var pointer = attachment.asPointer(); retrieveAttachmentPointer(pointer, outputStream); } else { - SignalServiceAttachmentStream stream = attachment.asStream(); + var stream = attachment.asStream(); IOUtils.copyStream(stream.getInputStream(), outputStream); } } @@ -2223,8 +2187,8 @@ public class Manager implements Closeable { private void retrieveAttachmentPointer( SignalServiceAttachmentPointer pointer, OutputStream outputStream ) throws IOException { - File tmpFile = IOUtils.createTempFile(); - try (InputStream input = retrieveAttachmentAsStream(pointer, tmpFile)) { + var tmpFile = IOUtils.createTempFile(); + try (var input = retrieveAttachmentAsStream(pointer, tmpFile)) { IOUtils.copyStream(input, outputStream); } catch (MissingConfigurationException | InvalidMessageException e) { throw new IOException(e); @@ -2246,14 +2210,14 @@ public class Manager implements Closeable { } void sendGroups() throws IOException, UntrustedIdentityException { - File groupsFile = IOUtils.createTempFile(); + var groupsFile = IOUtils.createTempFile(); try { try (OutputStream fos = new FileOutputStream(groupsFile)) { - DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(fos); - for (GroupInfo record : getGroups()) { + var out = new DeviceGroupsOutputStream(fos); + for (var record : getGroups()) { if (record instanceof GroupInfoV1) { - GroupInfoV1 groupInfo = (GroupInfoV1) record; + var groupInfo = (GroupInfoV1) record; out.write(new DeviceGroup(groupInfo.getGroupId().serialize(), Optional.fromNullable(groupInfo.name), new ArrayList<>(groupInfo.getMembers()), @@ -2269,8 +2233,8 @@ public class Manager implements Closeable { } if (groupsFile.exists() && groupsFile.length() > 0) { - try (FileInputStream groupsFileStream = new FileInputStream(groupsFile)) { - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + try (var groupsFileStream = new FileInputStream(groupsFile)) { + var attachmentStream = SignalServiceAttachment.newStreamBuilder() .withStream(groupsFileStream) .withContentType("application/octet-stream") .withLength(groupsFile.length()) @@ -2289,14 +2253,14 @@ public class Manager implements Closeable { } public void sendContacts() throws IOException, UntrustedIdentityException { - File contactsFile = IOUtils.createTempFile(); + var contactsFile = IOUtils.createTempFile(); try { try (OutputStream fos = new FileOutputStream(contactsFile)) { - DeviceContactsOutputStream out = new DeviceContactsOutputStream(fos); - for (ContactInfo record : account.getContactStore().getContacts()) { + var out = new DeviceContactsOutputStream(fos); + for (var record : account.getContactStore().getContacts()) { VerifiedMessage verifiedMessage = null; - IdentityInfo currentIdentity = account.getSignalProtocolStore().getIdentity(record.getAddress()); + var currentIdentity = account.getSignalProtocolStore().getIdentity(record.getAddress()); if (currentIdentity != null) { verifiedMessage = new VerifiedMessage(record.getAddress(), currentIdentity.getIdentityKey(), @@ -2304,7 +2268,7 @@ public class Manager implements Closeable { currentIdentity.getDateAdded().getTime()); } - ProfileKey profileKey = account.getProfileStore().getProfileKey(record.getAddress()); + var profileKey = account.getProfileStore().getProfileKey(record.getAddress()); out.write(new DeviceContact(record.getAddress(), Optional.fromNullable(record.name), createContactAvatarAttachment(record.getAddress()), @@ -2333,8 +2297,8 @@ public class Manager implements Closeable { } if (contactsFile.exists() && contactsFile.length() > 0) { - try (FileInputStream contactsFileStream = new FileInputStream(contactsFile)) { - SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder() + try (var contactsFileStream = new FileInputStream(contactsFile)) { + var attachmentStream = SignalServiceAttachment.newStreamBuilder() .withStream(contactsFileStream) .withContentType("application/octet-stream") .withLength(contactsFile.length()) @@ -2353,14 +2317,14 @@ public class Manager implements Closeable { } void sendBlockedList() throws IOException, UntrustedIdentityException { - List addresses = new ArrayList<>(); - for (ContactInfo record : account.getContactStore().getContacts()) { + var addresses = new ArrayList(); + for (var record : account.getContactStore().getContacts()) { if (record.blocked) { addresses.add(record.getAddress()); } } - List groupIds = new ArrayList<>(); - for (GroupInfo record : getGroups()) { + var groupIds = new ArrayList(); + for (var record : getGroups()) { if (record.isBlocked()) { groupIds.add(record.getGroupId().serialize()); } @@ -2371,7 +2335,7 @@ public class Manager implements Closeable { private void sendVerifiedMessage( SignalServiceAddress destination, IdentityKey identityKey, TrustLevel trustLevel ) throws IOException, UntrustedIdentityException { - VerifiedMessage verifiedMessage = new VerifiedMessage(destination, + var verifiedMessage = new VerifiedMessage(destination, identityKey, trustLevel.toVerifiedState(), System.currentTimeMillis()); @@ -2383,14 +2347,14 @@ public class Manager implements Closeable { } public String getContactOrProfileName(String number) { - final SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(number); + final var address = Utils.getSignalServiceAddressFromIdentifier(number); - final ContactInfo contact = account.getContactStore().getContact(address); + final var contact = account.getContactStore().getContact(address); if (contact != null && !Util.isEmpty(contact.name)) { return contact.name; } - final SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address); + final var profileEntry = account.getProfileStore().getProfileEntry(address); if (profileEntry != null && profileEntry.getProfile() != null) { return profileEntry.getProfile().getName(); } @@ -2399,9 +2363,9 @@ public class Manager implements Closeable { } public GroupInfo getGroup(GroupId groupId) { - final GroupInfo group = account.getGroupStore().getGroup(groupId); + final var group = account.getGroupStore().getGroup(groupId); if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(((GroupInfoV2) group).getMasterKey()); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(((GroupInfoV2) group).getMasterKey()); ((GroupInfoV2) group).setGroup(groupHelper.getDecryptedGroup(groupSecretParams)); account.getGroupStore().updateGroup(group); } @@ -2423,12 +2387,12 @@ public class Manager implements Closeable { * @param fingerprint Fingerprint */ public boolean trustIdentityVerified(String name, byte[] fingerprint) throws InvalidNumberException { - SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); + var address = canonicalizeAndResolveSignalServiceAddress(name); + var ids = account.getSignalProtocolStore().getIdentities(address); if (ids == null) { return false; } - for (IdentityInfo id : ids) { + for (var id : ids) { if (!Arrays.equals(id.getIdentityKey().serialize(), fingerprint)) { continue; } @@ -2453,12 +2417,12 @@ public class Manager implements Closeable { * @param safetyNumber Safety number */ public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) throws InvalidNumberException { - SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); + var address = canonicalizeAndResolveSignalServiceAddress(name); + var ids = account.getSignalProtocolStore().getIdentities(address); if (ids == null) { return false; } - for (IdentityInfo id : ids) { + for (var id : ids) { if (!safetyNumber.equals(computeSafetyNumber(address, id.getIdentityKey()))) { continue; } @@ -2482,12 +2446,12 @@ public class Manager implements Closeable { * @param name username of the identity */ public boolean trustIdentityAllKeys(String name) { - SignalServiceAddress address = resolveSignalServiceAddress(name); - List ids = account.getSignalProtocolStore().getIdentities(address); + var address = resolveSignalServiceAddress(name); + var ids = account.getSignalProtocolStore().getIdentities(address); if (ids == null) { return false; } - for (IdentityInfo id : ids) { + for (var id : ids) { if (id.getTrustLevel() == TrustLevel.UNTRUSTED) { account.getSignalProtocolStore() .setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_UNVERIFIED); @@ -2513,14 +2477,14 @@ public class Manager implements Closeable { } public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException { - String canonicalizedNumber = UuidUtil.isUuid(identifier) + var canonicalizedNumber = UuidUtil.isUuid(identifier) ? identifier : PhoneNumberFormatter.formatNumber(identifier, account.getUsername()); return resolveSignalServiceAddress(canonicalizedNumber); } public SignalServiceAddress resolveSignalServiceAddress(String identifier) { - SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(identifier); + var address = Utils.getSignalServiceAddressFromIdentifier(identifier); return resolveSignalServiceAddress(address); } diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index f48ade7c..0ce43bb9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -79,37 +79,30 @@ public class ProvisioningManager { public static ProvisioningManager init( File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent ) { - PathConfig pathConfig = PathConfig.createDefault(settingsPath); + var pathConfig = PathConfig.createDefault(settingsPath); - final ServiceEnvironmentConfig serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig( - serviceEnvironment, - userAgent); + final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); return new ProvisioningManager(pathConfig, serviceConfiguration, userAgent); } public String getDeviceLinkUri() throws TimeoutException, IOException { - String deviceUuid = accountManager.getNewDeviceUuid(); + var deviceUuid = accountManager.getNewDeviceUuid(); return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri(); } public String finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists { - SignalServiceAccountManager.NewDeviceRegistrationReturn ret = accountManager.finishNewDeviceRegistration( - identityKey, - false, - true, - registrationId, - deviceName); + var ret = accountManager.finishNewDeviceRegistration(identityKey, false, true, registrationId, deviceName); - String username = ret.getNumber(); + var username = ret.getNumber(); // TODO do this check before actually registering if (SignalAccount.userExists(pathConfig.getDataPath(), username)) { throw new UserAlreadyExists(username, SignalAccount.getFileName(pathConfig.getDataPath(), username)); } // Create new account with the synced identity - byte[] profileKeyBytes = ret.getProfileKey(); + var profileKeyBytes = ret.getProfileKey(); ProfileKey profileKey; if (profileKeyBytes == null) { profileKey = KeyUtils.createProfileKey(); @@ -121,7 +114,7 @@ public class ProvisioningManager { } } - try (SignalAccount account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(), + try (var account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(), username, ret.getUuid(), password, @@ -131,7 +124,7 @@ public class ProvisioningManager { profileKey)) { account.save(); - try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { + try (var m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { try { m.refreshPreKeys(); diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index baff2047..6ded4d0b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -22,12 +22,8 @@ import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.helper.PinHelper; import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.util.KeyUtils; -import org.signal.zkgroup.profiles.ProfileKey; -import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.util.KeyHelper; import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.KbsPinData; -import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; import org.whispersystems.signalservice.api.SignalServiceAccountManager; @@ -82,7 +78,7 @@ public class RegistrationManager implements Closeable { groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer); - final KeyBackupService keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), + final var keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(), serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(), serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(), serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(), @@ -93,17 +89,15 @@ public class RegistrationManager implements Closeable { public static RegistrationManager init( String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent ) throws IOException { - PathConfig pathConfig = PathConfig.createDefault(settingsPath); + var pathConfig = PathConfig.createDefault(settingsPath); - final ServiceEnvironmentConfig serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig( - serviceEnvironment, - userAgent); + final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { - IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair(); - int registrationId = KeyHelper.generateRegistrationId(false); + var identityKey = KeyUtils.generateIdentityKeyPair(); + var registrationId = KeyHelper.generateRegistrationId(false); - ProfileKey profileKey = KeyUtils.createProfileKey(); - SignalAccount account = SignalAccount.create(pathConfig.getDataPath(), + var profileKey = KeyUtils.createProfileKey(); + var account = SignalAccount.create(pathConfig.getDataPath(), username, identityKey, registrationId, @@ -113,7 +107,7 @@ public class RegistrationManager implements Closeable { return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); } - SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username); + var account = SignalAccount.load(pathConfig.getDataPath(), username); return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); } @@ -147,12 +141,12 @@ public class RegistrationManager implements Closeable { throw e; } - KbsPinData registrationLockData = pinHelper.getRegistrationLockData(pin, e); + var registrationLockData = pinHelper.getRegistrationLockData(pin, e); if (registrationLockData == null) { throw e; } - String registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock(); + var registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock(); try { response = verifyAccountWithCode(verificationCode, null, registrationLock); } catch (LockedException _e) { @@ -175,7 +169,7 @@ public class RegistrationManager implements Closeable { account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(), TrustLevel.TRUSTED_VERIFIED); - try (Manager m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { + try (var m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { m.refreshPreKeys(); diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java index 0b83f53e..4cf86537 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -39,7 +39,7 @@ public class ServiceConfig { try { TrustStore contactTrustStore = new IasTrustStore(); - KeyStore keyStore = KeyStore.getInstance("BKS"); + var keyStore = KeyStore.getInstance("BKS"); keyStore.load(contactTrustStore.getKeyStoreInputStream(), contactTrustStore.getKeyStorePassword().toCharArray()); @@ -74,7 +74,7 @@ public class ServiceConfig { .header("User-Agent", userAgent) .build()); - final List interceptors = List.of(userAgentInterceptor); + final var interceptors = List.of(userAgentInterceptor); switch (serviceEnvironment) { case LIVE: diff --git a/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java index 5c5d639c..9ecb9630 100644 --- a/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java +++ b/lib/src/main/java/org/asamk/signal/manager/groups/GroupId.java @@ -50,7 +50,7 @@ public abstract class GroupId { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - final GroupId groupId = (GroupId) o; + final var groupId = (GroupId) o; return Arrays.equals(id, groupId.id); } diff --git a/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java index bf9e0e55..dd9dd2d2 100644 --- a/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java +++ b/lib/src/main/java/org/asamk/signal/manager/groups/GroupInviteLinkUrl.java @@ -35,7 +35,7 @@ public final class GroupInviteLinkUrl { * @throws InvalidGroupLinkException If group url, but cannot be parsed. */ public static GroupInviteLinkUrl fromUri(String urlString) throws InvalidGroupLinkException, UnknownGroupLinkVersionException { - URI uri = getGroupUrl(urlString); + var uri = getGroupUrl(urlString); if (uri == null) { return null; @@ -46,21 +46,21 @@ public final class GroupInviteLinkUrl { throw new InvalidGroupLinkException("No path was expected in uri"); } - String encoding = uri.getFragment(); + var encoding = uri.getFragment(); if (encoding == null || encoding.length() == 0) { throw new InvalidGroupLinkException("No reference was in the uri"); } - byte[] bytes = Base64UrlSafe.decodePaddingAgnostic(encoding); - GroupInviteLink groupInviteLink = GroupInviteLink.parseFrom(bytes); + var bytes = Base64UrlSafe.decodePaddingAgnostic(encoding); + var groupInviteLink = GroupInviteLink.parseFrom(bytes); switch (groupInviteLink.getContentsCase()) { case V1CONTENTS: { - GroupInviteLink.GroupInviteLinkContentsV1 groupInviteLinkContentsV1 = groupInviteLink.getV1Contents(); - GroupMasterKey groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.getGroupMasterKey() + var groupInviteLinkContentsV1 = groupInviteLink.getV1Contents(); + var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.getGroupMasterKey() .toByteArray()); - GroupLinkPassword password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.getInviteLinkPassword() + var password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.getInviteLinkPassword() .toByteArray()); return new GroupInviteLinkUrl(groupMasterKey, password); @@ -78,7 +78,7 @@ public final class GroupInviteLinkUrl { */ private static URI getGroupUrl(String urlString) { try { - URI url = new URI(urlString); + var url = new URI(urlString); if (!"https".equalsIgnoreCase(url.getScheme()) && !"sgnl".equalsIgnoreCase(url.getScheme())) { return null; @@ -97,13 +97,13 @@ public final class GroupInviteLinkUrl { } protected static String createUrl(GroupMasterKey groupMasterKey, GroupLinkPassword password) { - GroupInviteLink groupInviteLink = GroupInviteLink.newBuilder() + var groupInviteLink = GroupInviteLink.newBuilder() .setV1Contents(GroupInviteLink.GroupInviteLinkContentsV1.newBuilder() .setGroupMasterKey(ByteString.copyFrom(groupMasterKey.serialize())) .setInviteLinkPassword(ByteString.copyFrom(password.serialize()))) .build(); - String encoding = Base64UrlSafe.encodeBytesWithoutPadding(groupInviteLink.toByteArray()); + var encoding = Base64UrlSafe.encodeBytesWithoutPadding(groupInviteLink.toByteArray()); return GROUP_URL_PREFIX + encoding; } diff --git a/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java b/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java index f56639e3..c1db77bf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/groups/GroupUtils.java @@ -18,13 +18,13 @@ public class GroupUtils { final SignalServiceDataMessage.Builder messageBuilder, final GroupInfo groupInfo ) { if (groupInfo instanceof GroupInfoV1) { - SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.DELIVER) + var group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.DELIVER) .withId(groupInfo.getGroupId().serialize()) .build(); messageBuilder.asGroupMessage(group); } else { - final GroupInfoV2 groupInfoV2 = (GroupInfoV2) groupInfo; - SignalServiceGroupV2 group = SignalServiceGroupV2.newBuilder(groupInfoV2.getMasterKey()) + final var groupInfoV2 = (GroupInfoV2) groupInfo; + var group = SignalServiceGroupV2.newBuilder(groupInfoV2.getMasterKey()) .withRevision(groupInfoV2.getGroup() == null ? 0 : groupInfoV2.getGroup().getRevision()) .build(); messageBuilder.asGroupMessage(group); @@ -46,13 +46,12 @@ public class GroupUtils { } public static GroupIdV2 getGroupIdV2(GroupMasterKey groupMasterKey) { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); return getGroupIdV2(groupSecretParams); } public static GroupIdV2 getGroupIdV2(GroupIdV1 groupIdV1) { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(deriveV2MigrationMasterKey( - groupIdV1)); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(deriveV2MigrationMasterKey(groupIdV1)); return getGroupIdV2(groupSecretParams); } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index d39da8a3..2efc18ad 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -2,7 +2,6 @@ package org.asamk.signal.manager.helper; import com.google.protobuf.InvalidProtocolBufferException; -import org.asamk.signal.manager.groups.GroupIdV2; import org.asamk.signal.manager.groups.GroupLinkPassword; import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.storage.groups.GroupInfoV2; @@ -20,7 +19,6 @@ import org.signal.zkgroup.VerificationFailedException; import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupSecretParams; import org.signal.zkgroup.groups.UuidCiphertext; -import org.signal.zkgroup.profiles.ProfileKeyCredential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.util.Pair; @@ -41,7 +39,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; -import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -80,7 +77,7 @@ public class GroupHelper { public DecryptedGroup getDecryptedGroup(final GroupSecretParams groupSecretParams) { try { - final GroupsV2AuthorizationString groupsV2AuthorizationString = groupAuthorizationProvider.getAuthorizationForToday( + final var groupsV2AuthorizationString = groupAuthorizationProvider.getAuthorizationForToday( groupSecretParams); return groupsV2Api.getGroup(groupSecretParams, groupsV2AuthorizationString); } catch (IOException | VerificationFailedException | InvalidGroupStateException e) { @@ -92,7 +89,7 @@ public class GroupHelper { public DecryptedGroupJoinInfo getDecryptedGroupJoinInfo( GroupMasterKey groupMasterKey, GroupLinkPassword password ) throws IOException, GroupLinkNotActiveException { - GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); + var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); return groupsV2Api.getGroupJoinInfo(groupSecretParams, Optional.fromNullable(password).transform(GroupLinkPassword::serialize), @@ -102,13 +99,13 @@ public class GroupHelper { public GroupInfoV2 createGroupV2( String name, Collection members, File avatarFile ) throws IOException { - final byte[] avatarBytes = readAvatarBytes(avatarFile); - final GroupsV2Operations.NewGroup newGroup = buildNewGroupV2(name, members, avatarBytes); + final var avatarBytes = readAvatarBytes(avatarFile); + final var newGroup = buildNewGroupV2(name, members, avatarBytes); if (newGroup == null) { return null; } - final GroupSecretParams groupSecretParams = newGroup.getGroupSecretParams(); + final var groupSecretParams = newGroup.getGroupSecretParams(); final GroupsV2AuthorizationString groupAuthForToday; final DecryptedGroup decryptedGroup; @@ -125,9 +122,9 @@ public class GroupHelper { return null; } - final GroupIdV2 groupId = GroupUtils.getGroupIdV2(groupSecretParams); - final GroupMasterKey masterKey = groupSecretParams.getMasterKey(); - GroupInfoV2 g = new GroupInfoV2(groupId, masterKey); + final var groupId = GroupUtils.getGroupIdV2(groupSecretParams); + final var masterKey = groupSecretParams.getMasterKey(); + var g = new GroupInfoV2(groupId, masterKey); g.setGroup(decryptedGroup); return g; @@ -144,8 +141,7 @@ public class GroupHelper { private GroupsV2Operations.NewGroup buildNewGroupV2( String name, Collection members, byte[] avatar ) { - final ProfileKeyCredential profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential( - selfAddressProvider.getSelfAddress()); + final var profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential(selfAddressProvider.getSelfAddress()); if (profileKeyCredential == null) { logger.warn("Cannot create a V2 group as self does not have a versioned profile"); return null; @@ -153,14 +149,14 @@ public class GroupHelper { if (!areMembersValid(members)) return null; - GroupCandidate self = new GroupCandidate(selfAddressProvider.getSelfAddress().getUuid().orNull(), + var self = new GroupCandidate(selfAddressProvider.getSelfAddress().getUuid().orNull(), Optional.fromNullable(profileKeyCredential)); - Set candidates = members.stream() + var candidates = members.stream() .map(member -> new GroupCandidate(member.getUuid().get(), Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member)))) .collect(Collectors.toSet()); - final GroupSecretParams groupSecretParams = GroupSecretParams.generate(); + final var groupSecretParams = GroupSecretParams.generate(); return groupsV2Operations.createNewGroup(groupSecretParams, name, Optional.fromNullable(avatar), @@ -171,7 +167,7 @@ public class GroupHelper { } private boolean areMembersValid(final Collection members) { - final Set noUuidCapability = members.stream() + final var noUuidCapability = members.stream() .filter(address -> !address.getUuid().isPresent()) .map(SignalServiceAddress::getLegacyIdentifier) .collect(Collectors.toSet()); @@ -181,7 +177,7 @@ public class GroupHelper { return false; } - final Set noGv2Capability = members.stream() + final var noGv2Capability = members.stream() .map(profileProvider::getProfile) .filter(profile -> profile != null && !profile.getCapabilities().gv2) .collect(Collectors.toSet()); @@ -197,22 +193,20 @@ public class GroupHelper { public Pair updateGroupV2( GroupInfoV2 groupInfoV2, String name, File avatarFile ) throws IOException { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); - GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); + var groupOperations = groupsV2Operations.forGroup(groupSecretParams); - GroupChange.Actions.Builder change = name != null - ? groupOperations.createModifyGroupTitle(name) - : GroupChange.Actions.newBuilder(); + var change = name != null ? groupOperations.createModifyGroupTitle(name) : GroupChange.Actions.newBuilder(); if (avatarFile != null) { - final byte[] avatarBytes = readAvatarBytes(avatarFile); - String avatarCdnKey = groupsV2Api.uploadAvatar(avatarBytes, + final var avatarBytes = readAvatarBytes(avatarFile); + var avatarCdnKey = groupsV2Api.uploadAvatar(avatarBytes, groupSecretParams, groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams)); change.setModifyAvatar(GroupChange.Actions.ModifyAvatarAction.newBuilder().setAvatar(avatarCdnKey)); } - final Optional uuid = this.selfAddressProvider.getSelfAddress().getUuid(); + final var uuid = this.selfAddressProvider.getSelfAddress().getUuid(); if (uuid.isPresent()) { change.setSourceUuid(UuidUtil.toByteString(uuid.get())); } @@ -223,22 +217,22 @@ public class GroupHelper { public Pair updateGroupV2( GroupInfoV2 groupInfoV2, Set newMembers ) throws IOException { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); - GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); + var groupOperations = groupsV2Operations.forGroup(groupSecretParams); if (!areMembersValid(newMembers)) { throw new IOException("Failed to update group"); } - Set candidates = newMembers.stream() + var candidates = newMembers.stream() .map(member -> new GroupCandidate(member.getUuid().get(), Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member)))) .collect(Collectors.toSet()); - final GroupChange.Actions.Builder change = groupOperations.createModifyGroupMembershipChange(candidates, + final var change = groupOperations.createModifyGroupMembershipChange(candidates, selfAddressProvider.getSelfAddress().getUuid().get()); - final Optional uuid = this.selfAddressProvider.getSelfAddress().getUuid(); + final var uuid = this.selfAddressProvider.getSelfAddress().getUuid(); if (uuid.isPresent()) { change.setSourceUuid(UuidUtil.toByteString(uuid.get())); } @@ -247,10 +241,9 @@ public class GroupHelper { } public Pair leaveGroup(GroupInfoV2 groupInfoV2) throws IOException { - List pendingMembersList = groupInfoV2.getGroup().getPendingMembersList(); - final UUID selfUuid = selfAddressProvider.getSelfAddress().getUuid().get(); - Optional selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList, - selfUuid); + var pendingMembersList = groupInfoV2.getGroup().getPendingMembersList(); + final var selfUuid = selfAddressProvider.getSelfAddress().getUuid().get(); + var selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList, selfUuid); if (selfPendingMember.isPresent()) { return revokeInvites(groupInfoV2, Set.of(selfPendingMember.get())); @@ -264,19 +257,17 @@ public class GroupHelper { GroupLinkPassword groupLinkPassword, DecryptedGroupJoinInfo decryptedGroupJoinInfo ) throws IOException { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); - final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey); + final var groupOperations = groupsV2Operations.forGroup(groupSecretParams); - final SignalServiceAddress selfAddress = this.selfAddressProvider.getSelfAddress(); - final ProfileKeyCredential profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential( - selfAddress); + final var selfAddress = this.selfAddressProvider.getSelfAddress(); + final var profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential(selfAddress); if (profileKeyCredential == null) { throw new IOException("Cannot join a V2 group as self does not have a versioned profile"); } - boolean requestToJoin = decryptedGroupJoinInfo.getAddFromInviteLink() - == AccessControl.AccessRequired.ADMINISTRATOR; - GroupChange.Actions.Builder change = requestToJoin + var requestToJoin = decryptedGroupJoinInfo.getAddFromInviteLink() == AccessControl.AccessRequired.ADMINISTRATOR; + var change = requestToJoin ? groupOperations.createGroupJoinRequest(profileKeyCredential) : groupOperations.createGroupJoinDirect(profileKeyCredential); @@ -286,19 +277,18 @@ public class GroupHelper { } public Pair acceptInvite(GroupInfoV2 groupInfoV2) throws IOException { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); - final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); + final var groupOperations = groupsV2Operations.forGroup(groupSecretParams); - final SignalServiceAddress selfAddress = this.selfAddressProvider.getSelfAddress(); - final ProfileKeyCredential profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential( - selfAddress); + final var selfAddress = this.selfAddressProvider.getSelfAddress(); + final var profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential(selfAddress); if (profileKeyCredential == null) { throw new IOException("Cannot join a V2 group as self does not have a versioned profile"); } - final GroupChange.Actions.Builder change = groupOperations.createAcceptInviteChange(profileKeyCredential); + final var change = groupOperations.createAcceptInviteChange(profileKeyCredential); - final Optional uuid = selfAddress.getUuid(); + final var uuid = selfAddress.getUuid(); if (uuid.isPresent()) { change.setSourceUuid(UuidUtil.toByteString(uuid.get())); } @@ -309,9 +299,9 @@ public class GroupHelper { public Pair revokeInvites( GroupInfoV2 groupInfoV2, Set pendingMembers ) throws IOException { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); - final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); - final Set uuidCipherTexts = pendingMembers.stream().map(member -> { + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); + final var groupOperations = groupsV2Operations.forGroup(groupSecretParams); + final var uuidCipherTexts = pendingMembers.stream().map(member -> { try { return new UuidCiphertext(member.getUuidCipherText().toByteArray()); } catch (InvalidInputException e) { @@ -322,19 +312,19 @@ public class GroupHelper { } public Pair ejectMembers(GroupInfoV2 groupInfoV2, Set uuids) throws IOException { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); - final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); + final var groupOperations = groupsV2Operations.forGroup(groupSecretParams); return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(uuids)); } private Pair commitChange( GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change ) throws IOException { - final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); - final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams); - final DecryptedGroup previousGroupState = groupInfoV2.getGroup(); - final int nextRevision = previousGroupState.getRevision() + 1; - final GroupChange.Actions changeActions = change.setRevision(nextRevision).build(); + final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); + final var groupOperations = groupsV2Operations.forGroup(groupSecretParams); + final var previousGroupState = groupInfoV2.getGroup(); + final var nextRevision = previousGroupState.getRevision() + 1; + final var changeActions = change.setRevision(nextRevision).build(); final DecryptedGroupChange decryptedChange; final DecryptedGroup decryptedGroupState; @@ -346,7 +336,7 @@ public class GroupHelper { throw new IOException(e); } - GroupChange signedGroupChange = groupsV2Api.patchGroup(changeActions, + var signedGroupChange = groupsV2Api.patchGroup(changeActions, groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams), Optional.absent()); @@ -359,8 +349,8 @@ public class GroupHelper { GroupChange.Actions.Builder change, GroupLinkPassword password ) throws IOException { - final int nextRevision = currentRevision + 1; - final GroupChange.Actions changeActions = change.setRevision(nextRevision).build(); + final var nextRevision = currentRevision + 1; + final var changeActions = change.setRevision(nextRevision).build(); return groupsV2Api.patchGroup(changeActions, groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams), @@ -371,8 +361,7 @@ public class GroupHelper { DecryptedGroup group, byte[] signedGroupChange, GroupMasterKey groupMasterKey ) { try { - final DecryptedGroupChange decryptedGroupChange = getDecryptedGroupChange(signedGroupChange, - groupMasterKey); + final var decryptedGroupChange = getDecryptedGroupChange(signedGroupChange, groupMasterKey); if (decryptedGroupChange == null) { return null; } @@ -384,8 +373,7 @@ public class GroupHelper { private DecryptedGroupChange getDecryptedGroupChange(byte[] signedGroupChange, GroupMasterKey groupMasterKey) { if (signedGroupChange != null) { - GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey( - groupMasterKey)); + var groupOperations = groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey(groupMasterKey)); try { return groupOperations.decryptChange(GroupChange.parseFrom(signedGroupChange), true).orNull(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java index 090b20b6..cf98c4c9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java @@ -6,7 +6,6 @@ import org.whispersystems.signalservice.api.KbsPinData; import org.whispersystems.signalservice.api.KeyBackupService; import org.whispersystems.signalservice.api.KeyBackupServicePinException; import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; -import org.whispersystems.signalservice.api.kbs.HashedPin; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse; @@ -25,15 +24,15 @@ public class PinHelper { public void setRegistrationLockPin( String pin, MasterKey masterKey ) throws IOException, UnauthenticatedResponseException { - final KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession(); - final HashedPin hashedPin = PinHashing.hashPin(pin, pinChangeSession); + final var pinChangeSession = keyBackupService.newPinChangeSession(); + final var hashedPin = PinHashing.hashPin(pin, pinChangeSession); pinChangeSession.setPin(hashedPin, masterKey); pinChangeSession.enableRegistrationLock(masterKey); } public void removeRegistrationLockPin() throws IOException, UnauthenticatedResponseException { - final KeyBackupService.PinChangeSession pinChangeSession = keyBackupService.newPinChangeSession(); + final var pinChangeSession = keyBackupService.newPinChangeSession(); pinChangeSession.disableRegistrationLock(); pinChangeSession.removePin(); } @@ -41,7 +40,7 @@ public class PinHelper { public KbsPinData getRegistrationLockData( String pin, LockedException e ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { - String basicStorageCredentials = e.getBasicStorageCredentials(); + var basicStorageCredentials = e.getBasicStorageCredentials(); if (basicStorageCredentials == null) { return null; } @@ -52,12 +51,12 @@ public class PinHelper { private KbsPinData getRegistrationLockData( String pin, String basicStorageCredentials ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { - TokenResponse tokenResponse = keyBackupService.getToken(basicStorageCredentials); + var tokenResponse = keyBackupService.getToken(basicStorageCredentials); if (tokenResponse == null || tokenResponse.getTries() == 0) { throw new IOException("KBS Account locked"); } - KbsPinData registrationLockData = restoreMasterKey(pin, basicStorageCredentials, tokenResponse); + var registrationLockData = restoreMasterKey(pin, basicStorageCredentials, tokenResponse); if (registrationLockData == null) { throw new AssertionError("Failed to restore master key"); } @@ -73,12 +72,11 @@ public class PinHelper { throw new AssertionError("Cannot restore KBS key, no storage credentials supplied"); } - KeyBackupService.RestoreSession session = keyBackupService.newRegistrationSession(basicStorageCredentials, - tokenResponse); + var session = keyBackupService.newRegistrationSession(basicStorageCredentials, tokenResponse); try { - HashedPin hashedPin = PinHashing.hashPin(pin, session); - KbsPinData kbsData = session.restorePin(hashedPin); + var hashedPin = PinHashing.hashPin(pin, session); + var kbsData = session.restorePin(hashedPin); if (kbsData == null) { throw new AssertionError("Null not expected"); } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java index 60c47d8b..5411bb06 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java @@ -2,10 +2,7 @@ package org.asamk.signal.manager.helper; import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.SignalServiceMessagePipe; -import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; -import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -63,8 +60,8 @@ public final class ProfileHelper { public ListenableFuture retrieveProfile( SignalServiceAddress address, SignalServiceProfile.RequestType requestType ) { - Optional unidentifiedAccess = getUnidentifiedAccess(address); - Optional profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(address)); + var unidentifiedAccess = getUnidentifiedAccess(address); + var profileKey = Optional.fromNullable(profileKeyProvider.getProfileKey(address)); if (unidentifiedAccess.isPresent()) { return new CascadingFuture<>(Arrays.asList(() -> getPipeRetrievalFuture(address, @@ -90,8 +87,8 @@ public final class ProfileHelper { Optional unidentifiedAccess, SignalServiceProfile.RequestType requestType ) throws IOException { - SignalServiceMessagePipe unidentifiedPipe = messagePipeProvider.getMessagePipe(true); - SignalServiceMessagePipe pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() + var unidentifiedPipe = messagePipeProvider.getMessagePipe(true); + var pipe = unidentifiedPipe != null && unidentifiedAccess.isPresent() ? unidentifiedPipe : messagePipeProvider.getMessagePipe(false); if (pipe != null) { @@ -102,8 +99,7 @@ public final class ProfileHelper { if (!address.getNumber().isPresent()) { throw new NotFoundException("Can't request profile without number"); } - SignalServiceAddress addressWithoutUuid = new SignalServiceAddress(Optional.absent(), - address.getNumber()); + var addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); return pipe.getProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); } } @@ -117,7 +113,7 @@ public final class ProfileHelper { Optional unidentifiedAccess, SignalServiceProfile.RequestType requestType ) throws NotFoundException { - SignalServiceMessageReceiver receiver = messageReceiverProvider.getMessageReceiver(); + var receiver = messageReceiverProvider.getMessageReceiver(); try { return receiver.retrieveProfile(address, profileKey, unidentifiedAccess, requestType); } catch (NoClassDefFoundError e) { @@ -125,13 +121,13 @@ public final class ProfileHelper { if (!address.getNumber().isPresent()) { throw new NotFoundException("Can't request profile without number"); } - SignalServiceAddress addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); + var addressWithoutUuid = new SignalServiceAddress(Optional.absent(), address.getNumber()); return receiver.retrieveProfile(addressWithoutUuid, profileKey, unidentifiedAccess, requestType); } } private Optional getUnidentifiedAccess(SignalServiceAddress recipient) { - Optional unidentifiedAccess = unidentifiedAccessProvider.getAccessFor(recipient); + var unidentifiedAccess = unidentifiedAccessProvider.getAccessFor(recipient); if (unidentifiedAccess.isPresent()) { return unidentifiedAccess.get().getTargetUnidentifiedAccess(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java index 3930154c..a3b8e3b5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java @@ -1,8 +1,6 @@ package org.asamk.signal.manager.helper; -import org.asamk.signal.manager.storage.profiles.SignalProfile; import org.signal.libsignal.metadata.certificate.InvalidCertificateException; -import org.signal.zkgroup.profiles.ProfileKey; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; @@ -41,12 +39,12 @@ public class UnidentifiedAccessHelper { } public byte[] getTargetUnidentifiedAccessKey(SignalServiceAddress recipient) { - ProfileKey theirProfileKey = profileKeyProvider.getProfileKey(recipient); + var theirProfileKey = profileKeyProvider.getProfileKey(recipient); if (theirProfileKey == null) { return null; } - SignalProfile targetProfile = profileProvider.getProfile(recipient); + var targetProfile = profileProvider.getProfile(recipient); if (targetProfile == null || targetProfile.getUnidentifiedAccess() == null) { return null; } @@ -59,8 +57,8 @@ public class UnidentifiedAccessHelper { } public Optional getAccessForSync() { - byte[] selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(); - byte[] selfUnidentifiedAccessCertificate = senderCertificateProvider.getSenderCertificate(); + var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(); + var selfUnidentifiedAccessCertificate = senderCertificateProvider.getSenderCertificate(); if (selfUnidentifiedAccessKey == null || selfUnidentifiedAccessCertificate == null) { return Optional.absent(); @@ -80,9 +78,9 @@ public class UnidentifiedAccessHelper { } public Optional getAccessFor(SignalServiceAddress recipient) { - byte[] recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient); - byte[] selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(); - byte[] selfUnidentifiedAccessCertificate = senderCertificateProvider.getSenderCertificate(); + var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient); + var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(); + var selfUnidentifiedAccessCertificate = senderCertificateProvider.getSenderCertificate(); if (recipientUnidentifiedAccessKey == null || selfUnidentifiedAccessKey == null diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index bac42733..d4e2a253 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -8,24 +8,18 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.node.ObjectNode; import org.asamk.signal.manager.groups.GroupId; -import org.asamk.signal.manager.storage.contacts.ContactInfo; import org.asamk.signal.manager.storage.contacts.JsonContactsStore; -import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.storage.groups.GroupInfoV1; import org.asamk.signal.manager.storage.groups.JsonGroupStore; import org.asamk.signal.manager.storage.messageCache.MessageCache; import org.asamk.signal.manager.storage.profiles.ProfileStore; -import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.manager.storage.protocol.JsonSignalProtocolStore; import org.asamk.signal.manager.storage.protocol.RecipientStore; -import org.asamk.signal.manager.storage.protocol.SessionInfo; import org.asamk.signal.manager.storage.protocol.SignalServiceAddressResolver; import org.asamk.signal.manager.storage.stickers.StickerStore; import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore; -import org.asamk.signal.manager.storage.threads.ThreadInfo; import org.asamk.signal.manager.util.IOUtils; import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.Utils; @@ -99,10 +93,10 @@ public class SignalAccount implements Closeable { } public static SignalAccount load(File dataPath, String username) throws IOException { - final File fileName = getFileName(dataPath, username); - final Pair pair = openFileChannel(fileName); + final var fileName = getFileName(dataPath, username); + final var pair = openFileChannel(fileName); try { - SignalAccount account = new SignalAccount(pair.first(), pair.second()); + var account = new SignalAccount(pair.first(), pair.second()); account.load(dataPath); account.migrateLegacyConfigs(); @@ -118,13 +112,13 @@ public class SignalAccount implements Closeable { File dataPath, String username, IdentityKeyPair identityKey, int registrationId, ProfileKey profileKey ) throws IOException { IOUtils.createPrivateDirectories(dataPath); - File fileName = getFileName(dataPath, username); + var fileName = getFileName(dataPath, username); if (!fileName.exists()) { IOUtils.createPrivateFile(fileName); } - final Pair pair = openFileChannel(fileName); - SignalAccount account = new SignalAccount(pair.first(), pair.second()); + final var pair = openFileChannel(fileName); + var account = new SignalAccount(pair.first(), pair.second()); account.username = username; account.profileKey = profileKey; @@ -155,13 +149,13 @@ public class SignalAccount implements Closeable { ProfileKey profileKey ) throws IOException { IOUtils.createPrivateDirectories(dataPath); - File fileName = getFileName(dataPath, username); + var fileName = getFileName(dataPath, username); if (!fileName.exists()) { IOUtils.createPrivateFile(fileName); } - final Pair pair = openFileChannel(fileName); - SignalAccount account = new SignalAccount(pair.first(), pair.second()); + final var pair = openFileChannel(fileName); + var account = new SignalAccount(pair.first(), pair.second()); account.username = username; account.uuid = uuid; @@ -192,8 +186,8 @@ public class SignalAccount implements Closeable { save(); } // Store profile keys only in profile store - for (ContactInfo contact : getContactStore().getContacts()) { - String profileKeyString = contact.profileKey; + for (var contact : getContactStore().getContacts()) { + var profileKeyString = contact.profileKey; if (profileKeyString == null) { continue; } @@ -230,7 +224,7 @@ public class SignalAccount implements Closeable { if (username == null) { return false; } - File f = getFileName(dataPath, username); + var f = getFileName(dataPath, username); return !(!f.exists() || f.isDirectory()); } @@ -288,7 +282,7 @@ public class SignalAccount implements Closeable { signalProtocolStore = jsonProcessor.convertValue(Utils.getNotNullNode(rootNode, "axolotlStore"), JsonSignalProtocolStore.class); registered = Utils.getNotNullNode(rootNode, "registered").asBoolean(); - JsonNode groupStoreNode = rootNode.get("groupStore"); + var groupStoreNode = rootNode.get("groupStore"); if (groupStoreNode != null) { groupStore = jsonProcessor.convertValue(groupStoreNode, JsonGroupStore.class); groupStore.groupCachePath = getGroupCachePath(dataPath, username); @@ -297,7 +291,7 @@ public class SignalAccount implements Closeable { groupStore = new JsonGroupStore(getGroupCachePath(dataPath, username)); } - JsonNode contactStoreNode = rootNode.get("contactStore"); + var contactStoreNode = rootNode.get("contactStore"); if (contactStoreNode != null) { contactStore = jsonProcessor.convertValue(contactStoreNode, JsonContactsStore.class); } @@ -305,7 +299,7 @@ public class SignalAccount implements Closeable { contactStore = new JsonContactsStore(); } - JsonNode recipientStoreNode = rootNode.get("recipientStore"); + var recipientStoreNode = rootNode.get("recipientStore"); if (recipientStoreNode != null) { recipientStore = jsonProcessor.convertValue(recipientStoreNode, RecipientStore.class); } @@ -314,29 +308,29 @@ public class SignalAccount implements Closeable { recipientStore.resolveServiceAddress(getSelfAddress()); - for (ContactInfo contact : contactStore.getContacts()) { + for (var contact : contactStore.getContacts()) { recipientStore.resolveServiceAddress(contact.getAddress()); } - for (GroupInfo group : groupStore.getGroups()) { + for (var group : groupStore.getGroups()) { if (group instanceof GroupInfoV1) { - GroupInfoV1 groupInfoV1 = (GroupInfoV1) group; + var groupInfoV1 = (GroupInfoV1) group; groupInfoV1.members = groupInfoV1.members.stream() .map(m -> recipientStore.resolveServiceAddress(m)) .collect(Collectors.toSet()); } } - for (SessionInfo session : signalProtocolStore.getSessions()) { + for (var session : signalProtocolStore.getSessions()) { session.address = recipientStore.resolveServiceAddress(session.address); } - for (IdentityInfo identity : signalProtocolStore.getIdentities()) { + for (var identity : signalProtocolStore.getIdentities()) { identity.setAddress(recipientStore.resolveServiceAddress(identity.getAddress())); } } - JsonNode profileStoreNode = rootNode.get("profileStore"); + var profileStoreNode = rootNode.get("profileStore"); if (profileStoreNode != null) { profileStore = jsonProcessor.convertValue(profileStoreNode, ProfileStore.class); } @@ -344,7 +338,7 @@ public class SignalAccount implements Closeable { profileStore = new ProfileStore(); } - JsonNode stickerStoreNode = rootNode.get("stickerStore"); + var stickerStoreNode = rootNode.get("stickerStore"); if (stickerStoreNode != null) { stickerStore = jsonProcessor.convertValue(stickerStoreNode, StickerStore.class); } @@ -354,22 +348,21 @@ public class SignalAccount implements Closeable { messageCache = new MessageCache(getMessageCachePath(dataPath, username)); - JsonNode threadStoreNode = rootNode.get("threadStore"); + var threadStoreNode = rootNode.get("threadStore"); if (threadStoreNode != null && !threadStoreNode.isNull()) { - LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode, - LegacyJsonThreadStore.class); + var threadStore = jsonProcessor.convertValue(threadStoreNode, LegacyJsonThreadStore.class); // Migrate thread info to group and contact store - for (ThreadInfo thread : threadStore.getThreads()) { + for (var thread : threadStore.getThreads()) { if (thread.id == null || thread.id.isEmpty()) { continue; } try { - ContactInfo contactInfo = contactStore.getContact(new SignalServiceAddress(null, thread.id)); + var contactInfo = contactStore.getContact(new SignalServiceAddress(null, thread.id)); if (contactInfo != null) { contactInfo.messageExpirationTime = thread.messageExpirationTime; contactStore.updateContact(contactInfo); } else { - GroupInfo groupInfo = groupStore.getGroup(GroupId.fromBase64(thread.id)); + var groupInfo = groupStore.getGroup(GroupId.fromBase64(thread.id)); if (groupInfo instanceof GroupInfoV1) { ((GroupInfoV1) groupInfo).messageExpirationTime = thread.messageExpirationTime; groupStore.updateGroup(groupInfo); @@ -385,7 +378,7 @@ public class SignalAccount implements Closeable { if (fileChannel == null) { return; } - ObjectNode rootNode = jsonProcessor.createObjectNode(); + var rootNode = jsonProcessor.createObjectNode(); rootNode.put("username", username) .put("uuid", uuid == null ? null : uuid.toString()) .put("deviceId", deviceId) @@ -407,10 +400,10 @@ public class SignalAccount implements Closeable { .putPOJO("profileStore", profileStore) .putPOJO("stickerStore", stickerStore); try { - try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { + try (var output = new ByteArrayOutputStream()) { // Write to memory first to prevent corrupting the file in case of serialization errors jsonProcessor.writeValue(output, rootNode); - ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); + var input = new ByteArrayInputStream(output.toByteArray()); synchronized (fileChannel) { fileChannel.position(0); input.transferTo(Channels.newOutputStream(fileChannel)); @@ -424,8 +417,8 @@ public class SignalAccount implements Closeable { } private static Pair openFileChannel(File fileName) throws IOException { - FileChannel fileChannel = new RandomAccessFile(fileName, "rw").getChannel(); - FileLock lock = fileChannel.tryLock(); + var fileChannel = new RandomAccessFile(fileName, "rw").getChannel(); + var lock = fileChannel.tryLock(); if (lock == null) { logger.info("Config file is in use by another instance, waiting…"); lock = fileChannel.lock(); @@ -439,7 +432,7 @@ public class SignalAccount implements Closeable { } public void addPreKeys(Collection records) { - for (PreKeyRecord record : records) { + for (var record : records) { signalProtocolStore.storePreKey(record.getId(), record); } preKeyIdOffset = (preKeyIdOffset + records.size()) % Medium.MAX_VALUE; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java index d2859f3f..b80dfe95 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/contacts/JsonContactsStore.java @@ -13,8 +13,8 @@ public class JsonContactsStore { private List contacts = new ArrayList<>(); public void updateContact(ContactInfo contact) { - final SignalServiceAddress contactAddress = contact.getAddress(); - for (int i = 0; i < contacts.size(); i++) { + final var contactAddress = contact.getAddress(); + for (var i = 0; i < contacts.size(); i++) { if (contacts.get(i).getAddress().matches(contactAddress)) { contacts.set(i, contact); return; @@ -25,7 +25,7 @@ public class JsonContactsStore { } public ContactInfo getContact(SignalServiceAddress address) { - for (ContactInfo contact : contacts) { + for (var contact : contacts) { if (contact.getAddress().matches(address)) { if (contact.uuid == null) { contact.uuid = address.getUuid().orNull(); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java index a644b620..68af2b80 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java @@ -57,7 +57,7 @@ public abstract class GroupInfo { @JsonIgnore public boolean isMember(SignalServiceAddress address) { - for (SignalServiceAddress member : getMembers()) { + for (var member : getMembers()) { if (member.matches(address)) { return true; } @@ -67,7 +67,7 @@ public abstract class GroupInfo { @JsonIgnore public boolean isPendingMember(SignalServiceAddress address) { - for (SignalServiceAddress member : getPendingMembers()) { + for (var member : getPendingMembers()) { if (member.matches(address)) { return true; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java index d1230b27..970ec5c3 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV1.java @@ -135,7 +135,7 @@ public class GroupInfoV1 extends GroupInfo { } public void addMembers(Collection addresses) { - for (SignalServiceAddress address : addresses) { + for (var address : addresses) { if (this.members.contains(address)) { continue; } @@ -178,7 +178,7 @@ public class GroupInfoV1 extends GroupInfo { final Set value, final JsonGenerator jgen, final SerializerProvider provider ) throws IOException { jgen.writeStartArray(value.size()); - for (SignalServiceAddress address : value) { + for (var address : value) { if (address.getUuid().isPresent()) { jgen.writeObject(new JsonSignalServiceAddress(address)); } else { @@ -195,13 +195,13 @@ public class GroupInfoV1 extends GroupInfo { public Set deserialize( JsonParser jsonParser, DeserializationContext deserializationContext ) throws IOException { - Set addresses = new HashSet<>(); + var addresses = new HashSet(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); - for (JsonNode n : node) { + for (var n : node) { if (n.isTextual()) { addresses.add(new SignalServiceAddress(null, n.textValue())); } else { - JsonSignalServiceAddress address = jsonProcessor.treeToValue(n, JsonSignalServiceAddress.class); + var address = jsonProcessor.treeToValue(n, JsonSignalServiceAddress.class); addresses.add(address.toSignalServiceAddress()); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java index 52b789c7..8e37895a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/JsonGroupStore.java @@ -30,7 +30,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Base64; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -59,10 +58,10 @@ public class JsonGroupStore { if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() != null) { try { IOUtils.createPrivateDirectories(groupCachePath); - try (FileOutputStream stream = new FileOutputStream(getGroupFile(group.getGroupId()))) { + try (var stream = new FileOutputStream(getGroupFile(group.getGroupId()))) { ((GroupInfoV2) group).getGroup().writeTo(stream); } - final File groupFileLegacy = getGroupFileLegacy(group.getGroupId()); + final var groupFileLegacy = getGroupFileLegacy(group.getGroupId()); if (groupFileLegacy.exists()) { groupFileLegacy.delete(); } @@ -77,7 +76,7 @@ public class JsonGroupStore { } public GroupInfo getGroup(GroupId groupId) { - GroupInfo group = groups.get(groupId); + var group = groups.get(groupId); if (group == null) { if (groupId instanceof GroupIdV1) { group = groups.get(GroupUtils.getGroupIdV2((GroupIdV1) groupId)); @@ -90,9 +89,9 @@ public class JsonGroupStore { } private GroupInfoV1 getGroupV1ByV2Id(GroupIdV2 groupIdV2) { - for (GroupInfo g : groups.values()) { + for (var g : groups.values()) { if (g instanceof GroupInfoV1) { - final GroupInfoV1 gv1 = (GroupInfoV1) g; + final var gv1 = (GroupInfoV1) g; if (groupIdV2.equals(gv1.getExpectedV2Id())) { return gv1; } @@ -103,14 +102,14 @@ public class JsonGroupStore { private void loadDecryptedGroup(final GroupInfo group) { if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) { - File groupFile = getGroupFile(group.getGroupId()); + var groupFile = getGroupFile(group.getGroupId()); if (!groupFile.exists()) { groupFile = getGroupFileLegacy(group.getGroupId()); } if (!groupFile.exists()) { return; } - try (FileInputStream stream = new FileInputStream(groupFile)) { + try (var stream = new FileInputStream(groupFile)) { ((GroupInfoV2) group).setGroup(DecryptedGroup.parseFrom(stream)); } catch (IOException ignored) { } @@ -126,7 +125,7 @@ public class JsonGroupStore { } public GroupInfoV1 getOrCreateGroupV1(GroupIdV1 groupId) { - GroupInfo group = getGroup(groupId); + var group = getGroup(groupId); if (group instanceof GroupInfoV1) { return (GroupInfoV1) group; } @@ -139,8 +138,8 @@ public class JsonGroupStore { } public List getGroups() { - final Collection groups = this.groups.values(); - for (GroupInfo group : groups) { + final var groups = this.groups.values(); + for (var group : groups) { loadDecryptedGroup(group); } return new ArrayList<>(groups); @@ -152,13 +151,13 @@ public class JsonGroupStore { public void serialize( final Map value, final JsonGenerator jgen, final SerializerProvider provider ) throws IOException { - final Collection groups = value.values(); + final var groups = value.values(); jgen.writeStartArray(groups.size()); - for (GroupInfo group : groups) { + for (var group : groups) { if (group instanceof GroupInfoV1) { jgen.writeObject(group); } else if (group instanceof GroupInfoV2) { - final GroupInfoV2 groupV2 = (GroupInfoV2) group; + final var groupV2 = (GroupInfoV2) group; jgen.writeStartObject(); jgen.writeStringField("groupId", groupV2.getGroupId().toBase64()); jgen.writeStringField("masterKey", @@ -179,16 +178,15 @@ public class JsonGroupStore { public Map deserialize( JsonParser jsonParser, DeserializationContext deserializationContext ) throws IOException { - Map groups = new HashMap<>(); + var groups = new HashMap(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); - for (JsonNode n : node) { + for (var n : node) { GroupInfo g; if (n.hasNonNull("masterKey")) { // a v2 group - GroupIdV2 groupId = GroupIdV2.fromBase64(n.get("groupId").asText()); + var groupId = GroupIdV2.fromBase64(n.get("groupId").asText()); try { - GroupMasterKey masterKey = new GroupMasterKey(Base64.getDecoder() - .decode(n.get("masterKey").asText())); + var masterKey = new GroupMasterKey(Base64.getDecoder().decode(n.get("masterKey").asText())); g = new GroupInfoV2(groupId, masterKey); } catch (InvalidInputException | IllegalArgumentException e) { throw new AssertionError("Invalid master key for group " + groupId.toBase64()); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java b/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java index 3e728c28..6d604300 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java @@ -36,7 +36,7 @@ public class MessageCache { return Stream.of(dir); } - final File[] files = Objects.requireNonNull(dir.listFiles()); + final var files = Objects.requireNonNull(dir.listFiles()); if (files.length == 0) { try { Files.delete(dir.toPath()); @@ -50,11 +50,11 @@ public class MessageCache { } public CachedMessage cacheMessage(SignalServiceEnvelope envelope) { - final long now = new Date().getTime(); - final String source = envelope.hasSource() ? envelope.getSourceAddress().getLegacyIdentifier() : ""; + final var now = new Date().getTime(); + final var source = envelope.hasSource() ? envelope.getSourceAddress().getLegacyIdentifier() : ""; try { - File cacheFile = getMessageCacheFile(source, now, envelope.getTimestamp()); + var cacheFile = getMessageCacheFile(source, now, envelope.getTimestamp()); MessageCacheUtils.storeEnvelope(envelope, cacheFile); return new CachedMessage(cacheFile); } catch (IOException e) { @@ -72,7 +72,7 @@ public class MessageCache { } private File getMessageCacheFile(String sender, long now, long timestamp) throws IOException { - File cachePath = getMessageCachePath(sender); + var cachePath = getMessageCachePath(sender); IOUtils.createPrivateDirectories(cachePath); return new File(cachePath, now + "_" + timestamp); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java index 516b57dc..dc69a7ee 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/ProfileStore.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Base64; import java.util.List; -import java.util.UUID; public class ProfileStore { @@ -34,7 +33,7 @@ public class ProfileStore { private final List profiles = new ArrayList<>(); public SignalProfileEntry getProfileEntry(SignalServiceAddress serviceAddress) { - for (SignalProfileEntry entry : profiles) { + for (var entry : profiles) { if (entry.getServiceAddress().matches(serviceAddress)) { return entry; } @@ -43,7 +42,7 @@ public class ProfileStore { } public ProfileKey getProfileKey(SignalServiceAddress serviceAddress) { - for (SignalProfileEntry entry : profiles) { + for (var entry : profiles) { if (entry.getServiceAddress().matches(serviceAddress)) { return entry.getProfileKey(); } @@ -58,12 +57,8 @@ public class ProfileStore { SignalProfile profile, ProfileKeyCredential profileKeyCredential ) { - SignalProfileEntry newEntry = new SignalProfileEntry(serviceAddress, - profileKey, - now, - profile, - profileKeyCredential); - for (int i = 0; i < profiles.size(); i++) { + var newEntry = new SignalProfileEntry(serviceAddress, profileKey, now, profile, profileKeyCredential); + for (var i = 0; i < profiles.size(); i++) { if (profiles.get(i).getServiceAddress().matches(serviceAddress)) { profiles.set(i, newEntry); return; @@ -74,8 +69,8 @@ public class ProfileStore { } public void storeProfileKey(SignalServiceAddress serviceAddress, ProfileKey profileKey) { - SignalProfileEntry newEntry = new SignalProfileEntry(serviceAddress, profileKey, 0, null, null); - for (int i = 0; i < profiles.size(); i++) { + var newEntry = new SignalProfileEntry(serviceAddress, profileKey, 0, null, null); + for (var i = 0; i < profiles.size(); i++) { if (profiles.get(i).getServiceAddress().matches(serviceAddress)) { if (!profiles.get(i).getProfileKey().equals(profileKey)) { profiles.set(i, newEntry); @@ -95,13 +90,13 @@ public class ProfileStore { ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); - List addresses = new ArrayList<>(); + var addresses = new ArrayList(); if (node.isArray()) { - for (JsonNode entry : node) { - String name = entry.hasNonNull("name") ? entry.get("name").asText() : null; - UUID uuid = entry.hasNonNull("uuid") ? UuidUtil.parseOrNull(entry.get("uuid").asText()) : null; - final SignalServiceAddress serviceAddress = new SignalServiceAddress(uuid, name); + for (var entry : node) { + var name = entry.hasNonNull("name") ? entry.get("name").asText() : null; + var uuid = entry.hasNonNull("uuid") ? UuidUtil.parseOrNull(entry.get("uuid").asText()) : null; + final var serviceAddress = new SignalServiceAddress(uuid, name); ProfileKey profileKey = null; try { profileKey = new ProfileKey(Base64.getDecoder().decode(entry.get("profileKey").asText())); @@ -115,8 +110,8 @@ public class ProfileStore { } catch (Throwable ignored) { } } - long lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong(); - SignalProfile profile = jsonProcessor.treeToValue(entry.get("profile"), SignalProfile.class); + var lastUpdateTimestamp = entry.get("lastUpdateTimestamp").asLong(); + var profile = jsonProcessor.treeToValue(entry.get("profile"), SignalProfile.class); addresses.add(new SignalProfileEntry(serviceAddress, profileKey, lastUpdateTimestamp, @@ -136,8 +131,8 @@ public class ProfileStore { List profiles, JsonGenerator json, SerializerProvider serializerProvider ) throws IOException { json.writeStartArray(); - for (SignalProfileEntry profileEntry : profiles) { - final SignalServiceAddress address = profileEntry.getServiceAddress(); + for (var profileEntry : profiles) { + final var address = profileEntry.getServiceAddress(); json.writeStartObject(); if (address.getNumber().isPresent()) { json.writeStringField("name", address.getNumber().get()); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index 9f01b719..ed04e556 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -25,7 +25,6 @@ import java.util.ArrayList; import java.util.Base64; import java.util.Date; import java.util.List; -import java.util.UUID; public class JsonIdentityKeyStore implements IdentityKeyStore { @@ -85,7 +84,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { public boolean saveIdentity( SignalServiceAddress serviceAddress, IdentityKey identityKey, TrustLevel trustLevel, Date added ) { - for (IdentityInfo id : identities) { + for (var id : identities) { if (!id.address.matches(serviceAddress) || !id.identityKey.equals(identityKey)) { continue; } @@ -111,7 +110,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { public void setIdentityTrustLevel( SignalServiceAddress serviceAddress, IdentityKey identityKey, TrustLevel trustLevel ) { - for (IdentityInfo id : identities) { + for (var id : identities) { if (!id.address.matches(serviceAddress) || !id.identityKey.equals(identityKey)) { continue; } @@ -129,10 +128,10 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { @Override public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { // TODO implement possibility for different handling of incoming/outgoing trust decisions - SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName()); - boolean trustOnFirstUse = true; + var serviceAddress = resolveSignalServiceAddress(address.getName()); + var trustOnFirstUse = true; - for (IdentityInfo id : identities) { + for (var id : identities) { if (!id.address.matches(serviceAddress)) { continue; } @@ -149,20 +148,20 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { @Override public IdentityKey getIdentity(SignalProtocolAddress address) { - SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName()); - IdentityInfo identity = getIdentity(serviceAddress); + var serviceAddress = resolveSignalServiceAddress(address.getName()); + var identity = getIdentity(serviceAddress); return identity == null ? null : identity.getIdentityKey(); } public IdentityInfo getIdentity(SignalServiceAddress serviceAddress) { long maxDate = 0; IdentityInfo maxIdentity = null; - for (IdentityInfo id : this.identities) { + for (var id : this.identities) { if (!id.address.matches(serviceAddress)) { continue; } - final long time = id.getDateAdded().getTime(); + final var time = id.getDateAdded().getTime(); if (maxIdentity == null || maxDate <= time) { maxDate = time; maxIdentity = id; @@ -177,8 +176,8 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { } public List getIdentities(SignalServiceAddress serviceAddress) { - List identities = new ArrayList<>(); - for (IdentityInfo identity : this.identities) { + var identities = new ArrayList(); + for (var identity : this.identities) { if (identity.address.matches(serviceAddress)) { identities.add(identity); } @@ -194,34 +193,32 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); - int localRegistrationId = node.get("registrationId").asInt(); - IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.getDecoder() - .decode(node.get("identityKey").asText())); + var localRegistrationId = node.get("registrationId").asInt(); + var identityKeyPair = new IdentityKeyPair(Base64.getDecoder().decode(node.get("identityKey").asText())); - JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId); + var keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId); - JsonNode trustedKeysNode = node.get("trustedKeys"); + var trustedKeysNode = node.get("trustedKeys"); if (trustedKeysNode.isArray()) { - for (JsonNode trustedKey : trustedKeysNode) { - String trustedKeyName = trustedKey.hasNonNull("name") ? trustedKey.get("name").asText() : null; + for (var trustedKey : trustedKeysNode) { + var trustedKeyName = trustedKey.hasNonNull("name") ? trustedKey.get("name").asText() : null; if (UuidUtil.isUuid(trustedKeyName)) { // Ignore identities that were incorrectly created with UUIDs as name continue; } - UUID uuid = trustedKey.hasNonNull("uuid") + var uuid = trustedKey.hasNonNull("uuid") ? UuidUtil.parseOrNull(trustedKey.get("uuid").asText()) : null; - final SignalServiceAddress serviceAddress = uuid == null + final var serviceAddress = uuid == null ? Utils.getSignalServiceAddressFromIdentifier(trustedKeyName) : new SignalServiceAddress(uuid, trustedKeyName); try { - IdentityKey id = new IdentityKey(Base64.getDecoder() - .decode(trustedKey.get("identityKey").asText()), 0); - TrustLevel trustLevel = trustedKey.hasNonNull("trustLevel") ? TrustLevel.fromInt(trustedKey.get( + var id = new IdentityKey(Base64.getDecoder().decode(trustedKey.get("identityKey").asText()), 0); + var trustLevel = trustedKey.hasNonNull("trustLevel") ? TrustLevel.fromInt(trustedKey.get( "trustLevel").asInt()) : TrustLevel.TRUSTED_UNVERIFIED; - Date added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp") + var added = trustedKey.hasNonNull("addedTimestamp") ? new Date(trustedKey.get("addedTimestamp") .asLong()) : new Date(); keyStore.saveIdentity(serviceAddress, id, trustLevel, added); } catch (InvalidKeyException e) { @@ -251,7 +248,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { Base64.getEncoder() .encodeToString(jsonIdentityKeyStore.getIdentityKeyPair().getPublicKey().serialize())); json.writeArrayFieldStart("trustedKeys"); - for (IdentityInfo trustedKey : jsonIdentityKeyStore.identities) { + for (var trustedKey : jsonIdentityKeyStore.identities) { json.writeStartObject(); if (trustedKey.getAddress().getNumber().isPresent()) { json.writeStringField("name", trustedKey.getAddress().getNumber().get()); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java index 8529d0dd..9ff0d8ea 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonPreKeyStore.java @@ -69,16 +69,16 @@ class JsonPreKeyStore implements PreKeyStore { ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); - Map preKeyMap = new HashMap<>(); + var preKeyMap = new HashMap(); if (node.isArray()) { - for (JsonNode preKey : node) { - final int preKeyId = preKey.get("id").asInt(); - final byte[] preKeyRecord = Base64.getDecoder().decode(preKey.get("record").asText()); + for (var preKey : node) { + final var preKeyId = preKey.get("id").asInt(); + final var preKeyRecord = Base64.getDecoder().decode(preKey.get("record").asText()); preKeyMap.put(preKeyId, preKeyRecord); } } - JsonPreKeyStore keyStore = new JsonPreKeyStore(); + var keyStore = new JsonPreKeyStore(); keyStore.addPreKeys(preKeyMap); return keyStore; @@ -92,7 +92,7 @@ class JsonPreKeyStore implements PreKeyStore { JsonPreKeyStore jsonPreKeyStore, JsonGenerator json, SerializerProvider serializerProvider ) throws IOException { json.writeStartArray(); - for (Map.Entry preKey : jsonPreKeyStore.store.entrySet()) { + for (var preKey : jsonPreKeyStore.store.entrySet()) { json.writeStartObject(); json.writeNumberField("id", preKey.getKey()); json.writeStringField("record", Base64.getEncoder().encodeToString(preKey.getValue())); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java index 81a8adb2..94d2d681 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.Base64; import java.util.LinkedList; import java.util.List; -import java.util.UUID; class JsonSessionStore implements SignalServiceSessionStore { @@ -49,14 +48,14 @@ class JsonSessionStore implements SignalServiceSessionStore { @Override public synchronized SessionRecord loadSession(SignalProtocolAddress address) { - SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName()); - for (SessionInfo info : sessions) { + var serviceAddress = resolveSignalServiceAddress(address.getName()); + for (var info : sessions) { if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) { try { return new SessionRecord(info.sessionRecord); } catch (IOException e) { logger.warn("Failed to load session, resetting session: {}", e.getMessage()); - final SessionRecord sessionRecord = new SessionRecord(); + final var sessionRecord = new SessionRecord(); info.sessionRecord = sessionRecord.serialize(); return sessionRecord; } @@ -72,10 +71,10 @@ class JsonSessionStore implements SignalServiceSessionStore { @Override public synchronized List getSubDeviceSessions(String name) { - SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name); + var serviceAddress = resolveSignalServiceAddress(name); - List deviceIds = new LinkedList<>(); - for (SessionInfo info : sessions) { + var deviceIds = new LinkedList(); + for (var info : sessions) { if (info.address.matches(serviceAddress) && info.deviceId != 1) { deviceIds.add(info.deviceId); } @@ -86,8 +85,8 @@ class JsonSessionStore implements SignalServiceSessionStore { @Override public synchronized void storeSession(SignalProtocolAddress address, SessionRecord record) { - SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName()); - for (SessionInfo info : sessions) { + var serviceAddress = resolveSignalServiceAddress(address.getName()); + for (var info : sessions) { if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) { if (!info.address.getUuid().isPresent() || !info.address.getNumber().isPresent()) { info.address = serviceAddress; @@ -102,8 +101,8 @@ class JsonSessionStore implements SignalServiceSessionStore { @Override public synchronized boolean containsSession(SignalProtocolAddress address) { - SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName()); - for (SessionInfo info : sessions) { + var serviceAddress = resolveSignalServiceAddress(address.getName()); + for (var info : sessions) { if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) { return true; } @@ -113,13 +112,13 @@ class JsonSessionStore implements SignalServiceSessionStore { @Override public synchronized void deleteSession(SignalProtocolAddress address) { - SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName()); + var serviceAddress = resolveSignalServiceAddress(address.getName()); sessions.removeIf(info -> info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()); } @Override public synchronized void deleteAllSessions(String name) { - SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name); + var serviceAddress = resolveSignalServiceAddress(name); deleteAllSessions(serviceAddress); } @@ -129,7 +128,7 @@ class JsonSessionStore implements SignalServiceSessionStore { @Override public void archiveSession(final SignalProtocolAddress address) { - final SessionRecord sessionRecord = loadSession(address); + final var sessionRecord = loadSession(address); if (sessionRecord == null) { return; } @@ -138,9 +137,9 @@ class JsonSessionStore implements SignalServiceSessionStore { } public void archiveAllSessions() { - for (SessionInfo info : sessions) { + for (var info : sessions) { try { - final SessionRecord sessionRecord = new SessionRecord(info.sessionRecord); + final var sessionRecord = new SessionRecord(info.sessionRecord); sessionRecord.archiveCurrentState(); info.sessionRecord = sessionRecord.serialize(); } catch (IOException ignored) { @@ -156,23 +155,23 @@ class JsonSessionStore implements SignalServiceSessionStore { ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); - JsonSessionStore sessionStore = new JsonSessionStore(); + var sessionStore = new JsonSessionStore(); if (node.isArray()) { - for (JsonNode session : node) { - String sessionName = session.hasNonNull("name") ? session.get("name").asText() : null; + for (var session : node) { + var sessionName = session.hasNonNull("name") ? session.get("name").asText() : null; if (UuidUtil.isUuid(sessionName)) { // Ignore sessions that were incorrectly created with UUIDs as name continue; } - UUID uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null; - final SignalServiceAddress serviceAddress = uuid == null + var uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null; + final var serviceAddress = uuid == null ? Utils.getSignalServiceAddressFromIdentifier(sessionName) : new SignalServiceAddress(uuid, sessionName); - final int deviceId = session.get("deviceId").asInt(); - final byte[] record = Base64.getDecoder().decode(session.get("record").asText()); - SessionInfo sessionInfo = new SessionInfo(serviceAddress, deviceId, record); + final var deviceId = session.get("deviceId").asInt(); + final var record = Base64.getDecoder().decode(session.get("record").asText()); + var sessionInfo = new SessionInfo(serviceAddress, deviceId, record); sessionStore.sessions.add(sessionInfo); } } @@ -188,7 +187,7 @@ class JsonSessionStore implements SignalServiceSessionStore { JsonSessionStore jsonSessionStore, JsonGenerator json, SerializerProvider serializerProvider ) throws IOException { json.writeStartArray(); - for (SessionInfo sessionInfo : jsonSessionStore.sessions) { + for (var sessionInfo : jsonSessionStore.sessions) { json.writeStartObject(); if (sessionInfo.address.getNumber().isPresent()) { json.writeStringField("name", sessionInfo.address.getNumber().get()); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java index a45287b4..655e372a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignedPreKeyStore.java @@ -51,9 +51,9 @@ class JsonSignedPreKeyStore implements SignedPreKeyStore { @Override public List loadSignedPreKeys() { try { - List results = new LinkedList<>(); + var results = new LinkedList(); - for (byte[] serialized : store.values()) { + for (var serialized : store.values()) { results.add(new SignedPreKeyRecord(serialized)); } @@ -86,16 +86,16 @@ class JsonSignedPreKeyStore implements SignedPreKeyStore { ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); - Map preKeyMap = new HashMap<>(); + var preKeyMap = new HashMap(); if (node.isArray()) { - for (JsonNode preKey : node) { - final int preKeyId = preKey.get("id").asInt(); - final byte[] preKeyRecord = Base64.getDecoder().decode(preKey.get("record").asText()); + for (var preKey : node) { + final var preKeyId = preKey.get("id").asInt(); + final var preKeyRecord = Base64.getDecoder().decode(preKey.get("record").asText()); preKeyMap.put(preKeyId, preKeyRecord); } } - JsonSignedPreKeyStore keyStore = new JsonSignedPreKeyStore(); + var keyStore = new JsonSignedPreKeyStore(); keyStore.addSignedPreKeys(preKeyMap); return keyStore; @@ -109,7 +109,7 @@ class JsonSignedPreKeyStore implements SignedPreKeyStore { JsonSignedPreKeyStore jsonPreKeyStore, JsonGenerator json, SerializerProvider serializerProvider ) throws IOException { json.writeStartArray(); - for (Map.Entry signedPreKey : jsonPreKeyStore.store.entrySet()) { + for (var signedPreKey : jsonPreKeyStore.store.entrySet()) { json.writeStartObject(); json.writeNumberField("id", signedPreKey.getKey()); json.writeStringField("record", Base64.getEncoder().encodeToString(signedPreKey.getValue())); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java index 60634ae5..93cca233 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/RecipientStore.java @@ -17,7 +17,6 @@ import org.whispersystems.signalservice.api.util.UuidUtil; import java.io.IOException; import java.util.HashSet; import java.util.Set; -import java.util.UUID; public class RecipientStore { @@ -33,7 +32,7 @@ public class RecipientStore { return serviceAddress; } - for (SignalServiceAddress address : addresses) { + for (var address : addresses) { if (address.matches(serviceAddress)) { return address; } @@ -54,13 +53,13 @@ public class RecipientStore { ) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); - Set addresses = new HashSet<>(); + var addresses = new HashSet(); if (node.isArray()) { - for (JsonNode recipient : node) { - String recipientName = recipient.get("name").asText(); - UUID uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText()); - final SignalServiceAddress serviceAddress = new SignalServiceAddress(uuid, recipientName); + for (var recipient : node) { + var recipientName = recipient.get("name").asText(); + var uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText()); + final var serviceAddress = new SignalServiceAddress(uuid, recipientName); addresses.add(serviceAddress); } } @@ -76,7 +75,7 @@ public class RecipientStore { Set addresses, JsonGenerator json, SerializerProvider serializerProvider ) throws IOException { json.writeStartArray(); - for (SignalServiceAddress address : addresses) { + for (var address : addresses) { json.writeStartObject(); json.writeStringField("name", address.getNumber().get()); json.writeStringField("uuid", address.getUuid().get().toString()); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java index 710be035..8d227575 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java @@ -12,7 +12,6 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.io.IOException; import java.util.Base64; -import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -36,9 +35,9 @@ public class StickerStore { public void serialize( final Map value, final JsonGenerator jgen, final SerializerProvider provider ) throws IOException { - final Collection stickers = value.values(); + final var stickers = value.values(); jgen.writeStartArray(stickers.size()); - for (Sticker sticker : stickers) { + for (var sticker : stickers) { jgen.writeStartObject(); jgen.writeStringField("packId", Base64.getEncoder().encodeToString(sticker.getPackId())); jgen.writeStringField("packKey", Base64.getEncoder().encodeToString(sticker.getPackKey())); @@ -55,12 +54,12 @@ public class StickerStore { public Map deserialize( JsonParser jsonParser, DeserializationContext deserializationContext ) throws IOException { - Map stickers = new HashMap<>(); + var stickers = new HashMap(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); - for (JsonNode n : node) { - byte[] packId = Base64.getDecoder().decode(n.get("packId").asText()); - byte[] packKey = Base64.getDecoder().decode(n.get("packKey").asText()); - boolean installed = n.get("installed").asBoolean(false); + for (var n : node) { + var packId = Base64.getDecoder().decode(n.get("packId").asText()); + var packKey = Base64.getDecoder().decode(n.get("packKey").asText()); + var installed = n.get("installed").asBoolean(false); stickers.put(packId, new Sticker(packId, packKey, installed)); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java index f37360a2..81810713 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/threads/LegacyJsonThreadStore.java @@ -47,10 +47,10 @@ public class LegacyJsonThreadStore { public Map deserialize( JsonParser jsonParser, DeserializationContext deserializationContext ) throws IOException { - Map threads = new HashMap<>(); + var threads = new HashMap(); JsonNode node = jsonParser.getCodec().readTree(jsonParser); - for (JsonNode n : node) { - ThreadInfo t = jsonProcessor.treeToValue(n, ThreadInfo.class); + for (var n : node) { + var t = jsonProcessor.treeToValue(n, ThreadInfo.class); threads.put(t.id, t); } 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 ec043cfd..1909711d 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 @@ -18,7 +18,7 @@ public class AttachmentUtils { List signalServiceAttachments = null; if (attachments != null) { signalServiceAttachments = new ArrayList<>(attachments.size()); - for (String attachment : attachments) { + for (var attachment : attachments) { try { signalServiceAttachments.add(createAttachment(new File(attachment))); } catch (IOException e) { @@ -30,7 +30,7 @@ public class AttachmentUtils { } public static SignalServiceAttachmentStream createAttachment(File attachmentFile) throws IOException { - final StreamDetails streamDetails = Utils.createStreamDetailsFromFile(attachmentFile); + final var streamDetails = Utils.createStreamDetailsFromFile(attachmentFile); return createAttachment(streamDetails, Optional.of(attachmentFile.getName())); } @@ -38,7 +38,7 @@ public class AttachmentUtils { StreamDetails streamDetails, Optional name ) { // TODO mabybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option - final long uploadTimestamp = System.currentTimeMillis(); + final var uploadTimestamp = System.currentTimeMillis(); Optional preview = Optional.absent(); Optional caption = Optional.absent(); Optional blurHash = Optional.absent(); diff --git a/lib/src/main/java/org/asamk/signal/manager/util/IOUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/IOUtils.java index 8f47c9f4..3cc708d8 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/IOUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/IOUtils.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.EnumSet; @@ -20,13 +19,13 @@ import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; public class IOUtils { public static File createTempFile() throws IOException { - final File tempFile = File.createTempFile("signal-cli_tmp_", ".tmp"); + final var tempFile = File.createTempFile("signal-cli_tmp_", ".tmp"); tempFile.deleteOnExit(); return tempFile; } public static byte[] readFully(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); + var baos = new ByteArrayOutputStream(); IOUtils.copyStream(in, baos); return baos.toByteArray(); } @@ -36,7 +35,7 @@ public class IOUtils { return; } - final Path path = file.toPath(); + final var path = file.toPath(); try { Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE); Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms)); @@ -46,7 +45,7 @@ public class IOUtils { } public static void createPrivateFile(File path) throws IOException { - final Path file = path.toPath(); + final var file = path.toPath(); try { Set perms = EnumSet.of(OWNER_READ, OWNER_WRITE); Files.createFile(file, PosixFilePermissions.asFileAttribute(perms)); @@ -66,7 +65,7 @@ public class IOUtils { } public static void copyStream(InputStream input, OutputStream output, int bufferSize) throws IOException { - byte[] buffer = new byte[bufferSize]; + var buffer = new byte[bufferSize]; int read; while ((read = input.read(buffer)) != -1) { diff --git a/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java index b5567b57..5167331a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java @@ -6,8 +6,6 @@ import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.ecc.Curve; -import org.whispersystems.libsignal.ecc.ECKeyPair; -import org.whispersystems.libsignal.ecc.ECPrivateKey; import org.whispersystems.libsignal.state.PreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.util.Medium; @@ -26,19 +24,19 @@ public class KeyUtils { } public static IdentityKeyPair generateIdentityKeyPair() { - ECKeyPair djbKeyPair = Curve.generateKeyPair(); - IdentityKey djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey()); - ECPrivateKey djbPrivateKey = djbKeyPair.getPrivateKey(); + var djbKeyPair = Curve.generateKeyPair(); + var djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey()); + var djbPrivateKey = djbKeyPair.getPrivateKey(); return new IdentityKeyPair(djbIdentityKey, djbPrivateKey); } public static List generatePreKeyRecords(final int offset, final int batchSize) { - List records = new ArrayList<>(batchSize); - for (int i = 0; i < batchSize; i++) { - int preKeyId = (offset + i) % Medium.MAX_VALUE; - ECKeyPair keyPair = Curve.generateKeyPair(); - PreKeyRecord record = new PreKeyRecord(preKeyId, keyPair); + var records = new ArrayList(batchSize); + for (var i = 0; i < batchSize; i++) { + var preKeyId = (offset + i) % Medium.MAX_VALUE; + var keyPair = Curve.generateKeyPair(); + var record = new PreKeyRecord(preKeyId, keyPair); records.add(record); } @@ -48,7 +46,7 @@ public class KeyUtils { public static SignedPreKeyRecord generateSignedPreKeyRecord( final IdentityKeyPair identityKeyPair, final int signedPreKeyId ) { - ECKeyPair keyPair = Curve.generateKeyPair(); + var keyPair = Curve.generateKeyPair(); byte[] signature; try { signature = Curve.calculateSignature(identityKeyPair.getPrivateKey(), keyPair.getPublicKey().serialize()); @@ -83,12 +81,12 @@ public class KeyUtils { } private static String getSecret(int size) { - byte[] secret = getSecretBytes(size); + var secret = getSecretBytes(size); return Base64.getEncoder().encodeToString(secret); } public static byte[] getSecretBytes(int size) { - byte[] secret = new byte[size]; + var secret = new byte[size]; secureRandom.nextBytes(secret); return secret; } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java index 8661c10b..05ff976e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/MessageCacheUtils.java @@ -16,32 +16,32 @@ import java.util.UUID; public class MessageCacheUtils { public static SignalServiceEnvelope loadEnvelope(File file) throws IOException { - try (FileInputStream f = new FileInputStream(file)) { - DataInputStream in = new DataInputStream(f); - int version = in.readInt(); + try (var f = new FileInputStream(file)) { + var in = new DataInputStream(f); + var version = in.readInt(); if (version > 4) { return null; } - int type = in.readInt(); - String source = in.readUTF(); + var type = in.readInt(); + var source = in.readUTF(); UUID sourceUuid = null; if (version >= 3) { sourceUuid = UuidUtil.parseOrNull(in.readUTF()); } - int sourceDevice = in.readInt(); + var sourceDevice = in.readInt(); if (version == 1) { // read legacy relay field in.readUTF(); } - long timestamp = in.readLong(); + var timestamp = in.readLong(); byte[] content = null; - int contentLen = in.readInt(); + var contentLen = in.readInt(); if (contentLen > 0) { content = new byte[contentLen]; in.readFully(content); } byte[] legacyMessage = null; - int legacyMessageLen = in.readInt(); + var legacyMessageLen = in.readInt(); if (legacyMessageLen > 0) { legacyMessage = new byte[legacyMessageLen]; in.readFully(legacyMessage); @@ -75,8 +75,8 @@ public class MessageCacheUtils { } public static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException { - try (FileOutputStream f = new FileOutputStream(file)) { - try (DataOutputStream out = new DataOutputStream(f)) { + try (var f = new FileOutputStream(file)) { + try (var out = new DataOutputStream(f)) { out.writeInt(4); // version out.writeInt(envelope.getType()); out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : ""); @@ -96,7 +96,7 @@ public class MessageCacheUtils { out.writeInt(0); } out.writeLong(envelope.getServerReceivedTimestamp()); - String uuid = envelope.getUuid(); + var uuid = envelope.getUuid(); out.writeUTF(uuid == null ? "" : uuid); out.writeLong(envelope.getServerDeliveredTimestamp()); } diff --git a/lib/src/main/java/org/asamk/signal/manager/util/PinHashing.java b/lib/src/main/java/org/asamk/signal/manager/util/PinHashing.java index 2fd2d802..5dc5a5bc 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/PinHashing.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/PinHashing.java @@ -12,18 +12,18 @@ public final class PinHashing { } public static HashedPin hashPin(String pin, KeyBackupService.HashSession hashSession) { - final Argon2Parameters params = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id).withParallelism(1) + final var params = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id).withParallelism(1) .withIterations(32) .withVersion(Argon2Parameters.ARGON2_VERSION_13) .withMemoryAsKB(16 * 1024) .withSalt(hashSession.hashSalt()) .build(); - final Argon2BytesGenerator generator = new Argon2BytesGenerator(); + final var generator = new Argon2BytesGenerator(); generator.init(params); return PinHasher.hashPin(PinHasher.normalize(pin), password -> { - byte[] output = new byte[64]; + var output = new byte[64]; generator.generateBytes(password, output); return output; }); diff --git a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java index 11362444..63820b51 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/ProfileUtils.java @@ -13,11 +13,11 @@ public class ProfileUtils { public static SignalProfile decryptProfile( final ProfileKey profileKey, final SignalServiceProfile encryptedProfile ) { - ProfileCipher profileCipher = new ProfileCipher(profileKey); + var profileCipher = new ProfileCipher(profileKey); try { - String name = decryptName(encryptedProfile.getName(), profileCipher); - String about = decryptName(encryptedProfile.getAbout(), profileCipher); - String aboutEmoji = decryptName(encryptedProfile.getAboutEmoji(), profileCipher); + var name = decryptName(encryptedProfile.getName(), profileCipher); + var about = decryptName(encryptedProfile.getAbout(), profileCipher); + var aboutEmoji = decryptName(encryptedProfile.getAboutEmoji(), profileCipher); String unidentifiedAccess; try { unidentifiedAccess = encryptedProfile.getUnidentifiedAccess() == null diff --git a/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java b/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java index fd5ce77b..2fa8bc97 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/StickerUtils.java @@ -13,8 +13,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.List; -import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class StickerUtils { @@ -33,7 +31,7 @@ public class StickerUtils { throw new StickerPackInvalidException("Could not find manifest.json"); } - JsonStickerPack pack = parseStickerPack(rootPath, zip); + var pack = parseStickerPack(rootPath, zip); if (pack.stickers == null) { throw new StickerPackInvalidException("Must set a 'stickers' field."); @@ -43,8 +41,8 @@ public class StickerUtils { throw new StickerPackInvalidException("Must include stickers."); } - List stickers = new ArrayList<>(pack.stickers.size()); - for (JsonStickerPack.JsonSticker sticker : pack.stickers) { + var stickers = new ArrayList(pack.stickers.size()); + for (var sticker : pack.stickers) { if (sticker.file == null) { throw new StickerPackInvalidException("Must set a 'file' field on each sticker."); } @@ -56,9 +54,8 @@ public class StickerUtils { throw new StickerPackInvalidException("Could not find find " + sticker.file); } - String contentType = Utils.getFileMimeType(new File(sticker.file), null); - SignalServiceStickerManifestUpload.StickerInfo stickerInfo = new SignalServiceStickerManifestUpload.StickerInfo( - data.first(), + var contentType = Utils.getFileMimeType(new File(sticker.file), null); + var stickerInfo = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), data.second(), Optional.fromNullable(sticker.emoji).or(""), contentType); @@ -78,7 +75,7 @@ public class StickerUtils { throw new StickerPackInvalidException("Could not find find " + pack.cover.file); } - String contentType = Utils.getFileMimeType(new File(pack.cover.file), null); + var contentType = Utils.getFileMimeType(new File(pack.cover.file), null); cover = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), data.second(), Optional.fromNullable(pack.cover.emoji).or(""), @@ -102,10 +99,10 @@ public class StickerUtils { final String rootPath, final ZipFile zip, final String subfile ) throws IOException { if (zip != null) { - final ZipEntry entry = zip.getEntry(subfile); + final var entry = zip.getEntry(subfile); return new Pair<>(zip.getInputStream(entry), entry.getSize()); } else { - final File file = new File(rootPath, subfile); + final var file = new File(rootPath, subfile); return new Pair<>(new FileInputStream(file), file.length()); } } 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 fb38d01a..2963a996 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 @@ -3,7 +3,6 @@ package org.asamk.signal.manager.util; import com.fasterxml.jackson.databind.JsonNode; import org.whispersystems.libsignal.IdentityKey; -import org.whispersystems.libsignal.fingerprint.Fingerprint; import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.StreamDetails; @@ -21,7 +20,7 @@ import java.nio.file.Files; public class Utils { public static String getFileMimeType(File file, String defaultMimeType) throws IOException { - String mime = Files.probeContentType(file.toPath()); + var mime = Files.probeContentType(file.toPath()); if (mime == null) { try (InputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) { mime = URLConnection.guessContentTypeFromStream(bufferedStream); @@ -35,8 +34,8 @@ public class Utils { public static StreamDetails createStreamDetailsFromFile(File file) throws IOException { InputStream stream = new FileInputStream(file); - final long size = file.length(); - final String mime = getFileMimeType(file, "application/octet-stream"); + final var size = file.length(); + final var mime = getFileMimeType(file, "application/octet-stream"); return new StreamDetails(stream, mime, size); } @@ -66,7 +65,7 @@ public class Utils { theirId = theirAddress.getNumber().get().getBytes(); } - Fingerprint fingerprint = new NumericFingerprintGenerator(5200).createFor(version, + var fingerprint = new NumericFingerprintGenerator(5200).createFor(version, ownId, ownIdentityKey, theirId, @@ -83,7 +82,7 @@ public class Utils { } public static JsonNode getNotNullNode(JsonNode parent, String name) throws InvalidObjectException { - JsonNode node = parent.get(name); + var node = parent.get(name); if (node == null || node.isNull()) { throw new InvalidObjectException(String.format("Incorrect file format: expected parameter %s not found ", name)); diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 90efc20f..5f0f1cdf 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -3,10 +3,7 @@ package org.asamk.signal; import net.sourceforge.argparse4j.ArgumentParsers; import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.ArgumentParser; -import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup; import net.sourceforge.argparse4j.inf.Namespace; -import net.sourceforge.argparse4j.inf.Subparser; -import net.sourceforge.argparse4j.inf.Subparsers; import org.asamk.Signal; import org.asamk.signal.commands.Command; @@ -33,7 +30,6 @@ import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.File; import java.io.IOException; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -44,7 +40,7 @@ public class App { private final Namespace ns; static ArgumentParser buildArgumentParser() { - ArgumentParser parser = ArgumentParsers.newFor("signal-cli") + var parser = ArgumentParsers.newFor("signal-cli") .build() .defaultHelp(true) .description("Commandline interface for Signal.") @@ -59,7 +55,7 @@ public class App { parser.addArgument("-u", "--username").help("Specify your phone number, that will be used for verification."); - MutuallyExclusiveGroup mut = parser.addMutuallyExclusiveGroup(); + var mut = parser.addMutuallyExclusiveGroup(); mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue()); mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue()); @@ -68,11 +64,11 @@ public class App { .type(Arguments.enumStringType(OutputType.class)) .setDefault(OutputType.PLAIN_TEXT); - Subparsers subparsers = parser.addSubparsers().title("subcommands").dest("command"); + var subparsers = parser.addSubparsers().title("subcommands").dest("command"); - final Map commands = Commands.getCommands(); - for (Map.Entry entry : commands.entrySet()) { - Subparser subparser = subparsers.addParser(entry.getKey()); + final var commands = Commands.getCommands(); + for (var entry : commands.entrySet()) { + var subparser = subparsers.addParser(entry.getKey()); entry.getValue().attachToSubparser(subparser); } @@ -84,8 +80,8 @@ public class App { } public int init() { - String commandKey = ns.getString("command"); - Command command = Commands.getCommand(commandKey); + var commandKey = ns.getString("command"); + var command = Commands.getCommand(commandKey); if (command == null) { logger.error("Command not implemented!"); return 1; @@ -97,7 +93,7 @@ public class App { return 1; } - String username = ns.getString("username"); + var username = ns.getString("username"); final boolean useDbus = ns.getBoolean("dbus"); final boolean useDbusSystem = ns.getBoolean("dbus_system"); @@ -107,14 +103,14 @@ public class App { } final File dataPath; - String config = ns.getString("config"); + var config = ns.getString("config"); if (config != null) { dataPath = new File(config); } else { dataPath = getDefaultDataPath(); } - final ServiceEnvironment serviceEnvironment = ServiceEnvironment.LIVE; + final var serviceEnvironment = ServiceEnvironment.LIVE; if (!ServiceConfig.getCapabilities().isGv2()) { logger.warn("WARNING: Support for new group V2 is disabled," @@ -136,7 +132,7 @@ public class App { } if (username == null) { - List usernames = Manager.getAllLocalUsernames(dataPath); + var usernames = Manager.getAllLocalUsernames(dataPath); if (usernames.size() == 0) { System.err.println("No local users found, you first need to register or link an account"); return 1; @@ -172,7 +168,7 @@ public class App { private int handleProvisioningCommand( final ProvisioningCommand command, final File dataPath, final ServiceEnvironment serviceEnvironment ) { - ProvisioningManager pm = ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); + var pm = ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); return command.handleCommand(ns, pm); } @@ -189,7 +185,7 @@ public class App { logger.error("Error loading or creating state file: {}", e.getMessage()); return 2; } - try (RegistrationManager m = manager) { + try (var m = manager) { return command.handleCommand(ns, m); } catch (IOException e) { logger.error("Cleanup failed", e); @@ -203,7 +199,7 @@ public class App { final File dataPath, final ServiceEnvironment serviceEnvironment ) { - try (Manager m = loadManager(username, dataPath, serviceEnvironment)) { + try (var m = loadManager(username, dataPath, serviceEnvironment)) { if (m == null) { return 2; } @@ -221,14 +217,14 @@ public class App { final ServiceEnvironment serviceEnvironment, final List usernames ) { - final List managers = usernames.stream() + final var managers = usernames.stream() .map(u -> loadManager(u, dataPath, serviceEnvironment)) .filter(Objects::nonNull) .collect(Collectors.toList()); - int result = command.handleCommand(ns, managers); + var result = command.handleCommand(ns, managers); - for (Manager m : managers) { + for (var m : managers) { try { m.close(); } catch (IOException e) { @@ -270,8 +266,8 @@ public class App { } else { busType = DBusConnection.DBusBusType.SESSION; } - try (DBusConnection dBusConn = DBusConnection.getConnection(busType)) { - Signal ts = dBusConn.getRemoteObject(DbusConfig.getBusname(), + try (var dBusConn = DBusConnection.getConnection(busType)) { + var ts = dBusConn.getRemoteObject(DbusConfig.getBusname(), DbusConfig.getObjectPath(username), Signal.class); @@ -302,14 +298,14 @@ public class App { * @return the data directory to be used by signal-cli. */ private static File getDefaultDataPath() { - File dataPath = new File(IOUtils.getDataHomeDir(), "signal-cli"); + var dataPath = new File(IOUtils.getDataHomeDir(), "signal-cli"); if (dataPath.exists()) { return dataPath; } - File configPath = new File(System.getProperty("user.home"), ".config"); + var configPath = new File(System.getProperty("user.home"), ".config"); - File legacySettingsPath = new File(configPath, "signal"); + var legacySettingsPath = new File(configPath, "signal"); if (legacySettingsPath.exists()) { return legacySettingsPath; } diff --git a/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java index 0cffd7b1..e0bd793d 100644 --- a/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonDbusReceiveMessageHandler.java @@ -5,15 +5,10 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupUtils; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.ArrayList; import java.util.List; @@ -46,11 +41,11 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler { e.printStackTrace(); } } else if (content != null) { - final SignalServiceAddress sender = !envelope.isUnidentifiedSender() && envelope.hasSource() + final var sender = !envelope.isUnidentifiedSender() && envelope.hasSource() ? envelope.getSourceAddress() : content.getSender(); if (content.getReceiptMessage().isPresent()) { - final SignalServiceReceiptMessage receiptMessage = content.getReceiptMessage().get(); + final var receiptMessage = content.getReceiptMessage().get(); if (receiptMessage.isDeliveryReceipt()) { for (long timestamp : receiptMessage.getTimestamps()) { try { @@ -63,9 +58,9 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler { } } } else if (content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); + var message = content.getDataMessage().get(); - byte[] groupId = getGroupId(message); + var groupId = getGroupId(message); if (!message.isEndSession() && ( groupId == null || message.getGroupContext().get().getGroupV1Type() == null @@ -83,15 +78,15 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler { } } } else if (content.getSyncMessage().isPresent()) { - SignalServiceSyncMessage sync_message = content.getSyncMessage().get(); + var sync_message = content.getSyncMessage().get(); if (sync_message.getSent().isPresent()) { - SentTranscriptMessage transcript = sync_message.getSent().get(); + var transcript = sync_message.getSent().get(); if (transcript.getDestination().isPresent() || transcript.getMessage() .getGroupContext() .isPresent()) { - SignalServiceDataMessage message = transcript.getMessage(); - byte[] groupId = getGroupId(message); + var message = transcript.getMessage(); + var groupId = getGroupId(message); try { conn.sendMessage(new Signal.SyncMessageReceived(objectPath, @@ -118,9 +113,9 @@ public class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler { } static private List getAttachments(SignalServiceDataMessage message, Manager m) { - List attachments = new ArrayList<>(); + var attachments = new ArrayList(); if (message.getAttachments().isPresent()) { - for (SignalServiceAttachment attachment : message.getAttachments().get()) { + for (var attachment : message.getAttachments().get()) { if (attachment.isPointer()) { attachments.add(m.getAttachmentFile(attachment.asPointer().getRemoteId()).getAbsolutePath()); } diff --git a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java index 818f2881..38657f5e 100644 --- a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java @@ -10,7 +10,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import java.io.IOException; import java.util.HashMap; -import java.util.Map; public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler { @@ -26,7 +25,7 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler @Override public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { - final Map object = new HashMap<>(); + final var object = new HashMap(); if (exception != null) { object.put("error", new JsonError(exception)); } diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 2ec1470c..88ae06ca 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -18,7 +18,6 @@ package org.asamk.signal; import net.sourceforge.argparse4j.ArgumentParsers; import net.sourceforge.argparse4j.impl.Arguments; -import net.sourceforge.argparse4j.inf.ArgumentParser; import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.Namespace; @@ -36,11 +35,11 @@ public class Main { // Configuring the logger needs to happen before any logger is initialized configureLogging(isVerbose(args)); - ArgumentParser parser = App.buildArgumentParser(); + var parser = App.buildArgumentParser(); - Namespace ns = parser.parseArgsOrFail(args); + var ns = parser.parseArgsOrFail(args); - int res = new App(ns).init(); + var res = new App(ns).init(); System.exit(res); } @@ -51,7 +50,7 @@ public class Main { } private static boolean isVerbose(String[] args) { - ArgumentParser parser = ArgumentParsers.newFor("signal-cli").build().defaultHelp(false); + var parser = ArgumentParsers.newFor("signal-cli").build().defaultHelp(false); parser.addArgument("--verbose").action(Arguments.storeTrue()); Namespace ns; diff --git a/src/main/java/org/asamk/signal/PlainTextWriterImpl.java b/src/main/java/org/asamk/signal/PlainTextWriterImpl.java index 19874d55..d6536481 100644 --- a/src/main/java/org/asamk/signal/PlainTextWriterImpl.java +++ b/src/main/java/org/asamk/signal/PlainTextWriterImpl.java @@ -20,7 +20,7 @@ public final class PlainTextWriterImpl implements PlainTextWriter { @Override public void println(String format, Object... args) throws IOException { - final String message = MessageFormatter.arrayFormat(format, args).getMessage(); + final var message = MessageFormatter.arrayFormat(format, args).getMessage(); writer.write(message); writer.write(System.lineSeparator()); diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 6e9e1a94..8c75fcba 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -3,46 +3,24 @@ package org.asamk.signal; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupUtils; -import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.Util; import org.slf4j.helpers.MessageFormatter; import org.whispersystems.libsignal.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; -import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; -import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; -import org.whispersystems.signalservice.api.messages.calls.BusyMessage; -import org.whispersystems.signalservice.api.messages.calls.HangupMessage; -import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage; -import org.whispersystems.signalservice.api.messages.calls.OfferMessage; -import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; -import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage; -import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage; -import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; -import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; -import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage; -import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; -import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage; import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import java.io.File; import java.io.IOException; import java.util.Base64; -import java.util.List; import java.util.stream.Collectors; public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { @@ -68,7 +46,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { PlainTextWriter writer = new PlainTextWriterImpl(System.out); if (envelope.hasSource()) { - SignalServiceAddress source = envelope.getSourceAddress(); + var source = envelope.getSourceAddress(); writer.println("Envelope from: {} (device: {})", formatContact(source), envelope.getSourceDevice()); if (source.getRelay().isPresent()) { writer.println("Relayed by: {}", source.getRelay().get()); @@ -86,7 +64,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) { if (exception != null) { if (exception instanceof UntrustedIdentityException) { - UntrustedIdentityException e = (UntrustedIdentityException) exception; + var e = (UntrustedIdentityException) exception; writer.println( "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message."); writer.println( @@ -112,28 +90,28 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { DateUtils.formatTimestamp(content.getServerDeliveredTimestamp())); if (content.getDataMessage().isPresent()) { - SignalServiceDataMessage message = content.getDataMessage().get(); + var message = content.getDataMessage().get(); printDataMessage(writer, message); } if (content.getSyncMessage().isPresent()) { writer.println("Received a sync message"); - SignalServiceSyncMessage syncMessage = content.getSyncMessage().get(); + var syncMessage = content.getSyncMessage().get(); printSyncMessage(writer, syncMessage); } if (content.getCallMessage().isPresent()) { writer.println("Received a call message"); - SignalServiceCallMessage callMessage = content.getCallMessage().get(); + var callMessage = content.getCallMessage().get(); printCallMessage(writer.indentedWriter(), callMessage); } if (content.getReceiptMessage().isPresent()) { writer.println("Received a receipt message"); - SignalServiceReceiptMessage receiptMessage = content.getReceiptMessage().get(); + var receiptMessage = content.getReceiptMessage().get(); printReceiptMessage(writer.indentedWriter(), receiptMessage); } if (content.getTypingMessage().isPresent()) { writer.println("Received a typing message"); - SignalServiceTypingMessage typingMessage = content.getTypingMessage().get(); + var typingMessage = content.getTypingMessage().get(); printTypingMessage(writer.indentedWriter(), typingMessage); } } @@ -156,32 +134,32 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } if (message.getGroupContext().isPresent()) { writer.println("Group info:"); - final SignalServiceGroupContext groupContext = message.getGroupContext().get(); + final var groupContext = message.getGroupContext().get(); printGroupContext(writer.indentedWriter(), groupContext); } if (message.getGroupCallUpdate().isPresent()) { writer.println("Group call update:"); - final SignalServiceDataMessage.GroupCallUpdate groupCallUpdate = message.getGroupCallUpdate().get(); + final var groupCallUpdate = message.getGroupCallUpdate().get(); writer.indentedWriter().println("Era id: {}", groupCallUpdate.getEraId()); } if (message.getPreviews().isPresent()) { writer.println("Previews:"); - final List previews = message.getPreviews().get(); - for (SignalServiceDataMessage.Preview preview : previews) { + final var previews = message.getPreviews().get(); + for (var preview : previews) { writer.println("- Preview"); printPreview(writer.indentedWriter(), preview); } } if (message.getSharedContacts().isPresent()) { - final List sharedContacts = message.getSharedContacts().get(); + final var sharedContacts = message.getSharedContacts().get(); writer.println("Contacts:"); - for (SharedContact contact : sharedContacts) { + for (var contact : sharedContacts) { writer.println("- Contact:"); printSharedContact(writer.indentedWriter(), contact); } } if (message.getSticker().isPresent()) { - final SignalServiceDataMessage.Sticker sticker = message.getSticker().get(); + final var sticker = message.getSticker().get(); writer.println("Sticker:"); printSticker(writer.indentedWriter(), sticker); } @@ -199,27 +177,27 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } if (message.getReaction().isPresent()) { writer.println("Reaction:"); - final SignalServiceDataMessage.Reaction reaction = message.getReaction().get(); + final var reaction = message.getReaction().get(); printReaction(writer.indentedWriter(), reaction); } if (message.getQuote().isPresent()) { writer.println("Quote:"); - SignalServiceDataMessage.Quote quote = message.getQuote().get(); + var quote = message.getQuote().get(); printQuote(writer.indentedWriter(), quote); } if (message.getRemoteDelete().isPresent()) { - final SignalServiceDataMessage.RemoteDelete remoteDelete = message.getRemoteDelete().get(); + final var remoteDelete = message.getRemoteDelete().get(); writer.println("Remote delete message: timestamp = {}", remoteDelete.getTargetSentTimestamp()); } if (message.getMentions().isPresent()) { writer.println("Mentions:"); - for (SignalServiceDataMessage.Mention mention : message.getMentions().get()) { + for (var mention : message.getMentions().get()) { printMention(writer, mention); } } if (message.getAttachments().isPresent()) { writer.println("Attachments:"); - for (SignalServiceAttachment attachment : message.getAttachments().get()) { + for (var attachment : message.getAttachments().get()) { writer.println("- Attachment:"); printAttachment(writer.indentedWriter(), attachment); } @@ -233,7 +211,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.getTimestamp())); if (typingMessage.getGroupId().isPresent()) { writer.println("Group Info:"); - final GroupId groupId = GroupId.unknownVersion(typingMessage.getGroupId().get()); + final var groupId = GroupId.unknownVersion(typingMessage.getGroupId().get()); printGroupInfo(writer.indentedWriter(), groupId); } } @@ -261,34 +239,34 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { final PlainTextWriter writer, final SignalServiceCallMessage callMessage ) throws IOException { if (callMessage.getDestinationDeviceId().isPresent()) { - final Integer deviceId = callMessage.getDestinationDeviceId().get(); + final var deviceId = callMessage.getDestinationDeviceId().get(); writer.println("Destination device id: {}", deviceId); } if (callMessage.getAnswerMessage().isPresent()) { - AnswerMessage answerMessage = callMessage.getAnswerMessage().get(); + var answerMessage = callMessage.getAnswerMessage().get(); writer.println("Answer message: {}, sdp: {})", answerMessage.getId(), answerMessage.getSdp()); } if (callMessage.getBusyMessage().isPresent()) { - BusyMessage busyMessage = callMessage.getBusyMessage().get(); + var busyMessage = callMessage.getBusyMessage().get(); writer.println("Busy message: {}", busyMessage.getId()); } if (callMessage.getHangupMessage().isPresent()) { - HangupMessage hangupMessage = callMessage.getHangupMessage().get(); + var hangupMessage = callMessage.getHangupMessage().get(); writer.println("Hangup message: {}", hangupMessage.getId()); } if (callMessage.getIceUpdateMessages().isPresent()) { writer.println("Ice update messages:"); - List iceUpdateMessages = callMessage.getIceUpdateMessages().get(); - for (IceUpdateMessage iceUpdateMessage : iceUpdateMessages) { + var iceUpdateMessages = callMessage.getIceUpdateMessages().get(); + for (var iceUpdateMessage : iceUpdateMessages) { writer.println("- {}, sdp: {}", iceUpdateMessage.getId(), iceUpdateMessage.getSdp()); } } if (callMessage.getOfferMessage().isPresent()) { - OfferMessage offerMessage = callMessage.getOfferMessage().get(); + var offerMessage = callMessage.getOfferMessage().get(); writer.println("Offer message: {}, sdp: {}", offerMessage.getId(), offerMessage.getSdp()); } if (callMessage.getOpaqueMessage().isPresent()) { - final OpaqueMessage opaqueMessage = callMessage.getOpaqueMessage().get(); + final var opaqueMessage = callMessage.getOpaqueMessage().get(); writer.println("Opaque message: size {}", opaqueMessage.getOpaque().length); } } @@ -297,8 +275,8 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { final PlainTextWriter writer, final SignalServiceSyncMessage syncMessage ) throws IOException { if (syncMessage.getContacts().isPresent()) { - final ContactsMessage contactsMessage = syncMessage.getContacts().get(); - String type = contactsMessage.isComplete() ? "complete" : "partial"; + final var contactsMessage = syncMessage.getContacts().get(); + var type = contactsMessage.isComplete() ? "complete" : "partial"; writer.println("Received {} sync contacts:", type); printAttachment(writer.indentedWriter(), contactsMessage.getContactsStream()); } @@ -308,7 +286,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } if (syncMessage.getRead().isPresent()) { writer.println("Received sync read messages list"); - for (ReadMessage rm : syncMessage.getRead().get()) { + for (var rm : syncMessage.getRead().get()) { writer.println("- From: {} Message timestamp: {}", formatContact(rm.getSender()), DateUtils.formatTimestamp(rm.getTimestamp())); @@ -333,7 +311,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } if (syncMessage.getSent().isPresent()) { writer.println("Received sync sent message"); - final SentTranscriptMessage sentTranscriptMessage = syncMessage.getSent().get(); + final var sentTranscriptMessage = syncMessage.getSent().get(); String to; if (sentTranscriptMessage.getDestination().isPresent()) { to = formatContact(sentTranscriptMessage.getDestination().get()); @@ -353,28 +331,28 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { .println("Expiration started at: {}", DateUtils.formatTimestamp(sentTranscriptMessage.getExpirationStartTimestamp())); } - SignalServiceDataMessage message = sentTranscriptMessage.getMessage(); + var message = sentTranscriptMessage.getMessage(); printDataMessage(writer.indentedWriter(), message); } if (syncMessage.getBlockedList().isPresent()) { writer.println("Received sync message with block list"); writer.println("Blocked numbers:"); - final BlockedListMessage blockedList = syncMessage.getBlockedList().get(); - for (SignalServiceAddress address : blockedList.getAddresses()) { + final var blockedList = syncMessage.getBlockedList().get(); + for (var address : blockedList.getAddresses()) { writer.println("- {}", address.getLegacyIdentifier()); } } if (syncMessage.getVerified().isPresent()) { writer.println("Received sync message with verified identities:"); - final VerifiedMessage verifiedMessage = syncMessage.getVerified().get(); + final var verifiedMessage = syncMessage.getVerified().get(); writer.println("- {}: {}", formatContact(verifiedMessage.getDestination()), verifiedMessage.getVerified()); - String safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(verifiedMessage.getDestination(), + var safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(verifiedMessage.getDestination(), verifiedMessage.getIdentityKey())); writer.indentedWriter().println(safetyNumber); } if (syncMessage.getConfiguration().isPresent()) { writer.println("Received sync message with configuration:"); - final ConfigurationMessage configurationMessage = syncMessage.getConfiguration().get(); + final var configurationMessage = syncMessage.getConfiguration().get(); if (configurationMessage.getReadReceipts().isPresent()) { writer.println("- Read receipts: {}", configurationMessage.getReadReceipts().get() ? "enabled" : "disabled"); @@ -393,21 +371,20 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } if (syncMessage.getFetchType().isPresent()) { - final SignalServiceSyncMessage.FetchType fetchType = syncMessage.getFetchType().get(); + final var fetchType = syncMessage.getFetchType().get(); writer.println("Received sync message with fetch type: {}", fetchType); } if (syncMessage.getViewOnceOpen().isPresent()) { - final ViewOnceOpenMessage viewOnceOpenMessage = syncMessage.getViewOnceOpen().get(); + final var viewOnceOpenMessage = syncMessage.getViewOnceOpen().get(); writer.println("Received sync message with view once open message:"); writer.indentedWriter().println("Sender: {}", formatContact(viewOnceOpenMessage.getSender())); writer.indentedWriter() .println("Timestamp: {}", DateUtils.formatTimestamp(viewOnceOpenMessage.getTimestamp())); } if (syncMessage.getStickerPackOperations().isPresent()) { - final List stickerPackOperationMessages = syncMessage.getStickerPackOperations() - .get(); + final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get(); writer.println("Received sync message with sticker pack operations:"); - for (StickerPackOperationMessage m : stickerPackOperationMessages) { + for (var m : stickerPackOperationMessages) { writer.println("- {}", m.getType().isPresent() ? m.getType().get() : ""); if (m.getPackId().isPresent()) { writer.indentedWriter() @@ -420,7 +397,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } if (syncMessage.getMessageRequestResponse().isPresent()) { - final MessageRequestResponseMessage requestResponseMessage = syncMessage.getMessageRequestResponse().get(); + final var requestResponseMessage = syncMessage.getMessageRequestResponse().get(); writer.println("Received message request response:"); writer.indentedWriter().println("Type: {}", requestResponseMessage.getType()); if (requestResponseMessage.getGroupId().isPresent()) { @@ -434,7 +411,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } } if (syncMessage.getKeys().isPresent()) { - final KeysMessage keysMessage = syncMessage.getKeys().get(); + final var keysMessage = syncMessage.getKeys().get(); writer.println("Received sync message with keys:"); if (keysMessage.getStorageService().isPresent()) { writer.println("- storage key: length: {}", keysMessage.getStorageService().get().serialize().length); @@ -482,13 +459,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println("Text: {}", quote.getText()); if (quote.getMentions() != null && quote.getMentions().size() > 0) { writer.println("Mentions:"); - for (SignalServiceDataMessage.Mention mention : quote.getMentions()) { + for (var mention : quote.getMentions()) { printMention(writer, mention); } } if (quote.getAttachments().size() > 0) { writer.println("Attachments:"); - for (SignalServiceDataMessage.Quote.QuotedAttachment attachment : quote.getAttachments()) { + for (var attachment : quote.getAttachments()) { writer.println("- Filename: {}", attachment.getFileName()); writer.indent(w -> { w.println("Type: {}", attachment.getContentType()); @@ -503,7 +480,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { private void printSharedContact(final PlainTextWriter writer, final SharedContact contact) throws IOException { writer.println("Name:"); - SharedContact.Name name = contact.getName(); + var name = contact.getName(); writer.indent(w -> { if (name.getDisplay().isPresent() && !name.getDisplay().get().isBlank()) { w.println("Display name: {}", name.getDisplay().get()); @@ -526,7 +503,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { }); if (contact.getAvatar().isPresent()) { - SharedContact.Avatar avatar = contact.getAvatar().get(); + var avatar = contact.getAvatar().get(); writer.println("Avatar: (profile: {})", avatar.isProfile()); printAttachment(writer.indentedWriter(), avatar.getAttachment()); } @@ -537,7 +514,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (contact.getPhone().isPresent()) { writer.println("Phone details:"); - for (SharedContact.Phone phone : contact.getPhone().get()) { + for (var phone : contact.getPhone().get()) { writer.println("- Phone:"); writer.indent(w -> { if (phone.getValue() != null) { @@ -555,7 +532,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (contact.getEmail().isPresent()) { writer.println("Email details:"); - for (SharedContact.Email email : contact.getEmail().get()) { + for (var email : contact.getEmail().get()) { writer.println("- Email:"); writer.indent(w -> { if (email.getValue() != null) { @@ -573,7 +550,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (contact.getAddress().isPresent()) { writer.println("Address details:"); - for (SharedContact.PostalAddress address : contact.getAddress().get()) { + for (var address : contact.getAddress().get()) { writer.println("- Address:"); writer.indent(w -> { if (address.getType() != null) { @@ -611,14 +588,14 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { private void printGroupContext( final PlainTextWriter writer, final SignalServiceGroupContext groupContext ) throws IOException { - final GroupId groupId = GroupUtils.getGroupId(groupContext); + final var groupId = GroupUtils.getGroupId(groupContext); if (groupContext.getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = groupContext.getGroupV1().get(); + var groupInfo = groupContext.getGroupV1().get(); printGroupInfo(writer, groupId); writer.println("Type: {}", groupInfo.getType()); if (groupInfo.getMembers().isPresent()) { writer.println("Members:"); - for (SignalServiceAddress member : groupInfo.getMembers().get()) { + for (var member : groupInfo.getMembers().get()) { writer.println("- {}", formatContact(member)); } } @@ -627,7 +604,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { printAttachment(writer.indentedWriter(), groupInfo.getAvatar().get()); } } else if (groupContext.getGroupV2().isPresent()) { - final SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get(); + final var groupInfo = groupContext.getGroupV2().get(); printGroupInfo(writer, groupId); writer.println("Revision: {}", groupInfo.getRevision()); writer.println("Master key length: {}", groupInfo.getMasterKey().serialize().length); @@ -638,7 +615,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) throws IOException { writer.println("Id: {}", groupId.toBase64()); - GroupInfo group = m.getGroup(groupId); + var group = m.getGroup(groupId); if (group != null) { writer.println("Name: {}", group.getTitle()); } else { @@ -649,8 +626,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { private void printMention( PlainTextWriter writer, SignalServiceDataMessage.Mention mention ) throws IOException { - final SignalServiceAddress address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), - null)); + final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid(), null)); writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength()); } @@ -658,7 +634,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println("Content-Type: {}", attachment.getContentType()); writer.println("Type: {}", attachment.isPointer() ? "Pointer" : attachment.isStream() ? "Stream" : ""); if (attachment.isPointer()) { - final SignalServiceAttachmentPointer pointer = attachment.asPointer(); + final var pointer = attachment.asPointer(); writer.println("Id: {} Key length: {}", pointer.getRemoteId(), pointer.getKey().length); if (pointer.getUploadTimestamp() > 0) { writer.println("Upload timestamp: {}", DateUtils.formatTimestamp(pointer.getUploadTimestamp())); @@ -679,7 +655,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { if (pointer.getWidth() > 0 || pointer.getHeight() > 0) { writer.println("Dimensions: {}x{}", pointer.getWidth(), pointer.getHeight()); } - File file = m.getAttachmentFile(pointer.getRemoteId()); + var file = m.getAttachmentFile(pointer.getRemoteId()); if (file.exists()) { writer.println("Stored plaintext in: {}", file); } @@ -687,8 +663,8 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { } private String formatContact(SignalServiceAddress address) { - final String number = address.getLegacyIdentifier(); - String name = m.getContactOrProfileName(number); + final var number = address.getLegacyIdentifier(); + var name = m.getContactOrProfileName(number); if (name == null) { return number; } else { diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index 60009cfb..9dff25c6 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -4,7 +4,6 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.util.Util; @@ -21,7 +20,7 @@ public class BlockCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - for (String contact_number : ns.getList("contact")) { + for (var contact_number : ns.getList("contact")) { try { m.setContactBlocked(contact_number, true); } catch (InvalidNumberException e) { @@ -30,9 +29,9 @@ public class BlockCommand implements LocalCommand { } if (ns.getList("group") != null) { - for (String groupIdString : ns.getList("group")) { + for (var groupIdString : ns.getList("group")) { try { - GroupId groupId = Util.decodeGroupId(groupIdString); + var groupId = Util.decodeGroupId(groupIdString); m.setGroupBlocked(groupId, true); } catch (GroupIdFormatException | GroupNotFoundException e) { System.err.println(e.getMessage()); diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 19859d45..6ee4f316 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -45,7 +45,7 @@ public class DaemonCommand implements MultiLocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); + var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { @@ -61,9 +61,9 @@ public class DaemonCommand implements MultiLocalCommand { busType = DBusConnection.DBusBusType.SESSION; } - try (DBusConnection conn = DBusConnection.getConnection(busType)) { - String objectPath = DbusConfig.getObjectPath(); - Thread t = run(conn, objectPath, m, ignoreAttachments, inJson); + try (var conn = DBusConnection.getConnection(busType)) { + var objectPath = DbusConfig.getObjectPath(); + var t = run(conn, objectPath, m, ignoreAttachments, inJson); conn.requestBusName(DbusConfig.getBusname()); @@ -80,7 +80,7 @@ public class DaemonCommand implements MultiLocalCommand { @Override public int handleCommand(final Namespace ns, final List managers) { - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); + var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { @@ -96,17 +96,17 @@ public class DaemonCommand implements MultiLocalCommand { busType = DBusConnection.DBusBusType.SESSION; } - try (DBusConnection conn = DBusConnection.getConnection(busType)) { - List receiveThreads = new ArrayList<>(); - for (Manager m : managers) { - String objectPath = DbusConfig.getObjectPath(m.getUsername()); - Thread thread = run(conn, objectPath, m, ignoreAttachments, inJson); + try (var conn = DBusConnection.getConnection(busType)) { + var receiveThreads = new ArrayList(); + for (var m : managers) { + var objectPath = DbusConfig.getObjectPath(m.getUsername()); + var thread = run(conn, objectPath, m, ignoreAttachments, inJson); receiveThreads.add(thread); } conn.requestBusName(DbusConfig.getBusname()); - for (Thread t : receiveThreads) { + for (var t : receiveThreads) { try { t.join(); } catch (InterruptedException ignored) { @@ -124,7 +124,7 @@ public class DaemonCommand implements MultiLocalCommand { ) throws DBusException { conn.exportObject(objectPath, new DbusSignalImpl(m)); - final Thread thread = new Thread(() -> { + final var thread = new Thread(() -> { while (true) { try { m.receiveMessages(1, diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 4bc209cd..8fad2554 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -12,7 +12,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -38,7 +37,7 @@ public class GetUserStatusCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { // Setup the json object mapper - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); + var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { @@ -56,9 +55,9 @@ public class GetUserStatusCommand implements LocalCommand { // Output if (inJson) { - final JsonWriter jsonWriter = new JsonWriter(System.out); + final var jsonWriter = new JsonWriter(System.out); - List jsonUserStatuses = registered.entrySet() + var jsonUserStatuses = registered.entrySet() .stream() .map(entry -> new JsonUserStatus(entry.getKey(), entry.getValue())) .collect(Collectors.toList()); @@ -70,7 +69,7 @@ public class GetUserStatusCommand implements LocalCommand { return 3; } } else { - for (Map.Entry entry : registered.entrySet()) { + for (var entry : registered.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } } diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index bfe46650..2a8a9c8f 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -4,16 +4,12 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.freedesktop.dbus.exceptions.DBusExecutionException; -import org.whispersystems.libsignal.util.Pair; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; -import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException; import java.io.IOException; -import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; import static org.asamk.signal.util.ErrorUtils.handleIOException; @@ -29,7 +25,7 @@ public class JoinGroupCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { final GroupInviteLinkUrl linkUrl; - String uri = ns.getString("uri"); + var uri = ns.getString("uri"); try { linkUrl = GroupInviteLinkUrl.fromUri(uri); } catch (GroupInviteLinkUrl.InvalidGroupLinkException e) { @@ -46,8 +42,8 @@ public class JoinGroupCommand implements LocalCommand { } try { - final Pair> results = m.joinGroup(linkUrl); - GroupId newGroupId = results.first(); + final var results = m.joinGroup(linkUrl); + var newGroupId = results.first(); if (!m.getGroup(newGroupId).isMember(m.getSelfAddress())) { System.out.println("Requested to join group \"" + newGroupId.toBase64() + "\""); } else { diff --git a/src/main/java/org/asamk/signal/commands/LinkCommand.java b/src/main/java/org/asamk/signal/commands/LinkCommand.java index 7cc9daf5..5c40dba6 100644 --- a/src/main/java/org/asamk/signal/commands/LinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/LinkCommand.java @@ -21,13 +21,13 @@ public class LinkCommand implements ProvisioningCommand { @Override public int handleCommand(final Namespace ns, final ProvisioningManager m) { - String deviceName = ns.getString("name"); + var deviceName = ns.getString("name"); if (deviceName == null) { deviceName = "cli"; } try { System.out.println(m.getDeviceLinkUri()); - String username = m.finishDeviceLink(deviceName); + var username = m.finishDeviceLink(deviceName); System.out.println("Associated with: " + username); } catch (TimeoutException e) { System.err.println("Link request timed out, please try again."); diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 1a14e8df..dcc59856 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -4,9 +4,6 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.storage.contacts.ContactInfo; - -import java.util.List; public class ListContactsCommand implements LocalCommand { @@ -16,8 +13,8 @@ public class ListContactsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - List contacts = m.getContacts(); - for (ContactInfo c : contacts) { + var contacts = m.getContacts(); + for (var c : contacts) { System.out.println(String.format("Number: %s Name: %s Blocked: %b", c.number, c.name, c.blocked)); } return 0; diff --git a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java index a03b078f..4946cd4a 100644 --- a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java @@ -5,10 +5,8 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; import org.asamk.signal.util.DateUtils; -import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; import java.io.IOException; -import java.util.List; public class ListDevicesCommand implements LocalCommand { @@ -19,8 +17,8 @@ public class ListDevicesCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { try { - List devices = m.getLinkedDevices(); - for (DeviceInfo d : devices) { + var devices = m.getLinkedDevices(); + for (var d : devices) { System.out.println("Device " + d.getId() + (d.getId() == m.getDeviceId() ? " (this device)" : "") diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index e0086cae..f3418d93 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -7,7 +7,6 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,7 +14,6 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.IOException; import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -32,7 +30,7 @@ public class ListGroupsCommand implements LocalCommand { private static void printGroupPlainText(Manager m, GroupInfo group, boolean detailed) { if (detailed) { - final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); + final var groupInviteLink = group.getGroupInviteLink(); System.out.println(String.format( "Id: %s Name: %s Active: %s Blocked: %b Members: %s Pending members: %s Requesting members: %s Link: %s", @@ -70,11 +68,11 @@ public class ListGroupsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { if (ns.get("output") == OutputType.JSON) { - final JsonWriter jsonWriter = new JsonWriter(System.out); + final var jsonWriter = new JsonWriter(System.out); - List jsonGroups = new ArrayList<>(); - for (GroupInfo group : m.getGroups()) { - final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); + var jsonGroups = new ArrayList(); + for (var group : m.getGroups()) { + final var groupInviteLink = group.getGroupInviteLink(); jsonGroups.add(new JsonGroup(group.getGroupId().toBase64(), group.getTitle(), @@ -96,7 +94,7 @@ public class ListGroupsCommand implements LocalCommand { return 0; } else { boolean detailed = ns.getBoolean("detailed"); - for (GroupInfo group : m.getGroups()) { + for (var group : m.getGroups()) { printGroupPlainText(m, group, detailed); } } diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index 4caeca29..cb8aa8bc 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -9,12 +9,10 @@ import org.asamk.signal.util.Hex; import org.asamk.signal.util.Util; import org.whispersystems.signalservice.api.util.InvalidNumberException; -import java.util.List; - public class ListIdentitiesCommand implements LocalCommand { private static void printIdentityFingerprint(Manager m, IdentityInfo theirId) { - String digits = Util.formatSafetyNumber(m.computeSafetyNumber(theirId.getAddress(), theirId.getIdentityKey())); + var digits = Util.formatSafetyNumber(m.computeSafetyNumber(theirId.getAddress(), theirId.getIdentityKey())); System.out.println(String.format("%s: %s Added: %s Fingerprint: %s Safety Number: %s", theirId.getAddress().getNumber().orNull(), theirId.getTrustLevel(), @@ -31,14 +29,14 @@ public class ListIdentitiesCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { if (ns.get("number") == null) { - for (IdentityInfo identity : m.getIdentities()) { + for (var identity : m.getIdentities()) { printIdentityFingerprint(m, identity); } } else { - String number = ns.getString("number"); + var number = ns.getString("number"); try { - List identities = m.getIdentities(number); - for (IdentityInfo id : identities) { + var identities = m.getIdentities(number); + for (var id : identities) { printIdentityFingerprint(m, id); } } catch (InvalidNumberException e) { diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index f258ae24..338163b0 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -4,16 +4,12 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.util.Util; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.signalservice.api.messages.SendMessageResult; import java.io.IOException; -import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; @@ -32,8 +28,8 @@ public class QuitGroupCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { try { - final GroupId groupId = Util.decodeGroupId(ns.getString("group")); - final Pair> results = m.sendQuitGroupMessage(groupId); + final var groupId = Util.decodeGroupId(ns.getString("group")); + final var results = m.sendQuitGroupMessage(groupId); return handleTimestampAndSendMessageResults(results.first(), results.second()); } catch (IOException e) { handleIOException(e); diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 5d41dde0..9693d7c2 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -48,19 +48,19 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) { - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); + var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); } - final JsonWriter jsonWriter = inJson ? new JsonWriter(System.out) : null; + final var jsonWriter = inJson ? new JsonWriter(System.out) : null; try { dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { if (jsonWriter != null) { - JsonMessageEnvelope envelope = new JsonMessageEnvelope(messageReceived); - final Map object = Map.of("envelope", envelope); + var envelope = new JsonMessageEnvelope(messageReceived); + final var object = Map.of("envelope", envelope); try { jsonWriter.write(object); } catch (IOException e) { @@ -77,7 +77,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } if (messageReceived.getAttachments().size() > 0) { System.out.println("Attachments: "); - for (String attachment : messageReceived.getAttachments()) { + for (var attachment : messageReceived.getAttachments()) { System.out.println("- Stored plaintext in: " + attachment); } } @@ -87,8 +87,8 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { if (jsonWriter != null) { - JsonMessageEnvelope envelope = new JsonMessageEnvelope(receiptReceived); - final Map object = Map.of("envelope", envelope); + var envelope = new JsonMessageEnvelope(receiptReceived); + final var object = Map.of("envelope", envelope); try { jsonWriter.write(object); } catch (IOException e) { @@ -103,8 +103,8 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { if (jsonWriter != null) { - JsonMessageEnvelope envelope = new JsonMessageEnvelope(syncReceived); - final Map object = Map.of("envelope", envelope); + var envelope = new JsonMessageEnvelope(syncReceived); + final var object = Map.of("envelope", envelope); try { jsonWriter.write(object); } catch (IOException e) { @@ -122,7 +122,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } if (syncReceived.getAttachments().size() > 0) { System.out.println("Attachments: "); - for (String attachment : syncReceived.getAttachments()) { + for (var attachment : syncReceived.getAttachments()) { System.out.println("- Stored plaintext in: " + attachment); } } @@ -144,7 +144,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); + var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed if (ns.getBoolean("json")) { @@ -155,16 +155,14 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { if (ns.getDouble("timeout") != null) { timeout = ns.getDouble("timeout"); } - boolean returnOnTimeout = true; + var returnOnTimeout = true; if (timeout < 0) { returnOnTimeout = false; timeout = 3600; } boolean ignoreAttachments = ns.getBoolean("ignore_attachments"); try { - final Manager.ReceiveMessageHandler handler = inJson - ? new JsonReceiveMessageHandler(m) - : new ReceiveMessageHandler(m); + final var handler = inJson ? new JsonReceiveMessageHandler(m) : new ReceiveMessageHandler(m); m.receiveMessages((long) (timeout * 1000), TimeUnit.MILLISECONDS, returnOnTimeout, diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index f7736018..d656fe0e 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -23,7 +23,7 @@ public class RegisterCommand implements RegistrationCommand { @Override public int handleCommand(final Namespace ns, final RegistrationManager m) { final boolean voiceVerification = ns.getBoolean("voice"); - final String captcha = ns.getString("captcha"); + final var captcha = ns.getString("captcha"); try { m.register(voiceVerification, captcha); diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 73d8f2ed..f1768b76 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -1,7 +1,6 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.impl.Arguments; -import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; @@ -24,7 +23,7 @@ public class SendCommand implements DbusCommand { @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*"); - final MutuallyExclusiveGroup mutuallyExclusiveGroup = subparser.addMutuallyExclusiveGroup(); + final var mutuallyExclusiveGroup = subparser.addMutuallyExclusiveGroup(); mutuallyExclusiveGroup.addArgument("-g", "--group").help("Specify the recipient group ID."); mutuallyExclusiveGroup.addArgument("--note-to-self") .help("Send the message to self without notification.") @@ -40,11 +39,11 @@ public class SendCommand implements DbusCommand { @Override public int handleCommand(final Namespace ns, final Signal signal) { final List recipients = ns.getList("recipient"); - final Boolean isEndSession = ns.getBoolean("endsession"); - final String groupIdString = ns.getString("group"); - final Boolean isNoteToSelf = ns.getBoolean("note_to_self"); + final var isEndSession = ns.getBoolean("endsession"); + final var groupIdString = ns.getString("group"); + final var isNoteToSelf = ns.getBoolean("note_to_self"); - final boolean noRecipients = recipients == null || recipients.isEmpty(); + final var noRecipients = recipients == null || recipients.isEmpty(); if ((noRecipients && isEndSession) || (noRecipients && groupIdString == null && !isNoteToSelf)) { System.err.println("No recipients given"); System.err.println("Aborting sending."); @@ -75,7 +74,7 @@ public class SendCommand implements DbusCommand { } } - String messageText = ns.getString("message"); + var messageText = ns.getString("message"); if (messageText == null) { try { messageText = IOUtils.readAll(System.in, Charset.defaultCharset()); @@ -101,7 +100,7 @@ public class SendCommand implements DbusCommand { return 1; } - long timestamp = signal.sendGroupMessage(messageText, attachments, groupId); + var timestamp = signal.sendGroupMessage(messageText, attachments, groupId); System.out.println(timestamp); return 0; } catch (AssertionError e) { @@ -115,7 +114,7 @@ public class SendCommand implements DbusCommand { if (isNoteToSelf) { try { - long timestamp = signal.sendNoteToSelfMessage(messageText, attachments); + var timestamp = signal.sendNoteToSelfMessage(messageText, attachments); System.out.println(timestamp); return 0; } catch (AssertionError e) { @@ -131,7 +130,7 @@ public class SendCommand implements DbusCommand { } try { - long timestamp = signal.sendMessage(messageText, attachments, recipients); + var timestamp = signal.sendMessage(messageText, attachments, recipients); System.out.println(timestamp); return 0; } catch (AssertionError e) { diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index e9ddc1f0..36d04185 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -5,7 +5,6 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; @@ -48,9 +47,9 @@ public class SendReactionCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { final List recipients = ns.getList("recipient"); - final String groupIdString = ns.getString("group"); + final var groupIdString = ns.getString("group"); - final boolean noRecipients = recipients == null || recipients.isEmpty(); + final var noRecipients = recipients == null || recipients.isEmpty(); if (noRecipients && groupIdString == null) { System.err.println("No recipients given"); System.err.println("Aborting sending."); @@ -61,15 +60,15 @@ public class SendReactionCommand implements LocalCommand { return 1; } - final String emoji = ns.getString("emoji"); + final var emoji = ns.getString("emoji"); final boolean isRemove = ns.getBoolean("remove"); - final String targetAuthor = ns.getString("target_author"); + final var targetAuthor = ns.getString("target_author"); final long targetTimestamp = ns.getLong("target_timestamp"); try { final Pair> results; if (groupIdString != null) { - GroupId groupId = Util.decodeGroupId(groupIdString); + var groupId = Util.decodeGroupId(groupIdString); results = m.sendGroupMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, groupId); } else { results = m.sendMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, recipients); diff --git a/src/main/java/org/asamk/signal/commands/SetPinCommand.java b/src/main/java/org/asamk/signal/commands/SetPinCommand.java index dbdc83df..fff105be 100644 --- a/src/main/java/org/asamk/signal/commands/SetPinCommand.java +++ b/src/main/java/org/asamk/signal/commands/SetPinCommand.java @@ -20,7 +20,7 @@ public class SetPinCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { try { - String registrationLockPin = ns.getString("registrationLockPin"); + var registrationLockPin = ns.getString("registrationLockPin"); m.setRegistrationLockPin(Optional.of(registrationLockPin)); return 0; } catch (UnauthenticatedResponseException e) { diff --git a/src/main/java/org/asamk/signal/commands/TrustCommand.java b/src/main/java/org/asamk/signal/commands/TrustCommand.java index 58c7371f..277291eb 100644 --- a/src/main/java/org/asamk/signal/commands/TrustCommand.java +++ b/src/main/java/org/asamk/signal/commands/TrustCommand.java @@ -1,7 +1,6 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.impl.Arguments; -import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; @@ -17,7 +16,7 @@ public class TrustCommand implements LocalCommand { @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("number").help("Specify the phone number, for which to set the trust.").required(true); - MutuallyExclusiveGroup mutTrust = subparser.addMutuallyExclusiveGroup(); + var mutTrust = subparser.addMutuallyExclusiveGroup(); mutTrust.addArgument("-a", "--trust-all-known-keys") .help("Trust all known keys of this user, only use this for testing.") .action(Arguments.storeTrue()); @@ -27,15 +26,15 @@ public class TrustCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - String number = ns.getString("number"); + var number = ns.getString("number"); if (ns.getBoolean("trust_all_known_keys")) { - boolean res = m.trustIdentityAllKeys(number); + var res = m.trustIdentityAllKeys(number); if (!res) { System.err.println("Failed to set the trust for this number, make sure the number is correct."); return 1; } } else { - String safetyNumber = ns.getString("verified_safety_number"); + var safetyNumber = ns.getString("verified_safety_number"); if (safetyNumber != null) { safetyNumber = safetyNumber.replaceAll(" ", ""); if (safetyNumber.length() == 66) { diff --git a/src/main/java/org/asamk/signal/commands/UnblockCommand.java b/src/main/java/org/asamk/signal/commands/UnblockCommand.java index d191ef22..b9d6e849 100644 --- a/src/main/java/org/asamk/signal/commands/UnblockCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnblockCommand.java @@ -4,7 +4,6 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.util.Util; @@ -21,7 +20,7 @@ public class UnblockCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - for (String contact_number : ns.getList("contact")) { + for (var contact_number : ns.getList("contact")) { try { m.setContactBlocked(contact_number, false); } catch (InvalidNumberException e) { @@ -30,9 +29,9 @@ public class UnblockCommand implements LocalCommand { } if (ns.getList("group") != null) { - for (String groupIdString : ns.getList("group")) { + for (var groupIdString : ns.getList("group")) { try { - GroupId groupId = Util.decodeGroupId(groupIdString); + var groupId = Util.decodeGroupId(groupIdString); m.setGroupBlocked(groupId, false); } catch (GroupIdFormatException | GroupNotFoundException e) { System.err.println(e.getMessage()); diff --git a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java index c4da94a2..c2b994d6 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java @@ -23,13 +23,13 @@ public class UpdateContactCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - String number = ns.getString("number"); - String name = ns.getString("name"); + var number = ns.getString("number"); + var name = ns.getString("name"); try { m.setContactName(number, name); - Integer expiration = ns.getInt("expiration"); + var expiration = ns.getInt("expiration"); if (expiration != null) { m.setExpirationTimer(number, expiration); } diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index 736190b3..332bbe5e 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -40,7 +40,7 @@ public class UpdateGroupCommand implements DbusCommand { groupId = new byte[0]; } - String groupName = ns.getString("name"); + var groupName = ns.getString("name"); if (groupName == null) { groupName = ""; } @@ -50,13 +50,13 @@ public class UpdateGroupCommand implements DbusCommand { groupMembers = new ArrayList<>(); } - String groupAvatar = ns.getString("avatar"); + var groupAvatar = ns.getString("avatar"); if (groupAvatar == null) { groupAvatar = ""; } try { - byte[] newGroupId = signal.updateGroup(groupId, groupName, groupMembers, groupAvatar); + var newGroupId = signal.updateGroup(groupId, groupName, groupMembers, groupAvatar); if (groupId.length != newGroupId.length) { System.out.println("Creating new group \"" + Base64.getEncoder().encodeToString(newGroupId) + "\" …"); } diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index 5455f523..b8c7b08c 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -1,7 +1,6 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.impl.Arguments; -import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; @@ -19,7 +18,7 @@ public class UpdateProfileCommand implements LocalCommand { subparser.addArgument("--about").help("New profile about text"); subparser.addArgument("--about-emoji").help("New profile about emoji"); - final MutuallyExclusiveGroup avatarOptions = subparser.addMutuallyExclusiveGroup(); + final var avatarOptions = subparser.addMutuallyExclusiveGroup(); avatarOptions.addArgument("--avatar").help("Path to new profile avatar"); avatarOptions.addArgument("--remove-avatar").action(Arguments.storeTrue()); @@ -28,10 +27,10 @@ public class UpdateProfileCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { - String name = ns.getString("name"); - String about = ns.getString("about"); - String aboutEmoji = ns.getString("about_emoji"); - String avatarPath = ns.getString("avatar"); + var name = ns.getString("name"); + var about = ns.getString("about"); + var aboutEmoji = ns.getString("about_emoji"); + var avatarPath = ns.getString("avatar"); boolean removeAvatar = ns.getBoolean("remove_avatar"); try { diff --git a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java index 725f91b3..94ba9e9c 100644 --- a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java +++ b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java @@ -20,8 +20,8 @@ public class UploadStickerPackCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { try { - File path = new File(ns.getString("path")); - String url = m.uploadStickerPack(path); + var path = new File(ns.getString("path")); + var url = m.uploadStickerPack(path); System.out.println(url); return 0; } catch (IOException e) { diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index 7fa10b6a..a244581d 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -21,8 +21,8 @@ public class VerifyCommand implements RegistrationCommand { @Override public int handleCommand(final Namespace ns, final RegistrationManager m) { try { - String verificationCode = ns.getString("verificationCode"); - String pin = ns.getString("pin"); + var verificationCode = ns.getString("verificationCode"); + var pin = ns.getString("pin"); m.verifyAccount(verificationCode, pin); return 0; } catch (LockedException e) { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 4af7156d..8cbbbc03 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -6,10 +6,8 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.util.ErrorUtils; import org.freedesktop.dbus.exceptions.DBusExecutionException; -import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -41,19 +39,19 @@ public class DbusSignalImpl implements Signal { @Override public long sendMessage(final String message, final List attachments, final String recipient) { - List recipients = new ArrayList<>(1); + var recipients = new ArrayList(1); recipients.add(recipient); return sendMessage(message, attachments, recipients); } private static void checkSendMessageResult(long timestamp, SendMessageResult result) throws DBusExecutionException { - String error = ErrorUtils.getErrorMessageFromSendMessageResult(result); + var error = ErrorUtils.getErrorMessageFromSendMessageResult(result); if (error == null) { return; } - final String message = timestamp + "\nFailed to send message:\n" + error + '\n'; + final var message = timestamp + "\nFailed to send message:\n" + error + '\n'; if (result.getIdentityFailure() != null) { throw new Error.UntrustedIdentity(message); @@ -70,15 +68,15 @@ public class DbusSignalImpl implements Signal { return; } - List errors = ErrorUtils.getErrorMessagesFromSendMessageResults(results); + var errors = ErrorUtils.getErrorMessagesFromSendMessageResults(results); if (errors.size() == 0) { return; } - StringBuilder message = new StringBuilder(); + var message = new StringBuilder(); message.append(timestamp).append('\n'); message.append("Failed to send (some) messages:\n"); - for (String error : errors) { + for (var error : errors) { message.append(error).append('\n'); } @@ -88,7 +86,7 @@ public class DbusSignalImpl implements Signal { @Override public long sendMessage(final String message, final List attachments, final List recipients) { try { - final Pair> results = m.sendMessage(message, attachments, recipients); + final var results = m.sendMessage(message, attachments, recipients); checkSendMessageResults(results.first(), results.second()); return results.first(); } catch (InvalidNumberException e) { @@ -105,7 +103,7 @@ public class DbusSignalImpl implements Signal { final String message, final List attachments ) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { try { - final Pair results = m.sendSelfMessage(message, attachments); + final var results = m.sendSelfMessage(message, attachments); checkSendMessageResult(results.first(), results.second()); return results.first(); } catch (AttachmentInvalidException e) { @@ -118,7 +116,7 @@ public class DbusSignalImpl implements Signal { @Override public void sendEndSessionMessage(final List recipients) { try { - final Pair> results = m.sendEndSessionMessage(recipients); + final var results = m.sendEndSessionMessage(recipients); checkSendMessageResults(results.first(), results.second()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); @@ -130,9 +128,7 @@ public class DbusSignalImpl implements Signal { @Override public long sendGroupMessage(final String message, final List attachments, final byte[] groupId) { try { - Pair> results = m.sendGroupMessage(message, - attachments, - GroupId.unknownVersion(groupId)); + var results = m.sendGroupMessage(message, attachments, GroupId.unknownVersion(groupId)); checkSendMessageResults(results.first(), results.second()); return results.first(); } catch (IOException e) { @@ -182,9 +178,9 @@ public class DbusSignalImpl implements Signal { @Override public List getGroupIds() { - List groups = m.getGroups(); - List ids = new ArrayList<>(groups.size()); - for (GroupInfo group : groups) { + var groups = m.getGroups(); + var ids = new ArrayList(groups.size()); + for (var group : groups) { ids.add(group.getGroupId().serialize()); } return ids; @@ -192,7 +188,7 @@ public class DbusSignalImpl implements Signal { @Override public String getGroupName(final byte[] groupId) { - GroupInfo group = m.getGroup(GroupId.unknownVersion(groupId)); + var group = m.getGroup(GroupId.unknownVersion(groupId)); if (group == null) { return ""; } else { @@ -202,7 +198,7 @@ public class DbusSignalImpl implements Signal { @Override public List getGroupMembers(final byte[] groupId) { - GroupInfo group = m.getGroup(GroupId.unknownVersion(groupId)); + var group = m.getGroup(GroupId.unknownVersion(groupId)); if (group == null) { return List.of(); } else { @@ -229,9 +225,10 @@ public class DbusSignalImpl implements Signal { if (avatar.isEmpty()) { avatar = null; } - final Pair> results = m.updateGroup(groupId == null - ? null - : GroupId.unknownVersion(groupId), name, members, avatar == null ? null : new File(avatar)); + final var results = m.updateGroup(groupId == null ? null : GroupId.unknownVersion(groupId), + name, + members, + avatar == null ? null : new File(avatar)); checkSendMessageResults(0, results.second()); return results.first().serialize(); } catch (IOException e) { diff --git a/src/main/java/org/asamk/signal/json/JsonAttachment.java b/src/main/java/org/asamk/signal/json/JsonAttachment.java index 1b2a7bc7..a96fc534 100644 --- a/src/main/java/org/asamk/signal/json/JsonAttachment.java +++ b/src/main/java/org/asamk/signal/json/JsonAttachment.java @@ -3,8 +3,6 @@ package org.asamk.signal.json; import com.fasterxml.jackson.annotation.JsonProperty; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream; class JsonAttachment { @@ -24,12 +22,12 @@ class JsonAttachment { this.contentType = attachment.getContentType(); if (attachment.isPointer()) { - final SignalServiceAttachmentPointer pointer = attachment.asPointer(); + final var pointer = attachment.asPointer(); this.id = pointer.getRemoteId().toString(); this.filename = pointer.getFileName().orNull(); this.size = pointer.getSize().transform(Integer::longValue).orNull(); } else { - final SignalServiceAttachmentStream stream = attachment.asStream(); + final var stream = attachment.asStream(); this.id = null; this.filename = stream.getFileName().orNull(); this.size = stream.getLength(); diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 57facc99..6dbda978 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -6,9 +6,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.asamk.Signal; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; -import org.whispersystems.signalservice.api.messages.SignalServiceGroup; -import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext; -import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2; import java.util.List; import java.util.stream.Collectors; @@ -63,12 +60,12 @@ class JsonDataMessage { JsonDataMessage(SignalServiceDataMessage dataMessage, Manager m) { this.timestamp = dataMessage.getTimestamp(); if (dataMessage.getGroupContext().isPresent()) { - final SignalServiceGroupContext groupContext = dataMessage.getGroupContext().get(); + final var groupContext = dataMessage.getGroupContext().get(); if (groupContext.getGroupV1().isPresent()) { - SignalServiceGroup groupInfo = groupContext.getGroupV1().get(); + var groupInfo = groupContext.getGroupV1().get(); this.groupInfo = new JsonGroupInfo(groupInfo); } else if (groupContext.getGroupV2().isPresent()) { - SignalServiceGroupV2 groupInfo = groupContext.getGroupV2().get(); + var groupInfo = groupContext.getGroupV2().get(); this.groupInfo = new JsonGroupInfo(groupInfo); } else { this.groupInfo = null; diff --git a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java index 4944a74d..8506d493 100644 --- a/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java +++ b/src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java @@ -7,7 +7,6 @@ import org.asamk.Signal; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.util.List; @@ -48,7 +47,7 @@ public class JsonMessageEnvelope { public JsonMessageEnvelope(SignalServiceEnvelope envelope, SignalServiceContent content, Manager m) { if (!envelope.isUnidentifiedSender() && envelope.hasSource()) { - SignalServiceAddress source = envelope.getSourceAddress(); + var source = envelope.getSourceAddress(); this.source = source.getLegacyIdentifier(); this.sourceDevice = envelope.getSourceDevice(); this.relay = source.getRelay().orNull(); diff --git a/src/main/java/org/asamk/signal/json/JsonSyncMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncMessage.java index 11ecbbc8..7ba277d5 100644 --- a/src/main/java/org/asamk/signal/json/JsonSyncMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonSyncMessage.java @@ -45,7 +45,7 @@ class JsonSyncMessage { ? new JsonSyncDataMessage(syncMessage.getSent().get(), m) : null; if (syncMessage.getBlockedList().isPresent()) { - final Base64.Encoder base64 = Base64.getEncoder(); + final var base64 = Base64.getEncoder(); this.blockedNumbers = syncMessage.getBlockedList() .get() .getAddresses() diff --git a/src/main/java/org/asamk/signal/json/JsonTypingMessage.java b/src/main/java/org/asamk/signal/json/JsonTypingMessage.java index 18548ce3..7e3b1a44 100644 --- a/src/main/java/org/asamk/signal/json/JsonTypingMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonTypingMessage.java @@ -22,7 +22,7 @@ class JsonTypingMessage { JsonTypingMessage(SignalServiceTypingMessage typingMessage) { this.action = typingMessage.getAction().name(); this.timestamp = typingMessage.getTimestamp(); - final Base64.Encoder encoder = Base64.getEncoder(); + final var encoder = Base64.getEncoder(); this.groupId = typingMessage.getGroupId().transform(encoder::encodeToString).orNull(); } } diff --git a/src/main/java/org/asamk/signal/util/DateUtils.java b/src/main/java/org/asamk/signal/util/DateUtils.java index f94261bf..7f2974ae 100644 --- a/src/main/java/org/asamk/signal/util/DateUtils.java +++ b/src/main/java/org/asamk/signal/util/DateUtils.java @@ -13,7 +13,7 @@ public class DateUtils { } public static String formatTimestamp(long timestamp) { - Date date = new Date(timestamp); + var date = new Date(timestamp); final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX"); // Quoted "Z" to indicate UTC, no timezone offset df.setTimeZone(tzUTC); return timestamp + " (" + df.format(date) + ")"; diff --git a/src/main/java/org/asamk/signal/util/ErrorUtils.java b/src/main/java/org/asamk/signal/util/ErrorUtils.java index f7115ea9..65e41cc8 100644 --- a/src/main/java/org/asamk/signal/util/ErrorUtils.java +++ b/src/main/java/org/asamk/signal/util/ErrorUtils.java @@ -26,14 +26,14 @@ public class ErrorUtils { if (timestamp != 0) { System.out.println(timestamp); } - List errors = getErrorMessagesFromSendMessageResults(results); + var errors = getErrorMessagesFromSendMessageResults(results); return handleSendMessageResultErrors(errors); } public static List getErrorMessagesFromSendMessageResults(List results) { - List errors = new ArrayList<>(); - for (SendMessageResult result : results) { - String error = getErrorMessageFromSendMessageResult(result); + var errors = new ArrayList(); + for (var result : results) { + var error = getErrorMessageFromSendMessageResult(result); if (error != null) { errors.add(error); } @@ -58,7 +58,7 @@ public class ErrorUtils { return 0; } System.err.println("Failed to send (some) messages:"); - for (String error : errors) { + for (var error : errors) { System.err.println(error); } return 3; diff --git a/src/main/java/org/asamk/signal/util/Hex.java b/src/main/java/org/asamk/signal/util/Hex.java index 46609ceb..f5f7a6ad 100644 --- a/src/main/java/org/asamk/signal/util/Hex.java +++ b/src/main/java/org/asamk/signal/util/Hex.java @@ -8,8 +8,8 @@ public class Hex { } public static String toString(byte[] bytes) { - StringBuffer buf = new StringBuffer(); - for (final byte aByte : bytes) { + var buf = new StringBuffer(); + for (final var aByte : bytes) { appendHexChar(buf, aByte); buf.append(" "); } @@ -17,8 +17,8 @@ public class Hex { } public static String toStringCondensed(byte[] bytes) { - StringBuffer buf = new StringBuffer(); - for (final byte aByte : bytes) { + var buf = new StringBuffer(); + for (final var aByte : bytes) { appendHexChar(buf, aByte); } return buf.toString(); @@ -30,9 +30,9 @@ public class Hex { } public static byte[] toByteArray(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { + var len = s.length(); + var data = new byte[len / 2]; + for (var i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); } return data; diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java index 766d1905..5505e518 100644 --- a/src/main/java/org/asamk/signal/util/IOUtils.java +++ b/src/main/java/org/asamk/signal/util/IOUtils.java @@ -12,8 +12,8 @@ public class IOUtils { } public static String readAll(InputStream in, Charset charset) throws IOException { - StringWriter output = new StringWriter(); - byte[] buffer = new byte[4096]; + var output = new StringWriter(); + var buffer = new byte[4096]; int n; while (-1 != (n = in.read(buffer))) { output.write(new String(buffer, 0, n, charset)); @@ -22,7 +22,7 @@ public class IOUtils { } public static File getDataHomeDir() { - String dataHome = System.getenv("XDG_DATA_HOME"); + var dataHome = System.getenv("XDG_DATA_HOME"); if (dataHome != null) { return new File(dataHome); } diff --git a/src/main/java/org/asamk/signal/util/RandomUtils.java b/src/main/java/org/asamk/signal/util/RandomUtils.java index 19c3f18c..6af4ec12 100644 --- a/src/main/java/org/asamk/signal/util/RandomUtils.java +++ b/src/main/java/org/asamk/signal/util/RandomUtils.java @@ -6,7 +6,7 @@ import java.security.SecureRandom; public class RandomUtils { private static final ThreadLocal LOCAL_RANDOM = ThreadLocal.withInitial(() -> { - SecureRandom rand = getSecureRandomUnseeded(); + var rand = getSecureRandomUnseeded(); // Let the SecureRandom seed it self initially rand.nextBoolean(); diff --git a/src/main/java/org/asamk/signal/util/Util.java b/src/main/java/org/asamk/signal/util/Util.java index e798d537..4de49ec6 100644 --- a/src/main/java/org/asamk/signal/util/Util.java +++ b/src/main/java/org/asamk/signal/util/Util.java @@ -10,7 +10,7 @@ public class Util { } public static String getStringIfNotBlank(Optional value) { - String string = value.orNull(); + var string = value.orNull(); if (string == null || string.isBlank()) { return null; } @@ -18,10 +18,10 @@ public class Util { } public static String formatSafetyNumber(String digits) { - final int partCount = 12; - int partSize = digits.length() / partCount; - StringBuilder f = new StringBuilder(digits.length() + partCount); - for (int i = 0; i < partCount; i++) { + final var partCount = 12; + var partSize = digits.length() / partCount; + var f = new StringBuilder(digits.length() + partCount); + for (var i = 0; i < partCount; i++) { f.append(digits, i * partSize, (i * partSize) + partSize).append(" "); } return f.toString(); From c6395b9f35a4229d297aef063597bc96919a7f36 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 22 Feb 2021 19:16:51 +0100 Subject: [PATCH 095/124] Use PlainTextWriter for all cli stdout output --- .../signal/commands/GetUserStatusCommand.java | 12 +- .../signal/commands/JoinGroupCommand.java | 13 +- .../asamk/signal/commands/LinkCommand.java | 11 +- .../signal/commands/ListContactsCommand.java | 14 ++- .../signal/commands/ListDevicesCommand.java | 15 +-- .../signal/commands/ListGroupsCommand.java | 26 ++-- .../commands/ListIdentitiesCommand.java | 32 +++-- .../signal/commands/QuitGroupCommand.java | 5 +- .../asamk/signal/commands/ReceiveCommand.java | 115 ++++++++++-------- .../asamk/signal/commands/SendCommand.java | 40 ++++-- .../signal/commands/SendReactionCommand.java | 5 +- .../signal/commands/UpdateGroupCommand.java | 14 ++- .../commands/UploadStickerPackCommand.java | 8 +- .../org/asamk/signal/util/ErrorUtils.java | 9 +- 14 files changed, 220 insertions(+), 99 deletions(-) diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 8fad2554..07de8321 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -6,6 +6,7 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,8 +70,15 @@ public class GetUserStatusCommand implements LocalCommand { return 3; } } else { - for (var entry : registered.entrySet()) { - System.out.println(entry.getKey() + ": " + entry.getValue()); + final var writer = new PlainTextWriterImpl(System.out); + + try { + for (var entry : registered.entrySet()) { + writer.println("{}: {}", entry.getKey(), entry.getValue()); + } + } catch (IOException e) { + e.printStackTrace(); + return 3; } } diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index 2a8a9c8f..7f1cb0b0 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -3,9 +3,12 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException; @@ -17,6 +20,8 @@ import static org.asamk.signal.util.ErrorUtils.handleTimestampAndSendMessageResu public class JoinGroupCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(JoinGroupCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("--uri").required(true).help("Specify the uri with the group invitation link."); @@ -42,14 +47,16 @@ public class JoinGroupCommand implements LocalCommand { } try { + final var writer = new PlainTextWriterImpl(System.out); + final var results = m.joinGroup(linkUrl); var newGroupId = results.first(); if (!m.getGroup(newGroupId).isMember(m.getSelfAddress())) { - System.out.println("Requested to join group \"" + newGroupId.toBase64() + "\""); + writer.println("Requested to join group \"{}\"", newGroupId.toBase64()); } else { - System.out.println("Joined group \"" + newGroupId.toBase64() + "\""); + writer.println("Joined group \"{}\"", newGroupId.toBase64()); } - return handleTimestampAndSendMessageResults(0, results.second()); + return handleTimestampAndSendMessageResults(writer, 0, results.second()); } catch (AssertionError e) { handleAssertionError(e); return 1; diff --git a/src/main/java/org/asamk/signal/commands/LinkCommand.java b/src/main/java/org/asamk/signal/commands/LinkCommand.java index 5c40dba6..fe580916 100644 --- a/src/main/java/org/asamk/signal/commands/LinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/LinkCommand.java @@ -3,8 +3,11 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.UserAlreadyExists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyException; import java.io.IOException; @@ -14,6 +17,8 @@ import static org.asamk.signal.util.ErrorUtils.handleAssertionError; public class LinkCommand implements ProvisioningCommand { + private final static Logger logger = LoggerFactory.getLogger(LinkCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("-n", "--name").help("Specify a name to describe this new device."); @@ -21,14 +26,16 @@ public class LinkCommand implements ProvisioningCommand { @Override public int handleCommand(final Namespace ns, final ProvisioningManager m) { + final var writer = new PlainTextWriterImpl(System.out); + var deviceName = ns.getString("name"); if (deviceName == null) { deviceName = "cli"; } try { - System.out.println(m.getDeviceLinkUri()); + writer.println("{}", m.getDeviceLinkUri()); var username = m.finishDeviceLink(deviceName); - System.out.println("Associated with: " + username); + writer.println("Associated with: {}", username); } catch (TimeoutException e) { System.err.println("Link request timed out, please try again."); return 3; diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index dcc59856..6609ec60 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -3,8 +3,11 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; +import java.io.IOException; + public class ListContactsCommand implements LocalCommand { @Override @@ -13,9 +16,16 @@ public class ListContactsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { + final var writer = new PlainTextWriterImpl(System.out); + var contacts = m.getContacts(); - for (var c : contacts) { - System.out.println(String.format("Number: %s Name: %s Blocked: %b", c.number, c.name, c.blocked)); + try { + for (var c : contacts) { + writer.println("Number: {} Name: {} Blocked: {}", c.number, c.name, c.blocked); + } + } catch (IOException e) { + e.printStackTrace(); + return 3; } return 0; } diff --git a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java index 4946cd4a..f2037239 100644 --- a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java @@ -3,6 +3,7 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.util.DateUtils; @@ -16,16 +17,16 @@ public class ListDevicesCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { + final var writer = new PlainTextWriterImpl(System.out); try { var devices = m.getLinkedDevices(); for (var d : devices) { - System.out.println("Device " - + d.getId() - + (d.getId() == m.getDeviceId() ? " (this device)" : "") - + ":"); - System.out.println(" Name: " + d.getName()); - System.out.println(" Created: " + DateUtils.formatTimestamp(d.getCreated())); - System.out.println(" Last seen: " + DateUtils.formatTimestamp(d.getLastSeen())); + writer.println("- Device {}{}:", d.getId(), (d.getId() == m.getDeviceId() ? " (this device)" : "")); + writer.indent(w -> { + w.println("Name: {}", d.getName()); + w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated())); + w.println("Last seen: {}", DateUtils.formatTimestamp(d.getLastSeen())); + }); } return 0; } catch (IOException e) { diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index f3418d93..477d56a1 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -6,6 +6,8 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; +import org.asamk.signal.PlainTextWriter; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.slf4j.Logger; @@ -28,12 +30,14 @@ public class ListGroupsCommand implements LocalCommand { .collect(Collectors.toSet()); } - private static void printGroupPlainText(Manager m, GroupInfo group, boolean detailed) { + private static void printGroupPlainText( + PlainTextWriter writer, Manager m, GroupInfo group, boolean detailed + ) throws IOException { if (detailed) { final var groupInviteLink = group.getGroupInviteLink(); - System.out.println(String.format( - "Id: %s Name: %s Active: %s Blocked: %b Members: %s Pending members: %s Requesting members: %s Link: %s", + writer.println( + "Id: {} Name: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Link: {}", group.getGroupId().toBase64(), group.getTitle(), group.isMember(m.getSelfAddress()), @@ -41,13 +45,13 @@ public class ListGroupsCommand implements LocalCommand { resolveMembers(m, group.getMembers()), resolveMembers(m, group.getPendingMembers()), resolveMembers(m, group.getRequestingMembers()), - groupInviteLink == null ? '-' : groupInviteLink.getUrl())); + groupInviteLink == null ? '-' : groupInviteLink.getUrl()); } else { - System.out.println(String.format("Id: %s Name: %s Active: %s Blocked: %b", + writer.println("Id: {} Name: {} Active: {} Blocked: {}", group.getGroupId().toBase64(), group.getTitle(), group.isMember(m.getSelfAddress()), - group.isBlocked())); + group.isBlocked()); } } @@ -93,9 +97,15 @@ public class ListGroupsCommand implements LocalCommand { return 0; } else { + final var writer = new PlainTextWriterImpl(System.out); boolean detailed = ns.getBoolean("detailed"); - for (var group : m.getGroups()) { - printGroupPlainText(m, group, detailed); + try { + for (var group : m.getGroups()) { + printGroupPlainText(writer, m, group, detailed); + } + } catch (IOException e) { + e.printStackTrace(); + return 3; } } diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index cb8aa8bc..1f1b6258 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -3,22 +3,34 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.PlainTextWriter; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.util.Hex; import org.asamk.signal.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.InvalidNumberException; +import java.io.IOException; + public class ListIdentitiesCommand implements LocalCommand { - private static void printIdentityFingerprint(Manager m, IdentityInfo theirId) { + private final static Logger logger = LoggerFactory.getLogger(ListIdentitiesCommand.class); + + private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, IdentityInfo theirId) { var digits = Util.formatSafetyNumber(m.computeSafetyNumber(theirId.getAddress(), theirId.getIdentityKey())); - System.out.println(String.format("%s: %s Added: %s Fingerprint: %s Safety Number: %s", - theirId.getAddress().getNumber().orNull(), - theirId.getTrustLevel(), - theirId.getDateAdded(), - Hex.toString(theirId.getFingerprint()), - digits)); + try { + writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", + theirId.getAddress().getNumber().orNull(), + theirId.getTrustLevel(), + theirId.getDateAdded(), + Hex.toString(theirId.getFingerprint()), + digits); + } catch (IOException e) { + e.printStackTrace(); + } } @Override @@ -28,16 +40,18 @@ public class ListIdentitiesCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { + final var writer = new PlainTextWriterImpl(System.out); + if (ns.get("number") == null) { for (var identity : m.getIdentities()) { - printIdentityFingerprint(m, identity); + printIdentityFingerprint(writer, m, identity); } } else { var number = ns.getString("number"); try { var identities = m.getIdentities(number); for (var id : identities) { - printIdentityFingerprint(m, id); + printIdentityFingerprint(writer, m, id); } } catch (InvalidNumberException e) { System.err.println("Invalid number: " + e.getMessage()); diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index 338163b0..cdbccf19 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -3,6 +3,7 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; @@ -28,9 +29,11 @@ public class QuitGroupCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { try { + final var writer = new PlainTextWriterImpl(System.out); + final var groupId = Util.decodeGroupId(ns.getString("group")); final var results = m.sendQuitGroupMessage(groupId); - return handleTimestampAndSendMessageResults(results.first(), results.second()); + return handleTimestampAndSendMessageResults(writer, results.first(), results.second()); } catch (IOException e) { handleIOException(e); return 3; diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 9693d7c2..834bc5eb 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -8,6 +8,7 @@ import org.asamk.Signal; import org.asamk.signal.JsonReceiveMessageHandler; import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.ReceiveMessageHandler; import org.asamk.signal.json.JsonMessageEnvelope; import org.asamk.signal.manager.Manager; @@ -55,10 +56,11 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); } - final var jsonWriter = inJson ? new JsonWriter(System.out) : null; try { - dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { - if (jsonWriter != null) { + if (inJson) { + final var jsonWriter = new JsonWriter(System.out); + + dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { var envelope = new JsonMessageEnvelope(messageReceived); final var object = Map.of("envelope", envelope); try { @@ -66,27 +68,9 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } catch (IOException e) { logger.error("Failed to write json object: {}", e.getMessage()); } - } else { - System.out.print(String.format("Envelope from: %s\nTimestamp: %s\nBody: %s\n", - messageReceived.getSender(), - DateUtils.formatTimestamp(messageReceived.getTimestamp()), - messageReceived.getMessage())); - if (messageReceived.getGroupId().length > 0) { - System.out.println("Group info:"); - System.out.println(" Id: " + Base64.getEncoder().encodeToString(messageReceived.getGroupId())); - } - if (messageReceived.getAttachments().size() > 0) { - System.out.println("Attachments: "); - for (var attachment : messageReceived.getAttachments()) { - System.out.println("- Stored plaintext in: " + attachment); - } - } - System.out.println(); - } - }); + }); - dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { - if (jsonWriter != null) { + dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { var envelope = new JsonMessageEnvelope(receiptReceived); final var object = Map.of("envelope", envelope); try { @@ -94,15 +78,9 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } catch (IOException e) { logger.error("Failed to write json object: {}", e.getMessage()); } - } else { - System.out.print(String.format("Receipt from: %s\nTimestamp: %s\n", - receiptReceived.getSender(), - DateUtils.formatTimestamp(receiptReceived.getTimestamp()))); - } - }); + }); - dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { - if (jsonWriter != null) { + dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { var envelope = new JsonMessageEnvelope(syncReceived); final var object = Map.of("envelope", envelope); try { @@ -110,25 +88,66 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } catch (IOException e) { logger.error("Failed to write json object: {}", e.getMessage()); } - } else { - System.out.print(String.format("Sync Envelope from: %s to: %s\nTimestamp: %s\nBody: %s\n", - syncReceived.getSource(), - syncReceived.getDestination(), - DateUtils.formatTimestamp(syncReceived.getTimestamp()), - syncReceived.getMessage())); - if (syncReceived.getGroupId().length > 0) { - System.out.println("Group info:"); - System.out.println(" Id: " + Base64.getEncoder().encodeToString(syncReceived.getGroupId())); - } - if (syncReceived.getAttachments().size() > 0) { - System.out.println("Attachments: "); - for (var attachment : syncReceived.getAttachments()) { - System.out.println("- Stored plaintext in: " + attachment); + }); + } else { + final var writer = new PlainTextWriterImpl(System.out); + + dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { + try { + writer.println("Envelope from: {}", messageReceived.getSender()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(messageReceived.getTimestamp())); + writer.println("Body: {}", messageReceived.getMessage()); + if (messageReceived.getGroupId().length > 0) { + writer.println("Group info:"); + writer.indentedWriter() + .println("Id: {}", + Base64.getEncoder().encodeToString(messageReceived.getGroupId())); } + if (messageReceived.getAttachments().size() > 0) { + writer.println("Attachments:"); + for (var attachment : messageReceived.getAttachments()) { + writer.println("- Stored plaintext in: {}", attachment); + } + } + writer.println(); + } catch (IOException e) { + e.printStackTrace(); } - System.out.println(); - } - }); + }); + + dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { + try { + writer.println("Receipt from: {}", receiptReceived.getSender()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(receiptReceived.getTimestamp())); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { + try { + writer.println("Sync Envelope from: {} to: {}", + syncReceived.getSource(), + syncReceived.getDestination()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(syncReceived.getTimestamp())); + writer.println("Body: {}", syncReceived.getMessage()); + if (syncReceived.getGroupId().length > 0) { + writer.println("Group info:"); + writer.indentedWriter() + .println("Id: {}", Base64.getEncoder().encodeToString(syncReceived.getGroupId())); + } + if (syncReceived.getAttachments().size() > 0) { + writer.println("Attachments:"); + for (var attachment : syncReceived.getAttachments()) { + writer.println("- Stored plaintext in: {}", attachment); + } + } + writer.println(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } } catch (DBusException e) { e.printStackTrace(); return 2; diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index f1768b76..1976fb60 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -5,11 +5,14 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; import org.freedesktop.dbus.errors.UnknownObject; import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.charset.Charset; @@ -20,6 +23,8 @@ import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; public class SendCommand implements DbusCommand { + private final static Logger logger = LoggerFactory.getLogger(SendCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*"); @@ -90,18 +95,20 @@ public class SendCommand implements DbusCommand { attachments = List.of(); } - if (groupIdString != null) { - try { - byte[] groupId; - try { - groupId = Util.decodeGroupId(groupIdString).serialize(); - } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; - } + final var writer = new PlainTextWriterImpl(System.out); + if (groupIdString != null) { + byte[] groupId; + try { + groupId = Util.decodeGroupId(groupIdString).serialize(); + } catch (GroupIdFormatException e) { + handleGroupIdFormatException(e); + return 1; + } + + try { var timestamp = signal.sendGroupMessage(messageText, attachments, groupId); - System.out.println(timestamp); + writer.println("{}", timestamp); return 0; } catch (AssertionError e) { handleAssertionError(e); @@ -109,13 +116,16 @@ public class SendCommand implements DbusCommand { } catch (DBusExecutionException e) { System.err.println("Failed to send group message: " + e.getMessage()); return 2; + } catch (IOException e) { + e.printStackTrace(); + return 3; } } if (isNoteToSelf) { try { var timestamp = signal.sendNoteToSelfMessage(messageText, attachments); - System.out.println(timestamp); + writer.println("{}", timestamp); return 0; } catch (AssertionError e) { handleAssertionError(e); @@ -126,12 +136,15 @@ public class SendCommand implements DbusCommand { } catch (DBusExecutionException e) { System.err.println("Failed to send note to self message: " + e.getMessage()); return 2; + } catch (IOException e) { + e.printStackTrace(); + return 3; } } try { var timestamp = signal.sendMessage(messageText, attachments, recipients); - System.out.println(timestamp); + writer.println("{}", timestamp); return 0; } catch (AssertionError e) { handleAssertionError(e); @@ -145,6 +158,9 @@ public class SendCommand implements DbusCommand { } catch (DBusExecutionException e) { System.err.println("Failed to send message: " + e.getMessage()); return 2; + } catch (IOException e) { + e.printStackTrace(); + return 3; } } } diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index 36d04185..bcae9d41 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -4,6 +4,7 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; @@ -66,6 +67,8 @@ public class SendReactionCommand implements LocalCommand { final long targetTimestamp = ns.getLong("target_timestamp"); try { + final var writer = new PlainTextWriterImpl(System.out); + final Pair> results; if (groupIdString != null) { var groupId = Util.decodeGroupId(groupIdString); @@ -73,7 +76,7 @@ public class SendReactionCommand implements LocalCommand { } else { results = m.sendMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, recipients); } - return handleTimestampAndSendMessageResults(results.first(), results.second()); + return handleTimestampAndSendMessageResults(writer, results.first(), results.second()); } catch (IOException e) { handleIOException(e); return 3; diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index 332bbe5e..fdb23ea1 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -4,10 +4,14 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.Util; import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.Base64; import java.util.List; @@ -17,6 +21,8 @@ import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; public class UpdateGroupCommand implements DbusCommand { + private final static Logger logger = LoggerFactory.getLogger(UpdateGroupCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("-g", "--group").help("Specify the recipient group ID."); @@ -27,6 +33,7 @@ public class UpdateGroupCommand implements DbusCommand { @Override public int handleCommand(final Namespace ns, final Signal signal) { + final var writer = new PlainTextWriterImpl(System.out); byte[] groupId = null; if (ns.getString("group") != null) { try { @@ -58,7 +65,12 @@ public class UpdateGroupCommand implements DbusCommand { try { var newGroupId = signal.updateGroup(groupId, groupName, groupMembers, groupAvatar); if (groupId.length != newGroupId.length) { - System.out.println("Creating new group \"" + Base64.getEncoder().encodeToString(newGroupId) + "\" …"); + try { + writer.println("Creating new group \"{}\" …", Base64.getEncoder().encodeToString(newGroupId)); + } catch (IOException e) { + e.printStackTrace(); + return 3; + } } return 0; } catch (AssertionError e) { diff --git a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java index 94ba9e9c..9d72a6c5 100644 --- a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java +++ b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java @@ -3,14 +3,19 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.StickerPackInvalidException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; public class UploadStickerPackCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(UploadStickerPackCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("path") @@ -19,10 +24,11 @@ public class UploadStickerPackCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { + final var writer = new PlainTextWriterImpl(System.out); try { var path = new File(ns.getString("path")); var url = m.uploadStickerPack(path); - System.out.println(url); + writer.println("{}", url); return 0; } catch (IOException e) { System.err.println("Upload error: " + e.getMessage()); diff --git a/src/main/java/org/asamk/signal/util/ErrorUtils.java b/src/main/java/org/asamk/signal/util/ErrorUtils.java index 65e41cc8..149b16c0 100644 --- a/src/main/java/org/asamk/signal/util/ErrorUtils.java +++ b/src/main/java/org/asamk/signal/util/ErrorUtils.java @@ -1,5 +1,6 @@ package org.asamk.signal.util; +import org.asamk.signal.PlainTextWriter; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; @@ -22,9 +23,13 @@ public class ErrorUtils { "If you use an Oracle JRE please check if you have unlimited strength crypto enabled, see README"); } - public static int handleTimestampAndSendMessageResults(long timestamp, List results) { + public static int handleTimestampAndSendMessageResults( + PlainTextWriter writer, + long timestamp, + List results + ) throws IOException { if (timestamp != 0) { - System.out.println(timestamp); + writer.println("{}", timestamp); } var errors = getErrorMessagesFromSendMessageResults(results); return handleSendMessageResultErrors(errors); From 221d937eecca5c9b09a3bee7df812203b9927a56 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 22 Feb 2021 20:43:08 +0100 Subject: [PATCH 096/124] Add CommandException to abstract cli return codes for errors --- src/main/java/org/asamk/signal/App.java | 121 ++++++++---------- .../signal/JsonReceiveMessageHandler.java | 8 +- .../java/org/asamk/signal/JsonWriter.java | 16 ++- src/main/java/org/asamk/signal/Main.java | 29 ++++- .../org/asamk/signal/PlainTextWriter.java | 10 +- .../org/asamk/signal/PlainTextWriterImpl.java | 20 ++- .../asamk/signal/ReceiveMessageHandler.java | 2 +- .../signal/commands/AddDeviceCommand.java | 25 ++-- .../asamk/signal/commands/BlockCommand.java | 12 +- .../asamk/signal/commands/DaemonCommand.java | 12 +- .../asamk/signal/commands/DbusCommand.java | 7 +- .../signal/commands/ExtendedDbusCommand.java | 3 +- .../signal/commands/GetUserStatusCommand.java | 26 ++-- .../signal/commands/JoinGroupCommand.java | 37 ++---- .../asamk/signal/commands/LinkCommand.java | 22 ++-- .../signal/commands/ListContactsCommand.java | 14 +- .../signal/commands/ListDevicesCommand.java | 36 ++++-- .../signal/commands/ListGroupsCommand.java | 26 +--- .../commands/ListIdentitiesCommand.java | 49 +++---- .../asamk/signal/commands/LocalCommand.java | 3 +- .../signal/commands/MultiLocalCommand.java | 7 +- .../signal/commands/ProvisioningCommand.java | 3 +- .../signal/commands/QuitGroupCommand.java | 38 +++--- .../asamk/signal/commands/ReceiveCommand.java | 118 +++++++---------- .../signal/commands/RegisterCommand.java | 22 ++-- .../signal/commands/RegistrationCommand.java | 3 +- .../signal/commands/RemoveDeviceCommand.java | 8 +- .../signal/commands/RemovePinCommand.java | 12 +- .../asamk/signal/commands/SendCommand.java | 73 ++++------- .../signal/commands/SendContactsCommand.java | 12 +- .../signal/commands/SendReactionCommand.java | 56 ++++---- .../asamk/signal/commands/SetPinCommand.java | 12 +- .../asamk/signal/commands/TrustCommand.java | 30 ++--- .../asamk/signal/commands/UnblockCommand.java | 21 +-- .../signal/commands/UnregisterCommand.java | 8 +- .../signal/commands/UpdateAccountCommand.java | 8 +- .../signal/commands/UpdateContactCommand.java | 13 +- .../signal/commands/UpdateGroupCommand.java | 26 ++-- .../signal/commands/UpdateProfileCommand.java | 16 +-- .../commands/UploadStickerPackCommand.java | 15 ++- .../asamk/signal/commands/VerifyCommand.java | 29 +++-- .../commands/exceptions/CommandException.java | 8 ++ .../commands/exceptions/IOErrorException.java | 8 ++ .../exceptions/UnexpectedErrorException.java | 8 ++ .../UntrustedKeyErrorException.java | 8 ++ .../exceptions/UserErrorException.java | 8 ++ .../org/asamk/signal/util/ErrorUtils.java | 62 +++------ 47 files changed, 538 insertions(+), 572 deletions(-) create mode 100644 src/main/java/org/asamk/signal/commands/exceptions/CommandException.java create mode 100644 src/main/java/org/asamk/signal/commands/exceptions/IOErrorException.java create mode 100644 src/main/java/org/asamk/signal/commands/exceptions/UnexpectedErrorException.java create mode 100644 src/main/java/org/asamk/signal/commands/exceptions/UntrustedKeyErrorException.java create mode 100644 src/main/java/org/asamk/signal/commands/exceptions/UserErrorException.java diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 5f0f1cdf..5b2c91c6 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -14,6 +14,9 @@ import org.asamk.signal.commands.LocalCommand; import org.asamk.signal.commands.MultiLocalCommand; import org.asamk.signal.commands.ProvisioningCommand; import org.asamk.signal.commands.RegistrationCommand; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.NotRegisteredException; import org.asamk.signal.manager.ProvisioningManager; @@ -29,9 +32,8 @@ import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; public class App { @@ -79,18 +81,16 @@ public class App { this.ns = ns; } - public int init() { + public void init() throws CommandException { var commandKey = ns.getString("command"); var command = Commands.getCommand(commandKey); if (command == null) { - logger.error("Command not implemented!"); - return 1; + throw new UserErrorException("Command not implemented!"); } OutputType outputType = ns.get("output"); if (!command.getSupportedOutputTypes().contains(outputType)) { - logger.error("Command doesn't support output type {}", outputType.toString()); - return 1; + throw new UserErrorException("Command doesn't support output type " + outputType.toString()); } var username = ns.getString("username"); @@ -99,7 +99,8 @@ public class App { final boolean useDbusSystem = ns.getBoolean("dbus_system"); if (useDbus || useDbusSystem) { // If username is null, it will connect to the default object path - return initDbusClient(command, username, useDbusSystem); + initDbusClient(command, username, useDbusSystem); + return; } final File dataPath; @@ -118,111 +119,102 @@ public class App { } if (!ServiceConfig.isSignalClientAvailable()) { - logger.error("Missing required native library dependency: libsignal-client"); - return 1; + throw new UserErrorException("Missing required native library dependency: libsignal-client"); } if (command instanceof ProvisioningCommand) { if (username != null) { - System.err.println("You cannot specify a username (phone number) when linking"); - return 1; + throw new UserErrorException("You cannot specify a username (phone number) when linking"); } - return handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment); + handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment); + return; } if (username == null) { var usernames = Manager.getAllLocalUsernames(dataPath); if (usernames.size() == 0) { - System.err.println("No local users found, you first need to register or link an account"); - return 1; + throw new UserErrorException("No local users found, you first need to register or link an account"); } if (command instanceof MultiLocalCommand) { - return handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceEnvironment, usernames); + handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceEnvironment, usernames); + return; } if (usernames.size() > 1) { - System.err.println("Multiple users found, you need to specify a username (phone number) with -u"); - return 1; + throw new UserErrorException( + "Multiple users found, you need to specify a username (phone number) with -u"); } username = usernames.get(0); } else if (!PhoneNumberFormatter.isValidNumber(username, null)) { - System.err.println("Invalid username (phone number), make sure you include the country code."); - return 1; + throw new UserErrorException("Invalid username (phone number), make sure you include the country code."); } if (command instanceof RegistrationCommand) { - return handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment); + handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment); + return; } if (!(command instanceof LocalCommand)) { - System.err.println("Command only works via dbus"); - return 1; + throw new UserErrorException("Command only works via dbus"); } - return handleLocalCommand((LocalCommand) command, username, dataPath, serviceEnvironment); + handleLocalCommand((LocalCommand) command, username, dataPath, serviceEnvironment); } - private int handleProvisioningCommand( + private void handleProvisioningCommand( final ProvisioningCommand command, final File dataPath, final ServiceEnvironment serviceEnvironment - ) { + ) throws CommandException { var pm = ProvisioningManager.init(dataPath, serviceEnvironment, BaseConfig.USER_AGENT); - return command.handleCommand(ns, pm); + command.handleCommand(ns, pm); } - private int handleRegistrationCommand( + private void handleRegistrationCommand( final RegistrationCommand command, final String username, final File dataPath, final ServiceEnvironment serviceEnvironment - ) { + ) throws CommandException { final RegistrationManager manager; try { manager = RegistrationManager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (Throwable e) { - logger.error("Error loading or creating state file: {}", e.getMessage()); - return 2; + throw new UnexpectedErrorException("Error loading or creating state file: " + e.getMessage()); } try (var m = manager) { - return command.handleCommand(ns, m); + command.handleCommand(ns, m); } catch (IOException e) { - logger.error("Cleanup failed", e); - return 2; + logger.warn("Cleanup failed", e); } } - private int handleLocalCommand( + private void handleLocalCommand( final LocalCommand command, final String username, final File dataPath, final ServiceEnvironment serviceEnvironment - ) { + ) throws CommandException { try (var m = loadManager(username, dataPath, serviceEnvironment)) { - if (m == null) { - return 2; - } - - return command.handleCommand(ns, m); + command.handleCommand(ns, m); } catch (IOException e) { - logger.error("Cleanup failed", e); - return 2; + logger.warn("Cleanup failed", e); } } - private int handleMultiLocalCommand( + private void handleMultiLocalCommand( final MultiLocalCommand command, final File dataPath, final ServiceEnvironment serviceEnvironment, final List usernames - ) { - final var managers = usernames.stream() - .map(u -> loadManager(u, dataPath, serviceEnvironment)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + ) throws CommandException { + final var managers = new ArrayList(); + for (String u : usernames) { + managers.add(loadManager(u, dataPath, serviceEnvironment)); + } - var result = command.handleCommand(ns, managers); + command.handleCommand(ns, managers); for (var m : managers) { try { @@ -231,34 +223,32 @@ public class App { logger.warn("Cleanup failed", e); } } - return result; } private Manager loadManager( final String username, final File dataPath, final ServiceEnvironment serviceEnvironment - ) { + ) throws CommandException { Manager manager; try { manager = Manager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (NotRegisteredException e) { - logger.error("User " + username + " is not registered."); - return null; + throw new UserErrorException("User " + username + " is not registered."); } catch (Throwable e) { - logger.error("Error loading state file for user " + username + ": {}", e.getMessage()); - return null; + throw new UnexpectedErrorException("Error loading state file for user " + username + ": " + e.getMessage()); } try { manager.checkAccountState(); } catch (IOException e) { - logger.error("Error while checking account " + username + ": {}", e.getMessage()); - return null; + throw new UnexpectedErrorException("Error while checking account " + username + ": " + e.getMessage()); } return manager; } - private int initDbusClient(final Command command, final String username, final boolean systemBus) { + private void initDbusClient( + final Command command, final String username, final boolean systemBus + ) throws CommandException { try { DBusConnection.DBusBusType busType; if (systemBus) { @@ -271,22 +261,21 @@ public class App { DbusConfig.getObjectPath(username), Signal.class); - return handleCommand(command, ts, dBusConn); + handleCommand(command, ts, dBusConn); } } catch (DBusException | IOException e) { logger.error("Dbus client failed", e); - return 2; + throw new UnexpectedErrorException("Dbus client failed"); } } - private int handleCommand(Command command, Signal ts, DBusConnection dBusConn) { + private void handleCommand(Command command, Signal ts, DBusConnection dBusConn) throws CommandException { if (command instanceof ExtendedDbusCommand) { - return ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn); + ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn); } else if (command instanceof DbusCommand) { - return ((DbusCommand) command).handleCommand(ns, ts); + ((DbusCommand) command).handleCommand(ns, ts); } else { - System.err.println("Command is not yet implemented via dbus"); - return 1; + throw new UserErrorException("Command is not yet implemented via dbus"); } } diff --git a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java index 38657f5e..c8e1c24f 100644 --- a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java @@ -8,7 +8,6 @@ import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import java.io.IOException; import java.util.HashMap; public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler { @@ -32,10 +31,7 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler if (envelope != null) { object.put("envelope", new JsonMessageEnvelope(envelope, content, m)); } - try { - jsonWriter.write(object); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - } + + jsonWriter.write(object); } } diff --git a/src/main/java/org/asamk/signal/JsonWriter.java b/src/main/java/org/asamk/signal/JsonWriter.java index 8aed4487..e7549adf 100644 --- a/src/main/java/org/asamk/signal/JsonWriter.java +++ b/src/main/java/org/asamk/signal/JsonWriter.java @@ -26,14 +26,18 @@ public class JsonWriter { objectMapper.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); } - public void write(final Object object) throws IOException { + public void write(final Object object) { try { - objectMapper.writeValue(writer, object); - } catch (JsonProcessingException e) { - // Some issue with json serialization, probably caused by a bug + try { + objectMapper.writeValue(writer, object); + } catch (JsonProcessingException e) { + // Some issue with json serialization, probably caused by a bug + throw new AssertionError(e); + } + writer.write(System.lineSeparator()); + writer.flush(); + } catch (IOException e) { throw new AssertionError(e); } - writer.write(System.lineSeparator()); - writer.flush(); } } diff --git a/src/main/java/org/asamk/signal/Main.java b/src/main/java/org/asamk/signal/Main.java index 88ae06ca..775b5223 100644 --- a/src/main/java/org/asamk/signal/Main.java +++ b/src/main/java/org/asamk/signal/Main.java @@ -21,6 +21,11 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.LibSignalLogger; import org.asamk.signal.util.SecurityProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -39,8 +44,14 @@ public class Main { var ns = parser.parseArgsOrFail(args); - var res = new App(ns).init(); - System.exit(res); + int status = 0; + try { + new App(ns).init(); + } catch (CommandException e) { + System.err.println(e.getMessage()); + status = getStatusForError(e); + } + System.exit(status); } private static void installSecurityProviderWorkaround() { @@ -78,4 +89,18 @@ public class Main { System.setProperty("org.slf4j.simpleLogger.showDateTime", "false"); } } + + private static int getStatusForError(final CommandException e) { + if (e instanceof UserErrorException) { + return 1; + } else if (e instanceof UnexpectedErrorException) { + return 2; + } else if (e instanceof IOErrorException) { + return 3; + } else if (e instanceof UntrustedKeyErrorException) { + return 4; + } else { + return 2; + } + } } diff --git a/src/main/java/org/asamk/signal/PlainTextWriter.java b/src/main/java/org/asamk/signal/PlainTextWriter.java index 91a4dbba..de738de9 100644 --- a/src/main/java/org/asamk/signal/PlainTextWriter.java +++ b/src/main/java/org/asamk/signal/PlainTextWriter.java @@ -1,23 +1,21 @@ package org.asamk.signal; -import java.io.IOException; - public interface PlainTextWriter { - void println(String format, Object... args) throws IOException; + void println(String format, Object... args); PlainTextWriter indentedWriter(); - default void println() throws IOException { + default void println() { println(""); } - default void indent(final WriterConsumer subWriter) throws IOException { + default void indent(final WriterConsumer subWriter) { subWriter.consume(indentedWriter()); } interface WriterConsumer { - void consume(PlainTextWriter writer) throws IOException; + void consume(PlainTextWriter writer); } } diff --git a/src/main/java/org/asamk/signal/PlainTextWriterImpl.java b/src/main/java/org/asamk/signal/PlainTextWriterImpl.java index d6536481..bb18b7f3 100644 --- a/src/main/java/org/asamk/signal/PlainTextWriterImpl.java +++ b/src/main/java/org/asamk/signal/PlainTextWriterImpl.java @@ -19,12 +19,16 @@ public final class PlainTextWriterImpl implements PlainTextWriter { } @Override - public void println(String format, Object... args) throws IOException { + public void println(String format, Object... args) { final var message = MessageFormatter.arrayFormat(format, args).getMessage(); - writer.write(message); - writer.write(System.lineSeparator()); - writer.flush(); + try { + writer.write(message); + writer.write(System.lineSeparator()); + writer.flush(); + } catch (IOException e) { + throw new AssertionError(e); + } } @Override @@ -51,8 +55,12 @@ public final class PlainTextWriterImpl implements PlainTextWriter { } @Override - public void println(final String format, final Object... args) throws IOException { - writer.write(spaces); + public void println(final String format, final Object... args) { + try { + writer.write(spaces); + } catch (IOException e) { + throw new AssertionError(e); + } plainTextWriter.println(format, args); } diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 8c75fcba..6df283f3 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -630,7 +630,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength()); } - private void printAttachment(PlainTextWriter writer, SignalServiceAttachment attachment) throws IOException { + private void printAttachment(PlainTextWriter writer, SignalServiceAttachment attachment) { writer.println("Content-Type: {}", attachment.getContentType()); writer.println("Type: {}", attachment.isPointer() ? "Pointer" : attachment.isStream() ? "Stream" : ""); if (attachment.isPointer()) { diff --git a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java index c5d18ab1..cf993e6d 100644 --- a/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/AddDeviceCommand.java @@ -3,7 +3,13 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.InvalidKeyException; import java.io.IOException; @@ -14,6 +20,8 @@ import static org.asamk.signal.util.ErrorUtils.handleAssertionError; public class AddDeviceCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(AddDeviceCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("--uri") @@ -22,19 +30,20 @@ public class AddDeviceCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.addDeviceLink(new URI(ns.getString("uri"))); - return 0; } catch (IOException e) { - e.printStackTrace(); - return 3; - } catch (InvalidKeyException | URISyntaxException e) { - e.printStackTrace(); - return 2; + logger.error("Add device link failed", e); + throw new IOErrorException("Add device link failed"); + } catch (URISyntaxException e) { + throw new UserErrorException("Device link uri has invalid format: {}" + e.getMessage()); + } catch (InvalidKeyException e) { + logger.error("Add device link failed", e); + throw new UnexpectedErrorException("Add device link failed."); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } } } diff --git a/src/main/java/org/asamk/signal/commands/BlockCommand.java b/src/main/java/org/asamk/signal/commands/BlockCommand.java index 9dff25c6..98fce667 100644 --- a/src/main/java/org/asamk/signal/commands/BlockCommand.java +++ b/src/main/java/org/asamk/signal/commands/BlockCommand.java @@ -7,10 +7,14 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.InvalidNumberException; public class BlockCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(BlockCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("contact").help("Contact number").nargs("*"); @@ -19,12 +23,12 @@ public class BlockCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) { for (var contact_number : ns.getList("contact")) { try { m.setContactBlocked(contact_number, true); } catch (InvalidNumberException e) { - System.err.println(e.getMessage()); + logger.warn("Invalid number {}: {}", contact_number, e.getMessage()); } } @@ -34,11 +38,9 @@ public class BlockCommand implements LocalCommand { var groupId = Util.decodeGroupId(groupIdString); m.setGroupBlocked(groupId, true); } catch (GroupIdFormatException | GroupNotFoundException e) { - System.err.println(e.getMessage()); + logger.warn("Invalid group id {}: {}", groupIdString, e.getMessage()); } } } - - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/DaemonCommand.java b/src/main/java/org/asamk/signal/commands/DaemonCommand.java index 6ee4f316..8d26e452 100644 --- a/src/main/java/org/asamk/signal/commands/DaemonCommand.java +++ b/src/main/java/org/asamk/signal/commands/DaemonCommand.java @@ -8,6 +8,8 @@ import org.asamk.signal.DbusConfig; import org.asamk.signal.DbusReceiveMessageHandler; import org.asamk.signal.JsonDbusReceiveMessageHandler; import org.asamk.signal.OutputType; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; import org.freedesktop.dbus.connections.impl.DBusConnection; @@ -44,7 +46,7 @@ public class DaemonCommand implements MultiLocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -71,15 +73,14 @@ public class DaemonCommand implements MultiLocalCommand { t.join(); } catch (InterruptedException ignored) { } - return 0; } catch (DBusException | IOException e) { logger.error("Dbus command failed", e); - return 2; + throw new UnexpectedErrorException("Dbus command failed"); } } @Override - public int handleCommand(final Namespace ns, final List managers) { + public void handleCommand(final Namespace ns, final List managers) throws CommandException { var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -112,10 +113,9 @@ public class DaemonCommand implements MultiLocalCommand { } catch (InterruptedException ignored) { } } - return 0; } catch (DBusException | IOException e) { logger.error("Dbus command failed", e); - return 2; + throw new UnexpectedErrorException("Dbus command failed"); } } diff --git a/src/main/java/org/asamk/signal/commands/DbusCommand.java b/src/main/java/org/asamk/signal/commands/DbusCommand.java index 1b3a0268..e4c78c84 100644 --- a/src/main/java/org/asamk/signal/commands/DbusCommand.java +++ b/src/main/java/org/asamk/signal/commands/DbusCommand.java @@ -3,14 +3,15 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import org.asamk.Signal; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.dbus.DbusSignalImpl; import org.asamk.signal.manager.Manager; public interface DbusCommand extends LocalCommand { - int handleCommand(Namespace ns, Signal signal); + void handleCommand(Namespace ns, Signal signal) throws CommandException; - default int handleCommand(final Namespace ns, final Manager m) { - return handleCommand(ns, new DbusSignalImpl(m)); + default void handleCommand(final Namespace ns, final Manager m) throws CommandException { + handleCommand(ns, new DbusSignalImpl(m)); } } diff --git a/src/main/java/org/asamk/signal/commands/ExtendedDbusCommand.java b/src/main/java/org/asamk/signal/commands/ExtendedDbusCommand.java index f9cd9de8..1d454f4d 100644 --- a/src/main/java/org/asamk/signal/commands/ExtendedDbusCommand.java +++ b/src/main/java/org/asamk/signal/commands/ExtendedDbusCommand.java @@ -3,9 +3,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import org.asamk.Signal; +import org.asamk.signal.commands.exceptions.CommandException; import org.freedesktop.dbus.connections.impl.DBusConnection; public interface ExtendedDbusCommand extends Command { - int handleCommand(Namespace ns, Signal signal, DBusConnection dbusconnection); + void handleCommand(Namespace ns, Signal signal, DBusConnection dbusconnection) throws CommandException; } diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 07de8321..69140b23 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -7,6 +7,8 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +38,7 @@ public class GetUserStatusCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { // Setup the json object mapper var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); @@ -50,8 +52,8 @@ public class GetUserStatusCommand implements LocalCommand { try { registered = m.areUsersRegistered(new HashSet<>(ns.getList("number"))); } catch (IOException e) { - System.err.println("Unable to check if users are registered"); - return 3; + logger.debug("Failed to check registered users", e); + throw new IOErrorException("Unable to check if users are registered"); } // Output @@ -63,26 +65,14 @@ public class GetUserStatusCommand implements LocalCommand { .map(entry -> new JsonUserStatus(entry.getKey(), entry.getValue())) .collect(Collectors.toList()); - try { - jsonWriter.write(jsonUserStatuses); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - return 3; - } + jsonWriter.write(jsonUserStatuses); } else { final var writer = new PlainTextWriterImpl(System.out); - try { - for (var entry : registered.entrySet()) { - writer.println("{}: {}", entry.getKey(), entry.getValue()); - } - } catch (IOException e) { - e.printStackTrace(); - return 3; + for (var entry : registered.entrySet()) { + writer.println("{}: {}", entry.getKey(), entry.getValue()); } } - - return 0; } private static final class JsonUserStatus { diff --git a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java index 7f1cb0b0..e59ecec6 100644 --- a/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/JoinGroupCommand.java @@ -4,46 +4,42 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.freedesktop.dbus.exceptions.DBusExecutionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException; import java.io.IOException; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleIOException; import static org.asamk.signal.util.ErrorUtils.handleTimestampAndSendMessageResults; public class JoinGroupCommand implements LocalCommand { - private final static Logger logger = LoggerFactory.getLogger(JoinGroupCommand.class); - @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("--uri").required(true).help("Specify the uri with the group invitation link."); } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final GroupInviteLinkUrl linkUrl; var uri = ns.getString("uri"); try { linkUrl = GroupInviteLinkUrl.fromUri(uri); } catch (GroupInviteLinkUrl.InvalidGroupLinkException e) { - System.err.println("Group link is invalid: " + e.getMessage()); - return 1; + throw new UserErrorException("Group link is invalid: " + e.getMessage()); } catch (GroupInviteLinkUrl.UnknownGroupLinkVersionException e) { - System.err.println("Group link was created with an incompatible version: " + e.getMessage()); - return 1; + throw new UserErrorException("Group link was created with an incompatible version: " + e.getMessage()); } if (linkUrl == null) { - System.err.println("Link is not a signal group invitation link"); - return 1; + throw new UserErrorException("Link is not a signal group invitation link"); } try { @@ -56,23 +52,18 @@ public class JoinGroupCommand implements LocalCommand { } else { writer.println("Joined group \"{}\"", newGroupId.toBase64()); } - return handleTimestampAndSendMessageResults(writer, 0, results.second()); + handleTimestampAndSendMessageResults(writer, 0, results.second()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (GroupPatchNotAcceptedException e) { - System.err.println("Failed to join group, maybe already a member"); - return 1; + throw new UserErrorException("Failed to join group, maybe already a member"); } catch (IOException e) { - e.printStackTrace(); - handleIOException(e); - return 3; + throw new IOErrorException("Failed to send message: " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } catch (GroupLinkNotActiveException e) { - System.err.println("Group link is not valid: " + e.getMessage()); - return 1; + throw new UserErrorException("Group link is not valid: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/LinkCommand.java b/src/main/java/org/asamk/signal/commands/LinkCommand.java index fe580916..34775773 100644 --- a/src/main/java/org/asamk/signal/commands/LinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/LinkCommand.java @@ -4,6 +4,10 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.ProvisioningManager; import org.asamk.signal.manager.UserAlreadyExists; import org.slf4j.Logger; @@ -25,7 +29,7 @@ public class LinkCommand implements ProvisioningCommand { } @Override - public int handleCommand(final Namespace ns, final ProvisioningManager m) { + public void handleCommand(final Namespace ns, final ProvisioningManager m) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); var deviceName = ns.getString("name"); @@ -37,25 +41,21 @@ public class LinkCommand implements ProvisioningCommand { var username = m.finishDeviceLink(deviceName); writer.println("Associated with: {}", username); } catch (TimeoutException e) { - System.err.println("Link request timed out, please try again."); - return 3; + throw new UserErrorException("Link request timed out, please try again."); } catch (IOException e) { - System.err.println("Link request error: " + e.getMessage()); - return 3; + throw new IOErrorException("Link request error: " + e.getMessage()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (InvalidKeyException e) { - e.printStackTrace(); - return 2; + logger.debug("Finish device link failed", e); + throw new UnexpectedErrorException("Invalid key: " + e.getMessage()); } catch (UserAlreadyExists e) { - System.err.println("The user " + throw new UserErrorException("The user " + e.getUsername() + " already exists\nDelete \"" + e.getFileName() + "\" before trying again."); - return 1; } - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 6609ec60..4b27e17d 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -6,8 +6,6 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.manager.Manager; -import java.io.IOException; - public class ListContactsCommand implements LocalCommand { @Override @@ -15,18 +13,12 @@ public class ListContactsCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) { final var writer = new PlainTextWriterImpl(System.out); var contacts = m.getContacts(); - try { - for (var c : contacts) { - writer.println("Number: {} Name: {} Blocked: {}", c.number, c.name, c.blocked); - } - } catch (IOException e) { - e.printStackTrace(); - return 3; + for (var c : contacts) { + writer.println("Number: {} Name: {} Blocked: {}", c.number, c.name, c.blocked); } - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java index f2037239..7165d07c 100644 --- a/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListDevicesCommand.java @@ -4,34 +4,44 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.util.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo; import java.io.IOException; +import java.util.List; public class ListDevicesCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(ListDevicesCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); + + List devices; try { - var devices = m.getLinkedDevices(); - for (var d : devices) { - writer.println("- Device {}{}:", d.getId(), (d.getId() == m.getDeviceId() ? " (this device)" : "")); - writer.indent(w -> { - w.println("Name: {}", d.getName()); - w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated())); - w.println("Last seen: {}", DateUtils.formatTimestamp(d.getLastSeen())); - }); - } - return 0; + devices = m.getLinkedDevices(); } catch (IOException e) { - e.printStackTrace(); - return 3; + logger.debug("Failed to get linked devices", e); + throw new IOErrorException("Failed to get linked devices: " + e.getMessage()); + } + + for (var d : devices) { + writer.println("- Device {}{}:", d.getId(), (d.getId() == m.getDeviceId() ? " (this device)" : "")); + writer.indent(w -> { + w.println("Name: {}", d.getName()); + w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated())); + w.println("Last seen: {}", DateUtils.formatTimestamp(d.getLastSeen())); + }); } } } diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 477d56a1..a547cc15 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -8,13 +8,13 @@ import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.PlainTextWriter; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.storage.groups.GroupInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import java.io.IOException; import java.util.ArrayList; import java.util.Set; import java.util.stream.Collectors; @@ -32,7 +32,7 @@ public class ListGroupsCommand implements LocalCommand { private static void printGroupPlainText( PlainTextWriter writer, Manager m, GroupInfo group, boolean detailed - ) throws IOException { + ) { if (detailed) { final var groupInviteLink = group.getGroupInviteLink(); @@ -70,7 +70,7 @@ public class ListGroupsCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { if (ns.get("output") == OutputType.JSON) { final var jsonWriter = new JsonWriter(System.out); @@ -88,28 +88,14 @@ public class ListGroupsCommand implements LocalCommand { groupInviteLink == null ? null : groupInviteLink.getUrl())); } - try { - jsonWriter.write(jsonGroups); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - return 3; - } - - return 0; + jsonWriter.write(jsonGroups); } else { final var writer = new PlainTextWriterImpl(System.out); boolean detailed = ns.getBoolean("detailed"); - try { - for (var group : m.getGroups()) { - printGroupPlainText(writer, m, group, detailed); - } - } catch (IOException e) { - e.printStackTrace(); - return 3; + for (var group : m.getGroups()) { + printGroupPlainText(writer, m, group, detailed); } } - - return 0; } private static final class JsonGroup { diff --git a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java index 1f1b6258..dc2d92fb 100644 --- a/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListIdentitiesCommand.java @@ -5,6 +5,8 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriter; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.util.Hex; @@ -13,7 +15,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.InvalidNumberException; -import java.io.IOException; +import java.util.List; public class ListIdentitiesCommand implements LocalCommand { @@ -21,16 +23,12 @@ public class ListIdentitiesCommand implements LocalCommand { private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, IdentityInfo theirId) { var digits = Util.formatSafetyNumber(m.computeSafetyNumber(theirId.getAddress(), theirId.getIdentityKey())); - try { - writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", - theirId.getAddress().getNumber().orNull(), - theirId.getTrustLevel(), - theirId.getDateAdded(), - Hex.toString(theirId.getFingerprint()), - digits); - } catch (IOException e) { - e.printStackTrace(); - } + writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", + theirId.getAddress().getNumber().orNull(), + theirId.getTrustLevel(), + theirId.getDateAdded(), + Hex.toString(theirId.getFingerprint()), + digits); } @Override @@ -39,24 +37,27 @@ public class ListIdentitiesCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); - if (ns.get("number") == null) { + var number = ns.getString("number"); + + if (number == null) { for (var identity : m.getIdentities()) { printIdentityFingerprint(writer, m, identity); } - } else { - var number = ns.getString("number"); - try { - var identities = m.getIdentities(number); - for (var id : identities) { - printIdentityFingerprint(writer, m, id); - } - } catch (InvalidNumberException e) { - System.err.println("Invalid number: " + e.getMessage()); - } + return; + } + + List identities; + try { + identities = m.getIdentities(number); + } catch (InvalidNumberException e) { + throw new UserErrorException("Invalid number: " + e.getMessage()); + } + + for (var id : identities) { + printIdentityFingerprint(writer, m, id); } - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/LocalCommand.java b/src/main/java/org/asamk/signal/commands/LocalCommand.java index 7bcb1a4b..a7c64dc1 100644 --- a/src/main/java/org/asamk/signal/commands/LocalCommand.java +++ b/src/main/java/org/asamk/signal/commands/LocalCommand.java @@ -2,9 +2,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; public interface LocalCommand extends Command { - int handleCommand(Namespace ns, Manager m); + void handleCommand(Namespace ns, Manager m) throws CommandException; } diff --git a/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java b/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java index e8ee8e1d..2a8457bd 100644 --- a/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java +++ b/src/main/java/org/asamk/signal/commands/MultiLocalCommand.java @@ -2,16 +2,17 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; import java.util.List; public interface MultiLocalCommand extends LocalCommand { - int handleCommand(Namespace ns, List m); + void handleCommand(Namespace ns, List m) throws CommandException; @Override - default int handleCommand(final Namespace ns, final Manager m) { - return handleCommand(ns, List.of(m)); + default void handleCommand(final Namespace ns, final Manager m) throws CommandException { + handleCommand(ns, List.of(m)); } } diff --git a/src/main/java/org/asamk/signal/commands/ProvisioningCommand.java b/src/main/java/org/asamk/signal/commands/ProvisioningCommand.java index 12a612ff..354e4af3 100644 --- a/src/main/java/org/asamk/signal/commands/ProvisioningCommand.java +++ b/src/main/java/org/asamk/signal/commands/ProvisioningCommand.java @@ -2,9 +2,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.ProvisioningManager; public interface ProvisioningCommand extends Command { - int handleCommand(Namespace ns, ProvisioningManager m); + void handleCommand(Namespace ns, ProvisioningManager m) throws CommandException; } diff --git a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java index cdbccf19..d8a86585 100644 --- a/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/QuitGroupCommand.java @@ -4,7 +4,11 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; @@ -13,10 +17,6 @@ import org.asamk.signal.util.Util; import java.io.IOException; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; -import static org.asamk.signal.util.ErrorUtils.handleGroupNotFoundException; -import static org.asamk.signal.util.ErrorUtils.handleIOException; -import static org.asamk.signal.util.ErrorUtils.handleNotAGroupMemberException; import static org.asamk.signal.util.ErrorUtils.handleTimestampAndSendMessageResults; public class QuitGroupCommand implements LocalCommand { @@ -27,28 +27,28 @@ public class QuitGroupCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { - try { - final var writer = new PlainTextWriterImpl(System.out); + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { + final var writer = new PlainTextWriterImpl(System.out); - final var groupId = Util.decodeGroupId(ns.getString("group")); + final GroupId groupId; + try { + groupId = Util.decodeGroupId(ns.getString("group")); + } catch (GroupIdFormatException e) { + throw new UserErrorException("Invalid group id:" + e.getMessage()); + } + + try { final var results = m.sendQuitGroupMessage(groupId); - return handleTimestampAndSendMessageResults(writer, results.first(), results.second()); + handleTimestampAndSendMessageResults(writer, results.first(), results.second()); } catch (IOException e) { - handleIOException(e); - return 3; + throw new IOErrorException("Failed to send message: " + e.getMessage()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (GroupNotFoundException e) { - handleGroupNotFoundException(e); - return 1; + throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (NotAGroupMemberException e) { - handleNotAGroupMemberException(e); - return 1; - } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; + throw new UserErrorException("Failed to send to group: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 834bc5eb..8612a71b 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -10,6 +10,9 @@ import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.ReceiveMessageHandler; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.json.JsonMessageEnvelope; import org.asamk.signal.manager.Manager; import org.asamk.signal.util.DateUtils; @@ -48,7 +51,9 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { return Set.of(OutputType.PLAIN_TEXT, OutputType.JSON); } - public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) { + public void handleCommand( + final Namespace ns, final Signal signal, DBusConnection dbusconnection + ) throws CommandException { var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -63,106 +68,81 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { var envelope = new JsonMessageEnvelope(messageReceived); final var object = Map.of("envelope", envelope); - try { - jsonWriter.write(object); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - } + jsonWriter.write(object); }); dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { var envelope = new JsonMessageEnvelope(receiptReceived); final var object = Map.of("envelope", envelope); - try { - jsonWriter.write(object); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - } + jsonWriter.write(object); }); dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { var envelope = new JsonMessageEnvelope(syncReceived); final var object = Map.of("envelope", envelope); - try { - jsonWriter.write(object); - } catch (IOException e) { - logger.error("Failed to write json object: {}", e.getMessage()); - } + jsonWriter.write(object); }); } else { final var writer = new PlainTextWriterImpl(System.out); dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { - try { - writer.println("Envelope from: {}", messageReceived.getSender()); - writer.println("Timestamp: {}", DateUtils.formatTimestamp(messageReceived.getTimestamp())); - writer.println("Body: {}", messageReceived.getMessage()); - if (messageReceived.getGroupId().length > 0) { - writer.println("Group info:"); - writer.indentedWriter() - .println("Id: {}", - Base64.getEncoder().encodeToString(messageReceived.getGroupId())); - } - if (messageReceived.getAttachments().size() > 0) { - writer.println("Attachments:"); - for (var attachment : messageReceived.getAttachments()) { - writer.println("- Stored plaintext in: {}", attachment); - } - } - writer.println(); - } catch (IOException e) { - e.printStackTrace(); + writer.println("Envelope from: {}", messageReceived.getSender()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(messageReceived.getTimestamp())); + writer.println("Body: {}", messageReceived.getMessage()); + if (messageReceived.getGroupId().length > 0) { + writer.println("Group info:"); + writer.indentedWriter() + .println("Id: {}", Base64.getEncoder().encodeToString(messageReceived.getGroupId())); } + if (messageReceived.getAttachments().size() > 0) { + writer.println("Attachments:"); + for (var attachment : messageReceived.getAttachments()) { + writer.println("- Stored plaintext in: {}", attachment); + } + } + writer.println(); }); dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { - try { - writer.println("Receipt from: {}", receiptReceived.getSender()); - writer.println("Timestamp: {}", DateUtils.formatTimestamp(receiptReceived.getTimestamp())); - } catch (IOException e) { - e.printStackTrace(); - } + writer.println("Receipt from: {}", receiptReceived.getSender()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(receiptReceived.getTimestamp())); }); dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { - try { - writer.println("Sync Envelope from: {} to: {}", - syncReceived.getSource(), - syncReceived.getDestination()); - writer.println("Timestamp: {}", DateUtils.formatTimestamp(syncReceived.getTimestamp())); - writer.println("Body: {}", syncReceived.getMessage()); - if (syncReceived.getGroupId().length > 0) { - writer.println("Group info:"); - writer.indentedWriter() - .println("Id: {}", Base64.getEncoder().encodeToString(syncReceived.getGroupId())); - } - if (syncReceived.getAttachments().size() > 0) { - writer.println("Attachments:"); - for (var attachment : syncReceived.getAttachments()) { - writer.println("- Stored plaintext in: {}", attachment); - } - } - writer.println(); - } catch (IOException e) { - e.printStackTrace(); + writer.println("Sync Envelope from: {} to: {}", + syncReceived.getSource(), + syncReceived.getDestination()); + writer.println("Timestamp: {}", DateUtils.formatTimestamp(syncReceived.getTimestamp())); + writer.println("Body: {}", syncReceived.getMessage()); + if (syncReceived.getGroupId().length > 0) { + writer.println("Group info:"); + writer.indentedWriter() + .println("Id: {}", Base64.getEncoder().encodeToString(syncReceived.getGroupId())); } + if (syncReceived.getAttachments().size() > 0) { + writer.println("Attachments:"); + for (var attachment : syncReceived.getAttachments()) { + writer.println("- Stored plaintext in: {}", attachment); + } + } + writer.println(); }); } } catch (DBusException e) { - e.printStackTrace(); - return 2; + logger.error("Dbus client failed", e); + throw new UnexpectedErrorException("Dbus client failed"); } while (true) { try { Thread.sleep(10000); - } catch (InterruptedException e) { - return 0; + } catch (InterruptedException ignored) { + return; } } } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -187,13 +167,11 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { returnOnTimeout, ignoreAttachments, handler); - return 0; } catch (IOException e) { - System.err.println("Error while receiving messages: " + e.getMessage()); - return 3; + throw new IOErrorException("Error while receiving messages: " + e.getMessage()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } } } diff --git a/src/main/java/org/asamk/signal/commands/RegisterCommand.java b/src/main/java/org/asamk/signal/commands/RegisterCommand.java index d656fe0e..a4c613af 100644 --- a/src/main/java/org/asamk/signal/commands/RegisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegisterCommand.java @@ -4,6 +4,9 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.RegistrationManager; import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException; @@ -21,26 +24,25 @@ public class RegisterCommand implements RegistrationCommand { } @Override - public int handleCommand(final Namespace ns, final RegistrationManager m) { + public void handleCommand(final Namespace ns, final RegistrationManager m) throws CommandException { final boolean voiceVerification = ns.getBoolean("voice"); final var captcha = ns.getString("captcha"); try { m.register(voiceVerification, captcha); - return 0; } catch (CaptchaRequiredException e) { + String message; if (captcha == null) { - System.err.println("Captcha required for verification, use --captcha CAPTCHA"); - System.err.println("To get the token, go to https://signalcaptchas.org/registration/generate.html"); - System.err.println("Check the developer tools (F12) console for a failed redirect to signalcaptcha://"); - System.err.println("Everything after signalcaptcha:// is the captcha token."); + message = "Captcha required for verification, use --captcha CAPTCHA\n" + + "To get the token, go to https://signalcaptchas.org/registration/generate.html\n" + + "Check the developer tools (F12) console for a failed redirect to signalcaptcha://\n" + + "Everything after signalcaptcha:// is the captcha token."; } else { - System.err.println("Invalid captcha given."); + message = "Invalid captcha given."; } - return 1; + throw new UserErrorException(message); } catch (IOException e) { - System.err.println("Request verify error: " + e.getMessage()); - return 3; + throw new IOErrorException("Request verify error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/RegistrationCommand.java b/src/main/java/org/asamk/signal/commands/RegistrationCommand.java index 8683570f..425ac71d 100644 --- a/src/main/java/org/asamk/signal/commands/RegistrationCommand.java +++ b/src/main/java/org/asamk/signal/commands/RegistrationCommand.java @@ -2,9 +2,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.RegistrationManager; public interface RegistrationCommand extends Command { - int handleCommand(Namespace ns, RegistrationManager m); + void handleCommand(Namespace ns, RegistrationManager m) throws CommandException; } diff --git a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java index 78d14bbd..c9be92e8 100644 --- a/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemoveDeviceCommand.java @@ -3,6 +3,8 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import java.io.IOException; @@ -18,14 +20,12 @@ public class RemoveDeviceCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { int deviceId = ns.getInt("deviceId"); m.removeLinkedDevices(deviceId); - return 0; } catch (IOException e) { - e.printStackTrace(); - return 3; + throw new IOErrorException("Error while removing device: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java index 52b111ea..03d8d7cb 100644 --- a/src/main/java/org/asamk/signal/commands/RemovePinCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemovePinCommand.java @@ -3,6 +3,9 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; @@ -16,16 +19,13 @@ public class RemovePinCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.setRegistrationLockPin(Optional.absent()); - return 0; } catch (UnauthenticatedResponseException e) { - System.err.println("Remove pin error: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Remove pin failed with unauthenticated response: " + e.getMessage()); } catch (IOException e) { - System.err.println("Remove pin error: " + e.getMessage()); - return 3; + throw new IOErrorException("Remove pin error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 1976fb60..23d74165 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -6,6 +6,10 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.IOUtils; import org.asamk.signal.util.Util; @@ -19,7 +23,6 @@ import java.nio.charset.Charset; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; public class SendCommand implements DbusCommand { @@ -42,7 +45,7 @@ public class SendCommand implements DbusCommand { } @Override - public int handleCommand(final Namespace ns, final Signal signal) { + public void handleCommand(final Namespace ns, final Signal signal) throws CommandException { final List recipients = ns.getList("recipient"); final var isEndSession = ns.getBoolean("endsession"); final var groupIdString = ns.getString("group"); @@ -50,32 +53,27 @@ public class SendCommand implements DbusCommand { final var noRecipients = recipients == null || recipients.isEmpty(); if ((noRecipients && isEndSession) || (noRecipients && groupIdString == null && !isNoteToSelf)) { - System.err.println("No recipients given"); - System.err.println("Aborting sending."); - return 1; + throw new UserErrorException("No recipients given"); } if (!noRecipients && groupIdString != null) { - System.err.println("You cannot specify recipients by phone number and groups at the same time"); - return 1; + throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time"); } if (!noRecipients && isNoteToSelf) { - System.err.println("You cannot specify recipients by phone number and not to self at the same time"); - return 1; + throw new UserErrorException( + "You cannot specify recipients by phone number and not to self at the same time"); } if (isEndSession) { try { signal.sendEndSessionMessage(recipients); - return 0; + return; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (Signal.Error.UntrustedIdentity e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 4; + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } } @@ -84,9 +82,7 @@ public class SendCommand implements DbusCommand { try { messageText = IOUtils.readAll(System.in, Charset.defaultCharset()); } catch (IOException e) { - System.err.println("Failed to read message from stdin: " + e.getMessage()); - System.err.println("Aborting sending."); - return 1; + throw new UserErrorException("Failed to read message from stdin: " + e.getMessage()); } } @@ -102,23 +98,18 @@ public class SendCommand implements DbusCommand { try { groupId = Util.decodeGroupId(groupIdString).serialize(); } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; + throw new UserErrorException("Invalid group id:" + e.getMessage()); } try { var timestamp = signal.sendGroupMessage(messageText, attachments, groupId); writer.println("{}", timestamp); - return 0; + return; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (DBusExecutionException e) { - System.err.println("Failed to send group message: " + e.getMessage()); - return 2; - } catch (IOException e) { - e.printStackTrace(); - return 3; + throw new UnexpectedErrorException("Failed to send group message: " + e.getMessage()); } } @@ -126,41 +117,29 @@ public class SendCommand implements DbusCommand { try { var timestamp = signal.sendNoteToSelfMessage(messageText, attachments); writer.println("{}", timestamp); - return 0; + return; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (Signal.Error.UntrustedIdentity e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 4; + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send note to self message: " + e.getMessage()); - return 2; - } catch (IOException e) { - e.printStackTrace(); - return 3; + throw new UnexpectedErrorException("Failed to send note to self message: " + e.getMessage()); } } try { var timestamp = signal.sendMessage(messageText, attachments, recipients); writer.println("{}", timestamp); - return 0; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (UnknownObject e) { - System.err.println("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); - return 1; + throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); } catch (Signal.Error.UntrustedIdentity e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 4; + throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; - } catch (IOException e) { - e.printStackTrace(); - return 3; + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java index f5eacf81..176f1bb9 100644 --- a/src/main/java/org/asamk/signal/commands/SendContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendContactsCommand.java @@ -3,6 +3,9 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; @@ -16,16 +19,13 @@ public class SendContactsCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.sendContacts(); - return 0; } catch (UntrustedIdentityException e) { - System.err.println("SendContacts error: " + e.getMessage()); - return 2; + throw new UntrustedKeyErrorException("SendContacts error: " + e.getMessage()); } catch (IOException e) { - System.err.println("SendContacts error: " + e.getMessage()); - return 3; + throw new IOErrorException("SendContacts error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index bcae9d41..b9cbc047 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -5,7 +5,11 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; +import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; @@ -18,11 +22,6 @@ import java.io.IOException; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; -import static org.asamk.signal.util.ErrorUtils.handleGroupNotFoundException; -import static org.asamk.signal.util.ErrorUtils.handleIOException; -import static org.asamk.signal.util.ErrorUtils.handleInvalidNumberException; -import static org.asamk.signal.util.ErrorUtils.handleNotAGroupMemberException; import static org.asamk.signal.util.ErrorUtils.handleTimestampAndSendMessageResults; public class SendReactionCommand implements LocalCommand { @@ -46,19 +45,16 @@ public class SendReactionCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final List recipients = ns.getList("recipient"); final var groupIdString = ns.getString("group"); final var noRecipients = recipients == null || recipients.isEmpty(); if (noRecipients && groupIdString == null) { - System.err.println("No recipients given"); - System.err.println("Aborting sending."); - return 1; + throw new UserErrorException("No recipients given"); } if (!noRecipients && groupIdString != null) { - System.err.println("You cannot specify recipients by phone number and groups at the same time"); - return 1; + throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time"); } final var emoji = ns.getString("emoji"); @@ -66,35 +62,37 @@ public class SendReactionCommand implements LocalCommand { final var targetAuthor = ns.getString("target_author"); final long targetTimestamp = ns.getLong("target_timestamp"); - try { - final var writer = new PlainTextWriterImpl(System.out); + final var writer = new PlainTextWriterImpl(System.out); - final Pair> results; - if (groupIdString != null) { - var groupId = Util.decodeGroupId(groupIdString); + final Pair> results; + + GroupId groupId = null; + if (groupId != null) { + try { + groupId = Util.decodeGroupId(groupIdString); + } catch (GroupIdFormatException e) { + throw new UserErrorException("Invalid group id:" + e.getMessage()); + } + } + + try { + if (groupId != null) { results = m.sendGroupMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, groupId); } else { results = m.sendMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, recipients); } - return handleTimestampAndSendMessageResults(writer, results.first(), results.second()); + handleTimestampAndSendMessageResults(writer, results.first(), results.second()); } catch (IOException e) { - handleIOException(e); - return 3; + throw new IOErrorException("Failed to send message: " + e.getMessage()); } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (GroupNotFoundException e) { - handleGroupNotFoundException(e); - return 1; + throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (NotAGroupMemberException e) { - handleNotAGroupMemberException(e); - return 1; - } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; + throw new UserErrorException("Failed to send to group: " + e.getMessage()); } catch (InvalidNumberException e) { - handleInvalidNumberException(e); - return 1; + throw new UserErrorException("Invalid number: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/SetPinCommand.java b/src/main/java/org/asamk/signal/commands/SetPinCommand.java index fff105be..56b4b8a4 100644 --- a/src/main/java/org/asamk/signal/commands/SetPinCommand.java +++ b/src/main/java/org/asamk/signal/commands/SetPinCommand.java @@ -3,6 +3,9 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException; @@ -18,17 +21,14 @@ public class SetPinCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { var registrationLockPin = ns.getString("registrationLockPin"); m.setRegistrationLockPin(Optional.of(registrationLockPin)); - return 0; } catch (UnauthenticatedResponseException e) { - System.err.println("Set pin error: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Set pin error failed with unauthenticated response: " + e.getMessage()); } catch (IOException e) { - System.err.println("Set pin error: " + e.getMessage()); - return 3; + throw new IOErrorException("Set pin error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/TrustCommand.java b/src/main/java/org/asamk/signal/commands/TrustCommand.java index 277291eb..08fe6a41 100644 --- a/src/main/java/org/asamk/signal/commands/TrustCommand.java +++ b/src/main/java/org/asamk/signal/commands/TrustCommand.java @@ -4,8 +4,9 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; -import org.asamk.signal.util.ErrorUtils; import org.asamk.signal.util.Hex; import org.whispersystems.signalservice.api.util.InvalidNumberException; @@ -25,13 +26,12 @@ public class TrustCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var number = ns.getString("number"); if (ns.getBoolean("trust_all_known_keys")) { var res = m.trustIdentityAllKeys(number); if (!res) { - System.err.println("Failed to set the trust for this number, make sure the number is correct."); - return 1; + throw new UserErrorException("Failed to set the trust for this number, make sure the number is correct."); } } else { var safetyNumber = ns.getString("verified_safety_number"); @@ -42,46 +42,38 @@ public class TrustCommand implements LocalCommand { try { fingerprintBytes = Hex.toByteArray(safetyNumber.toLowerCase(Locale.ROOT)); } catch (Exception e) { - System.err.println( + throw new UserErrorException( "Failed to parse the fingerprint, make sure the fingerprint is a correctly encoded hex string without additional characters."); - return 1; } boolean res; try { res = m.trustIdentityVerified(number, fingerprintBytes); } catch (InvalidNumberException e) { - ErrorUtils.handleInvalidNumberException(e); - return 1; + throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); } if (!res) { - System.err.println( + throw new UserErrorException( "Failed to set the trust for the fingerprint of this number, make sure the number and the fingerprint are correct."); - return 1; } } else if (safetyNumber.length() == 60) { boolean res; try { res = m.trustIdentityVerifiedSafetyNumber(number, safetyNumber); } catch (InvalidNumberException e) { - ErrorUtils.handleInvalidNumberException(e); - return 1; + throw new UserErrorException("Failed to parse recipient: " + e.getMessage()); } if (!res) { - System.err.println( + throw new UserErrorException( "Failed to set the trust for the safety number of this phone number, make sure the phone number and the safety number are correct."); - return 1; } } else { - System.err.println( + throw new UserErrorException( "Safety number has invalid format, either specify the old hex fingerprint or the new safety number"); - return 1; } } else { - System.err.println( + throw new UserErrorException( "You need to specify the fingerprint/safety number you have verified with -v SAFETY_NUMBER"); - return 1; } } - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/UnblockCommand.java b/src/main/java/org/asamk/signal/commands/UnblockCommand.java index b9d6e849..6e067ee5 100644 --- a/src/main/java/org/asamk/signal/commands/UnblockCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnblockCommand.java @@ -3,14 +3,19 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.util.InvalidNumberException; public class UnblockCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(UnblockCommand.class); + @Override public void attachToSubparser(final Subparser subparser) { subparser.addArgument("contact").help("Contact number").nargs("*"); @@ -19,12 +24,12 @@ public class UnblockCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { - for (var contact_number : ns.getList("contact")) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { + for (var contactNumber : ns.getList("contact")) { try { - m.setContactBlocked(contact_number, false); + m.setContactBlocked(contactNumber, false); } catch (InvalidNumberException e) { - System.err.println(e.getMessage()); + logger.warn("Invalid number: {}", contactNumber); } } @@ -33,12 +38,12 @@ public class UnblockCommand implements LocalCommand { try { var groupId = Util.decodeGroupId(groupIdString); m.setGroupBlocked(groupId, false); - } catch (GroupIdFormatException | GroupNotFoundException e) { - System.err.println(e.getMessage()); + } catch (GroupIdFormatException e) { + logger.warn("Invalid group id: {}", groupIdString); + } catch (GroupNotFoundException e) { + logger.warn("Unknown group id: {}", groupIdString); } } } - - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java index 079070e6..1846eba1 100644 --- a/src/main/java/org/asamk/signal/commands/UnregisterCommand.java +++ b/src/main/java/org/asamk/signal/commands/UnregisterCommand.java @@ -3,6 +3,8 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import java.io.IOException; @@ -15,13 +17,11 @@ public class UnregisterCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.unregister(); - return 0; } catch (IOException e) { - System.err.println("Unregister error: " + e.getMessage()); - return 3; + throw new IOErrorException("Unregister error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java index 8211e190..13723f09 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateAccountCommand.java @@ -3,6 +3,8 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import java.io.IOException; @@ -15,13 +17,11 @@ public class UpdateAccountCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { try { m.updateAccountAttributes(); - return 0; } catch (IOException e) { - System.err.println("UpdateAccount error: " + e.getMessage()); - return 3; + throw new IOErrorException("UpdateAccount error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java index c2b994d6..c8ad613b 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateContactCommand.java @@ -3,6 +3,9 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.signalservice.api.util.InvalidNumberException; @@ -22,7 +25,7 @@ public class UpdateContactCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var number = ns.getString("number"); var name = ns.getString("name"); @@ -34,13 +37,9 @@ public class UpdateContactCommand implements LocalCommand { m.setExpirationTimer(number, expiration); } } catch (InvalidNumberException e) { - System.err.println("Invalid contact number: " + e.getMessage()); - return 1; + throw new UserErrorException("Invalid contact number: " + e.getMessage()); } catch (IOException e) { - System.err.println("Update contact error: " + e.getMessage()); - return 3; + throw new IOErrorException("Update contact error: " + e.getMessage()); } - - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index fdb23ea1..204dcfe2 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -5,19 +5,20 @@ import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.groups.GroupIdFormatException; import org.asamk.signal.util.Util; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.util.ArrayList; import java.util.Base64; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException; public class UpdateGroupCommand implements DbusCommand { @@ -32,15 +33,14 @@ public class UpdateGroupCommand implements DbusCommand { } @Override - public int handleCommand(final Namespace ns, final Signal signal) { + public void handleCommand(final Namespace ns, final Signal signal) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); byte[] groupId = null; if (ns.getString("group") != null) { try { groupId = Util.decodeGroupId(ns.getString("group")).serialize(); } catch (GroupIdFormatException e) { - handleGroupIdFormatException(e); - return 1; + throw new UserErrorException("Invalid group id:" + e.getMessage()); } } if (groupId == null) { @@ -65,23 +65,15 @@ public class UpdateGroupCommand implements DbusCommand { try { var newGroupId = signal.updateGroup(groupId, groupName, groupMembers, groupAvatar); if (groupId.length != newGroupId.length) { - try { - writer.println("Creating new group \"{}\" …", Base64.getEncoder().encodeToString(newGroupId)); - } catch (IOException e) { - e.printStackTrace(); - return 3; - } + writer.println("Created new group: \"{}\"", Base64.getEncoder().encodeToString(newGroupId)); } - return 0; } catch (AssertionError e) { handleAssertionError(e); - return 1; + throw e; } catch (Signal.Error.AttachmentInvalid e) { - System.err.println("Failed to add avatar attachment for group\": " + e.getMessage()); - return 1; + throw new UserErrorException("Failed to add avatar attachment for group\": " + e.getMessage()); } catch (DBusExecutionException e) { - System.err.println("Failed to send message: " + e.getMessage()); - return 2; + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java index b8c7b08c..c3fc2e88 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateProfileCommand.java @@ -4,6 +4,8 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.manager.Manager; import org.whispersystems.libsignal.util.guava.Optional; @@ -26,23 +28,21 @@ public class UpdateProfileCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { var name = ns.getString("name"); var about = ns.getString("about"); var aboutEmoji = ns.getString("about_emoji"); var avatarPath = ns.getString("avatar"); boolean removeAvatar = ns.getBoolean("remove_avatar"); + Optional avatarFile = removeAvatar + ? Optional.absent() + : avatarPath == null ? null : Optional.of(new File(avatarPath)); + try { - Optional avatarFile = removeAvatar - ? Optional.absent() - : avatarPath == null ? null : Optional.of(new File(avatarPath)); m.setProfile(name, about, aboutEmoji, avatarFile); } catch (IOException e) { - System.err.println("Update profile error: " + e.getMessage()); - return 3; + throw new IOErrorException("Update profile error: " + e.getMessage()); } - - return 0; } } diff --git a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java index 9d72a6c5..1ed14e21 100644 --- a/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java +++ b/src/main/java/org/asamk/signal/commands/UploadStickerPackCommand.java @@ -4,6 +4,9 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.StickerPackInvalidException; import org.slf4j.Logger; @@ -23,19 +26,17 @@ public class UploadStickerPackCommand implements LocalCommand { } @Override - public int handleCommand(final Namespace ns, final Manager m) { + public void handleCommand(final Namespace ns, final Manager m) throws CommandException { final var writer = new PlainTextWriterImpl(System.out); + var path = new File(ns.getString("path")); + try { - var path = new File(ns.getString("path")); var url = m.uploadStickerPack(path); writer.println("{}", url); - return 0; } catch (IOException e) { - System.err.println("Upload error: " + e.getMessage()); - return 3; + throw new IOErrorException("Upload error: " + e.getMessage()); } catch (StickerPackInvalidException e) { - System.err.println("Invalid sticker pack: " + e.getMessage()); - return 1; + throw new UserErrorException("Invalid sticker pack: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index a244581d..c26c1d3a 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -3,6 +3,10 @@ package org.asamk.signal.commands; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; import org.asamk.signal.manager.RegistrationManager; import org.whispersystems.signalservice.api.KeyBackupServicePinException; import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException; @@ -19,26 +23,23 @@ public class VerifyCommand implements RegistrationCommand { } @Override - public int handleCommand(final Namespace ns, final RegistrationManager m) { + public void handleCommand(final Namespace ns, final RegistrationManager m) throws CommandException { + var verificationCode = ns.getString("verificationCode"); + var pin = ns.getString("pin"); + try { - var verificationCode = ns.getString("verificationCode"); - var pin = ns.getString("pin"); m.verifyAccount(verificationCode, pin); - return 0; } catch (LockedException e) { - System.err.println("Verification failed! This number is locked with a pin. Hours remaining until reset: " - + (e.getTimeRemaining() / 1000 / 60 / 60)); - System.err.println("Use '--pin PIN_CODE' to specify the registration lock PIN"); - return 1; + throw new UserErrorException( + "Verification failed! This number is locked with a pin. Hours remaining until reset: " + + (e.getTimeRemaining() / 1000 / 60 / 60) + + "\nUse '--pin PIN_CODE' to specify the registration lock PIN"); } catch (KeyBackupServicePinException e) { - System.err.println("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining()); - return 1; + throw new UserErrorException("Verification failed! Invalid pin, tries remaining: " + e.getTriesRemaining()); } catch (KeyBackupSystemNoDataException e) { - System.err.println("Verification failed! No KBS data."); - return 3; + throw new UnexpectedErrorException("Verification failed! No KBS data."); } catch (IOException e) { - System.err.println("Verify error: " + e.getMessage()); - return 3; + throw new IOErrorException("Verify error: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/commands/exceptions/CommandException.java b/src/main/java/org/asamk/signal/commands/exceptions/CommandException.java new file mode 100644 index 00000000..c82ef542 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/CommandException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public class CommandException extends Exception { + + public CommandException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/commands/exceptions/IOErrorException.java b/src/main/java/org/asamk/signal/commands/exceptions/IOErrorException.java new file mode 100644 index 00000000..e405600c --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/IOErrorException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public final class IOErrorException extends CommandException { + + public IOErrorException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/commands/exceptions/UnexpectedErrorException.java b/src/main/java/org/asamk/signal/commands/exceptions/UnexpectedErrorException.java new file mode 100644 index 00000000..b6f231df --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/UnexpectedErrorException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public final class UnexpectedErrorException extends CommandException { + + public UnexpectedErrorException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/commands/exceptions/UntrustedKeyErrorException.java b/src/main/java/org/asamk/signal/commands/exceptions/UntrustedKeyErrorException.java new file mode 100644 index 00000000..c215f414 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/UntrustedKeyErrorException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public final class UntrustedKeyErrorException extends CommandException { + + public UntrustedKeyErrorException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/commands/exceptions/UserErrorException.java b/src/main/java/org/asamk/signal/commands/exceptions/UserErrorException.java new file mode 100644 index 00000000..84e957cc --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/exceptions/UserErrorException.java @@ -0,0 +1,8 @@ +package org.asamk.signal.commands.exceptions; + +public final class UserErrorException extends CommandException { + + public UserErrorException(final String message) { + super(message); + } +} diff --git a/src/main/java/org/asamk/signal/util/ErrorUtils.java b/src/main/java/org/asamk/signal/util/ErrorUtils.java index 149b16c0..595509a6 100644 --- a/src/main/java/org/asamk/signal/util/ErrorUtils.java +++ b/src/main/java/org/asamk/signal/util/ErrorUtils.java @@ -1,38 +1,34 @@ package org.asamk.signal.util; import org.asamk.signal.PlainTextWriter; -import org.asamk.signal.manager.groups.GroupIdFormatException; -import org.asamk.signal.manager.groups.GroupNotFoundException; -import org.asamk.signal.manager.groups.NotAGroupMemberException; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.IOErrorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.util.InvalidNumberException; -import java.io.IOException; import java.util.ArrayList; import java.util.List; public class ErrorUtils { + private final static Logger logger = LoggerFactory.getLogger(ErrorUtils.class); + private ErrorUtils() { } public static void handleAssertionError(AssertionError e) { - System.err.println("Failed to send/receive message (Assertion): " + e.getMessage()); - e.printStackTrace(); - System.err.println( - "If you use an Oracle JRE please check if you have unlimited strength crypto enabled, see README"); + logger.warn("If you use an Oracle JRE please check if you have unlimited strength crypto enabled, see README"); } - public static int handleTimestampAndSendMessageResults( - PlainTextWriter writer, - long timestamp, - List results - ) throws IOException { + public static void handleTimestampAndSendMessageResults( + PlainTextWriter writer, long timestamp, List results + ) throws CommandException { if (timestamp != 0) { writer.println("{}", timestamp); } var errors = getErrorMessagesFromSendMessageResults(results); - return handleSendMessageResultErrors(errors); + handleSendMessageResultErrors(errors); } public static List getErrorMessagesFromSendMessageResults(List results) { @@ -58,39 +54,15 @@ public class ErrorUtils { return null; } - private static int handleSendMessageResultErrors(List errors) { + private static void handleSendMessageResultErrors(List errors) throws CommandException { if (errors.size() == 0) { - return 0; + return; } - System.err.println("Failed to send (some) messages:"); + var message = new StringBuilder(); + message.append("Failed to send (some) messages:\n"); for (var error : errors) { - System.err.println(error); + message.append(error).append("\n"); } - return 3; - } - - public static void handleIOException(IOException e) { - System.err.println("Failed to send message: " + e.getMessage()); - } - - public static void handleGroupNotFoundException(GroupNotFoundException e) { - System.err.println("Failed to send to group: " + e.getMessage()); - System.err.println("Aborting sending."); - } - - public static void handleNotAGroupMemberException(NotAGroupMemberException e) { - System.err.println("Failed to send to group: " + e.getMessage()); - System.err.println("Update the group on another device to readd the user to this group."); - System.err.println("Aborting sending."); - } - - public static void handleGroupIdFormatException(GroupIdFormatException e) { - System.err.println(e.getMessage()); - System.err.println("Aborting sending."); - } - - public static void handleInvalidNumberException(InvalidNumberException e) { - System.err.println("Failed to parse recipient: " + e.getMessage()); - System.err.println("Aborting sending."); + throw new IOErrorException(message.toString()); } } From 8249f852f9244cc83698c9330a304604f385daaa Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 23 Feb 2021 21:50:31 +0100 Subject: [PATCH 097/124] Fix issue with broken sessions Fixes #530 --- .../storage/protocol/JsonSessionStore.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java index 94d2d681..1b5384a4 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java @@ -12,6 +12,7 @@ import org.asamk.signal.manager.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.libsignal.SignalProtocolAddress; +import org.whispersystems.libsignal.protocol.CiphertextMessage; import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.signalservice.api.SignalServiceSessionStore; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -55,9 +56,7 @@ class JsonSessionStore implements SignalServiceSessionStore { return new SessionRecord(info.sessionRecord); } catch (IOException e) { logger.warn("Failed to load session, resetting session: {}", e.getMessage()); - final var sessionRecord = new SessionRecord(); - info.sessionRecord = sessionRecord.serialize(); - return sessionRecord; + return new SessionRecord(); } } } @@ -104,7 +103,16 @@ class JsonSessionStore implements SignalServiceSessionStore { var serviceAddress = resolveSignalServiceAddress(address.getName()); for (var info : sessions) { if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) { - return true; + final SessionRecord sessionRecord; + try { + sessionRecord = new SessionRecord(info.sessionRecord); + } catch (IOException e) { + logger.warn("Failed to check session: {}", e.getMessage()); + return false; + } + + return sessionRecord.hasSenderChain() + && sessionRecord.getSessionVersion() == CiphertextMessage.CURRENT_VERSION; } } return false; From 4860caef63080c501e06418f1bca7030a7c7898d Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 25 Feb 2021 19:20:02 +0100 Subject: [PATCH 098/124] Improve behavior of trust command - Remove all other identities for the same number Fixes #540 --- .../org/asamk/signal/manager/Manager.java | 106 ++++++++++-------- .../storage/protocol/IdentityInfo.java | 7 -- .../protocol/JsonIdentityKeyStore.java | 8 ++ .../protocol/JsonSignalProtocolStore.java | 4 + 4 files changed, 70 insertions(+), 55 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index caedf2f1..88335916 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -156,6 +156,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Function; import java.util.stream.Collectors; import static org.asamk.signal.manager.config.ServiceConfig.capabilities; @@ -1211,10 +1212,12 @@ public class Manager implements Closeable { try { messageSender.sendMessage(message, unidentifiedAccessHelper.getAccessForSync()); } catch (UntrustedIdentityException e) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); + if (e.getIdentityKey() != null) { + account.getSignalProtocolStore() + .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), + e.getIdentityKey(), + TrustLevel.UNTRUSTED); + } throw e; } } @@ -1296,10 +1299,12 @@ public class Manager implements Closeable { } return new Pair<>(timestamp, result); } catch (UntrustedIdentityException e) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); + if (e.getIdentityKey() != null) { + account.getSignalProtocolStore() + .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), + e.getIdentityKey(), + TrustLevel.UNTRUSTED); + } return new Pair<>(timestamp, List.of()); } } else { @@ -1369,10 +1374,12 @@ public class Manager implements Closeable { false, System.currentTimeMillis() - startTime); } catch (UntrustedIdentityException e) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); + if (e.getIdentityKey() != null) { + account.getSignalProtocolStore() + .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), + e.getIdentityKey(), + TrustLevel.UNTRUSTED); + } return SendMessageResult.identityFailure(recipient, e.getIdentityKey()); } } @@ -1385,10 +1392,12 @@ public class Manager implements Closeable { try { return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(address), message); } catch (UntrustedIdentityException e) { - account.getSignalProtocolStore() - .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), - e.getIdentityKey(), - TrustLevel.UNTRUSTED); + if (e.getIdentityKey() != null) { + account.getSignalProtocolStore() + .saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), + e.getIdentityKey(), + TrustLevel.UNTRUSTED); + } return SendMessageResult.identityFailure(address, e.getIdentityKey()); } } @@ -2388,26 +2397,7 @@ public class Manager implements Closeable { */ public boolean trustIdentityVerified(String name, byte[] fingerprint) throws InvalidNumberException { var address = canonicalizeAndResolveSignalServiceAddress(name); - var ids = account.getSignalProtocolStore().getIdentities(address); - if (ids == null) { - return false; - } - for (var id : ids) { - if (!Arrays.equals(id.getIdentityKey().serialize(), fingerprint)) { - continue; - } - - account.getSignalProtocolStore() - .setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); - try { - sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); - } catch (IOException | UntrustedIdentityException e) { - logger.warn("Failed to send verification sync message: {}", e.getMessage()); - } - account.save(); - return true; - } - return false; + return trustIdentity(address, (identityKey) -> Arrays.equals(identityKey.serialize(), fingerprint)); } /** @@ -2418,26 +2408,46 @@ public class Manager implements Closeable { */ public boolean trustIdentityVerifiedSafetyNumber(String name, String safetyNumber) throws InvalidNumberException { var address = canonicalizeAndResolveSignalServiceAddress(name); + return trustIdentity(address, (identityKey) -> safetyNumber.equals(computeSafetyNumber(address, identityKey))); + } + + private boolean trustIdentity(SignalServiceAddress address, Function verifier) { var ids = account.getSignalProtocolStore().getIdentities(address); if (ids == null) { return false; } + + IdentityInfo foundIdentity = null; + for (var id : ids) { - if (!safetyNumber.equals(computeSafetyNumber(address, id.getIdentityKey()))) { + if (verifier.apply(id.getIdentityKey())) { + foundIdentity = id; + break; + } + } + + if (foundIdentity == null) { + return false; + } + + account.getSignalProtocolStore() + .setIdentityTrustLevel(address, foundIdentity.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); + try { + sendVerifiedMessage(address, foundIdentity.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); + } catch (IOException | UntrustedIdentityException e) { + logger.warn("Failed to send verification sync message: {}", e.getMessage()); + } + + // Successfully trusted the new identity, now remove all other identities for that number + for (var id : ids) { + if (id == foundIdentity) { continue; } - - account.getSignalProtocolStore() - .setIdentityTrustLevel(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); - try { - sendVerifiedMessage(address, id.getIdentityKey(), TrustLevel.TRUSTED_VERIFIED); - } catch (IOException | UntrustedIdentityException e) { - logger.warn("Failed to send verification sync message: {}", e.getMessage()); - } - account.save(); - return true; + account.getSignalProtocolStore().removeIdentity(address, id.getIdentityKey()); } - return false; + + account.save(); + return true; } /** diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java index d4af11f2..652bf524 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/IdentityInfo.java @@ -13,13 +13,6 @@ public class IdentityInfo { TrustLevel trustLevel; Date added; - public IdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel) { - this.address = address; - this.identityKey = identityKey; - this.trustLevel = trustLevel; - this.added = new Date(); - } - IdentityInfo(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel, Date added) { this.address = address; this.identityKey = identityKey; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java index ed04e556..561138c6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonIdentityKeyStore.java @@ -125,6 +125,10 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { identities.add(new IdentityInfo(serviceAddress, identityKey, trustLevel, new Date())); } + public void removeIdentity(SignalServiceAddress serviceAddress, IdentityKey identityKey) { + identities.removeIf(id -> id.address.matches(serviceAddress) && id.identityKey.equals(identityKey)); + } + @Override public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { // TODO implement possibility for different handling of incoming/outgoing trust decisions @@ -143,6 +147,10 @@ public class JsonIdentityKeyStore implements IdentityKeyStore { } } + if (!trustOnFirstUse) { + saveIdentity(resolveSignalServiceAddress(address.getName()), identityKey, TrustLevel.UNTRUSTED, null); + } + return trustOnFirstUse; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java index 5a863b6e..d47f7a1a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSignalProtocolStore.java @@ -91,6 +91,10 @@ public class JsonSignalProtocolStore implements SignalServiceProtocolStore { identityKeyStore.setIdentityTrustLevel(serviceAddress, identityKey, trustLevel); } + public void removeIdentity(SignalServiceAddress serviceAddress, IdentityKey identityKey) { + identityKeyStore.removeIdentity(serviceAddress, identityKey); + } + public List getIdentities() { return identityKeyStore.getIdentities(); } From ef2ba0b8e0f7e9bf3ccebf18a5ddf35f3c7d03c3 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 27 Feb 2021 10:30:38 +0100 Subject: [PATCH 099/124] Create FUNDING.yml --- FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 FUNDING.yml diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 00000000..d109a964 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1 @@ +liberapay: asamk From 17d6b256e86223f356d6d73916bc0798c9c52214 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 27 Feb 2021 10:42:56 +0100 Subject: [PATCH 100/124] Create CONTRIBUTING.md --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..0bc3167a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,8 @@ +# Question +If you have a question you can ask it in the [GitHub discussions page](https://github.com/AsamK/signal-cli/discussions) + +# Report a bug +- Search [existing issues](https://github.com/AsamK/signal-cli/issues?q=is%3Aissue) if it has been reported already +- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/AsamK/signal-cli/issues/new). + - Be sure to include a **title and clear description**, as much relevant information as possible. + - Run the failing command with `--verbose` flag to get a more detailed log output and include that in the bug report From 86711b0e5c37c7d202151c7d05b00e5bdeb7836b Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 27 Feb 2021 17:43:19 +0100 Subject: [PATCH 101/124] Print exception name when failing to load state fiel --- src/main/java/org/asamk/signal/App.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 5b2c91c6..75ac5280 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -181,7 +181,11 @@ public class App { try { manager = RegistrationManager.init(username, dataPath, serviceEnvironment, BaseConfig.USER_AGENT); } catch (Throwable e) { - throw new UnexpectedErrorException("Error loading or creating state file: " + e.getMessage()); + throw new UnexpectedErrorException("Error loading or creating state file: " + + e.getMessage() + + " (" + + e.getClass().getSimpleName() + + ")"); } try (var m = manager) { command.handleCommand(ns, m); @@ -234,7 +238,13 @@ public class App { } catch (NotRegisteredException e) { throw new UserErrorException("User " + username + " is not registered."); } catch (Throwable e) { - throw new UnexpectedErrorException("Error loading state file for user " + username + ": " + e.getMessage()); + throw new UnexpectedErrorException("Error loading state file for user " + + username + + ": " + + e.getMessage() + + " (" + + e.getClass().getSimpleName() + + ")"); } try { From 9ca89b3dfaf4f25b0c3d1b831abe7d190c623634 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 28 Feb 2021 09:49:02 +0100 Subject: [PATCH 102/124] Catch all exceptions when loading cached message --- .../signal/manager/storage/messageCache/CachedMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java b/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java index 8ea723cc..404153b3 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/messageCache/CachedMessage.java @@ -22,7 +22,7 @@ public final class CachedMessage { public SignalServiceEnvelope loadEnvelope() { try { return MessageCacheUtils.loadEnvelope(file); - } catch (IOException e) { + } catch (Exception e) { logger.error("Failed to load cached message envelope “{}”: {}", file, e.getMessage()); return null; } From 8d0de675302fa4f3e0ea953cbb473c7a018e2bae Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 28 Feb 2021 10:19:40 +0100 Subject: [PATCH 103/124] Add getDisplayName to SignalProfile --- lib/src/main/java/org/asamk/signal/manager/Manager.java | 2 +- .../java/org/asamk/signal/manager/helper/GroupHelper.java | 2 +- .../asamk/signal/manager/storage/profiles/SignalProfile.java | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 88335916..d34f4e08 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -2365,7 +2365,7 @@ public class Manager implements Closeable { final var profileEntry = account.getProfileStore().getProfileEntry(address); if (profileEntry != null && profileEntry.getProfile() != null) { - return profileEntry.getProfile().getName(); + return profileEntry.getProfile().getDisplayName(); } return null; diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java index 2efc18ad..c76075be 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupHelper.java @@ -183,7 +183,7 @@ public class GroupHelper { .collect(Collectors.toSet()); if (noGv2Capability.size() > 0) { logger.warn("Cannot create a V2 group as some members don't support Groups V2: {}", - noGv2Capability.stream().map(SignalProfile::getName).collect(Collectors.joining(", "))); + noGv2Capability.stream().map(SignalProfile::getDisplayName).collect(Collectors.joining(", "))); return false; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java index 6a761c29..9e5add98 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java @@ -75,6 +75,11 @@ public class SignalProfile { return name; } + public String getDisplayName() { + // First name and last name (if set) are separated by a NULL char + return name == null ? null : name.replace("\0", " "); + } + public String getAbout() { return about; } From 1d2c7a479d791dedde6c2022ac65e9c82400af79 Mon Sep 17 00:00:00 2001 From: Adimarantis <74186638+bublath@users.noreply.github.com> Date: Mon, 1 Mar 2021 20:31:17 +0100 Subject: [PATCH 104/124] Dbus enhancements (#543) * Dbus enhancements * reverted sendMessage, completed isContactBlocked, fixed formatting * Updates based on feedback and fixes * Added trim * fixed getContactName Exception handling --- .../org/asamk/signal/manager/Manager.java | 2 +- .../storage/profiles/SignalProfile.java | 4 +- src/main/java/org/asamk/Signal.java | 16 ++ .../org/asamk/signal/dbus/DbusSignalImpl.java | 152 +++++++++++++++--- 4 files changed, 146 insertions(+), 28 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index d34f4e08..d8a1f563 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -529,7 +529,7 @@ public class Manager implements Closeable { ServiceConfig.AUTOMATIC_NETWORK_RETRY); } - private SignalProfile getRecipientProfile( + public SignalProfile getRecipientProfile( SignalServiceAddress address ) { return getRecipientProfile(address, false); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java index 9e5add98..d55e7e6c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java @@ -76,8 +76,8 @@ public class SignalProfile { } public String getDisplayName() { - // First name and last name (if set) are separated by a NULL char - return name == null ? null : name.replace("\0", " "); + // First name and last name (if set) are separated by a NULL char + trim space in case only one is filled + return name == null ? null : name.replace("\0", " ").trim(); } public String getAbout() { diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 3bd1c0d7..9e81c788 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -54,6 +54,22 @@ public interface Signal extends DBusInterface { void updateProfile( String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar ) throws Error.Failure; + + public String version(); + + public List listNumbers(); + + public List getContactNumber(final String name) throws Error.Failure; + + public void quitGroup(final byte[] groupId) throws Error.GroupNotFound,Error.Failure; + + public boolean isContactBlocked(final String number); + + public boolean isGroupBlocked(final byte[] groupId); + + public boolean isMember(final byte[] groupId); + + public void joinGroup(final String groupLink) throws Error.Failure; class MessageReceived extends DBusSignal { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 8cbbbc03..53ba5300 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -6,17 +6,24 @@ import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; +import org.asamk.signal.manager.groups.GroupInviteLinkUrl; +import org.asamk.signal.manager.storage.protocol.IdentityInfo; import org.asamk.signal.util.ErrorUtils; +import org.asamk.signal.manager.util.Utils; +import org.asamk.signal.BaseConfig; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.InvalidNumberException; +import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; import java.util.stream.Collectors; public class DbusSignalImpl implements Signal { @@ -60,9 +67,8 @@ public class DbusSignalImpl implements Signal { } } - private static void checkSendMessageResults( - long timestamp, List results - ) throws DBusExecutionException { + private static void checkSendMessageResults(long timestamp, List results) + throws DBusExecutionException { if (results.size() == 1) { checkSendMessageResult(timestamp, results.get(0)); return; @@ -99,9 +105,8 @@ public class DbusSignalImpl implements Signal { } @Override - public long sendNoteToSelfMessage( - final String message, final List attachments - ) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { + public long sendNoteToSelfMessage(final String message, final List attachments) + throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { try { final var results = m.sendSelfMessage(message, attachments); checkSendMessageResult(results.first(), results.second()); @@ -140,13 +145,17 @@ public class DbusSignalImpl implements Signal { } } + // Since contact names might be empty if not defined, also potentially return + // the profile name @Override public String getContactName(final String number) { + String name=""; try { - return m.getContactName(number); - } catch (InvalidNumberException e) { + name = m.getContactOrProfileName(number); + } catch (Exception e) { throw new Error.InvalidNumber(e.getMessage()); } + return name; } @Override @@ -202,11 +211,8 @@ public class DbusSignalImpl implements Signal { if (group == null) { return List.of(); } else { - return group.getMembers() - .stream() - .map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier) - .collect(Collectors.toList()); + return group.getMembers().stream().map(m::resolveSignalServiceAddress) + .map(SignalServiceAddress::getLegacyIdentifier).collect(Collectors.toList()); } } @@ -225,9 +231,7 @@ public class DbusSignalImpl implements Signal { if (avatar.isEmpty()) { avatar = null; } - final var results = m.updateGroup(groupId == null ? null : GroupId.unknownVersion(groupId), - name, - members, + final var results = m.updateGroup(groupId == null ? null : GroupId.unknownVersion(groupId), name, members, avatar == null ? null : new File(avatar)); checkSendMessageResults(0, results.second()); return results.first().serialize(); @@ -248,19 +252,13 @@ public class DbusSignalImpl implements Signal { } @Override - public void updateProfile( - final String name, - final String about, - final String aboutEmoji, - String avatarPath, - final boolean removeAvatar - ) { + public void updateProfile(final String name, final String about, final String aboutEmoji, String avatarPath, + final boolean removeAvatar) { try { if (avatarPath.isEmpty()) { avatarPath = null; } - Optional avatarFile = removeAvatar - ? Optional.absent() + Optional avatarFile = removeAvatar ? Optional.absent() : avatarPath == null ? null : Optional.of(new File(avatarPath)); m.setProfile(name, about, aboutEmoji, avatarFile); } catch (IOException e) { @@ -268,4 +266,108 @@ public class DbusSignalImpl implements Signal { } } + // Provide option to query a version string in order to react on potential + // future interface changes + @Override + public String version() { + return BaseConfig.PROJECT_VERSION; + } + + // Create a unique list of Numbers from Identities and Contacts to really get + // all numbers the system knows + @Override + public List listNumbers() { + return Stream.concat(m.getIdentities().stream().map(i -> i.getAddress().getNumber().orNull()), + m.getContacts().stream().map(c -> c.number)) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List getContactNumber(final String name) { + // Contact names have precendence. + List numbers=new ArrayList<>(); + var contacts = m.getContacts(); + for (var c : contacts) { + if (c.name!=null && c.name.equals(name)) { + numbers.add(c.number); + } + } + // Try profiles if no contact name was found + for (IdentityInfo identity : m.getIdentities()) { + String number = identity.getAddress().getNumber().orNull(); + if (number != null) { + var address = Utils.getSignalServiceAddressFromIdentifier(number); + var profile = m.getRecipientProfile(address); + String profileName = profile.getDisplayName(); + if (profileName.equals(name)) { + numbers.add(number); + } + } + } + if (numbers.size()==0) { + throw new Error.Failure("Contact name not found"); + } + return numbers; + } + + @Override + public void quitGroup(final byte[] groupId) { + var group = GroupId.unknownVersion(groupId); + try { + m.sendQuitGroupMessage(group); + } catch (GroupNotFoundException | NotAGroupMemberException e) { + throw new Error.GroupNotFound(e.getMessage()); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } + } + + @Override + public void joinGroup(final String groupLink) { + final GroupInviteLinkUrl linkUrl; + try { + linkUrl = GroupInviteLinkUrl.fromUri(groupLink); + m.joinGroup(linkUrl); + } catch (GroupInviteLinkUrl.InvalidGroupLinkException | GroupLinkNotActiveException e) { + throw new Error.Failure("Group link is invalid: " + e.getMessage()); + } catch (GroupInviteLinkUrl.UnknownGroupLinkVersionException e) { + throw new Error.Failure("Group link was created with an incompatible version: " + e.getMessage()); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } + } + + @Override + public boolean isContactBlocked(final String number) { + var contacts = m.getContacts(); + for (var c : contacts) { + if (c.number.equals(number)) { + return c.blocked; + } + } + return false; + } + + @Override + public boolean isGroupBlocked(final byte[] groupId) { + var group = m.getGroup(GroupId.unknownVersion(groupId)); + if (group == null) { + return false; + } else { + return group.isBlocked(); + } + } + + @Override + public boolean isMember(final byte[] groupId) { + var group = m.getGroup(GroupId.unknownVersion(groupId)); + if (group == null) { + return false; + } else { + return group.isMember(m.getSelfAddress()); + } + } + } From 631f10c916652bf3fc521b2f799e8311282462b1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 1 Mar 2021 20:33:12 +0100 Subject: [PATCH 105/124] Reformat --- src/main/java/org/asamk/Signal.java | 32 +++++------ .../org/asamk/signal/dbus/DbusSignalImpl.java | 53 ++++++++++++------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 9e81c788..3c65deb0 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -54,22 +54,22 @@ public interface Signal extends DBusInterface { void updateProfile( String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar ) throws Error.Failure; - - public String version(); - - public List listNumbers(); - - public List getContactNumber(final String name) throws Error.Failure; - - public void quitGroup(final byte[] groupId) throws Error.GroupNotFound,Error.Failure; - - public boolean isContactBlocked(final String number); - - public boolean isGroupBlocked(final byte[] groupId); - - public boolean isMember(final byte[] groupId); - - public void joinGroup(final String groupLink) throws Error.Failure; + + public String version(); + + public List listNumbers(); + + public List getContactNumber(final String name) throws Error.Failure; + + public void quitGroup(final byte[] groupId) throws Error.GroupNotFound, Error.Failure; + + public boolean isContactBlocked(final String number); + + public boolean isGroupBlocked(final byte[] groupId); + + public boolean isMember(final byte[] groupId); + + public void joinGroup(final String groupLink) throws Error.Failure; class MessageReceived extends DBusSignal { diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 53ba5300..552c80ea 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -1,30 +1,30 @@ package org.asamk.signal.dbus; import org.asamk.Signal; +import org.asamk.signal.BaseConfig; import org.asamk.signal.manager.AttachmentInvalidException; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupId; +import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.storage.protocol.IdentityInfo; -import org.asamk.signal.util.ErrorUtils; import org.asamk.signal.manager.util.Utils; -import org.asamk.signal.BaseConfig; +import org.asamk.signal.util.ErrorUtils; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.whispersystems.libsignal.util.guava.Optional; +import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.InvalidNumberException; -import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.stream.Stream; import java.util.stream.Collectors; +import java.util.stream.Stream; public class DbusSignalImpl implements Signal { @@ -67,8 +67,9 @@ public class DbusSignalImpl implements Signal { } } - private static void checkSendMessageResults(long timestamp, List results) - throws DBusExecutionException { + private static void checkSendMessageResults( + long timestamp, List results + ) throws DBusExecutionException { if (results.size() == 1) { checkSendMessageResult(timestamp, results.get(0)); return; @@ -105,8 +106,9 @@ public class DbusSignalImpl implements Signal { } @Override - public long sendNoteToSelfMessage(final String message, final List attachments) - throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { + public long sendNoteToSelfMessage( + final String message, final List attachments + ) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity { try { final var results = m.sendSelfMessage(message, attachments); checkSendMessageResult(results.first(), results.second()); @@ -149,7 +151,7 @@ public class DbusSignalImpl implements Signal { // the profile name @Override public String getContactName(final String number) { - String name=""; + String name = ""; try { name = m.getContactOrProfileName(number); } catch (Exception e) { @@ -211,8 +213,11 @@ public class DbusSignalImpl implements Signal { if (group == null) { return List.of(); } else { - return group.getMembers().stream().map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier).collect(Collectors.toList()); + return group.getMembers() + .stream() + .map(m::resolveSignalServiceAddress) + .map(SignalServiceAddress::getLegacyIdentifier) + .collect(Collectors.toList()); } } @@ -231,7 +236,9 @@ public class DbusSignalImpl implements Signal { if (avatar.isEmpty()) { avatar = null; } - final var results = m.updateGroup(groupId == null ? null : GroupId.unknownVersion(groupId), name, members, + final var results = m.updateGroup(groupId == null ? null : GroupId.unknownVersion(groupId), + name, + members, avatar == null ? null : new File(avatar)); checkSendMessageResults(0, results.second()); return results.first().serialize(); @@ -252,13 +259,19 @@ public class DbusSignalImpl implements Signal { } @Override - public void updateProfile(final String name, final String about, final String aboutEmoji, String avatarPath, - final boolean removeAvatar) { + public void updateProfile( + final String name, + final String about, + final String aboutEmoji, + String avatarPath, + final boolean removeAvatar + ) { try { if (avatarPath.isEmpty()) { avatarPath = null; } - Optional avatarFile = removeAvatar ? Optional.absent() + Optional avatarFile = removeAvatar + ? Optional.absent() : avatarPath == null ? null : Optional.of(new File(avatarPath)); m.setProfile(name, about, aboutEmoji, avatarFile); } catch (IOException e) { @@ -277,7 +290,7 @@ public class DbusSignalImpl implements Signal { // all numbers the system knows @Override public List listNumbers() { - return Stream.concat(m.getIdentities().stream().map(i -> i.getAddress().getNumber().orNull()), + return Stream.concat(m.getIdentities().stream().map(i -> i.getAddress().getNumber().orNull()), m.getContacts().stream().map(c -> c.number)) .filter(Objects::nonNull) .distinct() @@ -287,10 +300,10 @@ public class DbusSignalImpl implements Signal { @Override public List getContactNumber(final String name) { // Contact names have precendence. - List numbers=new ArrayList<>(); + List numbers = new ArrayList<>(); var contacts = m.getContacts(); for (var c : contacts) { - if (c.name!=null && c.name.equals(name)) { + if (c.name != null && c.name.equals(name)) { numbers.add(c.number); } } @@ -306,7 +319,7 @@ public class DbusSignalImpl implements Signal { } } } - if (numbers.size()==0) { + if (numbers.size() == 0) { throw new Error.Failure("Contact name not found"); } return numbers; From 253757cb316f10df77e437fa443177d100275bf9 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 1 Mar 2021 20:39:45 +0100 Subject: [PATCH 106/124] Improve dbus impl --- .../org/asamk/signal/dbus/DbusSignalImpl.java | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 552c80ea..fe416c66 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -8,8 +8,6 @@ import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.groups.GroupNotFoundException; import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.storage.protocol.IdentityInfo; -import org.asamk.signal.manager.util.Utils; import org.asamk.signal.util.ErrorUtils; import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.whispersystems.libsignal.util.guava.Optional; @@ -151,13 +149,11 @@ public class DbusSignalImpl implements Signal { // the profile name @Override public String getContactName(final String number) { - String name = ""; try { - name = m.getContactOrProfileName(number); + return m.getContactOrProfileName(number); } catch (Exception e) { throw new Error.InvalidNumber(e.getMessage()); } - return name; } @Override @@ -299,8 +295,8 @@ public class DbusSignalImpl implements Signal { @Override public List getContactNumber(final String name) { - // Contact names have precendence. - List numbers = new ArrayList<>(); + // Contact names have precedence. + var numbers = new ArrayList(); var contacts = m.getContacts(); for (var c : contacts) { if (c.name != null && c.name.equals(name)) { @@ -308,20 +304,16 @@ public class DbusSignalImpl implements Signal { } } // Try profiles if no contact name was found - for (IdentityInfo identity : m.getIdentities()) { - String number = identity.getAddress().getNumber().orNull(); + for (var identity : m.getIdentities()) { + final var address = identity.getAddress(); + var number = address.getNumber().orNull(); if (number != null) { - var address = Utils.getSignalServiceAddressFromIdentifier(number); var profile = m.getRecipientProfile(address); - String profileName = profile.getDisplayName(); - if (profileName.equals(name)) { + if (profile != null && profile.getDisplayName().equals(name)) { numbers.add(number); } } } - if (numbers.size() == 0) { - throw new Error.Failure("Contact name not found"); - } return numbers; } @@ -339,9 +331,8 @@ public class DbusSignalImpl implements Signal { @Override public void joinGroup(final String groupLink) { - final GroupInviteLinkUrl linkUrl; try { - linkUrl = GroupInviteLinkUrl.fromUri(groupLink); + final var linkUrl = GroupInviteLinkUrl.fromUri(groupLink); m.joinGroup(linkUrl); } catch (GroupInviteLinkUrl.InvalidGroupLinkException | GroupLinkNotActiveException e) { throw new Error.Failure("Group link is invalid: " + e.getMessage()); @@ -382,5 +373,4 @@ public class DbusSignalImpl implements Signal { return group.isMember(m.getSelfAddress()); } } - } From 52739140ef7e1628d3bcda6474b4c13479fe86d1 Mon Sep 17 00:00:00 2001 From: AsamK Date: Tue, 2 Mar 2021 20:48:52 +0100 Subject: [PATCH 107/124] Bump version --- CHANGELOG.md | 13 +++++++++++++ build.gradle.kts | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6585c78a..ecaecbd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ ## [Unreleased] +## [0.8.1] - 2021-03-02 +### Added +- New dbus commands: updateProfile, listNumbers, getContactNumber, quitGroup, isContactBlocked, isGroupBlocked, isMember, joinGroup (Thanks @bublath) +- Additional output for json format: shared contacts (Thanks @Atomic-Bean) +- Improved plain text output to be more consistent and synced messages are now indented + +### Fixed +- Issue with broken sessions with linked devices + +### Changed +- Behavior of `trust` command improved, when trusting a new identity key all other known keys for + the same number are removed. + ## [0.8.0] - 2021-02-14 **Attention**: For all signal protocol functionality an additional native library is now required: [libsignal-client](https://github.com/signalapp/libsignal-client/). See https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal for more information. diff --git a/build.gradle.kts b/build.gradle.kts index 5d1fab6a..55d95145 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ plugins { `check-lib-versions` } -version = "0.8.0" +version = "0.8.1" java { sourceCompatibility = JavaVersion.VERSION_11 From 2ea56a1b67e3bb90096660797ec5cc001cc50010 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 7 Mar 2021 16:26:33 +0100 Subject: [PATCH 108/124] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a3900649..90a666e8 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ You can [build signal-cli](#building) yourself, or use the [provided binary file System requirements: - at least Java Runtime Environment (JRE) 11 - native libraries: libzkgroup, libsignal-client + Those are bundled for x86_64 Linux, for other systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) ### Install system-wide on Linux From a7b414a8703d27a012811f5621ecab28b329351f Mon Sep 17 00:00:00 2001 From: Adimarantis <74186638+bublath@users.noreply.github.com> Date: Sun, 7 Mar 2021 21:05:49 +0100 Subject: [PATCH 109/124] some small dbus return code fixes (#556) * some small dbus return code fixes * Added DBus manpage * changed getDisplayName behaviour * reverted change in manager --- lib/src/main/java/org/asamk/signal/manager/Manager.java | 1 - .../asamk/signal/manager/storage/profiles/SignalProfile.java | 2 +- src/main/java/org/asamk/signal/ReceiveMessageHandler.java | 2 +- src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java | 3 +++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index d8a1f563..99099e92 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -2367,7 +2367,6 @@ public class Manager implements Closeable { if (profileEntry != null && profileEntry.getProfile() != null) { return profileEntry.getProfile().getDisplayName(); } - return null; } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java index d55e7e6c..45201e18 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/profiles/SignalProfile.java @@ -77,7 +77,7 @@ public class SignalProfile { public String getDisplayName() { // First name and last name (if set) are separated by a NULL char + trim space in case only one is filled - return name == null ? null : name.replace("\0", " ").trim(); + return name == null ? "" : name.replace("\0", " ").trim(); } public String getAbout() { diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 6df283f3..b59eeee2 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -665,7 +665,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { private String formatContact(SignalServiceAddress address) { final var number = address.getLegacyIdentifier(); var name = m.getContactOrProfileName(number); - if (name == null) { + if (name == null || name.isEmpty()) { return number; } else { return MessageFormatter.arrayFormat("“{}” {}", new Object[]{name, number}).getMessage(); diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index fe416c66..24a90662 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -333,6 +333,9 @@ public class DbusSignalImpl implements Signal { public void joinGroup(final String groupLink) { try { final var linkUrl = GroupInviteLinkUrl.fromUri(groupLink); + if (linkUrl == null) { + throw new Error.Failure("Group link is invalid:"); + } m.joinGroup(linkUrl); } catch (GroupInviteLinkUrl.InvalidGroupLinkException | GroupLinkNotActiveException e) { throw new Error.Failure("Group link is invalid: " + e.getMessage()); From 2935b96070ee687c7a17382c64e3f85730b9f706 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 7 Mar 2021 14:48:02 +0100 Subject: [PATCH 110/124] Return a Manager from ProvisioningManager and RegistrationManager when finished --- .../signal/manager/ProvisioningManager.java | 44 ++++++++++++------- .../signal/manager/RegistrationManager.java | 20 ++++++--- .../asamk/signal/commands/LinkCommand.java | 5 ++- .../asamk/signal/commands/VerifyCommand.java | 3 +- 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index 0ce43bb9..e53f1b99 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -92,7 +92,7 @@ public class ProvisioningManager { return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri(); } - public String finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists { + public Manager finishDeviceLink(String deviceName) throws IOException, InvalidKeyException, TimeoutException, UserAlreadyExists { var ret = accountManager.finishNewDeviceRegistration(identityKey, false, true, registrationId, deviceName); var username = ret.getNumber(); @@ -114,17 +114,21 @@ public class ProvisioningManager { } } - try (var account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(), - username, - ret.getUuid(), - password, - ret.getDeviceId(), - ret.getIdentity(), - registrationId, - profileKey)) { + SignalAccount account = null; + try { + account = SignalAccount.createLinkedAccount(pathConfig.getDataPath(), + username, + ret.getUuid(), + password, + ret.getDeviceId(), + ret.getIdentity(), + registrationId, + profileKey); account.save(); - try (var m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { + Manager m = null; + try { + m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent); try { m.refreshPreKeys(); @@ -144,12 +148,22 @@ public class ProvisioningManager { throw e; } - m.close(false); + account.save(); + + final var result = m; + account = null; + m = null; + + return result; + } finally { + if (m != null) { + m.close(); + } + } + } finally { + if (account != null) { + account.close(); } - - account.save(); } - - return username; } } diff --git a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java index 6ded4d0b..aad731a0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/RegistrationManager.java @@ -128,7 +128,7 @@ public class RegistrationManager implements Closeable { account.save(); } - public void verifyAccount( + public Manager verifyAccount( String verificationCode, String pin ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException { verificationCode = verificationCode.replace("-", ""); @@ -169,14 +169,24 @@ public class RegistrationManager implements Closeable { account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(), TrustLevel.TRUSTED_VERIFIED); - try (var m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent)) { + Manager m = null; + try { + m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent); m.refreshPreKeys(); - m.close(false); - } + account.save(); - account.save(); + final var result = m; + account = null; + m = null; + + return result; + } finally { + if (m != null) { + m.close(); + } + } } private VerifyAccountResponse verifyAccountWithCode( diff --git a/src/main/java/org/asamk/signal/commands/LinkCommand.java b/src/main/java/org/asamk/signal/commands/LinkCommand.java index 34775773..3a018619 100644 --- a/src/main/java/org/asamk/signal/commands/LinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/LinkCommand.java @@ -38,8 +38,9 @@ public class LinkCommand implements ProvisioningCommand { } try { writer.println("{}", m.getDeviceLinkUri()); - var username = m.finishDeviceLink(deviceName); - writer.println("Associated with: {}", username); + try (var manager = m.finishDeviceLink(deviceName)) { + writer.println("Associated with: {}", manager.getUsername()); + } } catch (TimeoutException e) { throw new UserErrorException("Link request timed out, please try again."); } catch (IOException e) { diff --git a/src/main/java/org/asamk/signal/commands/VerifyCommand.java b/src/main/java/org/asamk/signal/commands/VerifyCommand.java index c26c1d3a..151d966a 100644 --- a/src/main/java/org/asamk/signal/commands/VerifyCommand.java +++ b/src/main/java/org/asamk/signal/commands/VerifyCommand.java @@ -28,7 +28,8 @@ public class VerifyCommand implements RegistrationCommand { var pin = ns.getString("pin"); try { - m.verifyAccount(verificationCode, pin); + final var manager = m.verifyAccount(verificationCode, pin); + manager.close(); } catch (LockedException e) { throw new UserErrorException( "Verification failed! This number is locked with a pin. Hours remaining until reset: " From 9944b666b0859a78a08725f9b8e4b93eab00c2d8 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 7 Mar 2021 14:50:33 +0100 Subject: [PATCH 111/124] Return URI instead of String --- .../org/asamk/signal/manager/DeviceLinkInfo.java | 15 ++++++++++----- .../asamk/signal/manager/ProvisioningManager.java | 3 ++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java b/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java index 82d8a37f..1f9d10ff 100644 --- a/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/DeviceLinkInfo.java @@ -5,6 +5,7 @@ import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.ECPublicKey; import java.net.URI; +import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -61,11 +62,15 @@ public class DeviceLinkInfo { this.deviceKey = deviceKey; } - public String createDeviceLinkUri() { + public URI createDeviceLinkUri() { final var deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", ""); - return "tsdevice:/?uuid=" - + URLEncoder.encode(deviceIdentifier, StandardCharsets.UTF_8) - + "&pub_key=" - + URLEncoder.encode(deviceKeyString, StandardCharsets.UTF_8); + try { + return new URI("tsdevice:/?uuid=" + + URLEncoder.encode(deviceIdentifier, StandardCharsets.UTF_8) + + "&pub_key=" + + URLEncoder.encode(deviceKeyString, StandardCharsets.UTF_8)); + } catch (URISyntaxException e) { + throw new AssertionError(e); + } } } diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java index e53f1b99..c8869212 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManager.java @@ -38,6 +38,7 @@ import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider import java.io.File; import java.io.IOException; +import java.net.URI; import java.util.concurrent.TimeoutException; public class ProvisioningManager { @@ -86,7 +87,7 @@ public class ProvisioningManager { return new ProvisioningManager(pathConfig, serviceConfiguration, userAgent); } - public String getDeviceLinkUri() throws TimeoutException, IOException { + public URI getDeviceLinkUri() throws TimeoutException, IOException { var deviceUuid = accountManager.getNewDeviceUuid(); return new DeviceLinkInfo(deviceUuid, identityKey.getPublicKey().getPublicKey()).createDeviceLinkUri(); From be4b683a4199cfa97246738a81a1bb19a1a2d5fd Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 8 Mar 2021 20:33:52 +0100 Subject: [PATCH 112/124] Allow MultiLocalCommands to be called when no local users exist --- src/main/java/org/asamk/signal/App.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index 75ac5280..d4da356c 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -133,16 +133,15 @@ public class App { if (username == null) { var usernames = Manager.getAllLocalUsernames(dataPath); - if (usernames.size() == 0) { - throw new UserErrorException("No local users found, you first need to register or link an account"); - } if (command instanceof MultiLocalCommand) { handleMultiLocalCommand((MultiLocalCommand) command, dataPath, serviceEnvironment, usernames); return; } - if (usernames.size() > 1) { + if (usernames.size() == 0) { + throw new UserErrorException("No local users found, you first need to register or link an account"); + } else if (usernames.size() > 1) { throw new UserErrorException( "Multiple users found, you need to specify a username (phone number) with -u"); } From 1bf848cde1311314ca754b734e2b7cd951a3f979 Mon Sep 17 00:00:00 2001 From: AsamK Date: Mon, 8 Mar 2021 21:19:46 +0100 Subject: [PATCH 113/124] Ignore broken user data again for multi account commands --- src/main/java/org/asamk/signal/App.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/App.java b/src/main/java/org/asamk/signal/App.java index d4da356c..873e96bd 100644 --- a/src/main/java/org/asamk/signal/App.java +++ b/src/main/java/org/asamk/signal/App.java @@ -214,7 +214,11 @@ public class App { ) throws CommandException { final var managers = new ArrayList(); for (String u : usernames) { - managers.add(loadManager(u, dataPath, serviceEnvironment)); + try { + managers.add(loadManager(u, dataPath, serviceEnvironment)); + } catch (CommandException e) { + logger.warn("Ignoring {}: {}", u, e.getMessage()); + } } command.handleCommand(ns, managers); From b5f0a26084adb82ece50d2db1f9350ba932163f7 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 13 Mar 2021 10:10:07 +0100 Subject: [PATCH 114/124] Use a new serviceID for the re-deployed KBS setup in staging. --- .../java/org/asamk/signal/manager/config/SandboxConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java index f5adb54d..9ca9dc8b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/SandboxConfig.java @@ -29,7 +29,7 @@ class SandboxConfig { private final static String KEY_BACKUP_ENCLAVE_NAME = "823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9"; private final static byte[] KEY_BACKUP_SERVICE_ID = Hex.decode( - "038c40bbbacdc873caa81ac793bb75afde6dfe436a99ab1f15e3f0cbb7434ced"); + "51a56084c0b21c6b8f62b1bc792ec9bedac4c7c3964bb08ddcab868158c09982"); private final static String KEY_BACKUP_MRENCLAVE = "a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87"; private final static String URL = "https://textsecure-service-staging.whispersystems.org"; From ea035db94f9377374682acaf0a4b72eff5c3557b Mon Sep 17 00:00:00 2001 From: Adaptive Garage Date: Wed, 24 Mar 2021 21:49:03 +0100 Subject: [PATCH 115/124] Fix sendReaction to group (#580) Co-authored-by: rknotek --- .../java/org/asamk/signal/commands/SendReactionCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index b9cbc047..ba40488a 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -67,7 +67,7 @@ public class SendReactionCommand implements LocalCommand { final Pair> results; GroupId groupId = null; - if (groupId != null) { + if (groupIdString != null) { try { groupId = Util.decodeGroupId(groupIdString); } catch (GroupIdFormatException e) { From 8f4d89e2f7e2658e4b6d28970fa447dff32a0a26 Mon Sep 17 00:00:00 2001 From: Adaptive Garage Date: Fri, 2 Apr 2021 08:53:54 +0200 Subject: [PATCH 116/124] dbus implementation of sendReaction command (#581) --- src/main/java/org/asamk/Signal.java | 12 +++++ .../asamk/signal/commands/SendCommand.java | 2 +- .../signal/commands/SendReactionCommand.java | 47 ++++++++----------- .../org/asamk/signal/dbus/DbusSignalImpl.java | 41 ++++++++++++++++ 4 files changed, 74 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 3c65deb0..6e76f4a7 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -21,6 +21,14 @@ public interface Signal extends DBusInterface { String message, List attachments, List recipients ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; + long sendMessageReaction( + String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, String recipient + ) throws Error.InvalidNumber, Error.Failure; + + long sendMessageReaction( + String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List recipients + ) throws Error.InvalidNumber, Error.Failure; + long sendNoteToSelfMessage( String message, List attachments ) throws Error.AttachmentInvalid, Error.Failure; @@ -31,6 +39,10 @@ public interface Signal extends DBusInterface { String message, List attachments, byte[] groupId ) throws Error.GroupNotFound, Error.Failure, Error.AttachmentInvalid; + long sendGroupMessageReaction( + String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, byte[] groupId + ) throws Error.GroupNotFound, Error.Failure, Error.InvalidNumber; + String getContactName(String number) throws Error.InvalidNumber; void setContactName(String number, String name) throws Error.InvalidNumber; diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 23d74165..bd94ca91 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -98,7 +98,7 @@ public class SendCommand implements DbusCommand { try { groupId = Util.decodeGroupId(groupIdString).serialize(); } catch (GroupIdFormatException e) { - throw new UserErrorException("Invalid group id:" + e.getMessage()); + throw new UserErrorException("Invalid group id: " + e.getMessage()); } try { diff --git a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java index ba40488a..a80b6a23 100644 --- a/src/main/java/org/asamk/signal/commands/SendReactionCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendReactionCommand.java @@ -4,27 +4,21 @@ import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.Signal; import org.asamk.signal.PlainTextWriterImpl; import org.asamk.signal.commands.exceptions.CommandException; -import org.asamk.signal.commands.exceptions.IOErrorException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; import org.asamk.signal.commands.exceptions.UserErrorException; -import org.asamk.signal.manager.Manager; -import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupIdFormatException; -import org.asamk.signal.manager.groups.GroupNotFoundException; -import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.util.Util; -import org.whispersystems.libsignal.util.Pair; -import org.whispersystems.signalservice.api.messages.SendMessageResult; -import org.whispersystems.signalservice.api.util.InvalidNumberException; +import org.freedesktop.dbus.errors.UnknownObject; +import org.freedesktop.dbus.exceptions.DBusExecutionException; -import java.io.IOException; import java.util.List; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; -import static org.asamk.signal.util.ErrorUtils.handleTimestampAndSendMessageResults; -public class SendReactionCommand implements LocalCommand { +public class SendReactionCommand implements DbusCommand { @Override public void attachToSubparser(final Subparser subparser) { @@ -45,7 +39,7 @@ public class SendReactionCommand implements LocalCommand { } @Override - public void handleCommand(final Namespace ns, final Manager m) throws CommandException { + public void handleCommand(final Namespace ns, final Signal signal) throws CommandException { final List recipients = ns.getList("recipient"); final var groupIdString = ns.getString("group"); @@ -64,35 +58,34 @@ public class SendReactionCommand implements LocalCommand { final var writer = new PlainTextWriterImpl(System.out); - final Pair> results; - - GroupId groupId = null; + byte[] groupId = null; if (groupIdString != null) { try { - groupId = Util.decodeGroupId(groupIdString); + groupId = Util.decodeGroupId(groupIdString).serialize(); } catch (GroupIdFormatException e) { - throw new UserErrorException("Invalid group id:" + e.getMessage()); + throw new UserErrorException("Invalid group id: " + e.getMessage()); } } try { + long timestamp; if (groupId != null) { - results = m.sendGroupMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, groupId); + timestamp = signal.sendGroupMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, groupId); } else { - results = m.sendMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, recipients); + timestamp = signal.sendMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, recipients); } - handleTimestampAndSendMessageResults(writer, results.first(), results.second()); - } catch (IOException e) { - throw new IOErrorException("Failed to send message: " + e.getMessage()); + writer.println("{}", timestamp); } catch (AssertionError e) { handleAssertionError(e); throw e; - } catch (GroupNotFoundException e) { - throw new UserErrorException("Failed to send to group: " + e.getMessage()); - } catch (NotAGroupMemberException e) { - throw new UserErrorException("Failed to send to group: " + e.getMessage()); - } catch (InvalidNumberException e) { + } catch (UnknownObject e) { + throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); + } catch (Signal.Error.InvalidNumber e) { throw new UserErrorException("Invalid number: " + e.getMessage()); + } catch (Signal.Error.GroupNotFound e) { + throw new UserErrorException("Failed to send to group: " + e.getMessage()); + } catch (DBusExecutionException e) { + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 24a90662..9bc4b67f 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -103,6 +103,30 @@ public class DbusSignalImpl implements Signal { } } + @Override + public long sendMessageReaction( + final String emoji, final boolean remove, final String targetAuthor, final long targetSentTimestamp, final String recipient + ) { + var recipients = new ArrayList(1); + recipients.add(recipient); + return sendMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, recipients); + } + + @Override + public long sendMessageReaction( + final String emoji, final boolean remove, final String targetAuthor, final long targetSentTimestamp, final List recipients + ) { + try { + final var results = m.sendMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, recipients); + checkSendMessageResults(results.first(), results.second()); + return results.first(); + } catch (InvalidNumberException e) { + throw new Error.InvalidNumber(e.getMessage()); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } + } + @Override public long sendNoteToSelfMessage( final String message, final List attachments @@ -145,6 +169,23 @@ public class DbusSignalImpl implements Signal { } } + @Override + public long sendGroupMessageReaction( + final String emoji, final boolean remove, final String targetAuthor, final long targetSentTimestamp, final byte[] groupId + ) { + try { + final var results = m.sendGroupMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, GroupId.unknownVersion(groupId)); + checkSendMessageResults(results.first(), results.second()); + return results.first(); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } catch (InvalidNumberException e) { + throw new Error.InvalidNumber(e.getMessage()); + } catch (GroupNotFoundException | NotAGroupMemberException e) { + throw new Error.GroupNotFound(e.getMessage()); + } + } + // Since contact names might be empty if not defined, also potentially return // the profile name @Override From caf16231aaf7550cedf74c66f5ddfa9a82b7bb88 Mon Sep 17 00:00:00 2001 From: Adimarantis <74186638+bublath@users.noreply.github.com> Date: Fri, 2 Apr 2021 09:19:07 +0200 Subject: [PATCH 117/124] Adding Dbus manpage (#564) * some small dbus return code fixes * Added DBus manpage * changed getDisplayName behaviour * reverted change in manager * some small dbus return code fixes * Added DBus manpage * changed getDisplayName behaviour * reverted change in manager * Dbus manpage --- man/signal-cli-dbus.5.adoc | 205 +++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100755 man/signal-cli-dbus.5.adoc diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc new file mode 100755 index 00000000..61091d4b --- /dev/null +++ b/man/signal-cli-dbus.5.adoc @@ -0,0 +1,205 @@ +///// +vim:set ts=4 sw=4 tw=82 noet: +///// +:quotes.~: + += signal-cli-dbus (5) + +== Name + +DBus API for signal-cli - A commandline and dbus interface for the Signal messenger + +== Synopsis + +*signal-cli* [--config CONFIG] daemon --dbus-system + +*dbus-send* --system --type=method_call --print-reply --dest="org.asamk.Signal" /org/asamk/Signal org.asamk.Signal. [string:] [array::] + + +== Description + +See signal-cli (1) for details on the application. + +This documentation handles the supported methods when running signal-cli as a DBus daemon. + +The method are described as follows: + +method(arg1, arg2, ...) -> return + +Where is according to DBus specification: + +* : String +* : Byte Array +* : Array of Byte Arrays +* : String Array +* : Boolean (0|1) +* : Signed 64 bit integer +* <> : no return value + +Exceptions are the names of the Java Exceptions returned in the body field. They typically contain an additional message with details. All Exceptions begin with "org.asamk.Signal.Error." which is omitted here for better readability. + +Phone numbers always have the format + + +== Methods + +updateGroup(groupId, newName, members, avatar) -> groupId:: +* groupId : Byte array representing the internal group identifier +* newName : New name of group (empty if unchanged) +* members : String array of new members to be invited to group +* avatar : Filename of avatar picture to be set for group (empty if none) + +Exceptions: AttachmentInvalid, Failure, InvalidNumber, GroupNotFound + +updateProfile(newName, about , aboutEmoji , avatar, remove) -> <>:: +* newName : New name for your own profile (empty if unchanged) +* about : About message for profile (empty if unchanged) +* aboutEmoji : Emoji for profile (empty if unchanged) +* avatar : Filename of avatar picutre for profile (empty if unchanged) +* remove : Set to 1 if the existing avatar picture should be removed + +Exceptions: Failure + +setContactBlocked(number, block) -> <>:: +* number : Phone number affected by method +* block : 0=remove block , 1=blocked + +Messages from blocked numbers will no longer be forwarded via DBus. + +Exceptions: InvalidNumber + +setGroupBlocked(groupId, block) -> <>:: +* groupId : Byte array representing the internal group identifier +* block : 0=remove block , 1=blocked + +Messages from blocked groups will no longer be forwarded via DBus. + +Exceptions: GroupNotFound + +joinGroup(inviteURI) -> <>:: +* inviteURI : String starting with https://signal.group which is generated when you share a group link via Signal App + +Exceptions: Failure + +quitGroup(groupId) -> <>:: +* groupId : Byte array representing the internal group identifier + +Note that quitting a group will not remove the group from the getGroupIds command, but set it inactive which can be tested with isMember() + +Exceptions: GroupNotFound, Failure + +isMember(groupId) -> active:: +* groupId : Byte array representing the internal group identifier + +Note that this method not raise an Exception for a non-existing/unknown group but will simply return 0 (false) + +sendEndSessionMessage(recipients) -> <>:: +* recipients : Array of phone numbers + +Exceptions: Failure, InvalidNumber, UntrustedIdentity + +sendGroupMessage(message, attachments, groupId) -> timestamp:: +* message : Text to send (can be UTF8) +* attachments : String array of filenames to send as attachments (passed as filename, so need to be readable by the user signal-cli is running under) +* groupId : Byte array representing the internal group identifier +* timestamp : Can be used to identify the corresponding signal reply + +Exceptions: GroupNotFound, Failure, AttachmentInvalid + +sendNoteToSelfMessage(message, attachments) -> timestamp:: +* message : Text to send (can be UTF8) +* attachments : String array of filenames to send as attachments (passed as filename, so need to be readable by the user signal-cli is running under) +* timestamp : Can be used to identify the corresponding signal reply + +Exceptions: Failure, AttachmentInvalid + +sendMessage(message, attachments, recipient timestamp:: +sendMessage(message, attachments, recipients) -> timestamp:: +* message : Text to send (can be UTF8) +* attachments : String array of filenames to send as attachments (passed as filename, so need to be readable by the user signal-cli is running under) +* recipient : Phone number of a single recipient +* recipients : Array of phone numbers +* timestamp : Can be used to identify the corresponding signal reply + +Depending on the type of the recipient field this sends a message to one or multiple recipients. + +Expections: AttachmentInvalid, Failure, InvalidNumber, UntrustedIdentity + +getContactName(number) -> name:: +* number : Phone number +* name : Name set in contacts (setContacts) or if not set the profile name + +setContactName(number,name<>) -> <>:: +* number : Phone number +* name : Name to be set in contacts (in local storage with signal-cli) + +getGroupIds() -> groupList:: +groupList : Array of Byte arrays representing the internal group identifiers + +All groups known are returned, regardsless of their active or blocked status. To query that use isMember() and isGroupBlocked() + +getGroupName(groupId) -> groupName:: +groupName : The display name of the group +groupId : Byte array representing the internal group identifier + +Exceptions: None, if the group name is not found an empty string is returned + +getGroupMembers(groupId) -> members:: +members : String array with the phone numbers of all active members of a group +groupId : Byte array representing the internal group identifier + +Exceptions: None, if the group name is not found an empty array is returned + +listNumbers() -> numbers:: +numbers : String array of all known numbers + +This is a concatenated list of all defined contacts as well of profiles known (e.g. peer group members or sender of received messages) + +getContactNumber(name) -> numbers:: +* numbers : Array of phone number +* name : Contact or profile name ("firstname lastname") + +Searches contacts and know profiles for a given name and returns the list of all known names. May result in e.g. two entries if a contact and profile name is set. + +isContactBlocked(number) -> state:: +* number : Phone number +* state : 1=blocked, 0=not blocked + +Exceptions: None, for unknown numbers 0 (false) is returned + +isGroupBlocked(groupId) -> state:: +* groupId : Byte array representing the internal group identifier +* state : 1=blocked, 0=not blocked + +Exceptions: None, for unknown groups 0 (false) is returned + +version() -> version:: +* version : Version string of signal-cli + +isRegistred -> result:: +* result : Currently always returns 1=true + +== Signals + +SyncMessageReceived (timestamp, sender, destination, groupId,message, attachments):: +The sync message is received when the user sends a message from a linked device. + +ReceiptReceived (timestamp, sender):: +* timestamp : Integer value that can be used to associate this e.g. with a sendMessage() +* sender : Phone number of the sender + +This signal is sent by each recipient (e.g. each group member) after the message was successfully delivered to the device + +MessageReceived(timestamp, sender, groupId, message, attachments):: +* timestamp : Integer value that is used by the system to send a ReceiptReceived reply +* sender : Phone number of the sender +* groupId : Byte array representing the internal group identifier (empty when private message) +* message : Message text +* attachments : String array of filenames for the attachments. These files are located in the signal-cli storage and the current user needs to have read access there + +This signal is received whenever we get a private message or a message is posted in a group we are an active member + +== Authors + +Maintained by AsamK , who is assisted by other open source contributors. +For more information about signal-cli development, see +. From 4f9c0172ef42b03e34d7756b96cbb6f522c68adf Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 2 Apr 2021 09:22:15 +0200 Subject: [PATCH 118/124] Update README --- CONTRIBUTING.md | 4 ++++ README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0bc3167a..c0afee3f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,3 +6,7 @@ If you have a question you can ask it in the [GitHub discussions page](https://g - If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/AsamK/signal-cli/issues/new). - Be sure to include a **title and clear description**, as much relevant information as possible. - Run the failing command with `--verbose` flag to get a more detailed log output and include that in the bug report + +# Pull request +- Code style should match the existing code, IntelliJ users can use the auto formatter +- Separate PRs should be opened for each implemented feature or bug fix diff --git a/README.md b/README.md index 90a666e8..5eb9e78d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ signal-cli is a commandline interface for [libsignal-service-java](https://github.com/WhisperSystems/libsignal-service-java). It supports registering, verifying, sending and receiving messages. To be able to link to an existing Signal-Android/signal-cli instance, signal-cli uses a [patched libsignal-service-java](https://github.com/AsamK/libsignal-service-java), because libsignal-service-java does not yet support [provisioning as a slave device](https://github.com/WhisperSystems/libsignal-service-java/pull/21). For registering you need a phone number where you can receive SMS or incoming calls. -signal-cli is primarily intended to be used on servers to notify admins of important events. For this use-case, it has a dbus interface, that can be used to send messages from any programming language that has dbus bindings. +signal-cli is primarily intended to be used on servers to notify admins of important events. For this use-case, it has a dbus interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)), that can be used to send messages from any programming language that has dbus bindings. ## Installation From b39f7c18071f65f86b00874c4d05024584bdb487 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 3 Apr 2021 19:08:45 +0200 Subject: [PATCH 119/124] Remove redundant public modifiers from interface --- src/main/java/org/asamk/Signal.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index 6e76f4a7..f8841da9 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -67,21 +67,21 @@ public interface Signal extends DBusInterface { String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar ) throws Error.Failure; - public String version(); + String version(); - public List listNumbers(); + List listNumbers(); - public List getContactNumber(final String name) throws Error.Failure; + List getContactNumber(final String name) throws Error.Failure; - public void quitGroup(final byte[] groupId) throws Error.GroupNotFound, Error.Failure; + void quitGroup(final byte[] groupId) throws Error.GroupNotFound, Error.Failure; - public boolean isContactBlocked(final String number); + boolean isContactBlocked(final String number); - public boolean isGroupBlocked(final byte[] groupId); + boolean isGroupBlocked(final byte[] groupId); - public boolean isMember(final byte[] groupId); + boolean isMember(final byte[] groupId); - public void joinGroup(final String groupLink) throws Error.Failure; + void joinGroup(final String groupLink) throws Error.Failure; class MessageReceived extends DBusSignal { From 7832afd8199bd7e224625ecfc9cd056f8709df3c Mon Sep 17 00:00:00 2001 From: exquo <62397152+exquo@users.noreply.github.com> Date: Sat, 3 Apr 2021 19:22:05 +0000 Subject: [PATCH 120/124] Improve DBus man page (#589) * Improve DBus man page - Expand synopsis - Add examples - Fix typos * Update to DBus man page --- man/signal-cli-dbus.5.adoc | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc index 61091d4b..e84032bf 100755 --- a/man/signal-cli-dbus.5.adoc +++ b/man/signal-cli-dbus.5.adoc @@ -11,10 +11,11 @@ DBus API for signal-cli - A commandline and dbus interface for the Signal messen == Synopsis -*signal-cli* [--config CONFIG] daemon --dbus-system +*signal-cli* [--verbose] [--config CONFIG] [-u USERNAME] [-o {plain-text,json}] daemon [--system] -*dbus-send* --system --type=method_call --print-reply --dest="org.asamk.Signal" /org/asamk/Signal org.asamk.Signal. [string:] [array::] +*dbus-send* [--system | --session] [--print-reply] --type=method_call --dest="org.asamk.Signal" /org/asamk/Signal[/_] org.asamk.Signal. [string:] [array::] +Note: when daemon was started without explicit `-u USERNAME`, the `dbus-send` command requires adding the phone number in `/org/asamk/Signal/_`. == Description @@ -54,7 +55,7 @@ updateProfile(newName, about , aboutEmoji , avatar, remove) -> <> * newName : New name for your own profile (empty if unchanged) * about : About message for profile (empty if unchanged) * aboutEmoji : Emoji for profile (empty if unchanged) -* avatar : Filename of avatar picutre for profile (empty if unchanged) +* avatar : Filename of avatar picture for profile (empty if unchanged) * remove : Set to 1 if the existing avatar picture should be removed Exceptions: Failure @@ -90,7 +91,7 @@ Exceptions: GroupNotFound, Failure isMember(groupId) -> active:: * groupId : Byte array representing the internal group identifier -Note that this method not raise an Exception for a non-existing/unknown group but will simply return 0 (false) +Note that this method does not raise an Exception for a non-existing/unknown group but will simply return 0 (false) sendEndSessionMessage(recipients) -> <>:: * recipients : Array of phone numbers @@ -126,7 +127,7 @@ Expections: AttachmentInvalid, Failure, InvalidNumber, UntrustedIdentity getContactName(number) -> name:: * number : Phone number -* name : Name set in contacts (setContacts) or if not set the profile name +* name : Contact's name in local storage (from the master device for a linked account, or the one set with setContactName); if not set, contact's profile name is used setContactName(number,name<>) -> <>:: * number : Phone number @@ -135,7 +136,7 @@ setContactName(number,name<>) -> <>:: getGroupIds() -> groupList:: groupList : Array of Byte arrays representing the internal group identifiers -All groups known are returned, regardsless of their active or blocked status. To query that use isMember() and isGroupBlocked() +All groups known are returned, regardless of their active or blocked status. To query that use isMember() and isGroupBlocked() getGroupName(groupId) -> groupName:: groupName : The display name of the group @@ -158,7 +159,7 @@ getContactNumber(name) -> numbers:: * numbers : Array of phone number * name : Contact or profile name ("firstname lastname") -Searches contacts and know profiles for a given name and returns the list of all known names. May result in e.g. two entries if a contact and profile name is set. +Searches contacts and known profiles for a given name and returns the list of all known numbers. May result in e.g. two entries if a contact and profile name is set. isContactBlocked(number) -> state:: * number : Phone number @@ -198,6 +199,17 @@ MessageReceived(timestamp, sender, groupId, message, attachments, who is assisted by other open source contributors. From 2ad18342a8f9105a4d84ad6788198505cb57f11e Mon Sep 17 00:00:00 2001 From: Adaptive Garage Date: Mon, 5 Apr 2021 17:00:20 +0200 Subject: [PATCH 121/124] Dbus sendreaction doc (#591) * Documentation of dbus methods sendGroupMessageReaction, sendMessageReaction * Documentation of dbus methods sendGroupMessageReaction, sendMessageReaction. Fix 1. --- man/signal-cli-dbus.5.adoc | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc index e84032bf..eccfd821 100755 --- a/man/signal-cli-dbus.5.adoc +++ b/man/signal-cli-dbus.5.adoc @@ -98,7 +98,7 @@ sendEndSessionMessage(recipients) -> <>:: Exceptions: Failure, InvalidNumber, UntrustedIdentity -sendGroupMessage(message, attachments, groupId) -> timestamp:: +sendGroupMessage(message, attachments, groupId) -> timestamp:: * message : Text to send (can be UTF8) * attachments : String array of filenames to send as attachments (passed as filename, so need to be readable by the user signal-cli is running under) * groupId : Byte array representing the internal group identifier @@ -113,7 +113,7 @@ sendNoteToSelfMessage(message, attachments) -> timestamp:: Exceptions: Failure, AttachmentInvalid -sendMessage(message, attachments, recipient timestamp:: +sendMessage(message, attachments, recipient) -> timestamp:: sendMessage(message, attachments, recipients) -> timestamp:: * message : Text to send (can be UTF8) * attachments : String array of filenames to send as attachments (passed as filename, so need to be readable by the user signal-cli is running under) @@ -123,7 +123,31 @@ sendMessage(message, attachments, recipients) -> timestamp:: Depending on the type of the recipient field this sends a message to one or multiple recipients. -Expections: AttachmentInvalid, Failure, InvalidNumber, UntrustedIdentity +Exceptions: AttachmentInvalid, Failure, InvalidNumber, UntrustedIdentity + +sendGroupMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, groupId) -> timestamp:: +* emoji : Unicode grapheme cluster of the emoji +* remove : Boolean, whether a previously sent reaction (emoji) should be removed +* targetAuthor : String with the phone number of the author of the message to which to react +* targetSentTimestamp : Long representing timestamp of the message to which to react +* groupId : Byte array with base64 encoded group identifier +* timestamp : Long, can be used to identify the corresponding signal reply + +Exceptions: Failure, InvalidNumber, GroupNotFound + +sendMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, recipient) -> timestamp:: +sendMessageReaction(emoji, remove, targetAuthor, targetSentTimestamp, recipients) -> timestamp:: +* emoji : Unicode grapheme cluster of the emoji +* remove : Boolean, whether a previously sent reaction (emoji) should be removed +* targetAuthor : String with the phone number of the author of the message to which to react +* targetSentTimestamp : Long representing timestamp of the message to which to react +* recipient : String with the phone number of a single recipient +* recipients : Array of strings with phone numbers, should there be more recipients +* timestamp : Long, can be used to identify the corresponding signal reply + +Depending on the type of the recipient(s) field this sends a reaction to one or multiple recipients. + +Exceptions: Failure, InvalidNumber getContactName(number) -> name:: * number : Phone number From 05abb3f9f6294677d2de97a9d88c8e9de04c10ec Mon Sep 17 00:00:00 2001 From: Adaptive Garage Date: Mon, 5 Apr 2021 19:49:45 +0200 Subject: [PATCH 122/124] Send remote delete (#593) * Implementation of remoteDelete command, iteration 1 * Implementation of remoteDelete command, iteration 2 * Implementation of remoteDelete command, iteration 3 * Implementation of remoteDelete command, iteration 4 * Implementation of remoteDelete command, iteration 5 * Implementation of remoteDelete command, iteration 6 * Renaming dbus methods remoteGroupDelete -> sendGroupRemoteDeleteMessage, remoteDelete -> sendRemoteDeleteMessage --- .../org/asamk/signal/manager/Manager.java | 16 ++++ man/signal-cli-dbus.5.adoc | 18 ++++ man/signal-cli.1.adoc | 13 +++ src/main/java/org/asamk/Signal.java | 12 +++ .../org/asamk/signal/commands/Commands.java | 7 +- .../signal/commands/RemoteDeleteCommand.java | 82 +++++++++++++++++++ .../org/asamk/signal/dbus/DbusSignalImpl.java | 39 +++++++++ 7 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 99099e92..5d0e7d3d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -992,6 +992,22 @@ public class Manager implements Closeable { return sendSelfMessage(messageBuilder); } + public Pair> sendRemoteDeleteMessage( + long targetSentTimestamp, List recipients + ) throws IOException, InvalidNumberException { + var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); + return sendMessage(messageBuilder, getSignalServiceAddresses(recipients)); + } + + public Pair> sendGroupRemoteDeleteMessage( + long targetSentTimestamp, GroupId groupId + ) throws IOException, NotAGroupMemberException, GroupNotFoundException { + var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); + return sendGroupMessage(messageBuilder, groupId); + } + public Pair> sendMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List recipients ) throws IOException, InvalidNumberException { diff --git a/man/signal-cli-dbus.5.adoc b/man/signal-cli-dbus.5.adoc index eccfd821..ece2460f 100755 --- a/man/signal-cli-dbus.5.adoc +++ b/man/signal-cli-dbus.5.adoc @@ -149,6 +149,24 @@ Depending on the type of the recipient(s) field this sends a reaction to one or Exceptions: Failure, InvalidNumber +sendGroupRemoteDeleteMessage(targetSentTimestamp, groupId) -> timestamp:: +* targetSentTimestamp : Long representing timestamp of the message to delete +* groupId : Byte array with base64 encoded group identifier +* timestamp : Long, can be used to identify the corresponding signal reply + +Exceptions: Failure, GroupNotFound + +sendRemoteDeleteMessage(targetSentTimestamp, recipient) -> timestamp:: +sendRemoteDeleteMessage(targetSentTimestamp, recipients) -> timestamp:: +* targetSentTimestamp : Long representing timestamp of the message to delete +* recipient : String with the phone number of a single recipient +* recipients : Array of strings with phone numbers, should there be more recipients +* timestamp : Long, can be used to identify the corresponding signal reply + +Depending on the type of the recipient(s) field this deletes a message with one or multiple recipients. + +Exceptions: Failure, InvalidNumber + getContactName(number) -> name:: * number : Phone number * name : Contact's name in local storage (from the master device for a linked account, or the one set with setContactName); if not set, contact's profile name is used diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 16b684ea..f6145385 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -193,6 +193,19 @@ Specify the timestamp of the message to which to react. *-r*, *--remove*:: Remove a reaction. +=== remoteDelete + +Remotely delete a previously sent message. + +RECIPIENT:: +Specify the recipients’ phone number. + +*-g* GROUP, *--group* GROUP:: +Specify the recipient group ID in base64 encoding. + +*-t* TIMESTAMP, *--target-timestamp* TIMESTAMP:: +Specify the timestamp of the message to delete. + === receive Query the server for new messages. diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index f8841da9..0093ab9b 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -21,6 +21,18 @@ public interface Signal extends DBusInterface { String message, List attachments, List recipients ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; + long sendRemoteDeleteMessage( + long targetSentTimestamp, String recipient + ) throws Error.Failure, Error.InvalidNumber; + + long sendRemoteDeleteMessage( + long targetSentTimestamp, List recipients + ) throws Error.Failure, Error.InvalidNumber; + + long sendGroupRemoteDeleteMessage( + long targetSentTimestamp, byte[] groupId + ) throws Error.Failure, Error.GroupNotFound; + long sendMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, String recipient ) throws Error.InvalidNumber, Error.Failure; diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index 4bc17930..830049a5 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -22,20 +22,21 @@ public class Commands { addCommand("receive", new ReceiveCommand()); addCommand("register", new RegisterCommand()); addCommand("removeDevice", new RemoveDeviceCommand()); + addCommand("remoteDelete", new RemoteDeleteCommand()); addCommand("removePin", new RemovePinCommand()); addCommand("send", new SendCommand()); - addCommand("sendReaction", new SendReactionCommand()); addCommand("sendContacts", new SendContactsCommand()); - addCommand("updateContact", new UpdateContactCommand()); + addCommand("sendReaction", new SendReactionCommand()); addCommand("setPin", new SetPinCommand()); addCommand("trust", new TrustCommand()); addCommand("unblock", new UnblockCommand()); addCommand("unregister", new UnregisterCommand()); addCommand("updateAccount", new UpdateAccountCommand()); + addCommand("updateContact", new UpdateContactCommand()); addCommand("updateGroup", new UpdateGroupCommand()); addCommand("updateProfile", new UpdateProfileCommand()); - addCommand("verify", new VerifyCommand()); addCommand("uploadStickerPack", new UploadStickerPackCommand()); + addCommand("verify", new VerifyCommand()); } public static Map getCommands() { diff --git a/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java b/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java new file mode 100644 index 00000000..796a3344 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java @@ -0,0 +1,82 @@ +package org.asamk.signal.commands; + +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + +import org.asamk.Signal; +import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; +import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.asamk.signal.util.Util; +import org.freedesktop.dbus.errors.UnknownObject; +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +import java.util.List; + +import static org.asamk.signal.util.ErrorUtils.handleAssertionError; + +public class RemoteDeleteCommand implements DbusCommand { + + @Override + public void attachToSubparser(final Subparser subparser) { + subparser.help("Remotely delete a previously sent message."); + subparser.addArgument("-t", "--target-timestamp") + .required(true) + .type(long.class) + .help("Specify the timestamp of the message to delete."); + subparser.addArgument("-g", "--group") + .help("Specify the recipient group ID."); + subparser.addArgument("recipient") + .help("Specify the recipients' phone number.").nargs("*"); + } + + @Override + public void handleCommand(final Namespace ns, final Signal signal) throws CommandException { + final List recipients = ns.getList("recipient"); + final var groupIdString = ns.getString("group"); + + final var noRecipients = recipients == null || recipients.isEmpty(); + if (noRecipients && groupIdString == null) { + throw new UserErrorException("No recipients given"); + } + if (!noRecipients && groupIdString != null) { + throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time"); + } + + final long targetTimestamp = ns.getLong("target_timestamp"); + + final var writer = new PlainTextWriterImpl(System.out); + + byte[] groupId = null; + if (groupIdString != null) { + try { + groupId = Util.decodeGroupId(groupIdString).serialize(); + } catch (GroupIdFormatException e) { + throw new UserErrorException("Invalid group id: " + e.getMessage()); + } + } + + try { + long timestamp; + if (groupId != null) { + timestamp = signal.sendGroupRemoteDeleteMessage(targetTimestamp, groupId); + } else { + timestamp = signal.sendRemoteDeleteMessage(targetTimestamp, recipients); + } + writer.println("{}", timestamp); + } catch (AssertionError e) { + handleAssertionError(e); + throw e; + } catch (UnknownObject e) { + throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); + } catch (Signal.Error.InvalidNumber e) { + throw new UserErrorException("Invalid number: " + e.getMessage()); + } catch (Signal.Error.GroupNotFound e) { + throw new UserErrorException("Failed to send to group: " + e.getMessage()); + } catch (DBusExecutionException e) { + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + } + } +} diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 9bc4b67f..6b22029b 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -103,6 +103,45 @@ public class DbusSignalImpl implements Signal { } } + @Override + public long sendRemoteDeleteMessage( + final long targetSentTimestamp, final String recipient + ) { + var recipients = new ArrayList(1); + recipients.add(recipient); + return sendRemoteDeleteMessage(targetSentTimestamp, recipients); + } + + @Override + public long sendRemoteDeleteMessage( + final long targetSentTimestamp, final List recipients + ) { + try { + final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, recipients); + checkSendMessageResults(results.first(), results.second()); + return results.first(); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } catch (InvalidNumberException e) { + throw new Error.InvalidNumber(e.getMessage()); + } + } + + @Override + public long sendGroupRemoteDeleteMessage( + final long targetSentTimestamp, final byte[] groupId + ) { + try { + final var results = m.sendGroupRemoteDeleteMessage(targetSentTimestamp, GroupId.unknownVersion(groupId)); + checkSendMessageResults(results.first(), results.second()); + return results.first(); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } catch (GroupNotFoundException | NotAGroupMemberException e) { + throw new Error.GroupNotFound(e.getMessage()); + } + } + @Override public long sendMessageReaction( final String emoji, final boolean remove, final String targetAuthor, final long targetSentTimestamp, final String recipient From 3935ebf81bf9ccdb51c0b62b0bfda8972020238d Mon Sep 17 00:00:00 2001 From: AsamK Date: Thu, 15 Apr 2021 19:37:15 +0200 Subject: [PATCH 123/124] Update gradle --- buildSrc/build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 1d88ae7d..4516d475 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } repositories { - jcenter() + mavenCentral() } gradlePlugin { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index da9702f9..f371643e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From b9b4d846d32b5a9b342ec795c56a8a1df26cdf23 Mon Sep 17 00:00:00 2001 From: AsamK Date: Fri, 23 Apr 2021 22:00:09 +0200 Subject: [PATCH 124/124] Resolve recipient address when displaying untrusted identity message --- .../java/org/asamk/signal/ReceiveMessageHandler.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index b59eeee2..91df4b06 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -67,14 +67,17 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { var e = (UntrustedIdentityException) exception; writer.println( "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message."); + final var recipientName = m.resolveSignalServiceAddress(e.getName()).getLegacyIdentifier(); writer.println( - "Use 'signal-cli -u {0} listIdentities -n {1}', verify the key and run 'signal-cli -u {0} trust -v \"FINGER_PRINT\" {1}' to mark it as trusted", + "Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted", m.getUsername(), - e.getName()); + recipientName, + m.getUsername(), + recipientName); writer.println( "If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification", m.getUsername(), - e.getName()); + recipientName); } else { writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName()); }