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 />
</value>
</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" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="120" />
<option name="KEEP_LINE_BREAKS" value="false" />
@ -52,6 +52,7 @@
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="5" />
<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" />
</codeStyleSettings>
<codeStyleSettings language="XML">

View File

@ -15,6 +15,7 @@ repositories {
dependencies {
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("org.bouncycastle:bcprov-jdk15on:1.69")
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;
public class DeviceLinkInfo {
final String deviceIdentifier;
final ECPublicKey deviceKey;
public record DeviceLinkInfo(String deviceIdentifier, ECPublicKey deviceKey) {
public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws InvalidKeyException {
final var rawQuery = linkUri.getRawQuery();
@ -57,11 +54,6 @@ public class DeviceLinkInfo {
return map;
}
public DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) {
this.deviceIdentifier = deviceIdentifier;
this.deviceKey = deviceKey;
}
public URI createDeviceLinkUri() {
final var deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", "");
try {

View File

@ -1,55 +1,8 @@
package org.asamk.signal.manager;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class JsonStickerPack {
public record JsonStickerPack(String title, String author, JsonSticker cover, List<JsonSticker> stickers) {
@JsonProperty
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;
}
}
public record JsonSticker(String emoji, String file, String contentType) {}
}

View File

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

View File

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

View File

@ -2,12 +2,9 @@ package org.asamk.signal.manager;
import java.io.File;
public class PathConfig {
private final File dataPath;
private final File attachmentsPath;
private final File avatarsPath;
private final File stickerPacksPath;
public record PathConfig(
File dataPath, File attachmentsPath, File avatarsPath, File stickerPacksPath
) {
public static PathConfig createDefault(final File settingsPath) {
return new PathConfig(new File(settingsPath, "data"),
@ -15,29 +12,4 @@ public class PathConfig {
new File(settingsPath, "avatars"),
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);
if (SignalAccount.userExists(pathConfig.getDataPath(), number) && !canRelinkExistingAccount(number)) {
throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.getDataPath(), number));
if (SignalAccount.userExists(pathConfig.dataPath(), number) && !canRelinkExistingAccount(number)) {
throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.dataPath(), number));
}
var encryptedDeviceName = deviceName == null
@ -115,7 +115,7 @@ public class ProvisioningManager {
SignalAccount account = null;
try {
account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.getDataPath(),
account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.dataPath(),
number,
ret.getUuid(),
password,
@ -165,7 +165,7 @@ public class ProvisioningManager {
private boolean canRelinkExistingAccount(final String number) throws IOException {
final SignalAccount signalAccount;
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) {
logger.debug("Account in use or failed to load.", e);
return false;

View File

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

View File

@ -1,38 +1,3 @@
package org.asamk.signal.manager.api;
public class Device {
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;
}
}
public record Device(long id, String name, long created, long lastSeen, boolean isThisDevice) {}

View File

@ -7,116 +7,20 @@ import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import java.util.Set;
public class Group {
private final GroupId groupId;
private final String title;
private final String description;
private final GroupInviteLinkUrl groupInviteLinkUrl;
private final Set<RecipientAddress> members;
private final Set<RecipientAddress> pendingMembers;
private final Set<RecipientAddress> requestingMembers;
private final Set<RecipientAddress> adminMembers;
private final boolean isBlocked;
private final int messageExpirationTimer;
private final GroupPermission permissionAddMember;
private final GroupPermission permissionEditDetails;
private final GroupPermission permissionSendMessage;
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;
}
}
public record Group(
GroupId groupId,
String title,
String description,
GroupInviteLinkUrl groupInviteLinkUrl,
Set<RecipientAddress> members,
Set<RecipientAddress> pendingMembers,
Set<RecipientAddress> requestingMembers,
Set<RecipientAddress> adminMembers,
boolean isBlocked,
int messageExpirationTimer,
GroupPermission permissionAddMember,
GroupPermission permissionEditDetails,
GroupPermission permissionSendMessage,
boolean isMember,
boolean isAdmin
) {}

View File

@ -6,60 +6,16 @@ import org.whispersystems.libsignal.IdentityKey;
import java.util.Date;
public class Identity {
private final RecipientAddress recipient;
private final IdentityKey identityKey;
private final String safetyNumber;
private final byte[] scannableSafetyNumber;
private final TrustLevel trustLevel;
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 record Identity(
RecipientAddress recipient,
IdentityKey identityKey,
String safetyNumber,
byte[] scannableSafetyNumber,
TrustLevel trustLevel,
Date dateAdded
) {
public byte[] getFingerprint() {
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;
public class Message {
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;
}
}
public record Message(String messageText, List<String> attachments) {}

View File

@ -4,23 +4,4 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult;
import java.util.List;
public class SendGroupMessageResults {
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;
}
}
public record SendGroupMessageResults(long timestamp, List<SendMessageResult> results) {}

View File

@ -5,23 +5,4 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult;
import java.util.List;
import java.util.Map;
public class SendMessageResults {
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;
}
}
public record SendMessageResults(long timestamp, Map<RecipientIdentifier, List<SendMessageResult>> results) {}

View File

@ -62,29 +62,9 @@ public class ConfigurationStore {
return new Storage(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
}
public static final class Storage {
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 record Storage(
Boolean readReceipts, Boolean unidentifiedDeliveryIndicators, Boolean typingIndicators, Boolean linkPreviews
) {}
public interface Saver {

View File

@ -262,7 +262,7 @@ public class GroupStore {
g1.blocked,
g1.archived,
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()));
}
@ -274,91 +274,22 @@ public class GroupStore {
}).collect(Collectors.toList()));
}
public static class Storage {
public record Storage(@JsonDeserialize(using = GroupsDeserializer.class) List<Object> groups) {
@JsonDeserialize(using = GroupsDeserializer.class)
public List<Storage.Group> groups;
// For deserialization
public Storage() {
}
public Storage(final List<Storage.Group> groups) {
this.groups = groups;
}
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
private record GroupV1(
String groupId,
String expectedV2Id,
String name,
String color,
int messageExpirationTime,
boolean blocked,
boolean archived,
@JsonDeserialize(using = MembersDeserializer.class) @JsonSerialize(using = MembersSerializer.class) 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;
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 record JsonRecipientAddress(String uuid, String number) {}
private static class MembersSerializer extends JsonSerializer<List<Member>> {
@ -366,7 +297,7 @@ public class GroupStore {
public void serialize(
final List<Member> value, final JsonGenerator jgen, final SerializerProvider provider
) throws IOException {
jgen.writeStartArray(value.size());
jgen.writeStartArray(null, value.size());
for (var address : value) {
if (address.recipientId != null) {
jgen.writeNumber(address.recipientId);
@ -404,39 +335,19 @@ public class GroupStore {
}
}
private static class GroupV2 extends Group {
public String groupId;
public String masterKey;
public boolean blocked;
public boolean permissionDenied;
// For deserialization
private GroupV2() {
private record GroupV2(String groupId, String masterKey, boolean blocked, boolean permissionDenied) {}
}
public GroupV2(
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>> {
private static class GroupsDeserializer extends JsonDeserializer<List<Object>> {
@Override
public List<Storage.Group> deserialize(
public List<Object> deserialize(
JsonParser jsonParser, DeserializationContext deserializationContext
) throws IOException {
var groups = new ArrayList<Storage.Group>();
var groups = new ArrayList<>();
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
for (var n : node) {
Storage.Group g;
Object g;
if (n.hasNonNull("masterKey")) {
// a v2 group
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) {
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) {
@ -203,9 +203,9 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
try (var inputStream = new FileInputStream(file)) {
var storage = objectMapper.readValue(inputStream, IdentityStorage.class);
var id = new IdentityKey(Base64.getDecoder().decode(storage.getIdentityKey()));
var trustLevel = TrustLevel.fromInt(storage.getTrustLevel());
var added = new Date(storage.getAddedTimestamp());
var id = new IdentityKey(Base64.getDecoder().decode(storage.identityKey()));
var trustLevel = TrustLevel.fromInt(storage.trustLevel());
var added = new Date(storage.addedTimestamp());
final var identityInfo = new IdentityInfo(recipientId, id, trustLevel, added);
cachedIdentities.put(recipientId, identityInfo);
@ -251,32 +251,5 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
}
}
private static final class IdentityStorage {
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;
}
}
private record IdentityStorage(String identityKey, int trustLevel, long addedTimestamp) {}
}

View File

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

View File

@ -1,38 +1,8 @@
package org.asamk.signal.manager.storage.recipients;
public class RecipientId {
private final long id;
RecipientId(final long id) {
this.id = id;
}
public record RecipientId(long id) {
public static RecipientId of(long 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()
.map(Enum::name)
.collect(Collectors.toSet()));
return new Storage.Recipient(pair.getKey().getId(),
return new Storage.Recipient(pair.getKey().id(),
recipient.getAddress().getNumber().orElse(null),
recipient.getAddress().getUuid().map(UUID::toString).orElse(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;
public long lastId;
// For deserialization
private Storage() {
}
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
private record Recipient(
long id,
String number,
String uuid,
String profileKey,
String profileKeyCredential,
Storage.Recipient.Contact contact,
Storage.Recipient.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;
public String color;
public int messageExpirationTime;
public boolean blocked;
public boolean archived;
// For deserialization
public Contact() {
}
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;
}
}
private record Profile(
long lastUpdateTimestamp,
String givenName,
String familyName,
String about,
String aboutEmoji,
String avatarUrlPath,
String unidentifiedAccessMode,
Set<String> capabilities
) {}
}
}

View File

@ -103,7 +103,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups
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);
if (senderKeyRecord != null) {
continue;
@ -126,7 +126,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups
}
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) {
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);
}
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) {
@ -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) {
return sharedSenderKeys.get(distributionId)
.stream()
.map(k -> new SignalProtocolAddress(addressResolver.resolveRecipientAddress(k.getRecipientId())
.getIdentifier(), k.getDeviceId()))
.map(k -> new SignalProtocolAddress(addressResolver.resolveRecipientAddress(k.recipientId())
.getIdentifier(), k.deviceId()))
.collect(Collectors.toSet());
}
}
@ -146,7 +146,7 @@ public class SenderKeySharedStore {
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()
.map(e -> e.recipientId.equals(toBeMergedRecipientId) ? new SenderKeySharedEntry(
recipientId,
e.getDeviceId()) : e)
e.deviceId()) : e)
.collect(Collectors.toSet()));
}
saveLocked();
@ -181,8 +181,8 @@ public class SenderKeySharedStore {
var storage = new Storage(sharedSenderKeys.entrySet().stream().flatMap(pair -> {
final var sharedWith = pair.getValue();
return sharedWith.stream()
.map(entry -> new Storage.SharedSenderKey(entry.getRecipientId().getId(),
entry.getDeviceId(),
.map(entry -> new Storage.SharedSenderKey(entry.recipientId().id(),
entry.deviceId(),
pair.getKey().asUuid().toString()));
}).collect(Collectors.toList()));
@ -199,72 +199,10 @@ public class SenderKeySharedStore {
}
}
private static class Storage {
private record Storage(List<SharedSenderKey> sharedSenderKeys) {
public List<SharedSenderKey> sharedSenderKeys;
// For deserialization
private Storage() {
private record SharedSenderKey(long recipientId, int deviceId, String distributionId) {}
}
public Storage(final List<SharedSenderKey> sharedSenderKeys) {
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;
}
}
private record SenderKeySharedEntry(RecipientId recipientId, int deviceId) {}
}

View File

@ -88,8 +88,8 @@ public class SessionStore implements SignalServiceSessionStore {
synchronized (cachedSessions) {
return getKeysLocked(recipientId).stream()
// get all sessions for recipient except main device session
.filter(key -> key.getDeviceId() != 1 && key.getRecipientId().equals(recipientId))
.map(Key::getDeviceId)
.filter(key -> key.deviceId() != 1 && key.recipientId().equals(recipientId))
.map(Key::deviceId)
.collect(Collectors.toList());
}
}
@ -155,7 +155,7 @@ public class SessionStore implements SignalServiceSessionStore {
.stream()
.flatMap(recipientId -> getKeysLocked(recipientId).stream())
.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());
}
}
@ -197,7 +197,7 @@ public class SessionStore implements SignalServiceSessionStore {
if (session == null) {
continue;
}
final var newKey = new Key(recipientId, key.getDeviceId());
final var newKey = new Key(recipientId, key.deviceId());
storeSessionLocked(newKey, session);
}
}
@ -217,7 +217,7 @@ public class SessionStore implements SignalServiceSessionStore {
}
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) {
return List.of();
}
@ -249,7 +249,7 @@ public class SessionStore implements SignalServiceSessionStore {
} catch (IOException 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) {
@ -324,40 +324,5 @@ public class SessionStore implements SignalServiceSessionStore {
&& record.getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
}
private static final class Key {
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;
}
}
private record Key(RecipientId recipientId, int deviceId) {}
}

View File

@ -65,33 +65,10 @@ public class StickerStore {
.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);
if (pack.stickers == null) {
if (pack.stickers() == null) {
throw new StickerPackInvalidException("Must set a 'stickers' field.");
}
if (pack.stickers.isEmpty()) {
if (pack.stickers().isEmpty()) {
throw new StickerPackInvalidException("Must include stickers.");
}
var stickers = new ArrayList<SignalServiceStickerManifestUpload.StickerInfo>(pack.stickers.size());
for (var sticker : pack.stickers) {
if (sticker.file == null) {
var stickers = new ArrayList<SignalServiceStickerManifestUpload.StickerInfo>(pack.stickers().size());
for (var sticker : pack.stickers()) {
if (sticker.file() == null) {
throw new StickerPackInvalidException("Must set a 'file' field on each sticker.");
}
Pair<InputStream, Long> data;
try {
data = getInputStreamAndLength(rootPath, zip, sticker.file);
data = getInputStreamAndLength(rootPath, zip, sticker.file());
} 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()
? sticker.contentType
: getContentType(rootPath, zip, sticker.file);
var contentType = sticker.contentType() != null && !sticker.contentType().isEmpty()
? sticker.contentType()
: getContentType(rootPath, zip, sticker.file());
var stickerInfo = new SignalServiceStickerManifestUpload.StickerInfo(data.first(),
data.second(),
Optional.fromNullable(sticker.emoji).or(""),
Optional.fromNullable(sticker.emoji()).or(""),
contentType);
stickers.add(stickerInfo);
}
SignalServiceStickerManifestUpload.StickerInfo cover = null;
if (pack.cover != null) {
if (pack.cover.file == null) {
if (pack.cover() != null) {
if (pack.cover().file() == null) {
throw new StickerPackInvalidException("Must set a 'file' field on the cover.");
}
Pair<InputStream, Long> data;
try {
data = getInputStreamAndLength(rootPath, zip, pack.cover.file);
data = getInputStreamAndLength(rootPath, zip, pack.cover().file());
} 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()
? pack.cover.contentType
: getContentType(rootPath, zip, pack.cover.file);
var contentType = pack.cover().contentType() != null && !pack.cover().contentType().isEmpty() ? pack.cover()
.contentType() : getContentType(rootPath, zip, pack.cover().file());
cover = new SignalServiceStickerManifestUpload.StickerInfo(data.first(),
data.second(),
Optional.fromNullable(pack.cover.emoji).or(""),
Optional.fromNullable(pack.cover().emoji()).or(""),
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 {

View File

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

View File

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

View File

@ -66,21 +66,5 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand {
}
}
private static final class JsonUserStatus {
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;
}
}
private record JsonUserStatus(String recipient, String number, String uuid, boolean isRegistered) {}
}

View File

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

View File

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

View File

@ -54,26 +54,5 @@ public class ListContactsCommand implements JsonRpcLocalCommand {
}
}
private static final class JsonContact {
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;
}
}
private record JsonContact(String number, String uuid, String name, boolean isBlocked, int messageExpirationTime) {}
}

View File

@ -45,36 +45,21 @@ public class ListDevicesCommand implements JsonRpcLocalCommand {
if (outputWriter instanceof PlainTextWriter writer) {
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 -> {
w.println("Name: {}", d.getName());
w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated()));
w.println("Last seen: {}", DateUtils.formatTimestamp(d.getLastSeen()));
w.println("Name: {}", d.name());
w.println("Created: {}", DateUtils.formatTimestamp(d.created()));
w.println("Last seen: {}", DateUtils.formatTimestamp(d.lastSeen()));
});
}
} else {
final var writer = (JsonWriter) outputWriter;
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());
writer.write(jsonDevices);
}
}
private static final class JsonDevice {
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;
}
}
private record JsonDevice(long id, String name, long createdTimestamp, long lastSeenTimestamp) {}
}

View File

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

View File

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

View File

@ -55,9 +55,9 @@ public class QuitGroupCommand implements JsonRpcLocalCommand {
try {
try {
final var results = m.quitGroup(groupId, groupAdmins);
final var timestamp = results.getTimestamp();
final var timestamp = results.timestamp();
outputResult(outputWriter, timestamp);
handleSendMessageResults(results.getResults());
handleSendMessageResults(results.results());
} catch (NotAGroupMemberException e) {
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) {
dbusconnection.addSigHandler(Signal.MessageReceived.class, signal, messageReceived -> {
var envelope = new JsonMessageEnvelope(messageReceived);
var envelope = JsonMessageEnvelope.from(messageReceived);
final var object = Map.of("envelope", envelope);
jsonWriter.write(object);
});
dbusconnection.addSigHandler(Signal.ReceiptReceived.class, signal, receiptReceived -> {
var envelope = new JsonMessageEnvelope(receiptReceived);
var envelope = JsonMessageEnvelope.from(receiptReceived);
final var object = Map.of("envelope", envelope);
jsonWriter.write(object);
});
dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, signal, syncReceived -> {
var envelope = new JsonMessageEnvelope(syncReceived);
var envelope = JsonMessageEnvelope.from(syncReceived);
final var object = Map.of("envelope", envelope);
jsonWriter.write(object);
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,37 +1,19 @@
package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
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
@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) {
static JsonMention from(SignalServiceDataMessage.Mention mention, Manager m) {
final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid()));
this.name = getLegacyIdentifier(address);
this.number = address.getNumber().orNull();
this.uuid = address.getUuid().toString();
this.start = mention.getStart();
this.length = mention.getLength();
return new JsonMention(getLegacyIdentifier(address),
address.getNumber().orNull(),
address.getUuid().toString(),
mention.getStart(),
mention.getLength());
}
}

View File

@ -1,7 +1,6 @@
package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.Signal;
import org.asamk.signal.manager.Manager;
@ -15,143 +14,133 @@ import java.util.List;
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
@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(
public static JsonMessageEnvelope from(
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()) {
var source = m.resolveSignalServiceAddress(envelope.getSourceAddress());
this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().toString();
this.sourceDevice = envelope.getSourceDevice();
final var sourceAddress = m.resolveSignalServiceAddress(envelope.getSourceAddress());
source = getLegacyIdentifier(sourceAddress);
sourceNumber = sourceAddress.getNumber().orNull();
sourceUuid = sourceAddress.getUuid().toString();
sourceDevice = envelope.getSourceDevice();
} else if (envelope.isUnidentifiedSender() && content != null) {
final var source = m.resolveSignalServiceAddress(content.getSender());
this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().toString();
this.sourceDevice = content.getSenderDevice();
final var sender = m.resolveSignalServiceAddress(content.getSender());
source = getLegacyIdentifier(sender);
sourceNumber = sender.getNumber().orNull();
sourceUuid = sender.getUuid().toString();
sourceDevice = content.getSenderDevice();
} else if (exception instanceof UntrustedIdentityException e) {
final var source = m.resolveSignalServiceAddress(e.getSender());
this.source = getLegacyIdentifier(source);
this.sourceNumber = source.getNumber().orNull();
this.sourceUuid = source.getUuid().toString();
this.sourceDevice = e.getSenderDevice();
final var sender = m.resolveSignalServiceAddress(e.getSender());
source = getLegacyIdentifier(sender);
sourceNumber = sender.getNumber().orNull();
sourceUuid = sender.getUuid().toString();
sourceDevice = e.getSenderDevice();
} else {
this.source = null;
this.sourceNumber = null;
this.sourceUuid = null;
this.sourceDevice = null;
source = null;
sourceNumber = null;
sourceUuid = null;
sourceDevice = null;
}
String name;
try {
name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(this.source, m.getSelfNumber()));
name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(source, m.getSelfNumber()));
} catch (InvalidNumberException | NullPointerException e) {
name = null;
}
this.sourceName = name;
this.timestamp = envelope.getTimestamp();
final var sourceName = name;
final var timestamp = envelope.getTimestamp();
final JsonReceiptMessage receiptMessage;
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));
dataMessage = null;
syncMessage = null;
callMessage = null;
typingMessage = null;
} else if (content != null && content.getReceiptMessage().isPresent()) {
receiptMessage = JsonReceiptMessage.from(content.getReceiptMessage().get());
} else {
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) {
source = messageReceived.getSource();
sourceNumber = null;
sourceUuid = null;
sourceName = null;
sourceDevice = null;
timestamp = messageReceived.getTimestamp();
receiptMessage = null;
dataMessage = null;
syncMessage = new JsonSyncMessage(messageReceived);
callMessage = null;
typingMessage = null;
public static JsonMessageEnvelope from(Signal.MessageReceived messageReceived) {
return new JsonMessageEnvelope(messageReceived.getSource(),
null,
null,
null,
null,
messageReceived.getTimestamp(),
JsonDataMessage.from(messageReceived),
null,
null,
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;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
@ -12,55 +11,41 @@ import java.util.stream.Collectors;
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
final long id;
@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();
static JsonQuote from(SignalServiceDataMessage.Quote quote, Manager m) {
final var id = quote.getId();
final var address = m.resolveSignalServiceAddress(quote.getAuthor());
this.author = getLegacyIdentifier(address);
this.authorNumber = address.getNumber().orNull();
this.authorUuid = address.getUuid().toString();
this.text = quote.getText();
final var author = getLegacyIdentifier(address);
final var authorNumber = address.getNumber().orNull();
final var authorUuid = address.getUuid().toString();
final var text = quote.getText();
final List<JsonMention> mentions;
if (quote.getMentions() != null && quote.getMentions().size() > 0) {
this.mentions = quote.getMentions()
mentions = quote.getMentions()
.stream()
.map(quotedMention -> new JsonMention(quotedMention, m))
.map(quotedMention -> JsonMention.from(quotedMention, m))
.collect(Collectors.toList());
} else {
this.mentions = null;
mentions = null;
}
final List<JsonQuotedAttachment> attachments;
if (quote.getAttachments().size() > 0) {
this.attachments = quote.getAttachments()
.stream()
.map(JsonQuotedAttachment::new)
.collect(Collectors.toList());
attachments = quote.getAttachments().stream().map(JsonQuotedAttachment::from).collect(Collectors.toList());
} 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;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
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
final String contentType;
@JsonProperty
final String filename;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
static JsonQuotedAttachment from(SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment) {
final var contentType = quotedAttachment.getContentType();
final var filename = quotedAttachment.getFileName();
final JsonAttachment thumbnail;
JsonQuotedAttachment(SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment) {
contentType = quotedAttachment.getContentType();
filename = quotedAttachment.getFileName();
if (quotedAttachment.getThumbnail() != null) {
thumbnail = new JsonAttachment(quotedAttachment.getThumbnail());
thumbnail = JsonAttachment.from(quotedAttachment.getThumbnail());
} else {
thumbnail = null;
}
return new JsonQuotedAttachment(contentType, filename, thumbnail);
}
}

View File

@ -1,40 +1,32 @@
package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction;
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
final String emoji;
@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();
static JsonReaction from(Reaction reaction, Manager m) {
final var emoji = reaction.getEmoji();
final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor());
this.targetAuthor = getLegacyIdentifier(address);
this.targetAuthorNumber = address.getNumber().orNull();
this.targetAuthorUuid = address.getUuid().toString();
this.targetSentTimestamp = reaction.getTargetSentTimestamp();
this.isRemove = reaction.isRemove();
final var targetAuthor = getLegacyIdentifier(address);
final var targetAuthorNumber = address.getNumber().orNull();
final var targetAuthorUuid = address.getUuid().toString();
final var targetSentTimestamp = reaction.getTargetSentTimestamp();
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;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
import java.util.List;
class JsonReceiptMessage {
record JsonReceiptMessage(long when, boolean isDelivery, boolean isRead, List<Long> timestamps) {
@JsonProperty
final long when;
@JsonProperty
final boolean isDelivery;
@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 from(SignalServiceReceiptMessage receiptMessage) {
final var when = receiptMessage.getWhen();
final var isDelivery = receiptMessage.isDeliveryReceipt();
final var isRead = receiptMessage.isReadReceipt();
final var timestamps = receiptMessage.getTimestamps();
return new JsonReceiptMessage(when, isDelivery, isRead, timestamps);
}
static JsonReceiptMessage deliveryReceipt(final long when, final List<Long> timestamps) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,28 +1,26 @@
package org.asamk.signal.json;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
import java.util.Base64;
class JsonTypingMessage {
record JsonTypingMessage(
String action, long timestamp, @JsonInclude(JsonInclude.Include.NON_NULL) String groupId
) {
@JsonProperty
final String action;
JsonTypingMessage(final String action, final long timestamp, final String groupId) {
this.action = action;
this.timestamp = timestamp;
this.groupId = groupId;
}
@JsonProperty
final long timestamp;
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_NULL)
final String groupId;
JsonTypingMessage(SignalServiceTypingMessage typingMessage) {
this.action = typingMessage.getAction().name();
this.timestamp = typingMessage.getTimestamp();
static JsonTypingMessage from(SignalServiceTypingMessage typingMessage) {
final var action = typingMessage.getAction().name();
final var timestamp = typingMessage.getTimestamp();
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);
}
}