mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
Store profile phone number sharing mode and discoverable state
This commit is contained in:
parent
71de8e63cc
commit
7e0d4c9b89
12 changed files with 202 additions and 47 deletions
|
@ -3,5 +3,16 @@ package org.asamk.signal.manager.api;
|
|||
public enum PhoneNumberSharingMode {
|
||||
EVERYBODY,
|
||||
CONTACTS,
|
||||
NOBODY,
|
||||
NOBODY;
|
||||
|
||||
public static PhoneNumberSharingMode valueOfOrNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return valueOf(value);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ public class Profile {
|
|||
|
||||
private final Set<Capability> capabilities;
|
||||
|
||||
private final PhoneNumberSharingMode phoneNumberSharingMode;
|
||||
|
||||
public Profile(
|
||||
final long lastUpdateTimestamp,
|
||||
final String givenName,
|
||||
|
@ -35,7 +37,8 @@ public class Profile {
|
|||
final String avatarUrlPath,
|
||||
final byte[] mobileCoinAddress,
|
||||
final UnidentifiedAccessMode unidentifiedAccessMode,
|
||||
final Set<Capability> capabilities
|
||||
final Set<Capability> capabilities,
|
||||
final PhoneNumberSharingMode phoneNumberSharingMode
|
||||
) {
|
||||
this.lastUpdateTimestamp = lastUpdateTimestamp;
|
||||
this.givenName = givenName;
|
||||
|
@ -46,6 +49,7 @@ public class Profile {
|
|||
this.mobileCoinAddress = mobileCoinAddress;
|
||||
this.unidentifiedAccessMode = unidentifiedAccessMode;
|
||||
this.capabilities = capabilities;
|
||||
this.phoneNumberSharingMode = phoneNumberSharingMode;
|
||||
}
|
||||
|
||||
private Profile(final Builder builder) {
|
||||
|
@ -58,6 +62,7 @@ public class Profile {
|
|||
mobileCoinAddress = builder.mobileCoinAddress;
|
||||
unidentifiedAccessMode = builder.unidentifiedAccessMode;
|
||||
capabilities = builder.capabilities;
|
||||
phoneNumberSharingMode = builder.phoneNumberSharingMode;
|
||||
}
|
||||
|
||||
public static Builder newBuilder() {
|
||||
|
@ -136,6 +141,10 @@ public class Profile {
|
|||
return capabilities;
|
||||
}
|
||||
|
||||
public PhoneNumberSharingMode getPhoneNumberSharingMode() {
|
||||
return phoneNumberSharingMode;
|
||||
}
|
||||
|
||||
public enum UnidentifiedAccessMode {
|
||||
UNKNOWN,
|
||||
DISABLED,
|
||||
|
@ -200,6 +209,7 @@ public class Profile {
|
|||
private byte[] mobileCoinAddress;
|
||||
private UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.UNKNOWN;
|
||||
private Set<Capability> capabilities = Collections.emptySet();
|
||||
private PhoneNumberSharingMode phoneNumberSharingMode;
|
||||
private long lastUpdateTimestamp = 0;
|
||||
|
||||
private Builder() {
|
||||
|
@ -240,6 +250,11 @@ public class Profile {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder withPhoneNumberSharingMode(final PhoneNumberSharingMode val) {
|
||||
phoneNumberSharingMode = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Profile build() {
|
||||
return new Profile(this);
|
||||
}
|
||||
|
|
|
@ -20,13 +20,16 @@ public class Recipient {
|
|||
|
||||
private final Profile profile;
|
||||
|
||||
private final Boolean discoverable;
|
||||
|
||||
public Recipient(
|
||||
final RecipientId recipientId,
|
||||
final RecipientAddress address,
|
||||
final Contact contact,
|
||||
final ProfileKey profileKey,
|
||||
final ExpiringProfileKeyCredential expiringProfileKeyCredential,
|
||||
final Profile profile
|
||||
final Profile profile,
|
||||
final Boolean discoverable
|
||||
) {
|
||||
this.recipientId = recipientId;
|
||||
this.address = address;
|
||||
|
@ -34,6 +37,7 @@ public class Recipient {
|
|||
this.profileKey = profileKey;
|
||||
this.expiringProfileKeyCredential = expiringProfileKeyCredential;
|
||||
this.profile = profile;
|
||||
this.discoverable = discoverable;
|
||||
}
|
||||
|
||||
private Recipient(final Builder builder) {
|
||||
|
@ -41,8 +45,9 @@ public class Recipient {
|
|||
address = builder.address;
|
||||
contact = builder.contact;
|
||||
profileKey = builder.profileKey;
|
||||
expiringProfileKeyCredential = builder.expiringProfileKeyCredential1;
|
||||
expiringProfileKeyCredential = builder.expiringProfileKeyCredential;
|
||||
profile = builder.profile;
|
||||
discoverable = builder.discoverable;
|
||||
}
|
||||
|
||||
public static Builder newBuilder() {
|
||||
|
@ -55,7 +60,7 @@ public class Recipient {
|
|||
builder.address = copy.getAddress();
|
||||
builder.contact = copy.getContact();
|
||||
builder.profileKey = copy.getProfileKey();
|
||||
builder.expiringProfileKeyCredential1 = copy.getExpiringProfileKeyCredential();
|
||||
builder.expiringProfileKeyCredential = copy.getExpiringProfileKeyCredential();
|
||||
builder.profile = copy.getProfile();
|
||||
return builder;
|
||||
}
|
||||
|
@ -84,6 +89,10 @@ public class Recipient {
|
|||
return profile;
|
||||
}
|
||||
|
||||
public Boolean getDiscoverable() {
|
||||
return discoverable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -108,8 +117,9 @@ public class Recipient {
|
|||
private RecipientAddress address;
|
||||
private Contact contact;
|
||||
private ProfileKey profileKey;
|
||||
private ExpiringProfileKeyCredential expiringProfileKeyCredential1;
|
||||
private ExpiringProfileKeyCredential expiringProfileKeyCredential;
|
||||
private Profile profile;
|
||||
private Boolean discoverable;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
@ -135,7 +145,7 @@ public class Recipient {
|
|||
}
|
||||
|
||||
public Builder withExpiringProfileKeyCredential(final ExpiringProfileKeyCredential val) {
|
||||
expiringProfileKeyCredential1 = val;
|
||||
expiringProfileKeyCredential = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -144,6 +154,11 @@ public class Recipient {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder withDiscoverable(final Boolean val) {
|
||||
discoverable = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Recipient build() {
|
||||
return new Recipient(this);
|
||||
}
|
||||
|
|
|
@ -363,6 +363,7 @@ public final class ProfileHelper {
|
|||
|
||||
logger.trace("Storing profile");
|
||||
account.getProfileStore().storeProfile(recipientId, newProfile);
|
||||
account.getRecipientStore().markRegistered(recipientId, true);
|
||||
|
||||
logger.trace("Done handling retrieved profile");
|
||||
}).doOnError(e -> {
|
||||
|
@ -374,6 +375,10 @@ public final class ProfileHelper {
|
|||
.withUnidentifiedAccessMode(Profile.UnidentifiedAccessMode.UNKNOWN)
|
||||
.withCapabilities(Set.of())
|
||||
.build();
|
||||
if (e instanceof NotFoundException) {
|
||||
logger.debug("Marking recipient {} as unregistered after 404 profile fetch.", recipientId);
|
||||
account.getRecipientStore().markRegistered(recipientId, false);
|
||||
}
|
||||
|
||||
account.getProfileStore().storeProfile(recipientId, newProfile);
|
||||
});
|
||||
|
|
|
@ -187,7 +187,8 @@ public class RecipientHelper {
|
|||
|
||||
final var unregisteredUsers = new HashSet<>(numbers);
|
||||
unregisteredUsers.removeAll(registeredUsers.keySet());
|
||||
account.getRecipientStore().markUnregistered(unregisteredUsers);
|
||||
account.getRecipientStore().markUndiscoverablePossiblyUnregistered(unregisteredUsers);
|
||||
account.getRecipientStore().markDiscoverable(registeredUsers.keySet());
|
||||
|
||||
return registeredUsers;
|
||||
}
|
||||
|
|
|
@ -1310,7 +1310,8 @@ public class ManagerImpl implements Manager {
|
|||
s.getContact(),
|
||||
s.getProfileKey(),
|
||||
s.getExpiringProfileKeyCredential(),
|
||||
s.getProfile()))
|
||||
s.getProfile(),
|
||||
s.getDiscoverable()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import java.util.UUID;
|
|||
public class AccountDatabase extends Database {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccountDatabase.class);
|
||||
private static final long DATABASE_VERSION = 25;
|
||||
private static final long DATABASE_VERSION = 26;
|
||||
|
||||
private AccountDatabase(final HikariDataSource dataSource) {
|
||||
super(logger, DATABASE_VERSION, dataSource);
|
||||
|
@ -591,6 +591,15 @@ public class AccountDatabase extends Database {
|
|||
""");
|
||||
}
|
||||
}
|
||||
if (oldVersion < 26) {
|
||||
logger.debug("Updating database: Create discoverabel and profile_phone_number_sharing columns");
|
||||
try (final var statement = connection.createStatement()) {
|
||||
statement.executeUpdate("""
|
||||
ALTER TABLE recipient ADD discoverable INTEGER;
|
||||
ALTER TABLE recipient ADD profile_phone_number_sharing TEXT;
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createUuidMappingTable(
|
||||
|
|
|
@ -914,7 +914,8 @@ public class SignalAccount implements Closeable {
|
|||
: profile.getUnidentifiedAccess() != null
|
||||
? Profile.UnidentifiedAccessMode.ENABLED
|
||||
: Profile.UnidentifiedAccessMode.DISABLED,
|
||||
capabilities);
|
||||
capabilities,
|
||||
null);
|
||||
getProfileStore().storeProfile(recipientId, newProfile);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,8 @@ public class LegacyRecipientStore2 {
|
|||
r.profile.capabilities.stream()
|
||||
.map(Profile.Capability::valueOfOrNull)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet()));
|
||||
.collect(Collectors.toSet()),
|
||||
null);
|
||||
}
|
||||
|
||||
return new Recipient(recipientId,
|
||||
|
@ -96,6 +97,7 @@ public class LegacyRecipientStore2 {
|
|||
profileKey,
|
||||
expiringProfileKeyCredential,
|
||||
profile,
|
||||
null,
|
||||
null);
|
||||
}).collect(Collectors.toMap(Recipient::getRecipientId, r -> r));
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ public class Recipient {
|
|||
|
||||
private final Profile profile;
|
||||
|
||||
private final Boolean discoverable;
|
||||
|
||||
private final byte[] storageRecord;
|
||||
|
||||
public Recipient(
|
||||
|
@ -30,6 +32,7 @@ public class Recipient {
|
|||
final ProfileKey profileKey,
|
||||
final ExpiringProfileKeyCredential expiringProfileKeyCredential,
|
||||
final Profile profile,
|
||||
final Boolean discoverable,
|
||||
final byte[] storageRecord
|
||||
) {
|
||||
this.recipientId = recipientId;
|
||||
|
@ -38,6 +41,7 @@ public class Recipient {
|
|||
this.profileKey = profileKey;
|
||||
this.expiringProfileKeyCredential = expiringProfileKeyCredential;
|
||||
this.profile = profile;
|
||||
this.discoverable = discoverable;
|
||||
this.storageRecord = storageRecord;
|
||||
}
|
||||
|
||||
|
@ -48,6 +52,7 @@ public class Recipient {
|
|||
profileKey = builder.profileKey;
|
||||
expiringProfileKeyCredential = builder.expiringProfileKeyCredential;
|
||||
profile = builder.profile;
|
||||
discoverable = builder.discoverable;
|
||||
storageRecord = builder.storageRecord;
|
||||
}
|
||||
|
||||
|
@ -91,6 +96,10 @@ public class Recipient {
|
|||
return profile;
|
||||
}
|
||||
|
||||
public Boolean getDiscoverable() {
|
||||
return discoverable;
|
||||
}
|
||||
|
||||
public byte[] getStorageRecord() {
|
||||
return storageRecord;
|
||||
}
|
||||
|
@ -121,6 +130,7 @@ public class Recipient {
|
|||
private ProfileKey profileKey;
|
||||
private ExpiringProfileKeyCredential expiringProfileKeyCredential;
|
||||
private Profile profile;
|
||||
private Boolean discoverable;
|
||||
private byte[] storageRecord;
|
||||
|
||||
private Builder() {
|
||||
|
@ -156,6 +166,11 @@ public class Recipient {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder withDiscoverable(final Boolean val) {
|
||||
discoverable = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withStorageRecord(final byte[] val) {
|
||||
storageRecord = val;
|
||||
return this;
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.asamk.signal.manager.storage.recipients;
|
|||
|
||||
import org.asamk.signal.manager.api.Contact;
|
||||
import org.asamk.signal.manager.api.Pair;
|
||||
import org.asamk.signal.manager.api.PhoneNumberSharingMode;
|
||||
import org.asamk.signal.manager.api.Profile;
|
||||
import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
||||
import org.asamk.signal.manager.storage.Database;
|
||||
|
@ -64,6 +65,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
aci TEXT UNIQUE,
|
||||
pni TEXT UNIQUE,
|
||||
unregistered_timestamp INTEGER,
|
||||
discoverable INTEGER,
|
||||
profile_key BLOB,
|
||||
profile_key_credential BLOB,
|
||||
needs_pni_signature INTEGER NOT NULL DEFAULT FALSE,
|
||||
|
@ -92,7 +94,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
profile_avatar_url_path TEXT,
|
||||
profile_mobile_coin_address BLOB,
|
||||
profile_unidentified_access_mode TEXT,
|
||||
profile_capabilities TEXT
|
||||
profile_capabilities TEXT,
|
||||
profile_phone_number_sharing TEXT
|
||||
) STRICT;
|
||||
""");
|
||||
}
|
||||
|
@ -354,7 +357,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
r.number, r.aci, r.pni, r.username,
|
||||
r.profile_key, r.profile_key_credential,
|
||||
r.given_name, r.family_name, r.nick_name, r.expiration_time, r.mute_until, r.hide_story, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden, r.unregistered_timestamp,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities, r.profile_phone_number_sharing,
|
||||
r.discoverable,
|
||||
r.storage_record
|
||||
FROM %s r
|
||||
WHERE r._id = ?
|
||||
|
@ -373,7 +377,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
r.number, r.aci, r.pni, r.username,
|
||||
r.profile_key, r.profile_key_credential,
|
||||
r.given_name, r.family_name, r.nick_name, r.expiration_time, r.mute_until, r.hide_story, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden, r.unregistered_timestamp,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities, r.profile_phone_number_sharing,
|
||||
r.discoverable,
|
||||
r.storage_record
|
||||
FROM %s r
|
||||
WHERE r.storage_id = ?
|
||||
|
@ -409,7 +414,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
r.number, r.aci, r.pni, r.username,
|
||||
r.profile_key, r.profile_key_credential,
|
||||
r.given_name, r.family_name, r.nick_name, r.expiration_time, r.mute_until, r.hide_story, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden, r.unregistered_timestamp,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities,
|
||||
r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities, r.profile_phone_number_sharing,
|
||||
r.discoverable,
|
||||
r.storage_record
|
||||
FROM %s r
|
||||
WHERE (r.number IS NOT NULL OR r.aci IS NOT NULL) AND %s
|
||||
|
@ -898,15 +904,19 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
}
|
||||
}
|
||||
|
||||
public void markUnregistered(final Set<String> unregisteredUsers) {
|
||||
logger.debug("Marking {} numbers as unregistered", unregisteredUsers.size());
|
||||
public void markUndiscoverablePossiblyUnregistered(final Set<String> numbers) {
|
||||
logger.debug("Marking {} numbers as unregistered", numbers.size());
|
||||
try (final var connection = database.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
for (final var number : unregisteredUsers) {
|
||||
final var recipient = findByNumber(connection, number);
|
||||
if (recipient.isPresent()) {
|
||||
final var recipientId = recipient.get().id();
|
||||
markUnregisteredAndSplitIfNecessary(connection, recipientId);
|
||||
for (final var number : numbers) {
|
||||
final var recipientAddress = findByNumber(connection, number);
|
||||
if (recipientAddress.isPresent()) {
|
||||
final var recipientId = recipientAddress.get().id();
|
||||
markDiscoverable(connection, recipientId, false);
|
||||
final var contact = getContact(connection, recipientId);
|
||||
if (recipientAddress.get().address().aci().isEmpty() || contact.unregisteredTimestamp() != null) {
|
||||
markUnregisteredAndSplitIfNecessary(connection, recipientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
connection.commit();
|
||||
|
@ -915,6 +925,38 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
}
|
||||
}
|
||||
|
||||
public void markDiscoverable(final Set<String> numbers) {
|
||||
logger.debug("Marking {} numbers as discoverable", numbers.size());
|
||||
try (final var connection = database.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
for (final var number : numbers) {
|
||||
final var recipientAddress = findByNumber(connection, number);
|
||||
if (recipientAddress.isPresent()) {
|
||||
final var recipientId = recipientAddress.get().id();
|
||||
markDiscoverable(connection, recipientId, true);
|
||||
}
|
||||
}
|
||||
connection.commit();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed update recipient store", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void markRegistered(final RecipientId recipientId, final boolean registered) {
|
||||
logger.debug("Marking {} as registered={}", recipientId, registered);
|
||||
try (final var connection = database.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
if (registered) {
|
||||
markRegistered(connection, recipientId);
|
||||
} else {
|
||||
markUnregistered(connection, recipientId);
|
||||
}
|
||||
connection.commit();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed update recipient store", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void markUnregisteredAndSplitIfNecessary(
|
||||
final Connection connection, final RecipientId recipientId
|
||||
) throws SQLException {
|
||||
|
@ -927,6 +969,23 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
}
|
||||
}
|
||||
|
||||
private void markDiscoverable(
|
||||
final Connection connection, final RecipientId recipientId, final boolean discoverable
|
||||
) throws SQLException {
|
||||
final var sql = (
|
||||
"""
|
||||
UPDATE %s
|
||||
SET discoverable = ?
|
||||
WHERE _id = ?
|
||||
"""
|
||||
).formatted(TABLE_RECIPIENT);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
statement.setBoolean(1, discoverable);
|
||||
statement.setLong(2, recipientId.id());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void markRegistered(
|
||||
final Connection connection, final RecipientId recipientId
|
||||
) throws SQLException {
|
||||
|
@ -949,8 +1008,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
final var sql = (
|
||||
"""
|
||||
UPDATE %s
|
||||
SET unregistered_timestamp = ?
|
||||
WHERE _id = ? AND unregistered_timestamp IS NULL
|
||||
SET unregistered_timestamp = ?, discoverable = FALSE
|
||||
WHERE _id = ?
|
||||
"""
|
||||
).formatted(TABLE_RECIPIENT);
|
||||
try (final var statement = connection.prepareStatement(sql)) {
|
||||
|
@ -985,7 +1044,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
final var sql = (
|
||||
"""
|
||||
UPDATE %s
|
||||
SET profile_last_update_timestamp = ?, profile_given_name = ?, profile_family_name = ?, profile_about = ?, profile_about_emoji = ?, profile_avatar_url_path = ?, profile_mobile_coin_address = ?, profile_unidentified_access_mode = ?, profile_capabilities = ?
|
||||
SET profile_last_update_timestamp = ?, profile_given_name = ?, profile_family_name = ?, profile_about = ?, profile_about_emoji = ?, profile_avatar_url_path = ?, profile_mobile_coin_address = ?, profile_unidentified_access_mode = ?, profile_capabilities = ?, profile_phone_number_sharing = ?
|
||||
WHERE _id = ?
|
||||
"""
|
||||
).formatted(TABLE_RECIPIENT);
|
||||
|
@ -1002,7 +1061,11 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
profile == null
|
||||
? null
|
||||
: profile.getCapabilities().stream().map(Enum::name).collect(Collectors.joining(",")));
|
||||
statement.setLong(10, recipientId.id());
|
||||
statement.setString(10,
|
||||
profile == null || profile.getPhoneNumberSharingMode() == null
|
||||
? null
|
||||
: profile.getPhoneNumberSharingMode().name());
|
||||
statement.setLong(11, recipientId.id());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
rotateStorageId(connection, recipientId);
|
||||
|
@ -1396,7 +1459,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
public Profile getProfile(final Connection connection, final RecipientId recipientId) throws SQLException {
|
||||
final var sql = (
|
||||
"""
|
||||
SELECT r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities
|
||||
SELECT r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities, r.profile_phone_number_sharing
|
||||
FROM %s r
|
||||
WHERE r._id = ? AND r.profile_capabilities IS NOT NULL
|
||||
"""
|
||||
|
@ -1431,6 +1494,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
getProfileKeyFromResultSet(resultSet),
|
||||
getExpiringProfileKeyCredentialFromResultSet(resultSet),
|
||||
getProfileFromResultSet(resultSet),
|
||||
getDiscoverableFromResultSet(resultSet),
|
||||
getStorageRecordFromResultSet(resultSet));
|
||||
}
|
||||
|
||||
|
@ -1453,6 +1517,14 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
unregisteredTimestamp == 0 ? null : unregisteredTimestamp);
|
||||
}
|
||||
|
||||
private static Boolean getDiscoverableFromResultSet(final ResultSet resultSet) throws SQLException {
|
||||
final var discoverable = resultSet.getBoolean("discoverable");
|
||||
if (resultSet.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return discoverable;
|
||||
}
|
||||
|
||||
private Profile getProfileFromResultSet(ResultSet resultSet) throws SQLException {
|
||||
final var profileCapabilities = resultSet.getString("profile_capabilities");
|
||||
final var profileUnidentifiedAccessMode = resultSet.getString("profile_unidentified_access_mode");
|
||||
|
@ -1471,7 +1543,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re
|
|||
: Arrays.stream(profileCapabilities.split(","))
|
||||
.map(Profile.Capability::valueOfOrNull)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet()));
|
||||
.collect(Collectors.toSet()),
|
||||
PhoneNumberSharingMode.valueOfOrNull(resultSet.getString("profile_phone_number_sharing")));
|
||||
}
|
||||
|
||||
private ProfileKey getProfileKeyFromResultSet(ResultSet resultSet) throws SQLException {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.asamk.signal.manager.util;
|
||||
|
||||
import org.asamk.signal.manager.api.Pair;
|
||||
import org.asamk.signal.manager.api.PhoneNumberSharingMode;
|
||||
import org.asamk.signal.manager.api.Profile;
|
||||
import org.signal.libsignal.protocol.IdentityKey;
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
|
@ -16,6 +17,7 @@ import org.whispersystems.signalservice.internal.push.PaymentAddress;
|
|||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ProfileUtils {
|
||||
|
||||
|
@ -33,11 +35,14 @@ public class ProfileUtils {
|
|||
}
|
||||
|
||||
try {
|
||||
var name = decrypt(encryptedProfile.getName(), profileCipher);
|
||||
var about = trimZeros(decrypt(encryptedProfile.getAbout(), profileCipher));
|
||||
var aboutEmoji = trimZeros(decrypt(encryptedProfile.getAboutEmoji(), profileCipher));
|
||||
var name = decryptString(encryptedProfile.getName(), profileCipher);
|
||||
var about = decryptString(encryptedProfile.getAbout(), profileCipher);
|
||||
var aboutEmoji = decryptString(encryptedProfile.getAboutEmoji(), profileCipher);
|
||||
|
||||
final var nameParts = splitName(name);
|
||||
final var remotePhoneNumberSharing = decryptBoolean(encryptedProfile.getPhoneNumberSharing(),
|
||||
profileCipher).map(v -> v ? PhoneNumberSharingMode.EVERYBODY : PhoneNumberSharingMode.NOBODY)
|
||||
.orElse(null);
|
||||
return new Profile(System.currentTimeMillis(),
|
||||
nameParts.first(),
|
||||
nameParts.second(),
|
||||
|
@ -50,7 +55,8 @@ public class ProfileUtils {
|
|||
profileCipher,
|
||||
identityKey.getPublicKey()),
|
||||
getUnidentifiedAccessMode(encryptedProfile, profileCipher),
|
||||
getCapabilities(encryptedProfile));
|
||||
getCapabilities(encryptedProfile),
|
||||
remotePhoneNumberSharing);
|
||||
} catch (InvalidCiphertextException e) {
|
||||
logger.debug("Failed to decrypt profile for {}", encryptedProfile.getServiceId(), e);
|
||||
return null;
|
||||
|
@ -83,18 +89,28 @@ public class ProfileUtils {
|
|||
return capabilities;
|
||||
}
|
||||
|
||||
private static String decrypt(
|
||||
final String encryptedName, final ProfileCipher profileCipher
|
||||
private static String decryptString(
|
||||
final String encrypted, final ProfileCipher profileCipher
|
||||
) throws InvalidCiphertextException {
|
||||
try {
|
||||
return encryptedName == null
|
||||
? null
|
||||
: new String(profileCipher.decrypt(Base64.getDecoder().decode(encryptedName)));
|
||||
return encrypted == null ? null : profileCipher.decryptString(Base64.getDecoder().decode(encrypted));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<Boolean> decryptBoolean(
|
||||
final String encrypted, final ProfileCipher profileCipher
|
||||
) throws InvalidCiphertextException {
|
||||
try {
|
||||
return encrypted == null
|
||||
? Optional.empty()
|
||||
: profileCipher.decryptBoolean(Base64.getDecoder().decode(encrypted));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] decryptAndVerifyMobileCoinAddress(
|
||||
final byte[] encryptedPaymentAddress, final ProfileCipher profileCipher, final ECPublicKey publicKey
|
||||
) throws InvalidCiphertextException {
|
||||
|
@ -129,13 +145,4 @@ public class ProfileUtils {
|
|||
default -> new Pair<>(parts[0], parts[1]);
|
||||
};
|
||||
}
|
||||
|
||||
static String trimZeros(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int pos = str.indexOf(0);
|
||||
return pos == -1 ? str : str.substring(0, pos);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue