Add support for versioned expiration timers.

Co-authored-by: Greyson Parrelli <greyson@signal.org>
This commit is contained in:
Cody Henthorne
2024-08-27 07:41:35 -04:00
committed by Nicholas Tinsley
parent 4152294b57
commit 1f196f74ff
43 changed files with 392 additions and 139 deletions

View File

@@ -1059,6 +1059,7 @@ public class SignalServiceMessageSender {
if (message.getExpiresInSeconds() > 0) {
builder.expireTimer(message.getExpiresInSeconds());
}
builder.expireTimerVersion(message.getExpireTimerVersion());
if (message.getProfileKey().isPresent()) {
builder.profileKey(ByteString.of(message.getProfileKey().get()));

View File

@@ -55,6 +55,7 @@ class AccountAttributes @JsonCreator constructor(
data class Capabilities @JsonCreator constructor(
@JsonProperty val storage: Boolean,
@JsonProperty val deleteSync: Boolean
@JsonProperty val deleteSync: Boolean,
@JsonProperty val expireTimerVersion: Boolean
)
}

View File

@@ -34,6 +34,7 @@ class SignalServiceDataMessage private constructor(
val body: Optional<String>,
val isEndSession: Boolean,
val expiresInSeconds: Int,
val expireTimerVersion: Int,
val isExpirationUpdate: Boolean,
val profileKey: Optional<ByteArray>,
val isProfileKeyUpdate: Boolean,
@@ -79,6 +80,7 @@ class SignalServiceDataMessage private constructor(
private var body: String? = null
private var endSession: Boolean = false
private var expiresInSeconds: Int = 0
private var expireTimerVersion: Int = 1
private var expirationUpdate: Boolean = false
private var profileKey: ByteArray? = null
private var profileKeyUpdate: Boolean = false
@@ -133,6 +135,11 @@ class SignalServiceDataMessage private constructor(
return this
}
fun withExpireTimerVersion(expireTimerVersion: Int): Builder {
this.expireTimerVersion = expireTimerVersion
return this
}
fun withProfileKey(profileKey: ByteArray?): Builder {
this.profileKey = profileKey
return this
@@ -225,6 +232,7 @@ class SignalServiceDataMessage private constructor(
body = body.emptyIfStringEmpty(),
isEndSession = endSession,
expiresInSeconds = expiresInSeconds,
expireTimerVersion = expireTimerVersion,
isExpirationUpdate = expirationUpdate,
profileKey = profileKey.asOptional(),
isProfileKeyUpdate = profileKeyUpdate,

View File

@@ -21,6 +21,7 @@ public class DeviceContact {
private final Optional<VerifiedMessage> verified;
private final Optional<ProfileKey> profileKey;
private final Optional<Integer> expirationTimer;
private final Optional<Integer> expirationTimerVersion;
private final Optional<Integer> inboxPosition;
private final boolean archived;
@@ -32,6 +33,7 @@ public class DeviceContact {
Optional<VerifiedMessage> verified,
Optional<ProfileKey> profileKey,
Optional<Integer> expirationTimer,
Optional<Integer> expirationTimerVersion,
Optional<Integer> inboxPosition,
boolean archived)
{
@@ -39,16 +41,17 @@ public class DeviceContact {
throw new IllegalArgumentException("Must have either ACI or E164");
}
this.aci = aci;
this.e164 = e164;
this.name = name;
this.avatar = avatar;
this.color = color;
this.verified = verified;
this.profileKey = profileKey;
this.expirationTimer = expirationTimer;
this.inboxPosition = inboxPosition;
this.archived = archived;
this.aci = aci;
this.e164 = e164;
this.name = name;
this.avatar = avatar;
this.color = color;
this.verified = verified;
this.profileKey = profileKey;
this.expirationTimer = expirationTimer;
this.expirationTimerVersion = expirationTimerVersion;
this.inboxPosition = inboxPosition;
this.archived = archived;
}
public Optional<DeviceContactAvatar> getAvatar() {
@@ -83,6 +86,10 @@ public class DeviceContact {
return expirationTimer;
}
public Optional<Integer> getExpirationTimerVersion() {
return expirationTimerVersion;
}
public Optional<Integer> getInboxPosition() {
return inboxPosition;
}

View File

@@ -47,16 +47,17 @@ public class DeviceContactsInputStream extends ChunkedInputStream {
throw new IOException("Missing contact address!");
}
Optional<ACI> aci = Optional.ofNullable(ACI.parseOrNull(details.aci));
Optional<String> e164 = Optional.ofNullable(details.number);
Optional<String> name = Optional.ofNullable(details.name);
Optional<DeviceContactAvatar> avatar = Optional.empty();
Optional<String> color = details.color != null ? Optional.of(details.color) : Optional.empty();
Optional<VerifiedMessage> verified = Optional.empty();
Optional<ProfileKey> profileKey = Optional.empty();
Optional<Integer> expireTimer = Optional.empty();
Optional<Integer> inboxPosition = Optional.empty();
boolean archived = false;
Optional<ACI> aci = Optional.ofNullable(ACI.parseOrNull(details.aci));
Optional<String> e164 = Optional.ofNullable(details.number);
Optional<String> name = Optional.ofNullable(details.name);
Optional<DeviceContactAvatar> avatar = Optional.empty();
Optional<String> color = details.color != null ? Optional.of(details.color) : Optional.empty();
Optional<VerifiedMessage> verified = Optional.empty();
Optional<ProfileKey> profileKey = Optional.empty();
Optional<Integer> expireTimer = Optional.empty();
Optional<Integer> expireTimerVersion = Optional.empty();
Optional<Integer> inboxPosition = Optional.empty();
boolean archived = false;
if (details.avatar != null && details.avatar.length != null) {
long avatarLength = details.avatar.length;
@@ -103,13 +104,17 @@ public class DeviceContactsInputStream extends ChunkedInputStream {
expireTimer = Optional.of(details.expireTimer);
}
if (details.expireTimerVersion != null && details.expireTimerVersion > 0) {
expireTimerVersion = Optional.of(details.expireTimerVersion);
}
if (details.inboxPosition != null) {
inboxPosition = Optional.of(details.inboxPosition);
}
archived = details.archived;
return new DeviceContact(aci, e164, name, avatar, color, verified, profileKey, expireTimer, inboxPosition, archived);
return new DeviceContact(aci, e164, name, avatar, color, verified, profileKey, expireTimer, expireTimerVersion, inboxPosition, archived);
}
}

View File

@@ -195,12 +195,16 @@ public class SignalServiceProfile {
@JsonProperty
private boolean deleteSync;
@JsonProperty
private boolean versionedExpirationTimer;
@JsonCreator
public Capabilities() {}
public Capabilities(boolean storage, boolean deleteSync) {
this.storage = storage;
this.deleteSync = deleteSync;
public Capabilities(boolean storage, boolean deleteSync, boolean versionedExpirationTimer) {
this.storage = storage;
this.deleteSync = deleteSync;
this.versionedExpirationTimer = versionedExpirationTimer;
}
public boolean isStorage() {
@@ -210,6 +214,10 @@ public class SignalServiceProfile {
public boolean isDeleteSync() {
return deleteSync;
}
public boolean isVersionedExpirationTimer() {
return versionedExpirationTimer;
}
}
public ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse() {

View File

@@ -332,6 +332,7 @@ message DataMessage {
optional GroupContextV2 groupV2 = 15;
optional uint32 flags = 4;
optional uint32 expireTimer = 5;
optional uint32 expireTimerVersion = 23;
optional bytes profileKey = 6;
optional uint64 timestamp = 7;
optional Quote quote = 8;
@@ -793,17 +794,18 @@ message ContactDetails {
optional uint32 length = 2;
}
optional string number = 1;
optional string aci = 9;
optional string name = 2;
optional Avatar avatar = 3;
optional string color = 4;
optional Verified verified = 5;
optional bytes profileKey = 6;
reserved /*blocked*/ 7;
optional uint32 expireTimer = 8;
optional uint32 inboxPosition = 10;
optional bool archived = 11;
optional string number = 1;
optional string aci = 9;
optional string name = 2;
optional Avatar avatar = 3;
optional string color = 4;
optional Verified verified = 5;
optional bytes profileKey = 6;
reserved /*blocked*/ 7;
optional uint32 expireTimer = 8;
optional uint32 expireTimerVersion = 12;
optional uint32 inboxPosition = 10;
optional bool archived = 11;
}
message GroupDetails {
@@ -817,17 +819,17 @@ message GroupDetails {
optional string e164 = 2;
}
optional bytes id = 1;
optional string name = 2;
repeated string membersE164 = 3;
repeated Member members = 9;
optional Avatar avatar = 4;
optional bool active = 5 [default = true];
optional uint32 expireTimer = 6;
optional string color = 7;
optional bool blocked = 8;
optional uint32 inboxPosition = 10;
optional bool archived = 11;
optional bytes id = 1;
optional string name = 2;
repeated string membersE164 = 3;
repeated Member members = 9;
optional Avatar avatar = 4;
optional bool active = 5 [default = true];
optional uint32 expireTimer = 6;
optional string color = 7;
optional bool blocked = 8;
optional uint32 inboxPosition = 10;
optional bool archived = 11;
}
message PaymentAddress {

View File

@@ -40,6 +40,7 @@ public class DeviceContactsInputStreamTest {
Optional.of(generateProfileKey()),
Optional.of(0),
Optional.of(0),
Optional.of(0),
false
);
@@ -53,6 +54,7 @@ public class DeviceContactsInputStreamTest {
Optional.of(generateProfileKey()),
Optional.of(0),
Optional.of(0),
Optional.of(0),
false
);