141 lines
7.1 KiB
Java
141 lines
7.1 KiB
Java
package org.thoughtcrime.securesms.storage;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
|
|
import com.annimon.stream.Stream;
|
|
|
|
import org.thoughtcrime.securesms.logging.Log;
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
import org.whispersystems.libsignal.util.guava.Optional;
|
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
|
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
|
|
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.IdentityState;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.UUID;
|
|
|
|
class ContactConflictMerger implements StorageSyncHelper.ConflictMerger<SignalContactRecord> {
|
|
|
|
private static final String TAG = Log.tag(ContactConflictMerger.class);
|
|
|
|
private final Map<UUID, SignalContactRecord> localByUuid = new HashMap<>();
|
|
private final Map<String, SignalContactRecord> localByE164 = new HashMap<>();
|
|
|
|
private final Recipient self;
|
|
|
|
ContactConflictMerger(@NonNull Collection<SignalContactRecord> localOnly, @NonNull Recipient self) {
|
|
for (SignalContactRecord contact : localOnly) {
|
|
if (contact.getAddress().getUuid().isPresent()) {
|
|
localByUuid.put(contact.getAddress().getUuid().get(), contact);
|
|
}
|
|
if (contact.getAddress().getNumber().isPresent()) {
|
|
localByE164.put(contact.getAddress().getNumber().get(), contact);
|
|
}
|
|
}
|
|
|
|
this.self = self.resolve();
|
|
}
|
|
|
|
@Override
|
|
public @NonNull Optional<SignalContactRecord> getMatching(@NonNull SignalContactRecord record) {
|
|
SignalContactRecord localUuid = record.getAddress().getUuid().isPresent() ? localByUuid.get(record.getAddress().getUuid().get()) : null;
|
|
SignalContactRecord localE164 = record.getAddress().getNumber().isPresent() ? localByE164.get(record.getAddress().getNumber().get()) : null;
|
|
|
|
return Optional.fromNullable(localUuid).or(Optional.fromNullable(localE164));
|
|
}
|
|
|
|
@Override
|
|
public @NonNull Collection<SignalContactRecord> getInvalidEntries(@NonNull Collection<SignalContactRecord> remoteRecords) {
|
|
List<SignalContactRecord> invalid = Stream.of(remoteRecords)
|
|
.filter(r -> r.getAddress().getUuid().equals(self.getUuid()) || r.getAddress().getNumber().equals(self.getE164()))
|
|
.toList();
|
|
if (invalid.size() > 0) {
|
|
Log.w(TAG, "Found invalid contact entries! Count: " + invalid.size());
|
|
}
|
|
|
|
return invalid;
|
|
}
|
|
|
|
@Override
|
|
public @NonNull SignalContactRecord merge(@NonNull SignalContactRecord remote, @NonNull SignalContactRecord local, @NonNull StorageSyncHelper.KeyGenerator keyGenerator) {
|
|
String givenName;
|
|
String familyName;
|
|
|
|
if (remote.getGivenName().isPresent() || remote.getFamilyName().isPresent()) {
|
|
givenName = remote.getGivenName().or("");
|
|
familyName = remote.getFamilyName().or("");
|
|
} else {
|
|
givenName = local.getGivenName().or("");
|
|
familyName = local.getFamilyName().or("");
|
|
}
|
|
|
|
byte[] unknownFields = remote.serializeUnknownFields();
|
|
UUID uuid = remote.getAddress().getUuid().or(local.getAddress().getUuid()).orNull();
|
|
String e164 = remote.getAddress().getNumber().or(local.getAddress().getNumber()).orNull();
|
|
SignalServiceAddress address = new SignalServiceAddress(uuid, e164);
|
|
byte[] profileKey = remote.getProfileKey().or(local.getProfileKey()).orNull();
|
|
String username = remote.getUsername().or(local.getUsername()).or("");
|
|
IdentityState identityState = remote.getIdentityState();
|
|
byte[] identityKey = remote.getIdentityKey().or(local.getIdentityKey()).orNull();
|
|
boolean blocked = remote.isBlocked();
|
|
boolean profileSharing = remote.isProfileSharingEnabled();
|
|
boolean archived = remote.isArchived();
|
|
boolean forcedUnread = remote.isForcedUnread();
|
|
boolean matchesRemote = doParamsMatch(remote, unknownFields, address, givenName, familyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread);
|
|
boolean matchesLocal = doParamsMatch(local, unknownFields, address, givenName, familyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread);
|
|
|
|
if (matchesRemote) {
|
|
return remote;
|
|
} else if (matchesLocal) {
|
|
return local;
|
|
} else {
|
|
return new SignalContactRecord.Builder(keyGenerator.generate(), address)
|
|
.setUnknownFields(unknownFields)
|
|
.setGivenName(givenName)
|
|
.setFamilyName(familyName)
|
|
.setProfileKey(profileKey)
|
|
.setUsername(username)
|
|
.setIdentityState(identityState)
|
|
.setIdentityKey(identityKey)
|
|
.setBlocked(blocked)
|
|
.setProfileSharingEnabled(profileSharing)
|
|
.setForcedUnread(forcedUnread)
|
|
.build();
|
|
}
|
|
}
|
|
|
|
private static boolean doParamsMatch(@NonNull SignalContactRecord contact,
|
|
@Nullable byte[] unknownFields,
|
|
@NonNull SignalServiceAddress address,
|
|
@NonNull String givenName,
|
|
@NonNull String familyName,
|
|
@Nullable byte[] profileKey,
|
|
@NonNull String username,
|
|
@Nullable IdentityState identityState,
|
|
@Nullable byte[] identityKey,
|
|
boolean blocked,
|
|
boolean profileSharing,
|
|
boolean archived,
|
|
boolean forcedUnread)
|
|
{
|
|
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
|
|
Objects.equals(contact.getAddress(), address) &&
|
|
Objects.equals(contact.getGivenName().or(""), givenName) &&
|
|
Objects.equals(contact.getFamilyName().or(""), familyName) &&
|
|
Arrays.equals(contact.getProfileKey().orNull(), profileKey) &&
|
|
Objects.equals(contact.getUsername().or(""), username) &&
|
|
Objects.equals(contact.getIdentityState(), identityState) &&
|
|
Arrays.equals(contact.getIdentityKey().orNull(), identityKey) &&
|
|
contact.isBlocked() == blocked &&
|
|
contact.isProfileSharingEnabled() == profileSharing &&
|
|
contact.isArchived() == archived &&
|
|
contact.isForcedUnread() == forcedUnread;
|
|
}
|
|
}
|