Use record classes

This commit is contained in:
AsamK 2021-10-24 22:26:12 +02:00
parent ce70a623c2
commit ce7aa580b6
66 changed files with 754 additions and 1877 deletions

View File

@ -26,14 +26,14 @@
<emptyLine /> <emptyLine />
</value> </value>
</option> </option>
<option name="RECORD_COMPONENTS_WRAP" value="5" />
<option name="NEW_LINE_AFTER_LPAREN_IN_RECORD_HEADER" value="true" />
<option name="RPAREN_ON_NEW_LINE_IN_RECORD_HEADER" value="true" />
<option name="JD_P_AT_EMPTY_LINES" value="false" /> <option name="JD_P_AT_EMPTY_LINES" value="false" />
</JavaCodeStyleSettings> </JavaCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA"> <codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="120" /> <option name="RIGHT_MARGIN" value="120" />
<option name="KEEP_LINE_BREAKS" value="false" /> <option name="KEEP_LINE_BREAKS" value="false" />
@ -52,6 +52,7 @@
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" /> <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="5" /> <option name="TERNARY_OPERATION_WRAP" value="5" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" /> <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
<option name="ENUM_CONSTANTS_WRAP" value="2" /> <option name="ENUM_CONSTANTS_WRAP" value="2" />
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">

View File

@ -15,6 +15,7 @@ repositories {
dependencies { dependencies {
api("com.github.turasa:signal-service-java:2.15.3_unofficial_31") api("com.github.turasa:signal-service-java:2.15.3_unofficial_31")
api("com.fasterxml.jackson.core", "jackson-databind", "2.13.0")
implementation("com.google.protobuf:protobuf-javalite:3.10.0") implementation("com.google.protobuf:protobuf-javalite:3.10.0")
implementation("org.bouncycastle:bcprov-jdk15on:1.69") implementation("org.bouncycastle:bcprov-jdk15on:1.69")
implementation("org.slf4j:slf4j-api:1.7.32") implementation("org.slf4j:slf4j-api:1.7.32")

View File

@ -15,10 +15,7 @@ import java.util.Map;
import static org.whispersystems.signalservice.internal.util.Util.isEmpty; import static org.whispersystems.signalservice.internal.util.Util.isEmpty;
public class DeviceLinkInfo { public record DeviceLinkInfo(String deviceIdentifier, ECPublicKey deviceKey) {
final String deviceIdentifier;
final ECPublicKey deviceKey;
public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws InvalidKeyException { public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws InvalidKeyException {
final var rawQuery = linkUri.getRawQuery(); final var rawQuery = linkUri.getRawQuery();
@ -57,11 +54,6 @@ public class DeviceLinkInfo {
return map; return map;
} }
public DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) {
this.deviceIdentifier = deviceIdentifier;
this.deviceKey = deviceKey;
}
public URI createDeviceLinkUri() { public URI createDeviceLinkUri() {
final var deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", ""); final var deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", "");
try { try {

View File

@ -1,55 +1,8 @@
package org.asamk.signal.manager; package org.asamk.signal.manager;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List; import java.util.List;
public class JsonStickerPack { public record JsonStickerPack(String title, String author, JsonSticker cover, List<JsonSticker> stickers) {
@JsonProperty public record JsonSticker(String emoji, String file, String contentType) {}
public String title;
@JsonProperty
public String author;
@JsonProperty
public JsonSticker cover;
@JsonProperty
public List<JsonSticker> stickers;
// For deserialization
private JsonStickerPack() {
}
public JsonStickerPack(
final String title, final String author, final JsonSticker cover, final List<JsonSticker> stickers
) {
this.title = title;
this.author = author;
this.cover = cover;
this.stickers = stickers;
}
public static class JsonSticker {
@JsonProperty
public String emoji;
@JsonProperty
public String file;
@JsonProperty
public String contentType;
// For deserialization
private JsonSticker() {
}
public JsonSticker(final String emoji, final String file, final String contentType) {
this.emoji = emoji;
this.file = file;
this.contentType = contentType;
}
}
} }

View File

@ -57,11 +57,11 @@ public interface Manager extends Closeable {
) throws IOException, NotRegisteredException { ) throws IOException, NotRegisteredException {
var pathConfig = PathConfig.createDefault(settingsPath); var pathConfig = PathConfig.createDefault(settingsPath);
if (!SignalAccount.userExists(pathConfig.getDataPath(), number)) { if (!SignalAccount.userExists(pathConfig.dataPath(), number)) {
throw new NotRegisteredException(); throw new NotRegisteredException();
} }
var account = SignalAccount.load(pathConfig.getDataPath(), number, true, trustNewIdentity); var account = SignalAccount.load(pathConfig.dataPath(), number, true, trustNewIdentity);
if (!account.isRegistered()) { if (!account.isRegistered()) {
throw new NotRegisteredException(); throw new NotRegisteredException();
@ -74,7 +74,7 @@ public interface Manager extends Closeable {
static List<String> getAllLocalNumbers(File settingsPath) { static List<String> getAllLocalNumbers(File settingsPath) {
var pathConfig = PathConfig.createDefault(settingsPath); var pathConfig = PathConfig.createDefault(settingsPath);
final var dataPath = pathConfig.getDataPath(); final var dataPath = pathConfig.dataPath();
final var files = dataPath.listFiles(); final var files = dataPath.listFiles();
if (files == null) { if (files == null) {

View File

@ -169,9 +169,9 @@ public class ManagerImpl implements Manager {
account.getSignalProtocolStore(), account.getSignalProtocolStore(),
executor, executor,
sessionLock); sessionLock);
final var avatarStore = new AvatarStore(pathConfig.getAvatarsPath()); final var avatarStore = new AvatarStore(pathConfig.avatarsPath());
final var attachmentStore = new AttachmentStore(pathConfig.getAttachmentsPath()); final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath());
final var stickerPackStore = new StickerPackStore(pathConfig.getStickerPacksPath()); final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath());
this.attachmentHelper = new AttachmentHelper(dependencies, attachmentStore); this.attachmentHelper = new AttachmentHelper(dependencies, attachmentStore);
this.pinHelper = new PinHelper(dependencies.getKeyBackupService()); this.pinHelper = new PinHelper(dependencies.getKeyBackupService());
@ -426,7 +426,7 @@ public class ManagerImpl implements Manager {
public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException { public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException {
var info = DeviceLinkInfo.parseDeviceLinkUri(linkUri); var info = DeviceLinkInfo.parseDeviceLinkUri(linkUri);
addDevice(info.deviceIdentifier, info.deviceKey); addDevice(info.deviceIdentifier(), info.deviceKey());
} }
private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException { private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException {
@ -642,8 +642,8 @@ public class ManagerImpl implements Manager {
private void applyMessage( private void applyMessage(
final SignalServiceDataMessage.Builder messageBuilder, final Message message final SignalServiceDataMessage.Builder messageBuilder, final Message message
) throws AttachmentInvalidException, IOException { ) throws AttachmentInvalidException, IOException {
messageBuilder.withBody(message.getMessageText()); messageBuilder.withBody(message.messageText());
final var attachments = message.getAttachments(); final var attachments = message.attachments();
if (attachments != null) { if (attachments != null) {
messageBuilder.withAttachments(attachmentHelper.uploadAttachments(attachments)); messageBuilder.withAttachments(attachmentHelper.uploadAttachments(attachments));
} }

View File

@ -2,12 +2,9 @@ package org.asamk.signal.manager;
import java.io.File; import java.io.File;
public class PathConfig { public record PathConfig(
File dataPath, File attachmentsPath, File avatarsPath, File stickerPacksPath
private final File dataPath; ) {
private final File attachmentsPath;
private final File avatarsPath;
private final File stickerPacksPath;
public static PathConfig createDefault(final File settingsPath) { public static PathConfig createDefault(final File settingsPath) {
return new PathConfig(new File(settingsPath, "data"), return new PathConfig(new File(settingsPath, "data"),
@ -15,29 +12,4 @@ public class PathConfig {
new File(settingsPath, "avatars"), new File(settingsPath, "avatars"),
new File(settingsPath, "stickers")); new File(settingsPath, "stickers"));
} }
private PathConfig(
final File dataPath, final File attachmentsPath, final File avatarsPath, final File stickerPacksPath
) {
this.dataPath = dataPath;
this.attachmentsPath = attachmentsPath;
this.avatarsPath = avatarsPath;
this.stickerPacksPath = stickerPacksPath;
}
public File getDataPath() {
return dataPath;
}
public File getAttachmentsPath() {
return attachmentsPath;
}
public File getAvatarsPath() {
return avatarsPath;
}
public File getStickerPacksPath() {
return stickerPacksPath;
}
} }

View File

@ -95,8 +95,8 @@ public class ProvisioningManager {
logger.info("Received link information from {}, linking in progress ...", number); logger.info("Received link information from {}, linking in progress ...", number);
if (SignalAccount.userExists(pathConfig.getDataPath(), number) && !canRelinkExistingAccount(number)) { if (SignalAccount.userExists(pathConfig.dataPath(), number) && !canRelinkExistingAccount(number)) {
throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.getDataPath(), number)); throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.dataPath(), number));
} }
var encryptedDeviceName = deviceName == null var encryptedDeviceName = deviceName == null
@ -115,7 +115,7 @@ public class ProvisioningManager {
SignalAccount account = null; SignalAccount account = null;
try { try {
account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.getDataPath(), account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.dataPath(),
number, number,
ret.getUuid(), ret.getUuid(),
password, password,
@ -165,7 +165,7 @@ public class ProvisioningManager {
private boolean canRelinkExistingAccount(final String number) throws IOException { private boolean canRelinkExistingAccount(final String number) throws IOException {
final SignalAccount signalAccount; final SignalAccount signalAccount;
try { try {
signalAccount = SignalAccount.load(pathConfig.getDataPath(), number, false, TrustNewIdentity.ON_FIRST_USE); signalAccount = SignalAccount.load(pathConfig.dataPath(), number, false, TrustNewIdentity.ON_FIRST_USE);
} catch (IOException e) { } catch (IOException e) {
logger.debug("Account in use or failed to load.", e); logger.debug("Account in use or failed to load.", e);
return false; return false;

View File

@ -96,12 +96,12 @@ public class RegistrationManager implements Closeable {
var pathConfig = PathConfig.createDefault(settingsPath); var pathConfig = PathConfig.createDefault(settingsPath);
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
if (!SignalAccount.userExists(pathConfig.getDataPath(), number)) { if (!SignalAccount.userExists(pathConfig.dataPath(), number)) {
var identityKey = KeyUtils.generateIdentityKeyPair(); var identityKey = KeyUtils.generateIdentityKeyPair();
var registrationId = KeyHelper.generateRegistrationId(false); var registrationId = KeyHelper.generateRegistrationId(false);
var profileKey = KeyUtils.createProfileKey(); var profileKey = KeyUtils.createProfileKey();
var account = SignalAccount.create(pathConfig.getDataPath(), var account = SignalAccount.create(pathConfig.dataPath(),
number, number,
identityKey, identityKey,
registrationId, registrationId,
@ -111,7 +111,7 @@ public class RegistrationManager implements Closeable {
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
} }
var account = SignalAccount.load(pathConfig.getDataPath(), number, true, TrustNewIdentity.ON_FIRST_USE); var account = SignalAccount.load(pathConfig.dataPath(), number, true, TrustNewIdentity.ON_FIRST_USE);
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
} }

View File

@ -1,38 +1,3 @@
package org.asamk.signal.manager.api; package org.asamk.signal.manager.api;
public class Device { public record Device(long id, String name, long created, long lastSeen, boolean isThisDevice) {}
private final long id;
private final String name;
private final long created;
private final long lastSeen;
private final boolean thisDevice;
public Device(long id, String name, long created, long lastSeen, final boolean thisDevice) {
this.id = id;
this.name = name;
this.created = created;
this.lastSeen = lastSeen;
this.thisDevice = thisDevice;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public long getCreated() {
return created;
}
public long getLastSeen() {
return lastSeen;
}
public boolean isThisDevice() {
return thisDevice;
}
}

View File

@ -7,116 +7,20 @@ import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import java.util.Set; import java.util.Set;
public class Group { public record Group(
GroupId groupId,
private final GroupId groupId; String title,
private final String title; String description,
private final String description; GroupInviteLinkUrl groupInviteLinkUrl,
private final GroupInviteLinkUrl groupInviteLinkUrl; Set<RecipientAddress> members,
private final Set<RecipientAddress> members; Set<RecipientAddress> pendingMembers,
private final Set<RecipientAddress> pendingMembers; Set<RecipientAddress> requestingMembers,
private final Set<RecipientAddress> requestingMembers; Set<RecipientAddress> adminMembers,
private final Set<RecipientAddress> adminMembers; boolean isBlocked,
private final boolean isBlocked; int messageExpirationTimer,
private final int messageExpirationTimer; GroupPermission permissionAddMember,
GroupPermission permissionEditDetails,
private final GroupPermission permissionAddMember; GroupPermission permissionSendMessage,
private final GroupPermission permissionEditDetails; boolean isMember,
private final GroupPermission permissionSendMessage; boolean isAdmin
private final boolean isMember; ) {}
private final boolean isAdmin;
public Group(
final GroupId groupId,
final String title,
final String description,
final GroupInviteLinkUrl groupInviteLinkUrl,
final Set<RecipientAddress> members,
final Set<RecipientAddress> pendingMembers,
final Set<RecipientAddress> requestingMembers,
final Set<RecipientAddress> adminMembers,
final boolean isBlocked,
final int messageExpirationTimer,
final GroupPermission permissionAddMember,
final GroupPermission permissionEditDetails,
final GroupPermission permissionSendMessage,
final boolean isMember,
final boolean isAdmin
) {
this.groupId = groupId;
this.title = title;
this.description = description;
this.groupInviteLinkUrl = groupInviteLinkUrl;
this.members = members;
this.pendingMembers = pendingMembers;
this.requestingMembers = requestingMembers;
this.adminMembers = adminMembers;
this.isBlocked = isBlocked;
this.messageExpirationTimer = messageExpirationTimer;
this.permissionAddMember = permissionAddMember;
this.permissionEditDetails = permissionEditDetails;
this.permissionSendMessage = permissionSendMessage;
this.isMember = isMember;
this.isAdmin = isAdmin;
}
public GroupId getGroupId() {
return groupId;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public GroupInviteLinkUrl getGroupInviteLinkUrl() {
return groupInviteLinkUrl;
}
public Set<RecipientAddress> getMembers() {
return members;
}
public Set<RecipientAddress> getPendingMembers() {
return pendingMembers;
}
public Set<RecipientAddress> getRequestingMembers() {
return requestingMembers;
}
public Set<RecipientAddress> getAdminMembers() {
return adminMembers;
}
public boolean isBlocked() {
return isBlocked;
}
public int getMessageExpirationTimer() {
return messageExpirationTimer;
}
public GroupPermission getPermissionAddMember() {
return permissionAddMember;
}
public GroupPermission getPermissionEditDetails() {
return permissionEditDetails;
}
public GroupPermission getPermissionSendMessage() {
return permissionSendMessage;
}
public boolean isMember() {
return isMember;
}
public boolean isAdmin() {
return isAdmin;
}
}

View File

@ -6,60 +6,16 @@ import org.whispersystems.libsignal.IdentityKey;
import java.util.Date; import java.util.Date;
public class Identity { public record Identity(
RecipientAddress recipient,
private final RecipientAddress recipient; IdentityKey identityKey,
private final IdentityKey identityKey; String safetyNumber,
private final String safetyNumber; byte[] scannableSafetyNumber,
private final byte[] scannableSafetyNumber; TrustLevel trustLevel,
private final TrustLevel trustLevel; Date dateAdded
private final Date dateAdded; ) {
public Identity(
final RecipientAddress recipient,
final IdentityKey identityKey,
final String safetyNumber,
final byte[] scannableSafetyNumber,
final TrustLevel trustLevel,
final Date dateAdded
) {
this.recipient = recipient;
this.identityKey = identityKey;
this.safetyNumber = safetyNumber;
this.scannableSafetyNumber = scannableSafetyNumber;
this.trustLevel = trustLevel;
this.dateAdded = dateAdded;
}
public RecipientAddress getRecipient() {
return recipient;
}
public IdentityKey getIdentityKey() {
return this.identityKey;
}
public TrustLevel getTrustLevel() {
return this.trustLevel;
}
boolean isTrusted() {
return trustLevel == TrustLevel.TRUSTED_UNVERIFIED || trustLevel == TrustLevel.TRUSTED_VERIFIED;
}
public Date getDateAdded() {
return this.dateAdded;
}
public byte[] getFingerprint() { public byte[] getFingerprint() {
return identityKey.getPublicKey().serialize(); return identityKey.getPublicKey().serialize();
} }
public String getSafetyNumber() {
return safetyNumber;
}
public byte[] getScannableSafetyNumber() {
return scannableSafetyNumber;
}
} }

View File

@ -2,21 +2,4 @@ package org.asamk.signal.manager.api;
import java.util.List; import java.util.List;
public class Message { public record Message(String messageText, List<String> attachments) {}
private final String messageText;
private final List<String> attachments;
public Message(final String messageText, final List<String> attachments) {
this.messageText = messageText;
this.attachments = attachments;
}
public String getMessageText() {
return messageText;
}
public List<String> getAttachments() {
return attachments;
}
}

View File

@ -4,23 +4,4 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult;
import java.util.List; import java.util.List;
public class SendGroupMessageResults { public record SendGroupMessageResults(long timestamp, List<SendMessageResult> results) {}
private final long timestamp;
private final List<SendMessageResult> results;
public SendGroupMessageResults(
final long timestamp, final List<SendMessageResult> results
) {
this.timestamp = timestamp;
this.results = results;
}
public long getTimestamp() {
return timestamp;
}
public List<SendMessageResult> getResults() {
return results;
}
}

View File

@ -5,23 +5,4 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class SendMessageResults { public record SendMessageResults(long timestamp, Map<RecipientIdentifier, List<SendMessageResult>> results) {}
private final long timestamp;
private final Map<RecipientIdentifier, List<SendMessageResult>> results;
public SendMessageResults(
final long timestamp, final Map<RecipientIdentifier, List<SendMessageResult>> results
) {
this.timestamp = timestamp;
this.results = results;
}
public long getTimestamp() {
return timestamp;
}
public Map<RecipientIdentifier, List<SendMessageResult>> getResults() {
return results;
}
}

View File

@ -62,29 +62,9 @@ public class ConfigurationStore {
return new Storage(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews); return new Storage(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
} }
public static final class Storage { public record Storage(
Boolean readReceipts, Boolean unidentifiedDeliveryIndicators, Boolean typingIndicators, Boolean linkPreviews
public Boolean readReceipts; ) {}
public Boolean unidentifiedDeliveryIndicators;
public Boolean typingIndicators;
public Boolean linkPreviews;
// For deserialization
private Storage() {
}
public Storage(
final Boolean readReceipts,
final Boolean unidentifiedDeliveryIndicators,
final Boolean typingIndicators,
final Boolean linkPreviews
) {
this.readReceipts = readReceipts;
this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators;
this.typingIndicators = typingIndicators;
this.linkPreviews = linkPreviews;
}
}
public interface Saver { public interface Saver {

View File

@ -262,7 +262,7 @@ public class GroupStore {
g1.blocked, g1.blocked,
g1.archived, g1.archived,
g1.members.stream() g1.members.stream()
.map(m -> new Storage.GroupV1.Member(m.getId(), null, null)) .map(m -> new Storage.GroupV1.Member(m.id(), null, null))
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
@ -274,91 +274,22 @@ public class GroupStore {
}).collect(Collectors.toList())); }).collect(Collectors.toList()));
} }
public static class Storage { public record Storage(@JsonDeserialize(using = GroupsDeserializer.class) List<Object> groups) {
@JsonDeserialize(using = GroupsDeserializer.class) private record GroupV1(
public List<Storage.Group> groups; String groupId,
String expectedV2Id,
// For deserialization String name,
public Storage() { String color,
} int messageExpirationTime,
boolean blocked,
public Storage(final List<Storage.Group> groups) { boolean archived,
this.groups = groups; @JsonDeserialize(using = MembersDeserializer.class) @JsonSerialize(using = MembersSerializer.class) List<Member> members
}
private abstract static class Group {
}
private static class GroupV1 extends Group {
public String groupId;
public String expectedV2Id;
public String name;
public String color;
public int messageExpirationTime;
public boolean blocked;
public boolean archived;
@JsonDeserialize(using = MembersDeserializer.class)
@JsonSerialize(using = MembersSerializer.class)
public List<Member> members;
// For deserialization
public GroupV1() {
}
public GroupV1(
final String groupId,
final String expectedV2Id,
final String name,
final String color,
final int messageExpirationTime,
final boolean blocked,
final boolean archived,
final List<Member> members
) { ) {
this.groupId = groupId;
this.expectedV2Id = expectedV2Id;
this.name = name;
this.color = color;
this.messageExpirationTime = messageExpirationTime;
this.blocked = blocked;
this.archived = archived;
this.members = members;
}
private static final class Member { private record Member(Long recipientId, String uuid, String number) {}
public Long recipientId; private record JsonRecipientAddress(String uuid, String number) {}
public String uuid;
public String number;
Member(Long recipientId, final String uuid, final String number) {
this.recipientId = recipientId;
this.uuid = uuid;
this.number = number;
}
}
private static final class JsonRecipientAddress {
public String uuid;
public String number;
// For deserialization
public JsonRecipientAddress() {
}
JsonRecipientAddress(final String uuid, final String number) {
this.uuid = uuid;
this.number = number;
}
}
private static class MembersSerializer extends JsonSerializer<List<Member>> { private static class MembersSerializer extends JsonSerializer<List<Member>> {
@ -366,7 +297,7 @@ public class GroupStore {
public void serialize( public void serialize(
final List<Member> value, final JsonGenerator jgen, final SerializerProvider provider final List<Member> value, final JsonGenerator jgen, final SerializerProvider provider
) throws IOException { ) throws IOException {
jgen.writeStartArray(value.size()); jgen.writeStartArray(null, value.size());
for (var address : value) { for (var address : value) {
if (address.recipientId != null) { if (address.recipientId != null) {
jgen.writeNumber(address.recipientId); jgen.writeNumber(address.recipientId);
@ -404,39 +335,19 @@ public class GroupStore {
} }
} }
private static class GroupV2 extends Group { private record GroupV2(String groupId, String masterKey, boolean blocked, boolean permissionDenied) {}
public String groupId;
public String masterKey;
public boolean blocked;
public boolean permissionDenied;
// For deserialization
private GroupV2() {
} }
public GroupV2( private static class GroupsDeserializer extends JsonDeserializer<List<Object>> {
final String groupId, final String masterKey, final boolean blocked, final boolean permissionDenied
) {
this.groupId = groupId;
this.masterKey = masterKey;
this.blocked = blocked;
this.permissionDenied = permissionDenied;
}
}
}
private static class GroupsDeserializer extends JsonDeserializer<List<Storage.Group>> {
@Override @Override
public List<Storage.Group> deserialize( public List<Object> deserialize(
JsonParser jsonParser, DeserializationContext deserializationContext JsonParser jsonParser, DeserializationContext deserializationContext
) throws IOException { ) throws IOException {
var groups = new ArrayList<Storage.Group>(); var groups = new ArrayList<>();
JsonNode node = jsonParser.getCodec().readTree(jsonParser); JsonNode node = jsonParser.getCodec().readTree(jsonParser);
for (var n : node) { for (var n : node) {
Storage.Group g; Object g;
if (n.hasNonNull("masterKey")) { if (n.hasNonNull("masterKey")) {
// a v2 group // a v2 group
g = jsonParser.getCodec().treeToValue(n, Storage.GroupV2.class); g = jsonParser.getCodec().treeToValue(n, Storage.GroupV2.class);

View File

@ -185,7 +185,7 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
} catch (IOException e) { } catch (IOException e) {
throw new AssertionError("Failed to create identities path", e); throw new AssertionError("Failed to create identities path", e);
} }
return new File(identitiesPath, String.valueOf(recipientId.getId())); return new File(identitiesPath, String.valueOf(recipientId.id()));
} }
private IdentityInfo loadIdentityLocked(final RecipientId recipientId) { private IdentityInfo loadIdentityLocked(final RecipientId recipientId) {
@ -203,9 +203,9 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
try (var inputStream = new FileInputStream(file)) { try (var inputStream = new FileInputStream(file)) {
var storage = objectMapper.readValue(inputStream, IdentityStorage.class); var storage = objectMapper.readValue(inputStream, IdentityStorage.class);
var id = new IdentityKey(Base64.getDecoder().decode(storage.getIdentityKey())); var id = new IdentityKey(Base64.getDecoder().decode(storage.identityKey()));
var trustLevel = TrustLevel.fromInt(storage.getTrustLevel()); var trustLevel = TrustLevel.fromInt(storage.trustLevel());
var added = new Date(storage.getAddedTimestamp()); var added = new Date(storage.addedTimestamp());
final var identityInfo = new IdentityInfo(recipientId, id, trustLevel, added); final var identityInfo = new IdentityInfo(recipientId, id, trustLevel, added);
cachedIdentities.put(recipientId, identityInfo); cachedIdentities.put(recipientId, identityInfo);
@ -251,32 +251,5 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
} }
} }
private static final class IdentityStorage { private record IdentityStorage(String identityKey, int trustLevel, long addedTimestamp) {}
private String identityKey;
private int trustLevel;
private long addedTimestamp;
// For deserialization
private IdentityStorage() {
}
private IdentityStorage(final String identityKey, final int trustLevel, final long addedTimestamp) {
this.identityKey = identityKey;
this.trustLevel = trustLevel;
this.addedTimestamp = addedTimestamp;
}
public String getIdentityKey() {
return identityKey;
}
public int getTrustLevel() {
return trustLevel;
}
public long getAddedTimestamp() {
return addedTimestamp;
}
}
} }

View File

@ -76,7 +76,7 @@ public class MessageCache {
return messageCachePath; return messageCachePath;
} }
var sender = String.valueOf(recipientId.getId()); var sender = String.valueOf(recipientId.id());
return new File(messageCachePath, sender.replace("/", "_")); return new File(messageCachePath, sender.replace("/", "_"));
} }

View File

@ -1,38 +1,8 @@
package org.asamk.signal.manager.storage.recipients; package org.asamk.signal.manager.storage.recipients;
public class RecipientId { public record RecipientId(long id) {
private final long id;
RecipientId(final long id) {
this.id = id;
}
public static RecipientId of(long id) { public static RecipientId of(long id) {
return new RecipientId(id); return new RecipientId(id);
} }
public long getId() {
return id;
}
@Override
public String toString() {
return "RecipientId{" + "id=" + id + '}';
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final RecipientId that = (RecipientId) o;
return id == that.id;
}
@Override
public int hashCode() {
return (int) (id ^ (id >>> 32));
}
} }

View File

@ -453,7 +453,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile
.stream() .stream()
.map(Enum::name) .map(Enum::name)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
return new Storage.Recipient(pair.getKey().getId(), return new Storage.Recipient(pair.getKey().id(),
recipient.getAddress().getNumber().orElse(null), recipient.getAddress().getNumber().orElse(null),
recipient.getAddress().getUuid().map(UUID::toString).orElse(null), recipient.getAddress().getUuid().map(UUID::toString).orElse(null),
recipient.getProfileKey() == null recipient.getProfileKey() == null
@ -479,115 +479,32 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile
} }
} }
private static class Storage { private record Storage(List<Recipient> recipients, long lastId) {
public List<Recipient> recipients; private record Recipient(
long id,
public long lastId; String number,
String uuid,
// For deserialization String profileKey,
private Storage() { String profileKeyCredential,
} Storage.Recipient.Contact contact,
Storage.Recipient.Profile profile
public Storage(final List<Recipient> recipients, final long lastId) {
this.recipients = recipients;
this.lastId = lastId;
}
private static class Recipient {
public long id;
public String number;
public String uuid;
public String profileKey;
public String profileKeyCredential;
public Contact contact;
public Profile profile;
// For deserialization
private Recipient() {
}
public Recipient(
final long id,
final String number,
final String uuid,
final String profileKey,
final String profileKeyCredential,
final Contact contact,
final Profile profile
) { ) {
this.id = id;
this.number = number;
this.uuid = uuid;
this.profileKey = profileKey;
this.profileKeyCredential = profileKeyCredential;
this.contact = contact;
this.profile = profile;
}
private static class Contact { private record Contact(
String name, String color, int messageExpirationTime, boolean blocked, boolean archived
) {}
public String name; private record Profile(
public String color; long lastUpdateTimestamp,
public int messageExpirationTime; String givenName,
public boolean blocked; String familyName,
public boolean archived; String about,
String aboutEmoji,
// For deserialization String avatarUrlPath,
public Contact() { String unidentifiedAccessMode,
} Set<String> capabilities
) {}
public Contact(
final String name,
final String color,
final int messageExpirationTime,
final boolean blocked,
final boolean archived
) {
this.name = name;
this.color = color;
this.messageExpirationTime = messageExpirationTime;
this.blocked = blocked;
this.archived = archived;
}
}
private static class Profile {
public long lastUpdateTimestamp;
public String givenName;
public String familyName;
public String about;
public String aboutEmoji;
public String avatarUrlPath;
public String unidentifiedAccessMode;
public Set<String> capabilities;
// For deserialization
private Profile() {
}
public Profile(
final long lastUpdateTimestamp,
final String givenName,
final String familyName,
final String about,
final String aboutEmoji,
final String avatarUrlPath,
final String unidentifiedAccessMode,
final Set<String> capabilities
) {
this.lastUpdateTimestamp = lastUpdateTimestamp;
this.givenName = givenName;
this.familyName = familyName;
this.about = about;
this.aboutEmoji = aboutEmoji;
this.avatarUrlPath = avatarUrlPath;
this.unidentifiedAccessMode = unidentifiedAccessMode;
this.capabilities = capabilities;
}
}
} }
} }

View File

@ -103,7 +103,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups
continue; continue;
} }
final var newKey = new Key(recipientId, key.getDeviceId(), key.distributionId); final var newKey = new Key(recipientId, key.deviceId(), key.distributionId);
final var senderKeyRecord = loadSenderKeyLocked(newKey); final var senderKeyRecord = loadSenderKeyLocked(newKey);
if (senderKeyRecord != null) { if (senderKeyRecord != null) {
continue; continue;
@ -126,7 +126,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups
} }
private List<Key> getKeysLocked(RecipientId recipientId) { private List<Key> getKeysLocked(RecipientId recipientId) {
final var files = senderKeysPath.listFiles((_file, s) -> s.startsWith(recipientId.getId() + "_")); final var files = senderKeysPath.listFiles((_file, s) -> s.startsWith(recipientId.id() + "_"));
if (files == null) { if (files == null) {
return List.of(); return List.of();
} }
@ -152,7 +152,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups
throw new AssertionError("Failed to create sender keys path", e); throw new AssertionError("Failed to create sender keys path", e);
} }
return new File(senderKeysPath, return new File(senderKeysPath,
key.getRecipientId().getId() + "_" + key.getDeviceId() + "_" + key.distributionId.toString()); key.recipientId().id() + "_" + key.deviceId() + "_" + key.distributionId.toString());
} }
private SenderKeyRecord loadSenderKeyLocked(final Key key) { private SenderKeyRecord loadSenderKeyLocked(final Key key) {
@ -212,50 +212,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups
} }
} }
private static final class Key { private record Key(RecipientId recipientId, int deviceId, UUID distributionId) {
private final RecipientId recipientId;
private final int deviceId;
private final UUID distributionId;
public Key(
final RecipientId recipientId, final int deviceId, final UUID distributionId
) {
this.recipientId = recipientId;
this.deviceId = deviceId;
this.distributionId = distributionId;
}
public RecipientId getRecipientId() {
return recipientId;
}
public int getDeviceId() {
return deviceId;
}
public UUID getDistributionId() {
return distributionId;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Key key = (Key) o;
if (deviceId != key.deviceId) return false;
if (!recipientId.equals(key.recipientId)) return false;
return distributionId.equals(key.distributionId);
}
@Override
public int hashCode() {
int result = recipientId.hashCode();
result = 31 * result + deviceId;
result = 31 * result + distributionId.hashCode();
return result;
}
} }
} }

View File

@ -87,8 +87,8 @@ public class SenderKeySharedStore {
synchronized (sharedSenderKeys) { synchronized (sharedSenderKeys) {
return sharedSenderKeys.get(distributionId) return sharedSenderKeys.get(distributionId)
.stream() .stream()
.map(k -> new SignalProtocolAddress(addressResolver.resolveRecipientAddress(k.getRecipientId()) .map(k -> new SignalProtocolAddress(addressResolver.resolveRecipientAddress(k.recipientId())
.getIdentifier(), k.getDeviceId())) .getIdentifier(), k.deviceId()))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
} }
@ -146,7 +146,7 @@ public class SenderKeySharedStore {
sharedSenderKeys.put(distributionId, new HashSet<>(entries) { sharedSenderKeys.put(distributionId, new HashSet<>(entries) {
{ {
entries.removeIf(e -> e.getRecipientId().equals(recipientId)); removeIf(e -> e.recipientId().equals(recipientId));
} }
}); });
} }
@ -163,7 +163,7 @@ public class SenderKeySharedStore {
entries.stream() entries.stream()
.map(e -> e.recipientId.equals(toBeMergedRecipientId) ? new SenderKeySharedEntry( .map(e -> e.recipientId.equals(toBeMergedRecipientId) ? new SenderKeySharedEntry(
recipientId, recipientId,
e.getDeviceId()) : e) e.deviceId()) : e)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
} }
saveLocked(); saveLocked();
@ -181,8 +181,8 @@ public class SenderKeySharedStore {
var storage = new Storage(sharedSenderKeys.entrySet().stream().flatMap(pair -> { var storage = new Storage(sharedSenderKeys.entrySet().stream().flatMap(pair -> {
final var sharedWith = pair.getValue(); final var sharedWith = pair.getValue();
return sharedWith.stream() return sharedWith.stream()
.map(entry -> new Storage.SharedSenderKey(entry.getRecipientId().getId(), .map(entry -> new Storage.SharedSenderKey(entry.recipientId().id(),
entry.getDeviceId(), entry.deviceId(),
pair.getKey().asUuid().toString())); pair.getKey().asUuid().toString()));
}).collect(Collectors.toList())); }).collect(Collectors.toList()));
@ -199,72 +199,10 @@ public class SenderKeySharedStore {
} }
} }
private static class Storage { private record Storage(List<SharedSenderKey> sharedSenderKeys) {
public List<SharedSenderKey> sharedSenderKeys; private record SharedSenderKey(long recipientId, int deviceId, String distributionId) {}
// For deserialization
private Storage() {
} }
public Storage(final List<SharedSenderKey> sharedSenderKeys) { private record SenderKeySharedEntry(RecipientId recipientId, int deviceId) {}
this.sharedSenderKeys = sharedSenderKeys;
}
private static class SharedSenderKey {
public long recipientId;
public int deviceId;
public String distributionId;
// For deserialization
private SharedSenderKey() {
}
public SharedSenderKey(final long recipientId, final int deviceId, final String distributionId) {
this.recipientId = recipientId;
this.deviceId = deviceId;
this.distributionId = distributionId;
}
}
}
private static final class SenderKeySharedEntry {
private final RecipientId recipientId;
private final int deviceId;
public SenderKeySharedEntry(
final RecipientId recipientId, final int deviceId
) {
this.recipientId = recipientId;
this.deviceId = deviceId;
}
public RecipientId getRecipientId() {
return recipientId;
}
public int getDeviceId() {
return deviceId;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final SenderKeySharedEntry that = (SenderKeySharedEntry) o;
if (deviceId != that.deviceId) return false;
return recipientId.equals(that.recipientId);
}
@Override
public int hashCode() {
int result = recipientId.hashCode();
result = 31 * result + deviceId;
return result;
}
}
} }

View File

@ -88,8 +88,8 @@ public class SessionStore implements SignalServiceSessionStore {
synchronized (cachedSessions) { synchronized (cachedSessions) {
return getKeysLocked(recipientId).stream() return getKeysLocked(recipientId).stream()
// get all sessions for recipient except main device session // get all sessions for recipient except main device session
.filter(key -> key.getDeviceId() != 1 && key.getRecipientId().equals(recipientId)) .filter(key -> key.deviceId() != 1 && key.recipientId().equals(recipientId))
.map(Key::getDeviceId) .map(Key::deviceId)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
} }
@ -155,7 +155,7 @@ public class SessionStore implements SignalServiceSessionStore {
.stream() .stream()
.flatMap(recipientId -> getKeysLocked(recipientId).stream()) .flatMap(recipientId -> getKeysLocked(recipientId).stream())
.filter(key -> isActive(this.loadSessionLocked(key))) .filter(key -> isActive(this.loadSessionLocked(key)))
.map(key -> new SignalProtocolAddress(recipientIdToNameMap.get(key.recipientId), key.getDeviceId())) .map(key -> new SignalProtocolAddress(recipientIdToNameMap.get(key.recipientId), key.deviceId()))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
} }
@ -197,7 +197,7 @@ public class SessionStore implements SignalServiceSessionStore {
if (session == null) { if (session == null) {
continue; continue;
} }
final var newKey = new Key(recipientId, key.getDeviceId()); final var newKey = new Key(recipientId, key.deviceId());
storeSessionLocked(newKey, session); storeSessionLocked(newKey, session);
} }
} }
@ -217,7 +217,7 @@ public class SessionStore implements SignalServiceSessionStore {
} }
private List<Key> getKeysLocked(RecipientId recipientId) { private List<Key> getKeysLocked(RecipientId recipientId) {
final var files = sessionsPath.listFiles((_file, s) -> s.startsWith(recipientId.getId() + "_")); final var files = sessionsPath.listFiles((_file, s) -> s.startsWith(recipientId.id() + "_"));
if (files == null) { if (files == null) {
return List.of(); return List.of();
} }
@ -249,7 +249,7 @@ public class SessionStore implements SignalServiceSessionStore {
} catch (IOException e) { } catch (IOException e) {
throw new AssertionError("Failed to create sessions path", e); throw new AssertionError("Failed to create sessions path", e);
} }
return new File(sessionsPath, key.getRecipientId().getId() + "_" + key.getDeviceId()); return new File(sessionsPath, key.recipientId().id() + "_" + key.deviceId());
} }
private SessionRecord loadSessionLocked(final Key key) { private SessionRecord loadSessionLocked(final Key key) {
@ -324,40 +324,5 @@ public class SessionStore implements SignalServiceSessionStore {
&& record.getSessionVersion() == CiphertextMessage.CURRENT_VERSION; && record.getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
} }
private static final class Key { private record Key(RecipientId recipientId, int deviceId) {}
private final RecipientId recipientId;
private final int deviceId;
public Key(final RecipientId recipientId, final int deviceId) {
this.recipientId = recipientId;
this.deviceId = deviceId;
}
public RecipientId getRecipientId() {
return recipientId;
}
public int getDeviceId() {
return deviceId;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final var key = (Key) o;
if (deviceId != key.deviceId) return false;
return recipientId.equals(key.recipientId);
}
@Override
public int hashCode() {
int result = recipientId.hashCode();
result = 31 * result + deviceId;
return result;
}
}
} }

View File

@ -65,33 +65,10 @@ public class StickerStore {
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
public static class Storage { public record Storage(List<Storage.Sticker> stickers) {
public List<Storage.Sticker> stickers; private record Sticker(String packId, String packKey, boolean installed) {
// For deserialization
private Storage() {
}
public Storage(final List<Sticker> stickers) {
this.stickers = stickers;
}
private static class Sticker {
public String packId;
public String packKey;
public boolean installed;
// For deserialization
private Sticker() {
}
public Sticker(final String packId, final String packKey, final boolean installed) {
this.packId = packId;
this.packKey = packKey;
this.installed = installed;
}
} }
} }

View File

@ -35,60 +35,59 @@ public class StickerUtils {
var pack = parseStickerPack(rootPath, zip); var pack = parseStickerPack(rootPath, zip);
if (pack.stickers == null) { if (pack.stickers() == null) {
throw new StickerPackInvalidException("Must set a 'stickers' field."); throw new StickerPackInvalidException("Must set a 'stickers' field.");
} }
if (pack.stickers.isEmpty()) { if (pack.stickers().isEmpty()) {
throw new StickerPackInvalidException("Must include stickers."); throw new StickerPackInvalidException("Must include stickers.");
} }
var stickers = new ArrayList<SignalServiceStickerManifestUpload.StickerInfo>(pack.stickers.size()); var stickers = new ArrayList<SignalServiceStickerManifestUpload.StickerInfo>(pack.stickers().size());
for (var sticker : pack.stickers) { for (var sticker : pack.stickers()) {
if (sticker.file == null) { if (sticker.file() == null) {
throw new StickerPackInvalidException("Must set a 'file' field on each sticker."); throw new StickerPackInvalidException("Must set a 'file' field on each sticker.");
} }
Pair<InputStream, Long> data; Pair<InputStream, Long> data;
try { try {
data = getInputStreamAndLength(rootPath, zip, sticker.file); data = getInputStreamAndLength(rootPath, zip, sticker.file());
} catch (IOException ignored) { } catch (IOException ignored) {
throw new StickerPackInvalidException("Could not find find " + sticker.file); throw new StickerPackInvalidException("Could not find find " + sticker.file());
} }
var contentType = sticker.contentType != null && !sticker.contentType.isEmpty() var contentType = sticker.contentType() != null && !sticker.contentType().isEmpty()
? sticker.contentType ? sticker.contentType()
: getContentType(rootPath, zip, sticker.file); : getContentType(rootPath, zip, sticker.file());
var stickerInfo = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), var stickerInfo = new SignalServiceStickerManifestUpload.StickerInfo(data.first(),
data.second(), data.second(),
Optional.fromNullable(sticker.emoji).or(""), Optional.fromNullable(sticker.emoji()).or(""),
contentType); contentType);
stickers.add(stickerInfo); stickers.add(stickerInfo);
} }
SignalServiceStickerManifestUpload.StickerInfo cover = null; SignalServiceStickerManifestUpload.StickerInfo cover = null;
if (pack.cover != null) { if (pack.cover() != null) {
if (pack.cover.file == null) { if (pack.cover().file() == null) {
throw new StickerPackInvalidException("Must set a 'file' field on the cover."); throw new StickerPackInvalidException("Must set a 'file' field on the cover.");
} }
Pair<InputStream, Long> data; Pair<InputStream, Long> data;
try { try {
data = getInputStreamAndLength(rootPath, zip, pack.cover.file); data = getInputStreamAndLength(rootPath, zip, pack.cover().file());
} catch (IOException ignored) { } catch (IOException ignored) {
throw new StickerPackInvalidException("Could not find find " + pack.cover.file); throw new StickerPackInvalidException("Could not find find " + pack.cover().file());
} }
var contentType = pack.cover.contentType != null && !pack.cover.contentType.isEmpty() var contentType = pack.cover().contentType() != null && !pack.cover().contentType().isEmpty() ? pack.cover()
? pack.cover.contentType .contentType() : getContentType(rootPath, zip, pack.cover().file());
: getContentType(rootPath, zip, pack.cover.file);
cover = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), cover = new SignalServiceStickerManifestUpload.StickerInfo(data.first(),
data.second(), data.second(),
Optional.fromNullable(pack.cover.emoji).or(""), Optional.fromNullable(pack.cover().emoji()).or(""),
contentType); contentType);
} }
return new SignalServiceStickerManifestUpload(pack.title, pack.author, cover, stickers); return new SignalServiceStickerManifestUpload(pack.title(), pack.author(), cover, stickers);
} }
private static JsonStickerPack parseStickerPack(String rootPath, ZipFile zip) throws IOException { private static JsonStickerPack parseStickerPack(String rootPath, ZipFile zip) throws IOException {

View File

@ -26,11 +26,11 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
final var object = new HashMap<String, Object>(); final var object = new HashMap<String, Object>();
if (exception != null) { if (exception != null) {
object.put("error", new JsonError(exception)); object.put("error", JsonError.from(exception));
} }
if (envelope != null) { if (envelope != null) {
object.put("envelope", new JsonMessageEnvelope(envelope, content, exception, m)); object.put("envelope", JsonMessageEnvelope.from(envelope, content, exception, m));
} }
jsonWriter.write(object); jsonWriter.write(object);

View File

@ -643,7 +643,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
var group = m.getGroup(groupId); var group = m.getGroup(groupId);
if (group != null) { if (group != null) {
writer.println("Name: {}", group.getTitle()); writer.println("Name: {}", group.title());
} else { } else {
writer.println("Name: <Unknown group>"); writer.println("Name: <Unknown group>");
} }

View File

@ -66,21 +66,5 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand {
} }
} }
private static final class JsonUserStatus { private record JsonUserStatus(String recipient, String number, String uuid, boolean isRegistered) {}
public final String recipient;
public final String number;
public final String uuid;
public final boolean isRegistered;
public JsonUserStatus(String recipient, String number, String uuid, boolean isRegistered) {
this.recipient = recipient;
this.number = number;
this.uuid = uuid;
this.isRegistered = isRegistered;
}
}
} }

View File

@ -69,7 +69,7 @@ public class JoinGroupCommand implements JsonRpcLocalCommand {
writer.println("Joined group \"{}\"", newGroupId.toBase64()); writer.println("Joined group \"{}\"", newGroupId.toBase64());
} }
} }
handleSendMessageResults(results.second().getResults()); handleSendMessageResults(results.second().results());
} catch (GroupPatchNotAcceptedException e) { } catch (GroupPatchNotAcceptedException e) {
throw new UserErrorException("Failed to join group, maybe already a member"); throw new UserErrorException("Failed to join group, maybe already a member");
} catch (IOException e) { } catch (IOException e) {

View File

@ -16,8 +16,7 @@ import java.util.Map;
public interface JsonRpcLocalCommand extends JsonRpcCommand<Map<String, Object>>, LocalCommand { public interface JsonRpcLocalCommand extends JsonRpcCommand<Map<String, Object>>, LocalCommand {
default TypeReference<Map<String, Object>> getRequestType() { default TypeReference<Map<String, Object>> getRequestType() {
return new TypeReference<>() { return new TypeReference<>() {};
};
} }
default void handleCommand( default void handleCommand(

View File

@ -54,26 +54,5 @@ public class ListContactsCommand implements JsonRpcLocalCommand {
} }
} }
private static final class JsonContact { private record JsonContact(String number, String uuid, String name, boolean isBlocked, int messageExpirationTime) {}
public final String number;
public final String uuid;
public final String name;
public final boolean isBlocked;
public final int messageExpirationTime;
private JsonContact(
final String number,
final String uuid,
final String name,
final boolean isBlocked,
final int messageExpirationTime
) {
this.number = number;
this.uuid = uuid;
this.name = name;
this.isBlocked = isBlocked;
this.messageExpirationTime = messageExpirationTime;
}
}
} }

View File

@ -45,36 +45,21 @@ public class ListDevicesCommand implements JsonRpcLocalCommand {
if (outputWriter instanceof PlainTextWriter writer) { if (outputWriter instanceof PlainTextWriter writer) {
for (var d : devices) { for (var d : devices) {
writer.println("- Device {}{}:", d.getId(), (d.isThisDevice() ? " (this device)" : "")); writer.println("- Device {}{}:", d.id(), (d.isThisDevice() ? " (this device)" : ""));
writer.indent(w -> { writer.indent(w -> {
w.println("Name: {}", d.getName()); w.println("Name: {}", d.name());
w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated())); w.println("Created: {}", DateUtils.formatTimestamp(d.created()));
w.println("Last seen: {}", DateUtils.formatTimestamp(d.getLastSeen())); w.println("Last seen: {}", DateUtils.formatTimestamp(d.lastSeen()));
}); });
} }
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;
final var jsonDevices = devices.stream() final var jsonDevices = devices.stream()
.map(d -> new JsonDevice(d.getId(), d.getName(), d.getCreated(), d.getLastSeen())) .map(d -> new JsonDevice(d.id(), d.name(), d.created(), d.lastSeen()))
.collect(Collectors.toList()); .collect(Collectors.toList());
writer.write(jsonDevices); writer.write(jsonDevices);
} }
} }
private static final class JsonDevice { private record JsonDevice(long id, String name, long createdTimestamp, long lastSeenTimestamp) {}
public final long id;
public final String name;
public final long createdTimestamp;
public final long lastSeenTimestamp;
private JsonDevice(
final long id, final String name, final long createdTimestamp, final long lastSeenTimestamp
) {
this.id = id;
this.name = name;
this.createdTimestamp = createdTimestamp;
this.lastSeenTimestamp = lastSeenTimestamp;
}
}
} }

View File

@ -50,25 +50,25 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
PlainTextWriter writer, Group group, boolean detailed PlainTextWriter writer, Group group, boolean detailed
) { ) {
if (detailed) { if (detailed) {
final var groupInviteLink = group.getGroupInviteLinkUrl(); final var groupInviteLink = group.groupInviteLinkUrl();
writer.println( writer.println(
"Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}", "Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}",
group.getGroupId().toBase64(), group.groupId().toBase64(),
group.getTitle(), group.title(),
group.getDescription(), group.description(),
group.isMember(), group.isMember(),
group.isBlocked(), group.isBlocked(),
resolveMembers(group.getMembers()), resolveMembers(group.members()),
resolveMembers(group.getPendingMembers()), resolveMembers(group.pendingMembers()),
resolveMembers(group.getRequestingMembers()), resolveMembers(group.requestingMembers()),
resolveMembers(group.getAdminMembers()), resolveMembers(group.adminMembers()),
group.getMessageExpirationTimer() == 0 ? "disabled" : group.getMessageExpirationTimer() + "s", group.messageExpirationTimer() == 0 ? "disabled" : group.messageExpirationTimer() + "s",
groupInviteLink == null ? '-' : groupInviteLink.getUrl()); groupInviteLink == null ? '-' : groupInviteLink.getUrl());
} else { } else {
writer.println("Id: {} Name: {} Active: {} Blocked: {}", writer.println("Id: {} Name: {} Active: {} Blocked: {}",
group.getGroupId().toBase64(), group.groupId().toBase64(),
group.getTitle(), group.title(),
group.isMember(), group.isMember(),
group.isBlocked()); group.isBlocked());
} }
@ -83,21 +83,21 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
if (outputWriter instanceof JsonWriter jsonWriter) { if (outputWriter instanceof JsonWriter jsonWriter) {
var jsonGroups = groups.stream().map(group -> { var jsonGroups = groups.stream().map(group -> {
final var groupInviteLink = group.getGroupInviteLinkUrl(); final var groupInviteLink = group.groupInviteLinkUrl();
return new JsonGroup(group.getGroupId().toBase64(), return new JsonGroup(group.groupId().toBase64(),
group.getTitle(), group.title(),
group.getDescription(), group.description(),
group.isMember(), group.isMember(),
group.isBlocked(), group.isBlocked(),
group.getMessageExpirationTimer(), group.messageExpirationTimer(),
resolveJsonMembers(group.getMembers()), resolveJsonMembers(group.members()),
resolveJsonMembers(group.getPendingMembers()), resolveJsonMembers(group.pendingMembers()),
resolveJsonMembers(group.getRequestingMembers()), resolveJsonMembers(group.requestingMembers()),
resolveJsonMembers(group.getAdminMembers()), resolveJsonMembers(group.adminMembers()),
group.getPermissionAddMember().name(), group.permissionAddMember().name(),
group.getPermissionEditDetails().name(), group.permissionEditDetails().name(),
group.getPermissionSendMessage().name(), group.permissionSendMessage().name(),
groupInviteLink == null ? null : groupInviteLink.getUrl()); groupInviteLink == null ? null : groupInviteLink.getUrl());
}).collect(Collectors.toList()); }).collect(Collectors.toList());
@ -111,66 +111,22 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
} }
} }
private static final class JsonGroup { private record JsonGroup(
public final String id;
public final String name;
public final String description;
public final boolean isMember;
public final boolean isBlocked;
public final int messageExpirationTime;
public final Set<JsonGroupMember> members;
public final Set<JsonGroupMember> pendingMembers;
public final Set<JsonGroupMember> requestingMembers;
public final Set<JsonGroupMember> admins;
public final String permissionAddMember;
public final String permissionEditDetails;
public final String permissionSendMessage;
public final String groupInviteLink;
public JsonGroup(
String id, String id,
String name, String name,
String description, String description,
boolean isMember, boolean isMember,
boolean isBlocked, boolean isBlocked,
final int messageExpirationTime, int messageExpirationTime,
Set<JsonGroupMember> members, Set<JsonGroupMember> members,
Set<JsonGroupMember> pendingMembers, Set<JsonGroupMember> pendingMembers,
Set<JsonGroupMember> requestingMembers, Set<JsonGroupMember> requestingMembers,
Set<JsonGroupMember> admins, Set<JsonGroupMember> admins,
final String permissionAddMember, String permissionAddMember,
final String permissionEditDetails, String permissionEditDetails,
final String permissionSendMessage, String permissionSendMessage,
String groupInviteLink String groupInviteLink
) { ) {}
this.id = id;
this.name = name;
this.description = description;
this.isMember = isMember;
this.isBlocked = isBlocked;
this.messageExpirationTime = messageExpirationTime;
this.members = members; private record JsonGroupMember(String number, String uuid) {}
this.pendingMembers = pendingMembers;
this.requestingMembers = requestingMembers;
this.admins = admins;
this.permissionAddMember = permissionAddMember;
this.permissionEditDetails = permissionEditDetails;
this.permissionSendMessage = permissionSendMessage;
this.groupInviteLink = groupInviteLink;
}
}
private static final class JsonGroupMember {
public final String number;
public final String uuid;
private JsonGroupMember(final String number, final String uuid) {
this.number = number;
this.uuid = uuid;
}
}
} }

View File

@ -30,12 +30,12 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
} }
private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, Identity theirId) { private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, Identity theirId) {
final SignalServiceAddress address = theirId.getRecipient().toSignalServiceAddress(); final SignalServiceAddress address = theirId.recipient().toSignalServiceAddress();
var digits = Util.formatSafetyNumber(theirId.getSafetyNumber()); var digits = Util.formatSafetyNumber(theirId.safetyNumber());
writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}",
address.getNumber().orNull(), address.getNumber().orNull(),
theirId.getTrustLevel(), theirId.trustLevel(),
theirId.getDateAdded(), theirId.dateAdded(),
Hex.toString(theirId.getFingerprint()), Hex.toString(theirId.getFingerprint()),
digits); digits);
} }
@ -66,9 +66,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;
final var jsonIdentities = identities.stream().map(id -> { final var jsonIdentities = identities.stream().map(id -> {
final var address = id.getRecipient().toSignalServiceAddress(); final var address = id.recipient().toSignalServiceAddress();
var safetyNumber = Util.formatSafetyNumber(id.getSafetyNumber()); var safetyNumber = Util.formatSafetyNumber(id.safetyNumber());
var scannableSafetyNumber = id.getScannableSafetyNumber(); var scannableSafetyNumber = id.scannableSafetyNumber();
return new JsonIdentity(address.getNumber().orNull(), return new JsonIdentity(address.getNumber().orNull(),
address.getUuid().toString(), address.getUuid().toString(),
Hex.toString(id.getFingerprint()), Hex.toString(id.getFingerprint()),
@ -76,40 +76,21 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
scannableSafetyNumber == null scannableSafetyNumber == null
? null ? null
: Base64.getEncoder().encodeToString(scannableSafetyNumber), : Base64.getEncoder().encodeToString(scannableSafetyNumber),
id.getTrustLevel().name(), id.trustLevel().name(),
id.getDateAdded().getTime()); id.dateAdded().getTime());
}).collect(Collectors.toList()); }).collect(Collectors.toList());
writer.write(jsonIdentities); writer.write(jsonIdentities);
} }
} }
private static final class JsonIdentity { private record JsonIdentity(
String number,
public final String number; String uuid,
public final String uuid; String fingerprint,
public final String fingerprint; String safetyNumber,
public final String safetyNumber; String scannableSafetyNumber,
public final String scannableSafetyNumber; String trustLevel,
public final String trustLevel; long addedTimestamp
public final long addedTimestamp; ) {}
private JsonIdentity(
final String number,
final String uuid,
final String fingerprint,
final String safetyNumber,
final String scannableSafetyNumber,
final String trustLevel,
final long addedTimestamp
) {
this.number = number;
this.uuid = uuid;
this.fingerprint = fingerprint;
this.safetyNumber = safetyNumber;
this.scannableSafetyNumber = scannableSafetyNumber;
this.trustLevel = trustLevel;
this.addedTimestamp = addedTimestamp;
}
}
} }

View File

@ -55,9 +55,9 @@ public class QuitGroupCommand implements JsonRpcLocalCommand {
try { try {
try { try {
final var results = m.quitGroup(groupId, groupAdmins); final var results = m.quitGroup(groupId, groupAdmins);
final var timestamp = results.getTimestamp(); final var timestamp = results.timestamp();
outputResult(outputWriter, timestamp); outputResult(outputWriter, timestamp);
handleSendMessageResults(results.getResults()); handleSendMessageResults(results.results());
} catch (NotAGroupMemberException e) { } catch (NotAGroupMemberException e) {
logger.info("User is not a group member"); logger.info("User is not a group member");
} }

View File

@ -61,19 +61,19 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand {
if (outputWriter instanceof JsonWriter jsonWriter) { if (outputWriter instanceof JsonWriter jsonWriter) {
dbusconnection.addSigHandler(Signal.MessageReceived.class, signal, messageReceived -> { dbusconnection.addSigHandler(Signal.MessageReceived.class, signal, messageReceived -> {
var envelope = new JsonMessageEnvelope(messageReceived); var envelope = JsonMessageEnvelope.from(messageReceived);
final var object = Map.of("envelope", envelope); final var object = Map.of("envelope", envelope);
jsonWriter.write(object); jsonWriter.write(object);
}); });
dbusconnection.addSigHandler(Signal.ReceiptReceived.class, signal, receiptReceived -> { dbusconnection.addSigHandler(Signal.ReceiptReceived.class, signal, receiptReceived -> {
var envelope = new JsonMessageEnvelope(receiptReceived); var envelope = JsonMessageEnvelope.from(receiptReceived);
final var object = Map.of("envelope", envelope); final var object = Map.of("envelope", envelope);
jsonWriter.write(object); jsonWriter.write(object);
}); });
dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, signal, syncReceived -> { dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, signal, syncReceived -> {
var envelope = new JsonMessageEnvelope(syncReceived); var envelope = JsonMessageEnvelope.from(syncReceived);
final var object = Map.of("envelope", envelope); final var object = Map.of("envelope", envelope);
jsonWriter.write(object); jsonWriter.write(object);
}); });

View File

@ -56,8 +56,8 @@ public class RemoteDeleteCommand implements JsonRpcLocalCommand {
try { try {
final var results = m.sendRemoteDeleteMessage(targetTimestamp, recipientIdentifiers); final var results = m.sendRemoteDeleteMessage(targetTimestamp, recipientIdentifiers);
outputResult(outputWriter, results.getTimestamp()); outputResult(outputWriter, results.timestamp());
ErrorUtils.handleSendMessageResults(results.getResults()); ErrorUtils.handleSendMessageResults(results.results());
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage()); throw new UserErrorException(e.getMessage());
} catch (IOException e) { } catch (IOException e) {

View File

@ -102,8 +102,8 @@ public class SendCommand implements JsonRpcLocalCommand {
try { try {
var results = m.sendMessage(new Message(messageText, attachments), recipientIdentifiers); var results = m.sendMessage(new Message(messageText, attachments), recipientIdentifiers);
outputResult(outputWriter, results.getTimestamp()); outputResult(outputWriter, results.timestamp());
ErrorUtils.handleSendMessageResults(results.getResults()); ErrorUtils.handleSendMessageResults(results.results());
} catch (AttachmentInvalidException | IOException e) { } catch (AttachmentInvalidException | IOException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")", e); .getSimpleName() + ")", e);

View File

@ -72,8 +72,8 @@ public class SendReactionCommand implements JsonRpcLocalCommand {
CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()), CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
targetTimestamp, targetTimestamp,
recipientIdentifiers); recipientIdentifiers);
outputResult(outputWriter, results.getTimestamp()); outputResult(outputWriter, results.timestamp());
ErrorUtils.handleSendMessageResults(results.getResults()); ErrorUtils.handleSendMessageResults(results.results());
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
throw new UserErrorException(e.getMessage()); throw new UserErrorException(e.getMessage());
} catch (IOException e) { } catch (IOException e) {

View File

@ -127,8 +127,8 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand {
var results = m.createGroup(groupName, var results = m.createGroup(groupName,
groupMembers, groupMembers,
groupAvatar == null ? null : new File(groupAvatar)); groupAvatar == null ? null : new File(groupAvatar));
timestamp = results.second().getTimestamp(); timestamp = results.second().timestamp();
ErrorUtils.handleSendMessageResults(results.second().getResults()); ErrorUtils.handleSendMessageResults(results.second().results());
groupId = results.first(); groupId = results.first();
groupName = null; groupName = null;
groupMembers = null; groupMembers = null;
@ -154,8 +154,8 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand {
: groupSendMessagesPermission == GroupPermission.ONLY_ADMINS) : groupSendMessagesPermission == GroupPermission.ONLY_ADMINS)
.build()); .build());
if (results != null) { if (results != null) {
timestamp = results.getTimestamp(); timestamp = results.timestamp();
ErrorUtils.handleSendMessageResults(results.getResults()); ErrorUtils.handleSendMessageResults(results.results());
} }
outputResult(outputWriter, timestamp, isNewGroup ? groupId : null); outputResult(outputWriter, timestamp, isNewGroup ? groupId : null);
} catch (AttachmentInvalidException e) { } catch (AttachmentInvalidException e) {

View File

@ -321,9 +321,9 @@ public class DbusManagerImpl implements Manager {
final Message message, final Set<RecipientIdentifier> recipients final Message message, final Set<RecipientIdentifier> recipients
) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
return handleMessage(recipients, return handleMessage(recipients,
numbers -> signal.sendMessage(message.getMessageText(), message.getAttachments(), numbers), numbers -> signal.sendMessage(message.messageText(), message.attachments(), numbers),
() -> signal.sendNoteToSelfMessage(message.getMessageText(), message.getAttachments()), () -> signal.sendNoteToSelfMessage(message.messageText(), message.attachments()),
groupId -> signal.sendGroupMessage(message.getMessageText(), message.getAttachments(), groupId)); groupId -> signal.sendGroupMessage(message.messageText(), message.attachments(), groupId));
} }
@Override @Override

View File

@ -153,8 +153,8 @@ public class DbusSignalImpl implements Signal {
.map(RecipientIdentifier.class::cast) .map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
return results.getTimestamp(); return results.timestamp();
} catch (AttachmentInvalidException e) { } catch (AttachmentInvalidException e) {
throw new Error.AttachmentInvalid(e.getMessage()); throw new Error.AttachmentInvalid(e.getMessage());
} catch (IOException e) { } catch (IOException e) {
@ -182,8 +182,8 @@ public class DbusSignalImpl implements Signal {
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast) .map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
return results.getTimestamp(); return results.timestamp();
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
@ -198,8 +198,8 @@ public class DbusSignalImpl implements Signal {
try { try {
final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, final var results = m.sendRemoteDeleteMessage(targetSentTimestamp,
Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
return results.getTimestamp(); return results.timestamp();
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
@ -236,8 +236,8 @@ public class DbusSignalImpl implements Signal {
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast) .map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
return results.getTimestamp(); return results.timestamp();
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
@ -303,8 +303,8 @@ public class DbusSignalImpl implements Signal {
try { try {
final var results = m.sendMessage(new Message(message, attachments), final var results = m.sendMessage(new Message(message, attachments),
Set.of(RecipientIdentifier.NoteToSelf.INSTANCE)); Set.of(RecipientIdentifier.NoteToSelf.INSTANCE));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
return results.getTimestamp(); return results.timestamp();
} catch (AttachmentInvalidException e) { } catch (AttachmentInvalidException e) {
throw new Error.AttachmentInvalid(e.getMessage()); throw new Error.AttachmentInvalid(e.getMessage());
} catch (IOException e) { } catch (IOException e) {
@ -318,7 +318,7 @@ public class DbusSignalImpl implements Signal {
public void sendEndSessionMessage(final List<String> recipients) { public void sendEndSessionMessage(final List<String> recipients) {
try { try {
final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getSelfNumber())); final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getSelfNumber()));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
} }
@ -329,8 +329,8 @@ public class DbusSignalImpl implements Signal {
try { try {
var results = m.sendMessage(new Message(message, attachments), var results = m.sendMessage(new Message(message, attachments),
Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
return results.getTimestamp(); return results.timestamp();
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
@ -354,8 +354,8 @@ public class DbusSignalImpl implements Signal {
getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()), getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
targetSentTimestamp, targetSentTimestamp,
Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
return results.getTimestamp(); return results.timestamp();
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
} catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) {
@ -420,7 +420,7 @@ public class DbusSignalImpl implements Signal {
var groups = m.getGroups(); var groups = m.getGroups();
var ids = new ArrayList<byte[]>(groups.size()); var ids = new ArrayList<byte[]>(groups.size());
for (var group : groups) { for (var group : groups) {
ids.add(group.getGroupId().serialize()); ids.add(group.groupId().serialize());
} }
return ids; return ids;
} }
@ -444,10 +444,10 @@ public class DbusSignalImpl implements Signal {
@Override @Override
public String getGroupName(final byte[] groupId) { public String getGroupName(final byte[] groupId) {
var group = m.getGroup(getGroupId(groupId)); var group = m.getGroup(getGroupId(groupId));
if (group == null || group.getTitle() == null) { if (group == null || group.title() == null) {
return ""; return "";
} else { } else {
return group.getTitle(); return group.title();
} }
} }
@ -457,7 +457,7 @@ public class DbusSignalImpl implements Signal {
if (group == null) { if (group == null) {
return List.of(); return List.of();
} else { } else {
final var members = group.getMembers(); final var members = group.members();
return getRecipientStrings(members); return getRecipientStrings(members);
} }
} }
@ -478,7 +478,7 @@ public class DbusSignalImpl implements Signal {
final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getSelfNumber()); final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getSelfNumber());
if (groupId == null) { if (groupId == null) {
final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar)); final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar));
checkSendMessageResults(results.second().getTimestamp(), results.second().getResults()); checkSendMessageResults(results.second().timestamp(), results.second().results());
return results.first().serialize(); return results.first().serialize();
} else { } else {
final var results = m.updateGroup(getGroupId(groupId), final var results = m.updateGroup(getGroupId(groupId),
@ -488,7 +488,7 @@ public class DbusSignalImpl implements Signal {
.withAvatarFile(avatar == null ? null : new File(avatar)) .withAvatarFile(avatar == null ? null : new File(avatar))
.build()); .build());
if (results != null) { if (results != null) {
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.timestamp(), results.results());
} }
return groupId; return groupId;
} }
@ -600,7 +600,7 @@ public class DbusSignalImpl implements Signal {
// all numbers the system knows // all numbers the system knows
@Override @Override
public List<String> listNumbers() { public List<String> listNumbers() {
return Stream.concat(m.getIdentities().stream().map(Identity::getRecipient), return Stream.concat(m.getIdentities().stream().map(Identity::recipient),
m.getContacts().stream().map(Pair::first)) m.getContacts().stream().map(Pair::first))
.map(a -> a.getNumber().orElse(null)) .map(a -> a.getNumber().orElse(null))
.filter(Objects::nonNull) .filter(Objects::nonNull)
@ -620,7 +620,7 @@ public class DbusSignalImpl implements Signal {
} }
// Try profiles if no contact name was found // Try profiles if no contact name was found
for (var identity : m.getIdentities()) { for (var identity : m.getIdentities()) {
final var address = identity.getRecipient(); final var address = identity.recipient();
var number = address.getNumber().orElse(null); var number = address.getNumber().orElse(null);
if (number != null) { if (number != null) {
Profile profile = null; Profile profile = null;
@ -835,7 +835,7 @@ public class DbusSignalImpl implements Signal {
if (d.isThisDevice()) { if (d.isThisDevice()) {
thisDevice = new DBusPath(deviceObjectPath); thisDevice = new DBusPath(deviceObjectPath);
} }
this.devices.add(new StructDevice(new DBusPath(deviceObjectPath), d.getId(), emptyIfNull(d.getName()))); this.devices.add(new StructDevice(new DBusPath(deviceObjectPath), d.id(), emptyIfNull(d.name())));
}); });
} }
@ -862,15 +862,15 @@ public class DbusSignalImpl implements Signal {
unExportGroups(); unExportGroups();
groups.forEach(g -> { groups.forEach(g -> {
final var object = new DbusSignalGroupImpl(g.getGroupId()); final var object = new DbusSignalGroupImpl(g.groupId());
try { try {
connection.exportObject(object); connection.exportObject(object);
} catch (DBusException e) { } catch (DBusException e) {
e.printStackTrace(); e.printStackTrace();
} }
this.groups.add(new StructGroup(new DBusPath(object.getObjectPath()), this.groups.add(new StructGroup(new DBusPath(object.getObjectPath()),
g.getGroupId().serialize(), g.groupId().serialize(),
emptyIfNull(g.getTitle()))); emptyIfNull(g.title())));
}); });
} }
@ -885,22 +885,22 @@ public class DbusSignalImpl implements Signal {
public DbusSignalDeviceImpl(final org.asamk.signal.manager.api.Device device) { public DbusSignalDeviceImpl(final org.asamk.signal.manager.api.Device device) {
super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Device", super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Device",
List.of(new DbusProperty<>("Id", device::getId), List.of(new DbusProperty<>("Id", device::id),
new DbusProperty<>("Name", () -> emptyIfNull(device.getName()), this::setDeviceName), new DbusProperty<>("Name", () -> emptyIfNull(device.name()), this::setDeviceName),
new DbusProperty<>("Created", device::getCreated), new DbusProperty<>("Created", device::created),
new DbusProperty<>("LastSeen", device::getLastSeen)))); new DbusProperty<>("LastSeen", device::lastSeen))));
this.device = device; this.device = device;
} }
@Override @Override
public String getObjectPath() { public String getObjectPath() {
return getDeviceObjectPath(objectPath, device.getId()); return getDeviceObjectPath(objectPath, device.id());
} }
@Override @Override
public void removeDevice() throws Error.Failure { public void removeDevice() throws Error.Failure {
try { try {
m.removeLinkedDevices(device.getId()); m.removeLinkedDevices(device.id());
updateDevices(); updateDevices();
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
@ -929,36 +929,36 @@ public class DbusSignalImpl implements Signal {
this.groupId = groupId; this.groupId = groupId;
super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Group", super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Group",
List.of(new DbusProperty<>("Id", groupId::serialize), List.of(new DbusProperty<>("Id", groupId::serialize),
new DbusProperty<>("Name", () -> emptyIfNull(getGroup().getTitle()), this::setGroupName), new DbusProperty<>("Name", () -> emptyIfNull(getGroup().title()), this::setGroupName),
new DbusProperty<>("Description", new DbusProperty<>("Description",
() -> emptyIfNull(getGroup().getDescription()), () -> emptyIfNull(getGroup().description()),
this::setGroupDescription), this::setGroupDescription),
new DbusProperty<>("Avatar", this::setGroupAvatar), new DbusProperty<>("Avatar", this::setGroupAvatar),
new DbusProperty<>("IsBlocked", () -> getGroup().isBlocked(), this::setIsBlocked), new DbusProperty<>("IsBlocked", () -> getGroup().isBlocked(), this::setIsBlocked),
new DbusProperty<>("IsMember", () -> getGroup().isMember()), new DbusProperty<>("IsMember", () -> getGroup().isMember()),
new DbusProperty<>("IsAdmin", () -> getGroup().isAdmin()), new DbusProperty<>("IsAdmin", () -> getGroup().isAdmin()),
new DbusProperty<>("MessageExpirationTimer", new DbusProperty<>("MessageExpirationTimer",
() -> getGroup().getMessageExpirationTimer(), () -> getGroup().messageExpirationTimer(),
this::setMessageExpirationTime), this::setMessageExpirationTime),
new DbusProperty<>("Members", new DbusProperty<>("Members",
() -> new Variant<>(getRecipientStrings(getGroup().getMembers()), "as")), () -> new Variant<>(getRecipientStrings(getGroup().members()), "as")),
new DbusProperty<>("PendingMembers", new DbusProperty<>("PendingMembers",
() -> new Variant<>(getRecipientStrings(getGroup().getPendingMembers()), "as")), () -> new Variant<>(getRecipientStrings(getGroup().pendingMembers()), "as")),
new DbusProperty<>("RequestingMembers", new DbusProperty<>("RequestingMembers",
() -> new Variant<>(getRecipientStrings(getGroup().getRequestingMembers()), "as")), () -> new Variant<>(getRecipientStrings(getGroup().requestingMembers()), "as")),
new DbusProperty<>("Admins", new DbusProperty<>("Admins",
() -> new Variant<>(getRecipientStrings(getGroup().getAdminMembers()), "as")), () -> new Variant<>(getRecipientStrings(getGroup().adminMembers()), "as")),
new DbusProperty<>("PermissionAddMember", new DbusProperty<>("PermissionAddMember",
() -> getGroup().getPermissionAddMember().name(), () -> getGroup().permissionAddMember().name(),
this::setGroupPermissionAddMember), this::setGroupPermissionAddMember),
new DbusProperty<>("PermissionEditDetails", new DbusProperty<>("PermissionEditDetails",
() -> getGroup().getPermissionEditDetails().name(), () -> getGroup().permissionEditDetails().name(),
this::setGroupPermissionEditDetails), this::setGroupPermissionEditDetails),
new DbusProperty<>("PermissionSendMessage", new DbusProperty<>("PermissionSendMessage",
() -> getGroup().getPermissionSendMessage().name(), () -> getGroup().permissionSendMessage().name(),
this::setGroupPermissionSendMessage), this::setGroupPermissionSendMessage),
new DbusProperty<>("GroupInviteLink", () -> { new DbusProperty<>("GroupInviteLink", () -> {
final var groupInviteLinkUrl = getGroup().getGroupInviteLinkUrl(); final var groupInviteLinkUrl = getGroup().groupInviteLinkUrl();
return groupInviteLinkUrl == null ? "" : groupInviteLinkUrl.getUrl(); return groupInviteLinkUrl == null ? "" : groupInviteLinkUrl.getUrl();
})))); }))));
} }

View File

@ -1,43 +1,25 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
class JsonAttachment { record JsonAttachment(String contentType, String filename, String id, Long size) {
@JsonProperty
final String contentType;
@JsonProperty
final String filename;
@JsonProperty
final String id;
@JsonProperty
final Long size;
JsonAttachment(SignalServiceAttachment attachment) {
this.contentType = attachment.getContentType();
static JsonAttachment from(SignalServiceAttachment attachment) {
if (attachment.isPointer()) { if (attachment.isPointer()) {
final var pointer = attachment.asPointer(); final var pointer = attachment.asPointer();
this.id = pointer.getRemoteId().toString(); final var id = pointer.getRemoteId().toString();
this.filename = pointer.getFileName().orNull(); final var filename = pointer.getFileName().orNull();
this.size = pointer.getSize().transform(Integer::longValue).orNull(); final var size = pointer.getSize().transform(Integer::longValue).orNull();
return new JsonAttachment(attachment.getContentType(), filename, id, size);
} else { } else {
final var stream = attachment.asStream(); final var stream = attachment.asStream();
this.id = null; final var filename = stream.getFileName().orNull();
this.filename = stream.getFileName().orNull(); final var size = stream.getLength();
this.size = stream.getLength(); return new JsonAttachment(attachment.getContentType(), filename, null, size);
} }
} }
JsonAttachment(String filename) { static JsonAttachment from(String filename) {
this.filename = filename; return new JsonAttachment(filename, null, null, null);
this.contentType = null;
this.id = null;
this.size = null;
} }
} }

View File

@ -1,7 +1,6 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; import org.whispersystems.signalservice.api.messages.calls.AnswerMessage;
import org.whispersystems.signalservice.api.messages.calls.BusyMessage; import org.whispersystems.signalservice.api.messages.calls.BusyMessage;
@ -12,33 +11,19 @@ import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMess
import java.util.List; import java.util.List;
class JsonCallMessage { record JsonCallMessage(
@JsonInclude(JsonInclude.Include.NON_NULL) OfferMessage offerMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) AnswerMessage answerMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) BusyMessage busyMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) HangupMessage hangupMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) List<IceUpdateMessage> iceUpdateMessages
) {
@JsonProperty static JsonCallMessage from(SignalServiceCallMessage callMessage) {
@JsonInclude(JsonInclude.Include.NON_NULL) return new JsonCallMessage(callMessage.getOfferMessage().orNull(),
final OfferMessage offerMessage; callMessage.getAnswerMessage().orNull(),
callMessage.getBusyMessage().orNull(),
@JsonProperty callMessage.getHangupMessage().orNull(),
@JsonInclude(JsonInclude.Include.NON_NULL) callMessage.getIceUpdateMessages().orNull());
final AnswerMessage answerMessage;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final BusyMessage busyMessage;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final HangupMessage hangupMessage;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<IceUpdateMessage> iceUpdateMessages;
JsonCallMessage(SignalServiceCallMessage callMessage) {
this.offerMessage = callMessage.getOfferMessage().orNull();
this.answerMessage = callMessage.getAnswerMessage().orNull();
this.busyMessage = callMessage.getBusyMessage().orNull();
this.hangupMessage = callMessage.getHangupMessage().orNull();
this.iceUpdateMessages = callMessage.getIceUpdateMessages().orNull();
} }
} }

View File

@ -1,48 +1,29 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.util.Util; import org.asamk.signal.util.Util;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
public class JsonContactAddress { public record JsonContactAddress(
SharedContact.PostalAddress.Type type,
String label,
String street,
String pobox,
String neighborhood,
String city,
String region,
String postcode,
String country
) {
@JsonProperty static JsonContactAddress from(SharedContact.PostalAddress address) {
private final SharedContact.PostalAddress.Type type; return new JsonContactAddress(address.getType(),
Util.getStringIfNotBlank(address.getLabel()),
@JsonProperty Util.getStringIfNotBlank(address.getStreet()),
private final String label; Util.getStringIfNotBlank(address.getPobox()),
Util.getStringIfNotBlank(address.getNeighborhood()),
@JsonProperty Util.getStringIfNotBlank(address.getCity()),
private final String street; Util.getStringIfNotBlank(address.getRegion()),
Util.getStringIfNotBlank(address.getPostcode()),
@JsonProperty Util.getStringIfNotBlank(address.getCountry()));
private final String pobox;
@JsonProperty
private final String neighborhood;
@JsonProperty
private final String city;
@JsonProperty
private final String region;
@JsonProperty
private final String postcode;
@JsonProperty
private final String country;
public JsonContactAddress(SharedContact.PostalAddress address) {
type = address.getType();
label = Util.getStringIfNotBlank(address.getLabel());
street = Util.getStringIfNotBlank(address.getStreet());
pobox = Util.getStringIfNotBlank(address.getPobox());
neighborhood = Util.getStringIfNotBlank(address.getNeighborhood());
city = Util.getStringIfNotBlank(address.getCity());
region = Util.getStringIfNotBlank(address.getRegion());
postcode = Util.getStringIfNotBlank(address.getPostcode());
country = Util.getStringIfNotBlank(address.getCountry());
} }
} }

View File

@ -1,19 +1,10 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
public class JsonContactAvatar { public record JsonContactAvatar(JsonAttachment attachment, boolean isProfile) {
@JsonProperty static JsonContactAvatar from(SharedContact.Avatar avatar) {
private final JsonAttachment attachment; return new JsonContactAvatar(JsonAttachment.from(avatar.getAttachment()), avatar.isProfile());
@JsonProperty
private final boolean isProfile;
public JsonContactAvatar(SharedContact.Avatar avatar) {
attachment = new JsonAttachment(avatar.getAttachment());
isProfile = avatar.isProfile();
} }
} }

View File

@ -1,24 +1,11 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.util.Util; import org.asamk.signal.util.Util;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
public class JsonContactEmail { public record JsonContactEmail(String value, SharedContact.Email.Type type, String label) {
@JsonProperty static JsonContactEmail from(SharedContact.Email email) {
private final String value; return new JsonContactEmail(email.getValue(), email.getType(), Util.getStringIfNotBlank(email.getLabel()));
@JsonProperty
private final SharedContact.Email.Type type;
@JsonProperty
private final String label;
public JsonContactEmail(SharedContact.Email email) {
value = email.getValue();
type = email.getType();
label = Util.getStringIfNotBlank(email.getLabel());
} }
} }

View File

@ -1,36 +1,18 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.util.Util; import org.asamk.signal.util.Util;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
public class JsonContactName { public record JsonContactName(
String display, String given, String family, String prefix, String suffix, String middle
) {
@JsonProperty static JsonContactName from(SharedContact.Name name) {
private final String display; return new JsonContactName(Util.getStringIfNotBlank(name.getDisplay()),
Util.getStringIfNotBlank(name.getGiven()),
@JsonProperty Util.getStringIfNotBlank(name.getFamily()),
private final String given; Util.getStringIfNotBlank(name.getPrefix()),
Util.getStringIfNotBlank(name.getSuffix()),
@JsonProperty Util.getStringIfNotBlank(name.getMiddle()));
private final String family;
@JsonProperty
private final String prefix;
@JsonProperty
private final String suffix;
@JsonProperty
private final String middle;
public JsonContactName(SharedContact.Name name) {
display = Util.getStringIfNotBlank(name.getDisplay());
given = Util.getStringIfNotBlank(name.getGiven());
family = Util.getStringIfNotBlank(name.getFamily());
prefix = Util.getStringIfNotBlank(name.getPrefix());
suffix = Util.getStringIfNotBlank(name.getSuffix());
middle = Util.getStringIfNotBlank(name.getMiddle());
} }
} }

View File

@ -1,24 +1,11 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.util.Util; import org.asamk.signal.util.Util;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
public class JsonContactPhone { public record JsonContactPhone(String value, SharedContact.Phone.Type type, String label) {
@JsonProperty static JsonContactPhone from(SharedContact.Phone phone) {
private final String value; return new JsonContactPhone(phone.getValue(), phone.getType(), Util.getStringIfNotBlank(phone.getLabel()));
@JsonProperty
private final SharedContact.Phone.Type type;
@JsonProperty
private final String label;
public JsonContactPhone(SharedContact.Phone phone) {
value = phone.getValue();
type = phone.getType();
label = Util.getStringIfNotBlank(phone.getLabel());
} }
} }

View File

@ -1,7 +1,6 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.Signal; import org.asamk.Signal;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
@ -10,136 +9,124 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
class JsonDataMessage { record JsonDataMessage(
long timestamp,
String message,
Integer expiresInSeconds,
@JsonInclude(JsonInclude.Include.NON_NULL) Boolean viewOnce,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonReaction reaction,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonQuote quote,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonMention> mentions,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonAttachment> attachments,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonSticker sticker,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonRemoteDelete remoteDelete,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSharedContact> contacts,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonGroupInfo groupInfo
) {
@JsonProperty static JsonDataMessage from(SignalServiceDataMessage dataMessage, Manager m) {
final long timestamp; final var timestamp = dataMessage.getTimestamp();
@JsonProperty
final String message;
@JsonProperty
final Integer expiresInSeconds;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final Boolean viewOnce;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonReaction reaction;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonQuote quote;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<JsonMention> mentions;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<JsonAttachment> attachments;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonSticker sticker;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonRemoteDelete remoteDelete;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<JsonSharedContact> contacts;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonGroupInfo groupInfo; final JsonGroupInfo groupInfo;
JsonDataMessage(SignalServiceDataMessage dataMessage, Manager m) {
this.timestamp = dataMessage.getTimestamp();
if (dataMessage.getGroupContext().isPresent()) { if (dataMessage.getGroupContext().isPresent()) {
final var groupContext = dataMessage.getGroupContext().get(); final var groupContext = dataMessage.getGroupContext().get();
if (groupContext.getGroupV1().isPresent()) { if (groupContext.getGroupV1().isPresent()) {
var groupInfo = groupContext.getGroupV1().get(); var group = groupContext.getGroupV1().get();
this.groupInfo = new JsonGroupInfo(groupInfo); groupInfo = JsonGroupInfo.from(group);
} else if (groupContext.getGroupV2().isPresent()) { } else if (groupContext.getGroupV2().isPresent()) {
var groupInfo = groupContext.getGroupV2().get(); var group = groupContext.getGroupV2().get();
this.groupInfo = new JsonGroupInfo(groupInfo); groupInfo = JsonGroupInfo.from(group);
} else { } else {
this.groupInfo = null; groupInfo = null;
} }
} else { } else {
this.groupInfo = null; groupInfo = null;
} }
this.message = dataMessage.getBody().orNull(); final var message = dataMessage.getBody().orNull();
this.expiresInSeconds = dataMessage.getExpiresInSeconds(); final var expiresInSeconds = dataMessage.getExpiresInSeconds();
this.viewOnce = dataMessage.isViewOnce(); final var viewOnce = dataMessage.isViewOnce();
this.reaction = dataMessage.getReaction().isPresent() final var reaction = dataMessage.getReaction().isPresent() ? JsonReaction.from(dataMessage.getReaction().get(),
? new JsonReaction(dataMessage.getReaction().get(), m) m) : null;
: null; final var quote = dataMessage.getQuote().isPresent() ? JsonQuote.from(dataMessage.getQuote().get(), m) : null;
this.quote = dataMessage.getQuote().isPresent() ? new JsonQuote(dataMessage.getQuote().get(), m) : null; final List<JsonMention> mentions;
if (dataMessage.getMentions().isPresent()) { if (dataMessage.getMentions().isPresent()) {
this.mentions = dataMessage.getMentions() mentions = dataMessage.getMentions()
.get() .get()
.stream() .stream()
.map(mention -> new JsonMention(mention, m)) .map(mention -> JsonMention.from(mention, m))
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
this.mentions = List.of(); mentions = List.of();
} }
remoteDelete = dataMessage.getRemoteDelete().isPresent() ? new JsonRemoteDelete(dataMessage.getRemoteDelete() final var remoteDelete = dataMessage.getRemoteDelete().isPresent()
.get()) : null; ? JsonRemoteDelete.from(dataMessage.getRemoteDelete().get())
: null;
final List<JsonAttachment> attachments;
if (dataMessage.getAttachments().isPresent()) { if (dataMessage.getAttachments().isPresent()) {
this.attachments = dataMessage.getAttachments() attachments = dataMessage.getAttachments()
.get() .get()
.stream() .stream()
.map(JsonAttachment::new) .map(JsonAttachment::from)
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
this.attachments = List.of(); attachments = List.of();
} }
this.sticker = dataMessage.getSticker().isPresent() ? new JsonSticker(dataMessage.getSticker().get()) : null; final var sticker = dataMessage.getSticker().isPresent()
? JsonSticker.from(dataMessage.getSticker().get())
: null;
final List<JsonSharedContact> contacts;
if (dataMessage.getSharedContacts().isPresent()) { if (dataMessage.getSharedContacts().isPresent()) {
this.contacts = dataMessage.getSharedContacts() contacts = dataMessage.getSharedContacts()
.get() .get()
.stream() .stream()
.map(JsonSharedContact::new) .map(JsonSharedContact::from)
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
this.contacts = List.of(); contacts = List.of();
} }
return new JsonDataMessage(timestamp,
message,
expiresInSeconds,
viewOnce,
reaction,
quote,
mentions,
attachments,
sticker,
remoteDelete,
contacts,
groupInfo);
} }
public JsonDataMessage(Signal.MessageReceived messageReceived) { static JsonDataMessage from(Signal.MessageReceived messageReceived) {
timestamp = messageReceived.getTimestamp(); return new JsonDataMessage(messageReceived.getTimestamp(),
message = messageReceived.getMessage(); messageReceived.getMessage(),
groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; // TODO Replace these with the proper commands
expiresInSeconds = null; null,
viewOnce = null; null,
remoteDelete = null; null,
reaction = null; // TODO Replace these 5 with the proper commands null,
quote = null; null,
mentions = null; messageReceived.getAttachments().stream().map(JsonAttachment::from).collect(Collectors.toList()),
sticker = null; null,
contacts = null; null,
attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); null,
messageReceived.getGroupId().length > 0 ? JsonGroupInfo.from(messageReceived.getGroupId()) : null);
} }
public JsonDataMessage(Signal.SyncMessageReceived messageReceived) { static JsonDataMessage from(Signal.SyncMessageReceived messageReceived) {
timestamp = messageReceived.getTimestamp(); return new JsonDataMessage(messageReceived.getTimestamp(),
message = messageReceived.getMessage(); messageReceived.getMessage(),
groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; // TODO Replace these with the proper commands
expiresInSeconds = null; null,
viewOnce = null; null,
remoteDelete = null; null,
reaction = null; // TODO Replace these 5 with the proper commands null,
quote = null; null,
mentions = null; messageReceived.getAttachments().stream().map(JsonAttachment::from).collect(Collectors.toList()),
sticker = null; null,
contacts = null; null,
attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); null,
messageReceived.getGroupId().length > 0 ? JsonGroupInfo.from(messageReceived.getGroupId()) : null);
} }
} }

View File

@ -1,17 +1,8 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty; public record JsonError(String message, String type) {
public class JsonError { public static JsonError from(Throwable exception) {
return new JsonError(exception.getMessage(), exception.getClass().getSimpleName());
@JsonProperty
final String message;
@JsonProperty
final String type;
public JsonError(Throwable exception) {
this.message = exception.getMessage();
this.type = exception.getClass().getSimpleName();
} }
} }

View File

@ -1,7 +1,6 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.util.Util; import org.asamk.signal.util.Util;
@ -12,48 +11,32 @@ import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
class JsonGroupInfo { record JsonGroupInfo(
String groupId,
String type,
@JsonInclude(JsonInclude.Include.NON_NULL) String name,
@JsonInclude(JsonInclude.Include.NON_NULL) List<String> members
) {
@JsonProperty static JsonGroupInfo from(SignalServiceGroup groupInfo) {
final String groupId; return new JsonGroupInfo(Base64.getEncoder().encodeToString(groupInfo.getGroupId()),
groupInfo.getType().toString(),
@JsonProperty groupInfo.getName().orNull(),
final String type; groupInfo.getMembers().isPresent() ? groupInfo.getMembers()
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final String name;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<String> members;
JsonGroupInfo(SignalServiceGroup groupInfo) {
this.groupId = Base64.getEncoder().encodeToString(groupInfo.getGroupId());
this.type = groupInfo.getType().toString();
this.name = groupInfo.getName().orNull();
if (groupInfo.getMembers().isPresent()) {
this.members = groupInfo.getMembers()
.get() .get()
.stream() .stream()
.map(Util::getLegacyIdentifier) .map(Util::getLegacyIdentifier)
.collect(Collectors.toList()); .collect(Collectors.toList()) : null);
} else {
this.members = null;
}
} }
JsonGroupInfo(SignalServiceGroupV2 groupInfo) { static JsonGroupInfo from(SignalServiceGroupV2 groupInfo) {
this.groupId = GroupUtils.getGroupIdV2(groupInfo.getMasterKey()).toBase64(); return new JsonGroupInfo(GroupUtils.getGroupIdV2(groupInfo.getMasterKey()).toBase64(),
this.type = groupInfo.hasSignedGroupChange() ? "UPDATE" : "DELIVER"; groupInfo.hasSignedGroupChange() ? "UPDATE" : "DELIVER",
this.members = null; null,
this.name = null; null);
} }
JsonGroupInfo(byte[] groupId) { static JsonGroupInfo from(byte[] groupId) {
this.groupId = Base64.getEncoder().encodeToString(groupId); return new JsonGroupInfo(Base64.getEncoder().encodeToString(groupId), "DELIVER", null, null);
this.type = "DELIVER";
this.members = null;
this.name = null;
} }
} }

View File

@ -1,37 +1,19 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class JsonMention { public record JsonMention(@Deprecated String name, String number, String uuid, int start, int length) {
@JsonProperty static JsonMention from(SignalServiceDataMessage.Mention mention, Manager m) {
@Deprecated
final String name;
@JsonProperty
final String number;
@JsonProperty
final String uuid;
@JsonProperty
final int start;
@JsonProperty
final int length;
JsonMention(SignalServiceDataMessage.Mention mention, Manager m) {
final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid())); final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid()));
this.name = getLegacyIdentifier(address); return new JsonMention(getLegacyIdentifier(address),
this.number = address.getNumber().orNull(); address.getNumber().orNull(),
this.uuid = address.getUuid().toString(); address.getUuid().toString(),
this.start = mention.getStart(); mention.getStart(),
this.length = mention.getLength(); mention.getLength());
} }
} }

View File

@ -1,7 +1,6 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.Signal; import org.asamk.Signal;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
@ -15,143 +14,133 @@ import java.util.List;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class JsonMessageEnvelope { public record JsonMessageEnvelope(
@Deprecated String source,
String sourceNumber,
String sourceUuid,
String sourceName,
Integer sourceDevice,
long timestamp,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonDataMessage dataMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncMessage syncMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonCallMessage callMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonReceiptMessage receiptMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonTypingMessage typingMessage
) {
@JsonProperty public static JsonMessageEnvelope from(
@Deprecated
final String source;
@JsonProperty
final String sourceNumber;
@JsonProperty
final String sourceUuid;
@JsonProperty
final String sourceName;
@JsonProperty
final Integer sourceDevice;
@JsonProperty
final long timestamp;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonDataMessage dataMessage;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonSyncMessage syncMessage;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonCallMessage callMessage;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonReceiptMessage receiptMessage;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonTypingMessage typingMessage;
public JsonMessageEnvelope(
SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception, Manager m SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception, Manager m
) { ) {
final String source;
final String sourceNumber;
final String sourceUuid;
final Integer sourceDevice;
if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) { if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
var source = m.resolveSignalServiceAddress(envelope.getSourceAddress()); final var sourceAddress = m.resolveSignalServiceAddress(envelope.getSourceAddress());
this.source = getLegacyIdentifier(source); source = getLegacyIdentifier(sourceAddress);
this.sourceNumber = source.getNumber().orNull(); sourceNumber = sourceAddress.getNumber().orNull();
this.sourceUuid = source.getUuid().toString(); sourceUuid = sourceAddress.getUuid().toString();
this.sourceDevice = envelope.getSourceDevice(); sourceDevice = envelope.getSourceDevice();
} else if (envelope.isUnidentifiedSender() && content != null) { } else if (envelope.isUnidentifiedSender() && content != null) {
final var source = m.resolveSignalServiceAddress(content.getSender()); final var sender = m.resolveSignalServiceAddress(content.getSender());
this.source = getLegacyIdentifier(source); source = getLegacyIdentifier(sender);
this.sourceNumber = source.getNumber().orNull(); sourceNumber = sender.getNumber().orNull();
this.sourceUuid = source.getUuid().toString(); sourceUuid = sender.getUuid().toString();
this.sourceDevice = content.getSenderDevice(); sourceDevice = content.getSenderDevice();
} else if (exception instanceof UntrustedIdentityException e) { } else if (exception instanceof UntrustedIdentityException e) {
final var source = m.resolveSignalServiceAddress(e.getSender()); final var sender = m.resolveSignalServiceAddress(e.getSender());
this.source = getLegacyIdentifier(source); source = getLegacyIdentifier(sender);
this.sourceNumber = source.getNumber().orNull(); sourceNumber = sender.getNumber().orNull();
this.sourceUuid = source.getUuid().toString(); sourceUuid = sender.getUuid().toString();
this.sourceDevice = e.getSenderDevice(); sourceDevice = e.getSenderDevice();
} else { } else {
this.source = null; source = null;
this.sourceNumber = null; sourceNumber = null;
this.sourceUuid = null; sourceUuid = null;
this.sourceDevice = null; sourceDevice = null;
} }
String name; String name;
try { try {
name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(this.source, m.getSelfNumber())); name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(source, m.getSelfNumber()));
} catch (InvalidNumberException | NullPointerException e) { } catch (InvalidNumberException | NullPointerException e) {
name = null; name = null;
} }
this.sourceName = name; final var sourceName = name;
this.timestamp = envelope.getTimestamp(); final var timestamp = envelope.getTimestamp();
final JsonReceiptMessage receiptMessage;
if (envelope.isReceipt()) { if (envelope.isReceipt()) {
this.receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp));
} else if (content != null && content.getReceiptMessage().isPresent()) {
this.receiptMessage = new JsonReceiptMessage(content.getReceiptMessage().get());
} else {
this.receiptMessage = null;
}
this.typingMessage = content != null && content.getTypingMessage().isPresent()
? new JsonTypingMessage(content.getTypingMessage().get())
: null;
this.dataMessage = content != null && content.getDataMessage().isPresent()
? new JsonDataMessage(content.getDataMessage().get(), m)
: null;
this.syncMessage = content != null && content.getSyncMessage().isPresent()
? new JsonSyncMessage(content.getSyncMessage().get(), m)
: null;
this.callMessage = content != null && content.getCallMessage().isPresent()
? new JsonCallMessage(content.getCallMessage().get())
: null;
}
public JsonMessageEnvelope(Signal.MessageReceived messageReceived) {
source = messageReceived.getSender();
sourceNumber = null;
sourceUuid = null;
sourceName = null;
sourceDevice = null;
timestamp = messageReceived.getTimestamp();
receiptMessage = null;
dataMessage = new JsonDataMessage(messageReceived);
syncMessage = null;
callMessage = null;
typingMessage = null;
}
public JsonMessageEnvelope(Signal.ReceiptReceived receiptReceived) {
source = receiptReceived.getSender();
sourceNumber = null;
sourceUuid = null;
sourceName = null;
sourceDevice = null;
timestamp = receiptReceived.getTimestamp();
receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp)); receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp));
dataMessage = null; } else if (content != null && content.getReceiptMessage().isPresent()) {
syncMessage = null; receiptMessage = JsonReceiptMessage.from(content.getReceiptMessage().get());
callMessage = null; } else {
typingMessage = null; receiptMessage = null;
}
final var typingMessage = content != null && content.getTypingMessage().isPresent() ? JsonTypingMessage.from(
content.getTypingMessage().get()) : null;
final var dataMessage = content != null && content.getDataMessage().isPresent()
? JsonDataMessage.from(content.getDataMessage().get(), m)
: null;
final var syncMessage = content != null && content.getSyncMessage().isPresent()
? JsonSyncMessage.from(content.getSyncMessage().get(), m)
: null;
final var callMessage = content != null && content.getCallMessage().isPresent()
? JsonCallMessage.from(content.getCallMessage().get())
: null;
return new JsonMessageEnvelope(source,
sourceNumber,
sourceUuid,
sourceName,
sourceDevice,
timestamp,
dataMessage,
syncMessage,
callMessage,
receiptMessage,
typingMessage);
} }
public JsonMessageEnvelope(Signal.SyncMessageReceived messageReceived) { public static JsonMessageEnvelope from(Signal.MessageReceived messageReceived) {
source = messageReceived.getSource(); return new JsonMessageEnvelope(messageReceived.getSource(),
sourceNumber = null; null,
sourceUuid = null; null,
sourceName = null; null,
sourceDevice = null; null,
timestamp = messageReceived.getTimestamp(); messageReceived.getTimestamp(),
receiptMessage = null; JsonDataMessage.from(messageReceived),
dataMessage = null; null,
syncMessage = new JsonSyncMessage(messageReceived); null,
callMessage = null; null,
typingMessage = null; null);
}
public static JsonMessageEnvelope from(Signal.ReceiptReceived receiptReceived) {
return new JsonMessageEnvelope(receiptReceived.getSender(),
null,
null,
null,
null,
receiptReceived.getTimestamp(),
null,
null,
null,
JsonReceiptMessage.deliveryReceipt(receiptReceived.getTimestamp(),
List.of(receiptReceived.getTimestamp())),
null);
}
public static JsonMessageEnvelope from(Signal.SyncMessageReceived messageReceived) {
return new JsonMessageEnvelope(messageReceived.getSource(),
null,
null,
null,
null,
messageReceived.getTimestamp(),
null,
JsonSyncMessage.from(messageReceived),
null,
null,
null);
} }
} }

View File

@ -1,7 +1,6 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
@ -12,55 +11,41 @@ import java.util.stream.Collectors;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class JsonQuote { public record JsonQuote(
long id,
@Deprecated String author,
String authorNumber,
String authorUuid,
String text,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonMention> mentions,
List<JsonQuotedAttachment> attachments
) {
@JsonProperty static JsonQuote from(SignalServiceDataMessage.Quote quote, Manager m) {
final long id; final var id = quote.getId();
@JsonProperty
@Deprecated
final String author;
@JsonProperty
final String authorNumber;
@JsonProperty
final String authorUuid;
@JsonProperty
final String text;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<JsonMention> mentions;
@JsonProperty
final List<JsonQuotedAttachment> attachments;
JsonQuote(SignalServiceDataMessage.Quote quote, Manager m) {
this.id = quote.getId();
final var address = m.resolveSignalServiceAddress(quote.getAuthor()); final var address = m.resolveSignalServiceAddress(quote.getAuthor());
this.author = getLegacyIdentifier(address); final var author = getLegacyIdentifier(address);
this.authorNumber = address.getNumber().orNull(); final var authorNumber = address.getNumber().orNull();
this.authorUuid = address.getUuid().toString(); final var authorUuid = address.getUuid().toString();
this.text = quote.getText(); final var text = quote.getText();
final List<JsonMention> mentions;
if (quote.getMentions() != null && quote.getMentions().size() > 0) { if (quote.getMentions() != null && quote.getMentions().size() > 0) {
this.mentions = quote.getMentions() mentions = quote.getMentions()
.stream() .stream()
.map(quotedMention -> new JsonMention(quotedMention, m)) .map(quotedMention -> JsonMention.from(quotedMention, m))
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
this.mentions = null; mentions = null;
} }
final List<JsonQuotedAttachment> attachments;
if (quote.getAttachments().size() > 0) { if (quote.getAttachments().size() > 0) {
this.attachments = quote.getAttachments() attachments = quote.getAttachments().stream().map(JsonQuotedAttachment::from).collect(Collectors.toList());
.stream()
.map(JsonQuotedAttachment::new)
.collect(Collectors.toList());
} else { } else {
this.attachments = new ArrayList<>(); attachments = new ArrayList<>();
} }
return new JsonQuote(id, author, authorNumber, authorUuid, text, mentions, attachments);
} }
} }

View File

@ -1,29 +1,22 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
public class JsonQuotedAttachment { public record JsonQuotedAttachment(
String contentType, String filename, @JsonInclude(JsonInclude.Include.NON_NULL) JsonAttachment thumbnail
) {
@JsonProperty static JsonQuotedAttachment from(SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment) {
final String contentType; final var contentType = quotedAttachment.getContentType();
final var filename = quotedAttachment.getFileName();
@JsonProperty
final String filename;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonAttachment thumbnail; final JsonAttachment thumbnail;
JsonQuotedAttachment(SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment) {
contentType = quotedAttachment.getContentType();
filename = quotedAttachment.getFileName();
if (quotedAttachment.getThumbnail() != null) { if (quotedAttachment.getThumbnail() != null) {
thumbnail = new JsonAttachment(quotedAttachment.getThumbnail()); thumbnail = JsonAttachment.from(quotedAttachment.getThumbnail());
} else { } else {
thumbnail = null; thumbnail = null;
} }
return new JsonQuotedAttachment(contentType, filename, thumbnail);
} }
} }

View File

@ -1,40 +1,32 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class JsonReaction { public record JsonReaction(
String emoji,
@Deprecated String targetAuthor,
String targetAuthorNumber,
String targetAuthorUuid,
long targetSentTimestamp,
boolean isRemove
) {
@JsonProperty static JsonReaction from(Reaction reaction, Manager m) {
final String emoji; final var emoji = reaction.getEmoji();
@JsonProperty
@Deprecated
final String targetAuthor;
@JsonProperty
final String targetAuthorNumber;
@JsonProperty
final String targetAuthorUuid;
@JsonProperty
final long targetSentTimestamp;
@JsonProperty
final boolean isRemove;
JsonReaction(Reaction reaction, Manager m) {
this.emoji = reaction.getEmoji();
final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor()); final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor());
this.targetAuthor = getLegacyIdentifier(address); final var targetAuthor = getLegacyIdentifier(address);
this.targetAuthorNumber = address.getNumber().orNull(); final var targetAuthorNumber = address.getNumber().orNull();
this.targetAuthorUuid = address.getUuid().toString(); final var targetAuthorUuid = address.getUuid().toString();
this.targetSentTimestamp = reaction.getTargetSentTimestamp(); final var targetSentTimestamp = reaction.getTargetSentTimestamp();
this.isRemove = reaction.isRemove(); final var isRemove = reaction.isRemove();
return new JsonReaction(emoji,
targetAuthor,
targetAuthorNumber,
targetAuthorUuid,
targetSentTimestamp,
isRemove);
} }
} }

View File

@ -1,39 +1,17 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
import java.util.List; import java.util.List;
class JsonReceiptMessage { record JsonReceiptMessage(long when, boolean isDelivery, boolean isRead, List<Long> timestamps) {
@JsonProperty static JsonReceiptMessage from(SignalServiceReceiptMessage receiptMessage) {
final long when; final var when = receiptMessage.getWhen();
final var isDelivery = receiptMessage.isDeliveryReceipt();
@JsonProperty final var isRead = receiptMessage.isReadReceipt();
final boolean isDelivery; final var timestamps = receiptMessage.getTimestamps();
return new JsonReceiptMessage(when, isDelivery, isRead, timestamps);
@JsonProperty
final boolean isRead;
@JsonProperty
final List<Long> timestamps;
JsonReceiptMessage(SignalServiceReceiptMessage receiptMessage) {
this.when = receiptMessage.getWhen();
this.isDelivery = receiptMessage.isDeliveryReceipt();
this.isRead = receiptMessage.isReadReceipt();
this.timestamps = receiptMessage.getTimestamps();
}
private JsonReceiptMessage(
final long when, final boolean isDelivery, final boolean isRead, final List<Long> timestamps
) {
this.when = when;
this.isDelivery = isDelivery;
this.isRead = isRead;
this.timestamps = timestamps;
} }
static JsonReceiptMessage deliveryReceipt(final long when, final List<Long> timestamps) { static JsonReceiptMessage deliveryReceipt(final long when, final List<Long> timestamps) {

View File

@ -1,15 +1,10 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
class JsonRemoteDelete { record JsonRemoteDelete(long timestamp) {
@JsonProperty static JsonRemoteDelete from(SignalServiceDataMessage.RemoteDelete remoteDelete) {
final long timestamp; return new JsonRemoteDelete(remoteDelete.getTargetSentTimestamp());
JsonRemoteDelete(SignalServiceDataMessage.RemoteDelete remoteDelete) {
this.timestamp = remoteDelete.getTargetSentTimestamp();
} }
} }

View File

@ -1,62 +1,45 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class JsonSharedContact { public record JsonSharedContact(
JsonContactName name,
JsonContactAvatar avatar,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonContactPhone> phone,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonContactEmail> email,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonContactAddress> address,
String organization
) {
@JsonProperty static JsonSharedContact from(SharedContact contact) {
final JsonContactName name; final var name = JsonContactName.from(contact.getName());
final var avatar = contact.getAvatar().isPresent() ? JsonContactAvatar.from(contact.getAvatar().get()) : null;
@JsonProperty final var phone = contact.getPhone().isPresent() ? contact.getPhone()
final JsonContactAvatar avatar; .get()
.stream()
.map(JsonContactPhone::from)
.collect(Collectors.toList()) : null;
@JsonProperty final var email = contact.getEmail().isPresent() ? contact.getEmail()
@JsonInclude(JsonInclude.Include.NON_NULL) .get()
final List<JsonContactPhone> phone; .stream()
.map(JsonContactEmail::from)
.collect(Collectors.toList()) : null;
@JsonProperty final var address = contact.getAddress().isPresent() ? contact.getAddress()
@JsonInclude(JsonInclude.Include.NON_NULL) .get()
final List<JsonContactEmail> email; .stream()
.map(JsonContactAddress::from)
.collect(Collectors.toList()) : null;
@JsonProperty final var organization = contact.getOrganization().orNull();
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<JsonContactAddress> address;
@JsonProperty return new JsonSharedContact(name, avatar, phone, email, address, organization);
final String organization;
public JsonSharedContact(SharedContact contact) {
name = new JsonContactName(contact.getName());
if (contact.getAvatar().isPresent()) {
avatar = new JsonContactAvatar(contact.getAvatar().get());
} else {
avatar = null;
}
if (contact.getPhone().isPresent()) {
phone = contact.getPhone().get().stream().map(JsonContactPhone::new).collect(Collectors.toList());
} else {
phone = null;
}
if (contact.getEmail().isPresent()) {
email = contact.getEmail().get().stream().map(JsonContactEmail::new).collect(Collectors.toList());
} else {
email = null;
}
if (contact.getAddress().isPresent()) {
address = contact.getAddress().get().stream().map(JsonContactAddress::new).collect(Collectors.toList());
} else {
address = null;
}
organization = contact.getOrganization().orNull();
} }
} }

View File

@ -1,25 +1,15 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import java.util.Base64; import java.util.Base64;
public class JsonSticker { public record JsonSticker(String packId, String packKey, int stickerId) {
@JsonProperty static JsonSticker from(SignalServiceDataMessage.Sticker sticker) {
final String packId; final var packId = Base64.getEncoder().encodeToString(sticker.getPackId());
final var packKey = Base64.getEncoder().encodeToString(sticker.getPackKey());
@JsonProperty final var stickerId = sticker.getStickerId();
final String packKey; return new JsonSticker(packId, packKey, stickerId);
@JsonProperty
final int stickerId;
public JsonSticker(SignalServiceDataMessage.Sticker sticker) {
this.packId = Base64.getEncoder().encodeToString(sticker.getPackId());
this.packKey = Base64.getEncoder().encodeToString(sticker.getPackKey());
this.stickerId = sticker.getStickerId();
} }
} }

View File

@ -1,6 +1,6 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.asamk.Signal; import org.asamk.Signal;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
@ -8,37 +8,30 @@ import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptM
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
class JsonSyncDataMessage extends JsonDataMessage { record JsonSyncDataMessage(
@Deprecated String destination,
@JsonProperty String destinationNumber,
@Deprecated String destinationUuid,
final String destination; @JsonUnwrapped JsonDataMessage dataMessage
) {
@JsonProperty
final String destinationNumber;
@JsonProperty
final String destinationUuid;
JsonSyncDataMessage(SentTranscriptMessage transcriptMessage, Manager m) {
super(transcriptMessage.getMessage(), m);
static JsonSyncDataMessage from(SentTranscriptMessage transcriptMessage, Manager m) {
if (transcriptMessage.getDestination().isPresent()) { if (transcriptMessage.getDestination().isPresent()) {
final var address = transcriptMessage.getDestination().get(); final var address = transcriptMessage.getDestination().get();
this.destination = getLegacyIdentifier(address); return new JsonSyncDataMessage(getLegacyIdentifier(address),
this.destinationNumber = address.getNumber().orNull(); address.getNumber().orNull(),
this.destinationUuid = address.getUuid().toString(); address.getUuid().toString(),
JsonDataMessage.from(transcriptMessage.getMessage(), m));
} else { } else {
this.destination = null; return new JsonSyncDataMessage(null, null, null, JsonDataMessage.from(transcriptMessage.getMessage(), m));
this.destinationNumber = null;
this.destinationUuid = null;
} }
} }
JsonSyncDataMessage(Signal.SyncMessageReceived messageReceived) { static JsonSyncDataMessage from(Signal.SyncMessageReceived messageReceived) {
super(messageReceived); return new JsonSyncDataMessage(messageReceived.getDestination(),
this.destination = messageReceived.getDestination(); null,
this.destinationNumber = null; null,
this.destinationUuid = null; JsonDataMessage.from(messageReceived));
} }
} }

View File

@ -1,7 +1,6 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.Signal; import org.asamk.Signal;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
@ -18,76 +17,72 @@ enum JsonSyncMessageType {
REQUEST_SYNC REQUEST_SYNC
} }
class JsonSyncMessage { record JsonSyncMessage(
@JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncDataMessage sentMessage,
@JsonInclude(JsonInclude.Include.NON_NULL) List<String> blockedNumbers,
@JsonInclude(JsonInclude.Include.NON_NULL) List<String> blockedGroupIds,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSyncReadMessage> readMessages,
@JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncMessageType type
) {
@JsonProperty JsonSyncMessage(
@JsonInclude(JsonInclude.Include.NON_NULL) final JsonSyncDataMessage sentMessage,
final JsonSyncDataMessage sentMessage; final List<String> blockedNumbers,
final List<String> blockedGroupIds,
final List<JsonSyncReadMessage> readMessages,
final JsonSyncMessageType type
) {
this.sentMessage = sentMessage;
this.blockedNumbers = blockedNumbers;
this.blockedGroupIds = blockedGroupIds;
this.readMessages = readMessages;
this.type = type;
}
@JsonProperty static JsonSyncMessage from(SignalServiceSyncMessage syncMessage, Manager m) {
@JsonInclude(JsonInclude.Include.NON_NULL) final var sentMessage = syncMessage.getSent().isPresent() ? JsonSyncDataMessage.from(syncMessage.getSent()
.get(), m) : null;
final List<String> blockedNumbers; final List<String> blockedNumbers;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<String> blockedGroupIds; final List<String> blockedGroupIds;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final List<JsonSyncReadMessage> readMessages;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final JsonSyncMessageType type;
JsonSyncMessage(SignalServiceSyncMessage syncMessage, Manager m) {
this.sentMessage = syncMessage.getSent().isPresent()
? new JsonSyncDataMessage(syncMessage.getSent().get(), m)
: null;
if (syncMessage.getBlockedList().isPresent()) { if (syncMessage.getBlockedList().isPresent()) {
final var base64 = Base64.getEncoder(); final var base64 = Base64.getEncoder();
this.blockedNumbers = syncMessage.getBlockedList() blockedNumbers = syncMessage.getBlockedList()
.get() .get()
.getAddresses() .getAddresses()
.stream() .stream()
.map(Util::getLegacyIdentifier) .map(Util::getLegacyIdentifier)
.collect(Collectors.toList()); .collect(Collectors.toList());
this.blockedGroupIds = syncMessage.getBlockedList() blockedGroupIds = syncMessage.getBlockedList()
.get() .get()
.getGroupIds() .getGroupIds()
.stream() .stream()
.map(base64::encodeToString) .map(base64::encodeToString)
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
this.blockedNumbers = null; blockedNumbers = null;
this.blockedGroupIds = null; blockedGroupIds = null;
} }
if (syncMessage.getRead().isPresent()) {
this.readMessages = syncMessage.getRead() final var readMessages = syncMessage.getRead().isPresent() ? syncMessage.getRead()
.get() .get()
.stream() .stream()
.map(JsonSyncReadMessage::new) .map(JsonSyncReadMessage::from)
.collect(Collectors.toList()); .collect(Collectors.toList()) : null;
} else {
this.readMessages = null;
}
final JsonSyncMessageType type;
if (syncMessage.getContacts().isPresent()) { if (syncMessage.getContacts().isPresent()) {
this.type = JsonSyncMessageType.CONTACTS_SYNC; type = JsonSyncMessageType.CONTACTS_SYNC;
} else if (syncMessage.getGroups().isPresent()) { } else if (syncMessage.getGroups().isPresent()) {
this.type = JsonSyncMessageType.GROUPS_SYNC; type = JsonSyncMessageType.GROUPS_SYNC;
} else if (syncMessage.getRequest().isPresent()) { } else if (syncMessage.getRequest().isPresent()) {
this.type = JsonSyncMessageType.REQUEST_SYNC; type = JsonSyncMessageType.REQUEST_SYNC;
} else { } else {
this.type = null; type = null;
} }
return new JsonSyncMessage(sentMessage, blockedNumbers, blockedGroupIds, readMessages, type);
} }
JsonSyncMessage(Signal.SyncMessageReceived messageReceived) { static JsonSyncMessage from(Signal.SyncMessageReceived messageReceived) {
this.sentMessage = new JsonSyncDataMessage(messageReceived); return new JsonSyncMessage(JsonSyncDataMessage.from(messageReceived), null, null, null, null);
this.blockedNumbers = null;
this.blockedGroupIds = null;
this.readMessages = null;
this.type = null;
} }
} }

View File

@ -1,31 +1,19 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
class JsonSyncReadMessage { record JsonSyncReadMessage(
@Deprecated String sender, String senderNumber, String senderUuid, long timestamp
) {
@JsonProperty static JsonSyncReadMessage from(final ReadMessage readMessage) {
@Deprecated final var senderAddress = readMessage.getSender();
final String sender; final var sender = getLegacyIdentifier(senderAddress);
final var senderNumber = senderAddress.getNumber().orNull();
@JsonProperty final var senderUuid = senderAddress.getUuid().toString();
final String senderNumber; final var timestamp = readMessage.getTimestamp();
return new JsonSyncReadMessage(sender, senderNumber, senderUuid, timestamp);
@JsonProperty
final String senderUuid;
@JsonProperty
final long timestamp;
public JsonSyncReadMessage(final ReadMessage readMessage) {
final var sender = readMessage.getSender();
this.sender = getLegacyIdentifier(sender);
this.senderNumber = sender.getNumber().orNull();
this.senderUuid = sender.getUuid().toString();
this.timestamp = readMessage.getTimestamp();
} }
} }

View File

@ -1,28 +1,26 @@
package org.asamk.signal.json; package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
import java.util.Base64; import java.util.Base64;
class JsonTypingMessage { record JsonTypingMessage(
String action, long timestamp, @JsonInclude(JsonInclude.Include.NON_NULL) String groupId
) {
@JsonProperty JsonTypingMessage(final String action, final long timestamp, final String groupId) {
final String action; this.action = action;
this.timestamp = timestamp;
this.groupId = groupId;
}
@JsonProperty static JsonTypingMessage from(SignalServiceTypingMessage typingMessage) {
final long timestamp; final var action = typingMessage.getAction().name();
final var timestamp = typingMessage.getTimestamp();
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final String groupId;
JsonTypingMessage(SignalServiceTypingMessage typingMessage) {
this.action = typingMessage.getAction().name();
this.timestamp = typingMessage.getTimestamp();
final var encoder = Base64.getEncoder(); final var encoder = Base64.getEncoder();
this.groupId = typingMessage.getGroupId().transform(encoder::encodeToString).orNull(); final var groupId = typingMessage.getGroupId().transform(encoder::encodeToString).orNull();
return new JsonTypingMessage(action, timestamp, groupId);
} }
} }