Use Java 17

This commit is contained in:
AsamK 2021-10-24 21:06:13 +02:00
parent 9cb1409918
commit ce70a623c2
51 changed files with 142 additions and 236 deletions

View file

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
java: [ '11', '17' ] java: [ '17' ]
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1

View file

@ -19,7 +19,7 @@ jobs:
- name: Setup Java JDK - name: Setup Java JDK
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 11 java-version: 17
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v2

View file

@ -1,6 +1,7 @@
# Changelog # Changelog
## [Unreleased] ## [Unreleased]
**Attention**: Now requires Java 17
## [0.9.2] - 2021-10-24 ## [0.9.2] - 2021-10-24
### Fixed ### Fixed

View file

@ -11,7 +11,7 @@ It also has a JSON-RPC based interface, see the [documentation](https://github.c
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 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: System requirements:
- at least Java Runtime Environment (JRE) 11 - at least Java Runtime Environment (JRE) 17
- native libraries: libzkgroup, libsignal-client - native libraries: libzkgroup, libsignal-client
Those are bundled for x86_64 Linux (with recent enough glibc, see #643), for other systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal) Those are bundled for x86_64 Linux (with recent enough glibc, see #643), for other systems/architectures see: [Provide native lib for libsignal](https://github.com/AsamK/signal-cli/wiki/Provide-native-lib-for-libsignal)

View file

@ -9,8 +9,8 @@ plugins {
version = "0.9.2" version = "0.9.2"
java { java {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_17
} }
application { application {
@ -22,6 +22,7 @@ graalvmNative {
this["main"].run { this["main"].run {
configurationFileDirectories.from(file("graalvm-config-dir")) configurationFileDirectories.from(file("graalvm-config-dir"))
buildArgs.add("--allow-incomplete-classpath") buildArgs.add("--allow-incomplete-classpath")
buildArgs.add("--report-unsupported-elements-at-runtime")
} }
} }
} }

View file

@ -4,8 +4,8 @@ plugins {
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_17
} }
repositories { repositories {

View file

@ -20,22 +20,11 @@ public class LibSignalLogger implements SignalProtocolLogger {
public void log(final int priority, final String tag, final String message) { public void log(final int priority, final String tag, final String message) {
final var logMessage = String.format("[%s]: %s", tag, message); final var logMessage = String.format("[%s]: %s", tag, message);
switch (priority) { switch (priority) {
case SignalProtocolLogger.VERBOSE: case SignalProtocolLogger.VERBOSE -> logger.trace(logMessage);
logger.trace(logMessage); case SignalProtocolLogger.DEBUG -> logger.debug(logMessage);
break; case SignalProtocolLogger.INFO -> logger.info(logMessage);
case SignalProtocolLogger.DEBUG: case SignalProtocolLogger.WARN -> logger.warn(logMessage);
logger.debug(logMessage); case SignalProtocolLogger.ERROR, SignalProtocolLogger.ASSERT -> logger.error(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;
} }
} }
} }

View file

@ -569,16 +569,15 @@ public class ManagerImpl implements Manager {
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
messageBuilder.withTimestamp(timestamp); messageBuilder.withTimestamp(timestamp);
for (final var recipient : recipients) { for (final var recipient : recipients) {
if (recipient instanceof RecipientIdentifier.Single) { if (recipient instanceof RecipientIdentifier.Single single) {
final var recipientId = resolveRecipient((RecipientIdentifier.Single) recipient); final var recipientId = resolveRecipient(single);
final var result = sendHelper.sendMessage(messageBuilder, recipientId); final var result = sendHelper.sendMessage(messageBuilder, recipientId);
results.put(recipient, List.of(result)); results.put(recipient, List.of(result));
} else if (recipient instanceof RecipientIdentifier.NoteToSelf) { } else if (recipient instanceof RecipientIdentifier.NoteToSelf) {
final var result = sendHelper.sendSelfMessage(messageBuilder); final var result = sendHelper.sendSelfMessage(messageBuilder);
results.put(recipient, List.of(result)); results.put(recipient, List.of(result));
} else if (recipient instanceof RecipientIdentifier.Group) { } else if (recipient instanceof RecipientIdentifier.Group group) {
final var groupId = ((RecipientIdentifier.Group) recipient).groupId; final var result = sendHelper.sendAsGroupMessage(messageBuilder, group.groupId);
final var result = sendHelper.sendAsGroupMessage(messageBuilder, groupId);
results.put(recipient, result); results.put(recipient, result);
} }
} }

View file

@ -63,15 +63,9 @@ public final class SignalWebSocketHealthMonitor implements HealthMonitor {
private synchronized void onStateChange(WebSocketConnectionState connectionState, HealthState healthState) { private synchronized void onStateChange(WebSocketConnectionState connectionState, HealthState healthState) {
switch (connectionState) { switch (connectionState) {
case CONNECTED: case CONNECTED -> logger.debug("WebSocket is now connected");
logger.debug("WebSocket is now connected"); case AUTHENTICATION_FAILED -> logger.debug("WebSocket authentication failed");
break; case FAILED -> logger.debug("WebSocket connection failed");
case AUTHENTICATION_FAILED:
logger.debug("WebSocket authentication failed");
break;
case FAILED:
logger.debug("WebSocket connection failed");
break;
} }
healthState.needsKeepAlive = connectionState == WebSocketConnectionState.CONNECTED; healthState.needsKeepAlive = connectionState == WebSocketConnectionState.CONNECTED;

View file

@ -18,40 +18,27 @@ public enum TrustLevel {
} }
public static TrustLevel fromIdentityState(ContactRecord.IdentityState identityState) { public static TrustLevel fromIdentityState(ContactRecord.IdentityState identityState) {
switch (identityState) { return switch (identityState) {
case DEFAULT: case DEFAULT -> TRUSTED_UNVERIFIED;
return TRUSTED_UNVERIFIED; case UNVERIFIED -> UNTRUSTED;
case UNVERIFIED: case VERIFIED -> TRUSTED_VERIFIED;
return UNTRUSTED; case UNRECOGNIZED -> null;
case VERIFIED: };
return TRUSTED_VERIFIED;
case UNRECOGNIZED:
return null;
}
throw new RuntimeException("Unknown identity state: " + identityState);
} }
public static TrustLevel fromVerifiedState(VerifiedMessage.VerifiedState verifiedState) { public static TrustLevel fromVerifiedState(VerifiedMessage.VerifiedState verifiedState) {
switch (verifiedState) { return switch (verifiedState) {
case DEFAULT: case DEFAULT -> TRUSTED_UNVERIFIED;
return TRUSTED_UNVERIFIED; case UNVERIFIED -> UNTRUSTED;
case UNVERIFIED: case VERIFIED -> TRUSTED_VERIFIED;
return UNTRUSTED; };
case VERIFIED:
return TRUSTED_VERIFIED;
}
throw new RuntimeException("Unknown verified state: " + verifiedState);
} }
public VerifiedMessage.VerifiedState toVerifiedState() { public VerifiedMessage.VerifiedState toVerifiedState() {
switch (this) { return switch (this) {
case TRUSTED_UNVERIFIED: case TRUSTED_UNVERIFIED -> VerifiedMessage.VerifiedState.DEFAULT;
return VerifiedMessage.VerifiedState.DEFAULT; case UNTRUSTED -> VerifiedMessage.VerifiedState.UNVERIFIED;
case UNTRUSTED: case TRUSTED_VERIFIED -> VerifiedMessage.VerifiedState.VERIFIED;
return VerifiedMessage.VerifiedState.UNVERIFIED; };
case TRUSTED_VERIFIED:
return VerifiedMessage.VerifiedState.VERIFIED;
}
throw new RuntimeException("Unknown verified state: " + this);
} }
} }

View file

@ -54,17 +54,12 @@ public class SendRetryMessageRequestAction implements HandleAction {
} }
private static int envelopeTypeToCiphertextMessageType(int envelopeType) { private static int envelopeTypeToCiphertextMessageType(int envelopeType) {
switch (envelopeType) { return switch (envelopeType) {
case SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE: case SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE -> CiphertextMessage.PREKEY_TYPE;
return CiphertextMessage.PREKEY_TYPE; case SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE -> CiphertextMessage.SENDERKEY_TYPE;
case SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE: case SignalServiceProtos.Envelope.Type.PLAINTEXT_CONTENT_VALUE -> CiphertextMessage.PLAINTEXT_CONTENT_TYPE;
return CiphertextMessage.SENDERKEY_TYPE; default -> CiphertextMessage.WHISPER_TYPE;
case SignalServiceProtos.Envelope.Type.PLAINTEXT_CONTENT_VALUE: };
return CiphertextMessage.PLAINTEXT_CONTENT_TYPE;
case SignalServiceProtos.Envelope.Type.CIPHERTEXT_VALUE:
default:
return CiphertextMessage.WHISPER_TYPE;
}
} }
@Override @Override

View file

@ -9,9 +9,9 @@ import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.UUID; import java.util.UUID;
public abstract class RecipientIdentifier { public sealed abstract class RecipientIdentifier {
public static class NoteToSelf extends RecipientIdentifier { public static final class NoteToSelf extends RecipientIdentifier {
public static NoteToSelf INSTANCE = new NoteToSelf(); public static NoteToSelf INSTANCE = new NoteToSelf();
@ -19,7 +19,7 @@ public abstract class RecipientIdentifier {
} }
} }
public abstract static class Single extends RecipientIdentifier { public sealed static abstract class Single extends RecipientIdentifier {
public static Single fromString(String identifier, String localNumber) throws InvalidNumberException { public static Single fromString(String identifier, String localNumber) throws InvalidNumberException {
return UuidUtil.isUuid(identifier) return UuidUtil.isUuid(identifier)
@ -43,7 +43,7 @@ public abstract class RecipientIdentifier {
public abstract String getIdentifier(); public abstract String getIdentifier();
} }
public static class Uuid extends Single { public static final class Uuid extends Single {
public final UUID uuid; public final UUID uuid;
@ -72,7 +72,7 @@ public abstract class RecipientIdentifier {
} }
} }
public static class Number extends Single { public static final class Number extends Single {
public final String number; public final String number;
@ -101,7 +101,7 @@ public abstract class RecipientIdentifier {
} }
} }
public static class Group extends RecipientIdentifier { public static final class Group extends RecipientIdentifier {
public final GroupId groupId; public final GroupId groupId;

View file

@ -7,13 +7,9 @@ public enum TypingAction {
STOP; STOP;
public SignalServiceTypingMessage.Action toSignalService() { public SignalServiceTypingMessage.Action toSignalService() {
switch (this) { return switch (this) {
case START: case START -> SignalServiceTypingMessage.Action.STARTED;
return SignalServiceTypingMessage.Action.STARTED; case STOP -> SignalServiceTypingMessage.Action.STOPPED;
case STOP: };
return SignalServiceTypingMessage.Action.STOPPED;
default:
throw new IllegalStateException("Invalid typing action " + this);
}
} }
} }

View file

@ -88,19 +88,15 @@ public class ServiceConfig {
final var interceptors = List.of(userAgentInterceptor); final var interceptors = List.of(userAgentInterceptor);
switch (serviceEnvironment) { return switch (serviceEnvironment) {
case LIVE: case LIVE -> new ServiceEnvironmentConfig(LiveConfig.createDefaultServiceConfiguration(interceptors),
return new ServiceEnvironmentConfig(LiveConfig.createDefaultServiceConfiguration(interceptors), LiveConfig.getUnidentifiedSenderTrustRoot(),
LiveConfig.getUnidentifiedSenderTrustRoot(), LiveConfig.createKeyBackupConfig(),
LiveConfig.createKeyBackupConfig(), LiveConfig.getCdsMrenclave());
LiveConfig.getCdsMrenclave()); case SANDBOX -> new ServiceEnvironmentConfig(SandboxConfig.createDefaultServiceConfiguration(interceptors),
case SANDBOX: SandboxConfig.getUnidentifiedSenderTrustRoot(),
return new ServiceEnvironmentConfig(SandboxConfig.createDefaultServiceConfiguration(interceptors), SandboxConfig.createKeyBackupConfig(),
SandboxConfig.getUnidentifiedSenderTrustRoot(), SandboxConfig.getCdsMrenclave());
SandboxConfig.createKeyBackupConfig(), };
SandboxConfig.getCdsMrenclave());
default:
throw new IllegalArgumentException("Unsupported environment");
}
} }
} }

View file

@ -3,7 +3,7 @@ package org.asamk.signal.manager.groups;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
public abstract class GroupId { public abstract sealed class GroupId permits GroupIdV1, GroupIdV2 {
private final byte[] id; private final byte[] id;

View file

@ -4,7 +4,7 @@ import java.util.Base64;
import static org.asamk.signal.manager.util.KeyUtils.getSecretBytes; import static org.asamk.signal.manager.util.KeyUtils.getSecretBytes;
public class GroupIdV1 extends GroupId { public final class GroupIdV1 extends GroupId {
public static GroupIdV1 createRandom() { public static GroupIdV1 createRandom() {
return new GroupIdV1(getSecretBytes(16)); return new GroupIdV1(getSecretBytes(16));

View file

@ -2,7 +2,7 @@ package org.asamk.signal.manager.groups;
import java.util.Base64; import java.util.Base64;
public class GroupIdV2 extends GroupId { public final class GroupIdV2 extends GroupId {
public static GroupIdV2 fromBase64(String groupId) { public static GroupIdV2 fromBase64(String groupId) {
return new GroupIdV2(Base64.getDecoder().decode(groupId)); return new GroupIdV2(Base64.getDecoder().decode(groupId));

View file

@ -56,7 +56,7 @@ public final class GroupInviteLinkUrl {
var groupInviteLink = GroupInviteLink.parseFrom(bytes); var groupInviteLink = GroupInviteLink.parseFrom(bytes);
switch (groupInviteLink.getContentsCase()) { switch (groupInviteLink.getContentsCase()) {
case V1CONTENTS: { case V1CONTENTS -> {
var groupInviteLinkContentsV1 = groupInviteLink.getV1Contents(); var groupInviteLinkContentsV1 = groupInviteLink.getV1Contents();
var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.getGroupMasterKey() var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.getGroupMasterKey()
.toByteArray()); .toByteArray());
@ -65,8 +65,7 @@ public final class GroupInviteLinkUrl {
return new GroupInviteLinkUrl(groupMasterKey, password); return new GroupInviteLinkUrl(groupMasterKey, password);
} }
default: default -> throw new UnknownGroupLinkVersionException("Url contains no known group link content");
throw new UnknownGroupLinkVersionException("Url contains no known group link content");
} }
} catch (InvalidInputException | IOException e) { } catch (InvalidInputException | IOException e) {
throw new InvalidGroupLinkException(e); throw new InvalidGroupLinkException(e);

View file

@ -351,8 +351,7 @@ public class GroupHelper {
private GroupInfo getGroup(GroupId groupId, boolean forceUpdate) { private GroupInfo getGroup(GroupId groupId, boolean forceUpdate) {
final var group = account.getGroupStore().getGroup(groupId); final var group = account.getGroupStore().getGroup(groupId);
if (group instanceof GroupInfoV2) { if (group instanceof GroupInfoV2 groupInfoV2) {
final var groupInfoV2 = (GroupInfoV2) group;
if (forceUpdate || (!groupInfoV2.isPermissionDenied() && groupInfoV2.getGroup() == null)) { if (forceUpdate || (!groupInfoV2.isPermissionDenied() && groupInfoV2.getGroup() == null)) {
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey()); final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
DecryptedGroup decryptedGroup; DecryptedGroup decryptedGroup;

View file

@ -391,27 +391,18 @@ public class GroupV2Helper {
} }
private AccessControl.AccessRequired toAccessControl(final GroupLinkState state) { private AccessControl.AccessRequired toAccessControl(final GroupLinkState state) {
switch (state) { return switch (state) {
case DISABLED: case DISABLED -> AccessControl.AccessRequired.UNSATISFIABLE;
return AccessControl.AccessRequired.UNSATISFIABLE; case ENABLED -> AccessControl.AccessRequired.ANY;
case ENABLED: case ENABLED_WITH_APPROVAL -> AccessControl.AccessRequired.ADMINISTRATOR;
return AccessControl.AccessRequired.ANY; };
case ENABLED_WITH_APPROVAL:
return AccessControl.AccessRequired.ADMINISTRATOR;
default:
throw new AssertionError();
}
} }
private AccessControl.AccessRequired toAccessControl(final GroupPermission permission) { private AccessControl.AccessRequired toAccessControl(final GroupPermission permission) {
switch (permission) { return switch (permission) {
case EVERY_MEMBER: case EVERY_MEMBER -> AccessControl.AccessRequired.MEMBER;
return AccessControl.AccessRequired.MEMBER; case ONLY_ADMINS -> AccessControl.AccessRequired.ADMINISTRATOR;
case ONLY_ADMINS: };
return AccessControl.AccessRequired.ADMINISTRATOR;
default:
throw new AssertionError();
}
} }
private GroupsV2Operations.GroupOperations getGroupOperations(final GroupInfoV2 groupInfoV2) { private GroupsV2Operations.GroupOperations getGroupOperations(final GroupInfoV2 groupInfoV2) {

View file

@ -83,8 +83,7 @@ public class SyncHelper {
try (OutputStream fos = new FileOutputStream(groupsFile)) { try (OutputStream fos = new FileOutputStream(groupsFile)) {
var out = new DeviceGroupsOutputStream(fos); var out = new DeviceGroupsOutputStream(fos);
for (var record : account.getGroupStore().getGroups()) { for (var record : account.getGroupStore().getGroups()) {
if (record instanceof GroupInfoV1) { if (record instanceof GroupInfoV1 groupInfo) {
var groupInfo = (GroupInfoV1) record;
out.write(new DeviceGroup(groupInfo.getGroupId().serialize(), out.write(new DeviceGroup(groupInfo.getGroupId().serialize(),
Optional.fromNullable(groupInfo.name), Optional.fromNullable(groupInfo.name),
groupInfo.getMembers() groupInfo.getMembers()

View file

@ -9,7 +9,7 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
public abstract class GroupInfo { public sealed abstract class GroupInfo permits GroupInfoV1, GroupInfoV2 {
public abstract GroupId getGroupId(); public abstract GroupId getGroupId();

View file

@ -11,7 +11,7 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class GroupInfoV1 extends GroupInfo { public final class GroupInfoV1 extends GroupInfo {
private final GroupIdV1 groupId; private final GroupIdV1 groupId;

View file

@ -15,7 +15,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class GroupInfoV2 extends GroupInfo { public final class GroupInfoV2 extends GroupInfo {
private final GroupIdV2 groupId; private final GroupIdV2 groupId;
private final GroupMasterKey masterKey; private final GroupMasterKey masterKey;
@ -197,12 +197,9 @@ public class GroupInfoV2 extends GroupInfo {
} }
private static GroupPermission toGroupPermission(final AccessControl.AccessRequired permission) { private static GroupPermission toGroupPermission(final AccessControl.AccessRequired permission) {
switch (permission) { return switch (permission) {
case ADMINISTRATOR: case ADMINISTRATOR -> GroupPermission.ONLY_ADMINS;
return GroupPermission.ONLY_ADMINS; default -> GroupPermission.EVERY_MEMBER;
case MEMBER: };
default:
return GroupPermission.EVERY_MEMBER;
}
} }
} }

View file

@ -75,8 +75,7 @@ public class GroupStore {
final Saver saver final Saver saver
) { ) {
final var groups = storage.groups.stream().map(g -> { final var groups = storage.groups.stream().map(g -> {
if (g instanceof Storage.GroupV1) { if (g instanceof Storage.GroupV1 g1) {
final var g1 = (Storage.GroupV1) g;
final var members = g1.members.stream().map(m -> { final var members = g1.members.stream().map(m -> {
if (m.recipientId == null) { if (m.recipientId == null) {
return recipientResolver.resolveRecipient(new RecipientAddress(UuidUtil.parseOrNull(m.uuid), return recipientResolver.resolveRecipient(new RecipientAddress(UuidUtil.parseOrNull(m.uuid),
@ -186,8 +185,7 @@ public class GroupStore {
synchronized (groups) { synchronized (groups) {
var modified = false; var modified = false;
for (var group : this.groups.values()) { for (var group : this.groups.values()) {
if (group instanceof GroupInfoV1) { if (group instanceof GroupInfoV1 groupV1) {
var groupV1 = (GroupInfoV1) group;
if (groupV1.isMember(toBeMergedRecipientId)) { if (groupV1.isMember(toBeMergedRecipientId)) {
groupV1.removeMember(toBeMergedRecipientId); groupV1.removeMember(toBeMergedRecipientId);
groupV1.addMembers(List.of(recipientId)); groupV1.addMembers(List.of(recipientId));
@ -220,8 +218,7 @@ public class GroupStore {
private GroupInfoV1 getGroupV1ByV2IdLocked(GroupIdV2 groupIdV2) { private GroupInfoV1 getGroupV1ByV2IdLocked(GroupIdV2 groupIdV2) {
for (var g : groups.values()) { for (var g : groups.values()) {
if (g instanceof GroupInfoV1) { if (g instanceof GroupInfoV1 gv1) {
final var gv1 = (GroupInfoV1) g;
if (groupIdV2.equals(gv1.getExpectedV2Id())) { if (groupIdV2.equals(gv1.getExpectedV2Id())) {
return gv1; return gv1;
} }
@ -256,8 +253,7 @@ public class GroupStore {
private Storage toStorageLocked() { private Storage toStorageLocked() {
return new Storage(groups.values().stream().map(g -> { return new Storage(groups.values().stream().map(g -> {
if (g instanceof GroupInfoV1) { if (g instanceof GroupInfoV1 g1) {
final var g1 = (GroupInfoV1) g;
return new Storage.GroupV1(g1.getGroupId().toBase64(), return new Storage.GroupV1(g1.getGroupId().toBase64(),
g1.getExpectedV2Id().toBase64(), g1.getExpectedV2Id().toBase64(),
g1.name, g1.name,

View file

@ -19,7 +19,7 @@ public class RecipientAddress {
*/ */
public RecipientAddress(Optional<UUID> uuid, Optional<String> e164) { public RecipientAddress(Optional<UUID> uuid, Optional<String> e164) {
uuid = uuid.isPresent() && uuid.get().equals(UuidUtil.UNKNOWN_UUID) ? Optional.empty() : uuid; uuid = uuid.isPresent() && uuid.get().equals(UuidUtil.UNKNOWN_UUID) ? Optional.empty() : uuid;
if (!uuid.isPresent() && !e164.isPresent()) { if (uuid.isEmpty() && e164.isEmpty()) {
throw new AssertionError("Must have either a UUID or E164 number!"); throw new AssertionError("Must have either a UUID or E164 number!");
} }

View file

@ -91,14 +91,11 @@ public class ProfileUtils {
} }
String[] parts = name.split("\0"); String[] parts = name.split("\0");
switch (parts.length) { return switch (parts.length) {
case 0: case 0 -> new Pair<>(null, null);
return new Pair<>(null, null); case 1 -> new Pair<>(parts[0], null);
case 1: default -> new Pair<>(parts[0], parts[1]);
return new Pair<>(parts[0], null); };
default:
return new Pair<>(parts[0], parts[1]);
}
} }
static String trimZeros(String str) { static String trimZeros(String str) {

View file

@ -6,7 +6,7 @@ fi
set -e set -e
# To update graalvm config, set GRAALVM_HOME, e.g: # To update graalvm config, set GRAALVM_HOME, e.g:
# export GRAALVM_HOME=/usr/lib/jvm/java-11-graalvm # export GRAALVM_HOME=/usr/lib/jvm/java-17-graalvm
if [ ! -z "$GRAALVM_HOME" ]; then if [ ! -z "$GRAALVM_HOME" ]; then
export JAVA_HOME=$GRAALVM_HOME export JAVA_HOME=$GRAALVM_HOME
export SIGNAL_CLI_OPTS='-agentlib:native-image-agent=config-merge-dir=graalvm-config-dir/' export SIGNAL_CLI_OPTS='-agentlib:native-image-agent=config-merge-dir=graalvm-config-dir/'

View file

@ -26,7 +26,7 @@ public interface SignalControl extends DBusInterface {
String link(String newDeviceName) throws Error.Failure; String link(String newDeviceName) throws Error.Failure;
public String version(); String version();
List<DBusPath> listAccounts(); List<DBusPath> listAccounts();

View file

@ -53,8 +53,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
writer.println("Got receipt."); writer.println("Got receipt.");
} else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) { } else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) {
if (exception != null) { if (exception != null) {
if (exception instanceof UntrustedIdentityException) { if (exception instanceof UntrustedIdentityException e) {
var e = (UntrustedIdentityException) exception;
writer.println( writer.println(
"The users key is untrusted, either the user has reinstalled Signal or a third party sent this message."); "The users key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender())); final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));

View file

@ -47,8 +47,7 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand {
} }
// Output // Output
if (outputWriter instanceof JsonWriter) { if (outputWriter instanceof JsonWriter jsonWriter) {
final var jsonWriter = (JsonWriter) outputWriter;
var jsonUserStatuses = registered.entrySet().stream().map(entry -> { var jsonUserStatuses = registered.entrySet().stream().map(entry -> {
final var number = entry.getValue().first(); final var number = entry.getValue().first();

View file

@ -55,8 +55,7 @@ public class JoinGroupCommand implements JsonRpcLocalCommand {
try { try {
final var results = m.joinGroup(linkUrl); final var results = m.joinGroup(linkUrl);
var newGroupId = results.first(); var newGroupId = results.first();
if (outputWriter instanceof JsonWriter) { if (outputWriter instanceof JsonWriter writer) {
final var writer = (JsonWriter) outputWriter;
if (!m.getGroup(newGroupId).isMember()) { if (!m.getGroup(newGroupId).isMember()) {
writer.write(Map.of("groupId", newGroupId.toBase64(), "onlyRequested", true)); writer.write(Map.of("groupId", newGroupId.toBase64(), "onlyRequested", true));
} else { } else {

View file

@ -27,8 +27,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand {
public void handleCommand(final Namespace ns, final Manager m, final OutputWriter outputWriter) { public void handleCommand(final Namespace ns, final Manager m, final OutputWriter outputWriter) {
var contacts = m.getContacts(); var contacts = m.getContacts();
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
for (var c : contacts) { for (var c : contacts) {
final var contact = c.second(); final var contact = c.second();
writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}", writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}",

View file

@ -43,8 +43,7 @@ public class ListDevicesCommand implements JsonRpcLocalCommand {
throw new IOErrorException("Failed to get linked devices: " + e.getMessage(), e); throw new IOErrorException("Failed to get linked devices: " + e.getMessage(), e);
} }
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
for (var d : devices) { for (var d : devices) {
writer.println("- Device {}{}:", d.getId(), (d.isThisDevice() ? " (this device)" : "")); writer.println("- Device {}{}:", d.getId(), (d.isThisDevice() ? " (this device)" : ""));
writer.indent(w -> { writer.indent(w -> {

View file

@ -80,8 +80,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
) throws CommandException { ) throws CommandException {
final var groups = m.getGroups(); final var groups = m.getGroups();
if (outputWriter instanceof JsonWriter) { if (outputWriter instanceof JsonWriter jsonWriter) {
final var jsonWriter = (JsonWriter) outputWriter;
var jsonGroups = groups.stream().map(group -> { var jsonGroups = groups.stream().map(group -> {
final var groupInviteLink = group.getGroupInviteLinkUrl(); final var groupInviteLink = group.getGroupInviteLinkUrl();

View file

@ -59,8 +59,7 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getSelfNumber())); identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getSelfNumber()));
} }
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
for (var id : identities) { for (var id : identities) {
printIdentityFingerprint(writer, m, id); printIdentityFingerprint(writer, m, id);
} }

View file

@ -79,8 +79,7 @@ public class QuitGroupCommand implements JsonRpcLocalCommand {
} }
private void outputResult(final OutputWriter outputWriter, final long timestamp) { private void outputResult(final OutputWriter outputWriter, final long timestamp) {
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
writer.println("{}", timestamp); writer.println("{}", timestamp);
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;

View file

@ -58,8 +58,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand {
final Namespace ns, final Signal signal, DBusConnection dbusconnection, final OutputWriter outputWriter final Namespace ns, final Signal signal, DBusConnection dbusconnection, final OutputWriter outputWriter
) throws CommandException { ) throws CommandException {
try { try {
if (outputWriter instanceof JsonWriter) { if (outputWriter instanceof JsonWriter jsonWriter) {
final var jsonWriter = (JsonWriter) outputWriter;
dbusconnection.addSigHandler(Signal.MessageReceived.class, signal, messageReceived -> { dbusconnection.addSigHandler(Signal.MessageReceived.class, signal, messageReceived -> {
var envelope = new JsonMessageEnvelope(messageReceived); var envelope = new JsonMessageEnvelope(messageReceived);

View file

@ -40,10 +40,11 @@ public class RegisterCommand implements RegistrationCommand {
} catch (CaptchaRequiredException e) { } catch (CaptchaRequiredException e) {
String message; String message;
if (captcha == null) { if (captcha == null) {
message = "Captcha required for verification, use --captcha CAPTCHA\n" message = """
+ "To get the token, go to https://signalcaptchas.org/registration/generate.html\n" Captcha required for verification, use --captcha CAPTCHA
+ "Check the developer tools (F12) console for a failed redirect to signalcaptcha://\n" To get the token, go to https://signalcaptchas.org/registration/generate.html
+ "Everything after signalcaptcha:// is the captcha token."; Check the developer tools (F12) console for a failed redirect to signalcaptcha://
Everything after signalcaptcha:// is the captcha token.""";
} else { } else {
message = "Invalid captcha given."; message = "Invalid captcha given.";
} }

View file

@ -67,8 +67,7 @@ public class RemoteDeleteCommand implements JsonRpcLocalCommand {
} }
private void outputResult(final OutputWriter outputWriter, final long timestamp) { private void outputResult(final OutputWriter outputWriter, final long timestamp) {
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
writer.println("{}", timestamp); writer.println("{}", timestamp);
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;

View file

@ -113,8 +113,7 @@ public class SendCommand implements JsonRpcLocalCommand {
} }
private void outputResult(final OutputWriter outputWriter, final long timestamp) { private void outputResult(final OutputWriter outputWriter, final long timestamp) {
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
writer.println("{}", timestamp); writer.println("{}", timestamp);
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;

View file

@ -83,8 +83,7 @@ public class SendReactionCommand implements JsonRpcLocalCommand {
} }
private void outputResult(final OutputWriter outputWriter, final long timestamp) { private void outputResult(final OutputWriter outputWriter, final long timestamp) {
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
writer.println("{}", timestamp); writer.println("{}", timestamp);
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;

View file

@ -77,33 +77,23 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand {
if (value == null) { if (value == null) {
return null; return null;
} }
switch (value) { return switch (value) {
case "enabled": case "enabled" -> GroupLinkState.ENABLED;
return GroupLinkState.ENABLED; case "enabled-with-approval", "enabledWithApproval" -> GroupLinkState.ENABLED_WITH_APPROVAL;
case "enabled-with-approval": case "disabled" -> GroupLinkState.DISABLED;
case "enabledWithApproval": default -> throw new UserErrorException("Invalid group link state: " + value);
return GroupLinkState.ENABLED_WITH_APPROVAL; };
case "disabled":
return GroupLinkState.DISABLED;
default:
throw new UserErrorException("Invalid group link state: " + value);
}
} }
GroupPermission getGroupPermission(String value) throws UserErrorException { GroupPermission getGroupPermission(String value) throws UserErrorException {
if (value == null) { if (value == null) {
return null; return null;
} }
switch (value) { return switch (value) {
case "every-member": case "every-member", "everyMember" -> GroupPermission.EVERY_MEMBER;
case "everyMember": case "only-admins", "onlyAdmins" -> GroupPermission.ONLY_ADMINS;
return GroupPermission.EVERY_MEMBER; default -> throw new UserErrorException("Invalid group permission: " + value);
case "only-admins": };
case "onlyAdmins":
return GroupPermission.ONLY_ADMINS;
default:
throw new UserErrorException("Invalid group permission: " + value);
}
} }
@Override @Override
@ -179,8 +169,7 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand {
} }
private void outputResult(final OutputWriter outputWriter, final Long timestamp, final GroupId groupId) { private void outputResult(final OutputWriter outputWriter, final Long timestamp, final GroupId groupId) {
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
if (groupId != null) { if (groupId != null) {
writer.println("Created new group: \"{}\"", groupId.toBase64()); writer.println("Created new group: \"{}\"", groupId.toBase64());
} }

View file

@ -42,8 +42,7 @@ public class UploadStickerPackCommand implements JsonRpcLocalCommand {
try { try {
var url = m.uploadStickerPack(path); var url = m.uploadStickerPack(path);
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter writer) {
final var writer = (PlainTextWriter) outputWriter;
writer.println("{}", url); writer.println("{}", url);
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;

View file

@ -274,15 +274,9 @@ public class DbusManagerImpl implements Manager {
} }
if (updateGroup.getGroupLinkState() != null) { if (updateGroup.getGroupLinkState() != null) {
switch (updateGroup.getGroupLinkState()) { switch (updateGroup.getGroupLinkState()) {
case DISABLED: case DISABLED -> group.disableLink();
group.disableLink(); case ENABLED -> group.enableLink(false);
break; case ENABLED_WITH_APPROVAL -> group.enableLink(true);
case ENABLED:
group.enableLink(false);
break;
case ENABLED_WITH_APPROVAL:
group.enableLink(true);
break;
} }
} }
return new SendGroupMessageResults(0, List.of()); return new SendGroupMessageResults(0, List.of());

View file

@ -71,8 +71,7 @@ public class JsonMessageEnvelope {
this.sourceNumber = source.getNumber().orNull(); this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().toString(); this.sourceUuid = source.getUuid().toString();
this.sourceDevice = content.getSenderDevice(); this.sourceDevice = content.getSenderDevice();
} else if (exception instanceof UntrustedIdentityException) { } else if (exception instanceof UntrustedIdentityException e) {
var e = (UntrustedIdentityException) exception;
final var source = m.resolveSignalServiceAddress(e.getSender()); final var source = m.resolveSignalServiceAddress(e.getSender());
this.source = getLegacyIdentifier(source); this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull(); this.sourceNumber = source.getNumber().orNull();

View file

@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import java.util.List; import java.util.List;
public class JsonRpcBulkMessage extends JsonRpcMessage { public final class JsonRpcBulkMessage extends JsonRpcMessage {
List<JsonNode> messages; List<JsonNode> messages;

View file

@ -4,6 +4,6 @@ package org.asamk.signal.jsonrpc;
* Represents a JSON-RPC (bulk) request or (bulk) response. * Represents a JSON-RPC (bulk) request or (bulk) response.
* https://www.jsonrpc.org/specification * https://www.jsonrpc.org/specification
*/ */
public abstract class JsonRpcMessage { public sealed abstract class JsonRpcMessage permits JsonRpcBulkMessage, JsonRpcRequest, JsonRpcResponse {
} }

View file

@ -8,7 +8,7 @@ import com.fasterxml.jackson.databind.node.ValueNode;
* Represents a JSON-RPC request. * Represents a JSON-RPC request.
* https://www.jsonrpc.org/specification#request_object * https://www.jsonrpc.org/specification#request_object
*/ */
public class JsonRpcRequest extends JsonRpcMessage { public final class JsonRpcRequest extends JsonRpcMessage {
/** /**
* A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". * A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0".

View file

@ -8,7 +8,7 @@ import com.fasterxml.jackson.databind.node.ValueNode;
* Represents a JSON-RPC response. * Represents a JSON-RPC response.
* https://www.jsonrpc.org/specification#response_object * https://www.jsonrpc.org/specification#response_object
*/ */
public class JsonRpcResponse extends JsonRpcMessage { public final class JsonRpcResponse extends JsonRpcMessage {
/** /**
* A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". * A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0".

View file

@ -64,12 +64,12 @@ public class ErrorUtils {
"CAPTCHA proof required for sending to \"%s\", available options \"%s\" with challenge token \"%s\", or wait \"%d\" seconds.\n" "CAPTCHA proof required for sending to \"%s\", available options \"%s\" with challenge token \"%s\", or wait \"%d\" seconds.\n"
+ ( + (
failure.getOptions().contains(ProofRequiredException.Option.RECAPTCHA) failure.getOptions().contains(ProofRequiredException.Option.RECAPTCHA)
? ? """
"To get the captcha token, go to https://signalcaptchas.org/challenge/generate.html\n" To get the captcha token, go to https://signalcaptchas.org/challenge/generate.html
+ "Check the developer tools (F12) console for a failed redirect to signalcaptcha://\n" Check the developer tools (F12) console for a failed redirect to signalcaptcha://
+ "Everything after signalcaptcha:// is the captcha token.\n" Everything after signalcaptcha:// is the captcha token.
+ "Use the following command to submit the captcha token:\n" Use the following command to submit the captcha token:
+ "signal-cli submitRateLimitChallenge --challenge CHALLENGE_TOKEN --captcha CAPTCHA_TOKEN" signal-cli submitRateLimitChallenge --challenge CHALLENGE_TOKEN --captcha CAPTCHA_TOKEN"""
: "" : ""
), ),
identifier, identifier,