runs-on: ubuntu-latest
strategy:
matrix:
- java: [ '11', '17' ]
+ java: [ '17' ]
steps:
- uses: actions/checkout@v1
- name: Setup Java JDK
uses: actions/setup-java@v1
with:
- java-version: 11
+ java-version: 17
- name: Checkout repository
uses: actions/checkout@v2
# Changelog
## [Unreleased]
+**Attention**: Now requires Java 17
## [0.9.2] - 2021-10-24
### Fixed
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
+- at least Java Runtime Environment (JRE) 17
- 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)
version = "0.9.2"
java {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
}
application {
this["main"].run {
configurationFileDirectories.from(file("graalvm-config-dir"))
buildArgs.add("--allow-incomplete-classpath")
+ buildArgs.add("--report-unsupported-elements-at-runtime")
}
}
}
}
java {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
}
repositories {
public void log(final int priority, final String tag, final String message) {
final var 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;
+ case SignalProtocolLogger.VERBOSE -> logger.trace(logMessage);
+ case SignalProtocolLogger.DEBUG -> logger.debug(logMessage);
+ case SignalProtocolLogger.INFO -> logger.info(logMessage);
+ case SignalProtocolLogger.WARN -> logger.warn(logMessage);
+ case SignalProtocolLogger.ERROR, SignalProtocolLogger.ASSERT -> logger.error(logMessage);
}
}
}
long timestamp = System.currentTimeMillis();
messageBuilder.withTimestamp(timestamp);
for (final var recipient : recipients) {
- if (recipient instanceof RecipientIdentifier.Single) {
- final var recipientId = resolveRecipient((RecipientIdentifier.Single) recipient);
+ if (recipient instanceof RecipientIdentifier.Single single) {
+ final var recipientId = resolveRecipient(single);
final var result = sendHelper.sendMessage(messageBuilder, recipientId);
results.put(recipient, List.of(result));
} else if (recipient instanceof RecipientIdentifier.NoteToSelf) {
final var result = sendHelper.sendSelfMessage(messageBuilder);
results.put(recipient, List.of(result));
- } else if (recipient instanceof RecipientIdentifier.Group) {
- final var groupId = ((RecipientIdentifier.Group) recipient).groupId;
- final var result = sendHelper.sendAsGroupMessage(messageBuilder, groupId);
+ } else if (recipient instanceof RecipientIdentifier.Group group) {
+ final var result = sendHelper.sendAsGroupMessage(messageBuilder, group.groupId);
results.put(recipient, result);
}
}
private synchronized void onStateChange(WebSocketConnectionState connectionState, HealthState healthState) {
switch (connectionState) {
- case CONNECTED:
- logger.debug("WebSocket is now connected");
- break;
- case AUTHENTICATION_FAILED:
- logger.debug("WebSocket authentication failed");
- break;
- case FAILED:
- logger.debug("WebSocket connection failed");
- break;
+ case CONNECTED -> logger.debug("WebSocket is now connected");
+ case AUTHENTICATION_FAILED -> logger.debug("WebSocket authentication failed");
+ case FAILED -> logger.debug("WebSocket connection failed");
}
healthState.needsKeepAlive = connectionState == WebSocketConnectionState.CONNECTED;
}
public static TrustLevel fromIdentityState(ContactRecord.IdentityState identityState) {
- switch (identityState) {
- case DEFAULT:
- return TRUSTED_UNVERIFIED;
- case UNVERIFIED:
- return UNTRUSTED;
- case VERIFIED:
- return TRUSTED_VERIFIED;
- case UNRECOGNIZED:
- return null;
- }
- throw new RuntimeException("Unknown identity state: " + identityState);
+ return switch (identityState) {
+ case DEFAULT -> TRUSTED_UNVERIFIED;
+ case UNVERIFIED -> UNTRUSTED;
+ case VERIFIED -> TRUSTED_VERIFIED;
+ case UNRECOGNIZED -> null;
+ };
}
public static TrustLevel fromVerifiedState(VerifiedMessage.VerifiedState verifiedState) {
- switch (verifiedState) {
- case DEFAULT:
- return TRUSTED_UNVERIFIED;
- case UNVERIFIED:
- return UNTRUSTED;
- case VERIFIED:
- return TRUSTED_VERIFIED;
- }
- throw new RuntimeException("Unknown verified state: " + verifiedState);
+ return switch (verifiedState) {
+ case DEFAULT -> TRUSTED_UNVERIFIED;
+ case UNVERIFIED -> UNTRUSTED;
+ case VERIFIED -> TRUSTED_VERIFIED;
+ };
}
public VerifiedMessage.VerifiedState toVerifiedState() {
- switch (this) {
- case TRUSTED_UNVERIFIED:
- return VerifiedMessage.VerifiedState.DEFAULT;
- case UNTRUSTED:
- return VerifiedMessage.VerifiedState.UNVERIFIED;
- case TRUSTED_VERIFIED:
- return VerifiedMessage.VerifiedState.VERIFIED;
- }
- throw new RuntimeException("Unknown verified state: " + this);
+ return switch (this) {
+ case TRUSTED_UNVERIFIED -> VerifiedMessage.VerifiedState.DEFAULT;
+ case UNTRUSTED -> VerifiedMessage.VerifiedState.UNVERIFIED;
+ case TRUSTED_VERIFIED -> VerifiedMessage.VerifiedState.VERIFIED;
+ };
}
}
}
private static int envelopeTypeToCiphertextMessageType(int envelopeType) {
- switch (envelopeType) {
- case SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE:
- return CiphertextMessage.PREKEY_TYPE;
- case SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE:
- return CiphertextMessage.SENDERKEY_TYPE;
- case SignalServiceProtos.Envelope.Type.PLAINTEXT_CONTENT_VALUE:
- return CiphertextMessage.PLAINTEXT_CONTENT_TYPE;
- case SignalServiceProtos.Envelope.Type.CIPHERTEXT_VALUE:
- default:
- return CiphertextMessage.WHISPER_TYPE;
- }
+ return switch (envelopeType) {
+ case SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE -> CiphertextMessage.PREKEY_TYPE;
+ case SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE -> CiphertextMessage.SENDERKEY_TYPE;
+ case SignalServiceProtos.Envelope.Type.PLAINTEXT_CONTENT_VALUE -> CiphertextMessage.PLAINTEXT_CONTENT_TYPE;
+ default -> CiphertextMessage.WHISPER_TYPE;
+ };
}
@Override
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 abstract static class Single extends RecipientIdentifier {
+ public sealed static abstract class Single extends RecipientIdentifier {
public static Single fromString(String identifier, String localNumber) throws InvalidNumberException {
return UuidUtil.isUuid(identifier)
public abstract String getIdentifier();
}
- public static class Uuid extends Single {
+ public static final class Uuid extends Single {
public final UUID uuid;
}
}
- public static class Number extends Single {
+ public static final class Number extends Single {
public final String number;
}
}
- public static class Group extends RecipientIdentifier {
+ public static final class Group extends RecipientIdentifier {
public final GroupId groupId;
STOP;
public SignalServiceTypingMessage.Action toSignalService() {
- switch (this) {
- case START:
- return SignalServiceTypingMessage.Action.STARTED;
- case STOP:
- return SignalServiceTypingMessage.Action.STOPPED;
- default:
- throw new IllegalStateException("Invalid typing action " + this);
- }
+ return switch (this) {
+ case START -> SignalServiceTypingMessage.Action.STARTED;
+ case STOP -> SignalServiceTypingMessage.Action.STOPPED;
+ };
}
}
final var 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");
- }
+ return switch (serviceEnvironment) {
+ case LIVE -> new ServiceEnvironmentConfig(LiveConfig.createDefaultServiceConfiguration(interceptors),
+ LiveConfig.getUnidentifiedSenderTrustRoot(),
+ LiveConfig.createKeyBackupConfig(),
+ LiveConfig.getCdsMrenclave());
+ case SANDBOX -> new ServiceEnvironmentConfig(SandboxConfig.createDefaultServiceConfiguration(interceptors),
+ SandboxConfig.getUnidentifiedSenderTrustRoot(),
+ SandboxConfig.createKeyBackupConfig(),
+ SandboxConfig.getCdsMrenclave());
+ };
}
}
import java.util.Arrays;
import java.util.Base64;
-public abstract class GroupId {
+public abstract sealed class GroupId permits GroupIdV1, GroupIdV2 {
private final byte[] id;
import static org.asamk.signal.manager.util.KeyUtils.getSecretBytes;
-public class GroupIdV1 extends GroupId {
+public final class GroupIdV1 extends GroupId {
public static GroupIdV1 createRandom() {
return new GroupIdV1(getSecretBytes(16));
import java.util.Base64;
-public class GroupIdV2 extends GroupId {
+public final class GroupIdV2 extends GroupId {
public static GroupIdV2 fromBase64(String groupId) {
return new GroupIdV2(Base64.getDecoder().decode(groupId));
var groupInviteLink = GroupInviteLink.parseFrom(bytes);
switch (groupInviteLink.getContentsCase()) {
- case V1CONTENTS: {
+ case V1CONTENTS -> {
var groupInviteLinkContentsV1 = groupInviteLink.getV1Contents();
var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.getGroupMasterKey()
.toByteArray());
return new GroupInviteLinkUrl(groupMasterKey, password);
}
- default:
- throw new UnknownGroupLinkVersionException("Url contains no known group link content");
+ default -> throw new UnknownGroupLinkVersionException("Url contains no known group link content");
}
} catch (InvalidInputException | IOException e) {
throw new InvalidGroupLinkException(e);
private GroupInfo getGroup(GroupId groupId, boolean forceUpdate) {
final var group = account.getGroupStore().getGroup(groupId);
- if (group instanceof GroupInfoV2) {
- final var groupInfoV2 = (GroupInfoV2) group;
+ if (group instanceof GroupInfoV2 groupInfoV2) {
if (forceUpdate || (!groupInfoV2.isPermissionDenied() && groupInfoV2.getGroup() == null)) {
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
DecryptedGroup decryptedGroup;
}
private AccessControl.AccessRequired toAccessControl(final GroupLinkState state) {
- switch (state) {
- case DISABLED:
- return AccessControl.AccessRequired.UNSATISFIABLE;
- case ENABLED:
- return AccessControl.AccessRequired.ANY;
- case ENABLED_WITH_APPROVAL:
- return AccessControl.AccessRequired.ADMINISTRATOR;
- default:
- throw new AssertionError();
- }
+ return switch (state) {
+ case DISABLED -> AccessControl.AccessRequired.UNSATISFIABLE;
+ case ENABLED -> AccessControl.AccessRequired.ANY;
+ case ENABLED_WITH_APPROVAL -> AccessControl.AccessRequired.ADMINISTRATOR;
+ };
}
private AccessControl.AccessRequired toAccessControl(final GroupPermission permission) {
- switch (permission) {
- case EVERY_MEMBER:
- return AccessControl.AccessRequired.MEMBER;
- case ONLY_ADMINS:
- return AccessControl.AccessRequired.ADMINISTRATOR;
- default:
- throw new AssertionError();
- }
+ return switch (permission) {
+ case EVERY_MEMBER -> AccessControl.AccessRequired.MEMBER;
+ case ONLY_ADMINS -> AccessControl.AccessRequired.ADMINISTRATOR;
+ };
}
private GroupsV2Operations.GroupOperations getGroupOperations(final GroupInfoV2 groupInfoV2) {
try (OutputStream fos = new FileOutputStream(groupsFile)) {
var out = new DeviceGroupsOutputStream(fos);
for (var record : account.getGroupStore().getGroups()) {
- if (record instanceof GroupInfoV1) {
- var groupInfo = (GroupInfoV1) record;
+ if (record instanceof GroupInfoV1 groupInfo) {
out.write(new DeviceGroup(groupInfo.getGroupId().serialize(),
Optional.fromNullable(groupInfo.name),
groupInfo.getMembers()
import java.util.stream.Collectors;
import java.util.stream.Stream;
-public abstract class GroupInfo {
+public sealed abstract class GroupInfo permits GroupInfoV1, GroupInfoV2 {
public abstract GroupId getGroupId();
import java.util.HashSet;
import java.util.Set;
-public class GroupInfoV1 extends GroupInfo {
+public final class GroupInfoV1 extends GroupInfo {
private final GroupIdV1 groupId;
import java.util.Set;
import java.util.stream.Collectors;
-public class GroupInfoV2 extends GroupInfo {
+public final class GroupInfoV2 extends GroupInfo {
private final GroupIdV2 groupId;
private final GroupMasterKey masterKey;
}
private static GroupPermission toGroupPermission(final AccessControl.AccessRequired permission) {
- switch (permission) {
- case ADMINISTRATOR:
- return GroupPermission.ONLY_ADMINS;
- case MEMBER:
- default:
- return GroupPermission.EVERY_MEMBER;
- }
+ return switch (permission) {
+ case ADMINISTRATOR -> GroupPermission.ONLY_ADMINS;
+ default -> GroupPermission.EVERY_MEMBER;
+ };
}
}
final Saver saver
) {
final var groups = storage.groups.stream().map(g -> {
- if (g instanceof Storage.GroupV1) {
- final var g1 = (Storage.GroupV1) g;
+ if (g instanceof Storage.GroupV1 g1) {
final var members = g1.members.stream().map(m -> {
if (m.recipientId == null) {
return recipientResolver.resolveRecipient(new RecipientAddress(UuidUtil.parseOrNull(m.uuid),
synchronized (groups) {
var modified = false;
for (var group : this.groups.values()) {
- if (group instanceof GroupInfoV1) {
- var groupV1 = (GroupInfoV1) group;
+ if (group instanceof GroupInfoV1 groupV1) {
if (groupV1.isMember(toBeMergedRecipientId)) {
groupV1.removeMember(toBeMergedRecipientId);
groupV1.addMembers(List.of(recipientId));
private GroupInfoV1 getGroupV1ByV2IdLocked(GroupIdV2 groupIdV2) {
for (var g : groups.values()) {
- if (g instanceof GroupInfoV1) {
- final var gv1 = (GroupInfoV1) g;
+ if (g instanceof GroupInfoV1 gv1) {
if (groupIdV2.equals(gv1.getExpectedV2Id())) {
return gv1;
}
private Storage toStorageLocked() {
return new Storage(groups.values().stream().map(g -> {
- if (g instanceof GroupInfoV1) {
- final var g1 = (GroupInfoV1) g;
+ if (g instanceof GroupInfoV1 g1) {
return new Storage.GroupV1(g1.getGroupId().toBase64(),
g1.getExpectedV2Id().toBase64(),
g1.name,
*/
public RecipientAddress(Optional<UUID> uuid, Optional<String> e164) {
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!");
}
}
String[] parts = name.split("\0");
- switch (parts.length) {
- case 0:
- return new Pair<>(null, null);
- case 1:
- return new Pair<>(parts[0], null);
- default:
- return new Pair<>(parts[0], parts[1]);
- }
+ return switch (parts.length) {
+ case 0 -> new Pair<>(null, null);
+ case 1 -> new Pair<>(parts[0], null);
+ default -> new Pair<>(parts[0], parts[1]);
+ };
}
static String trimZeros(String str) {
set -e
# 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
export JAVA_HOME=$GRAALVM_HOME
export SIGNAL_CLI_OPTS='-agentlib:native-image-agent=config-merge-dir=graalvm-config-dir/'
String link(String newDeviceName) throws Error.Failure;
- public String version();
+ String version();
List<DBusPath> listAccounts();
writer.println("Got receipt.");
} else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) {
if (exception != null) {
- if (exception instanceof UntrustedIdentityException) {
- var e = (UntrustedIdentityException) exception;
+ if (exception instanceof UntrustedIdentityException e) {
writer.println(
"The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));
}
// Output
- if (outputWriter instanceof JsonWriter) {
- final var jsonWriter = (JsonWriter) outputWriter;
+ if (outputWriter instanceof JsonWriter jsonWriter) {
var jsonUserStatuses = registered.entrySet().stream().map(entry -> {
final var number = entry.getValue().first();
try {
final var results = m.joinGroup(linkUrl);
var newGroupId = results.first();
- if (outputWriter instanceof JsonWriter) {
- final var writer = (JsonWriter) outputWriter;
+ if (outputWriter instanceof JsonWriter writer) {
if (!m.getGroup(newGroupId).isMember()) {
writer.write(Map.of("groupId", newGroupId.toBase64(), "onlyRequested", true));
} else {
public void handleCommand(final Namespace ns, final Manager m, final OutputWriter outputWriter) {
var contacts = m.getContacts();
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
for (var c : contacts) {
final var contact = c.second();
writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}",
throw new IOErrorException("Failed to get linked devices: " + e.getMessage(), e);
}
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
for (var d : devices) {
writer.println("- Device {}{}:", d.getId(), (d.isThisDevice() ? " (this device)" : ""));
writer.indent(w -> {
) throws CommandException {
final var groups = m.getGroups();
- if (outputWriter instanceof JsonWriter) {
- final var jsonWriter = (JsonWriter) outputWriter;
+ if (outputWriter instanceof JsonWriter jsonWriter) {
var jsonGroups = groups.stream().map(group -> {
final var groupInviteLink = group.getGroupInviteLinkUrl();
identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getSelfNumber()));
}
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
for (var id : identities) {
printIdentityFingerprint(writer, m, id);
}
}
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
writer.println("{}", timestamp);
} else {
final var writer = (JsonWriter) outputWriter;
final Namespace ns, final Signal signal, DBusConnection dbusconnection, final OutputWriter outputWriter
) throws CommandException {
try {
- if (outputWriter instanceof JsonWriter) {
- final var jsonWriter = (JsonWriter) outputWriter;
+ if (outputWriter instanceof JsonWriter jsonWriter) {
dbusconnection.addSigHandler(Signal.MessageReceived.class, signal, messageReceived -> {
var envelope = new JsonMessageEnvelope(messageReceived);
} catch (CaptchaRequiredException e) {
String message;
if (captcha == null) {
- 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.";
+ message = """
+ Captcha required for verification, use --captcha CAPTCHA
+ To get the token, go to https://signalcaptchas.org/registration/generate.html
+ Check the developer tools (F12) console for a failed redirect to signalcaptcha://
+ Everything after signalcaptcha:// is the captcha token.""";
} else {
message = "Invalid captcha given.";
}
}
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
writer.println("{}", timestamp);
} else {
final var writer = (JsonWriter) outputWriter;
}
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
writer.println("{}", timestamp);
} else {
final var writer = (JsonWriter) outputWriter;
}
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
writer.println("{}", timestamp);
} else {
final var writer = (JsonWriter) outputWriter;
if (value == null) {
return null;
}
- switch (value) {
- case "enabled":
- return GroupLinkState.ENABLED;
- case "enabled-with-approval":
- case "enabledWithApproval":
- return GroupLinkState.ENABLED_WITH_APPROVAL;
- case "disabled":
- return GroupLinkState.DISABLED;
- default:
- throw new UserErrorException("Invalid group link state: " + value);
- }
+ return switch (value) {
+ case "enabled" -> GroupLinkState.ENABLED;
+ case "enabled-with-approval", "enabledWithApproval" -> GroupLinkState.ENABLED_WITH_APPROVAL;
+ case "disabled" -> GroupLinkState.DISABLED;
+ default -> throw new UserErrorException("Invalid group link state: " + value);
+ };
}
GroupPermission getGroupPermission(String value) throws UserErrorException {
if (value == null) {
return null;
}
- switch (value) {
- case "every-member":
- case "everyMember":
- return GroupPermission.EVERY_MEMBER;
- case "only-admins":
- case "onlyAdmins":
- return GroupPermission.ONLY_ADMINS;
- default:
- throw new UserErrorException("Invalid group permission: " + value);
- }
+ return switch (value) {
+ case "every-member", "everyMember" -> GroupPermission.EVERY_MEMBER;
+ case "only-admins", "onlyAdmins" -> GroupPermission.ONLY_ADMINS;
+ default -> throw new UserErrorException("Invalid group permission: " + value);
+ };
}
@Override
}
private void outputResult(final OutputWriter outputWriter, final Long timestamp, final GroupId groupId) {
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
if (groupId != null) {
writer.println("Created new group: \"{}\"", groupId.toBase64());
}
try {
var url = m.uploadStickerPack(path);
- if (outputWriter instanceof PlainTextWriter) {
- final var writer = (PlainTextWriter) outputWriter;
+ if (outputWriter instanceof PlainTextWriter writer) {
writer.println("{}", url);
} else {
final var writer = (JsonWriter) outputWriter;
}
if (updateGroup.getGroupLinkState() != null) {
switch (updateGroup.getGroupLinkState()) {
- case DISABLED:
- group.disableLink();
- break;
- case ENABLED:
- group.enableLink(false);
- break;
- case ENABLED_WITH_APPROVAL:
- group.enableLink(true);
- break;
+ case DISABLED -> group.disableLink();
+ case ENABLED -> group.enableLink(false);
+ case ENABLED_WITH_APPROVAL -> group.enableLink(true);
}
}
return new SendGroupMessageResults(0, List.of());
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().toString();
this.sourceDevice = content.getSenderDevice();
- } else if (exception instanceof UntrustedIdentityException) {
- var e = (UntrustedIdentityException) exception;
+ } else if (exception instanceof UntrustedIdentityException e) {
final var source = m.resolveSignalServiceAddress(e.getSender());
this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
import java.util.List;
-public class JsonRpcBulkMessage extends JsonRpcMessage {
+public final class JsonRpcBulkMessage extends JsonRpcMessage {
List<JsonNode> messages;
* Represents a JSON-RPC (bulk) request or (bulk) response.
* https://www.jsonrpc.org/specification
*/
-public abstract class JsonRpcMessage {
+public sealed abstract class JsonRpcMessage permits JsonRpcBulkMessage, JsonRpcRequest, JsonRpcResponse {
}
* Represents a JSON-RPC request.
* 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".
* Represents a JSON-RPC response.
* 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".
"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)
- ?
- "To get the captcha token, go to https://signalcaptchas.org/challenge/generate.html\n"
- + "Check the developer tools (F12) console for a failed redirect to signalcaptcha://\n"
- + "Everything after signalcaptcha:// is the captcha token.\n"
- + "Use the following command to submit the captcha token:\n"
- + "signal-cli submitRateLimitChallenge --challenge CHALLENGE_TOKEN --captcha CAPTCHA_TOKEN"
+ ? """
+ 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://
+ Everything after signalcaptcha:// is the captcha token.
+ Use the following command to submit the captcha token:
+ signal-cli submitRateLimitChallenge --challenge CHALLENGE_TOKEN --captcha CAPTCHA_TOKEN"""
: ""
),
identifier,