mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
Use Java 17
This commit is contained in:
parent
9cb1409918
commit
ce70a623c2
51 changed files with 142 additions and 236 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -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
|
||||||
|
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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/'
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
|
"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()));
|
final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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: {}",
|
||||||
|
|
|
@ -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 -> {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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".
|
||||||
|
|
|
@ -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".
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue