import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
+import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
-import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
+import org.whispersystems.signalservice.api.storage.StorageKey;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
import org.whispersystems.signalservice.internal.util.Hex;
+import org.whispersystems.signalservice.internal.util.Util;
import java.io.Closeable;
import java.io.File;
account.getDeviceId()),
userAgent,
groupsV2Operations,
+ ServiceConfig.AUTOMATIC_NETWORK_RETRY,
timer);
this.groupsV2Api = accountManager.getGroupsV2Api();
final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager);
userAgent,
null,
timer,
- clientZkProfileOperations);
+ clientZkProfileOperations,
+ ServiceConfig.AUTOMATIC_NETWORK_RETRY);
this.account.setResolver(this::resolveSignalServiceAddress);
* if it's Optional.absent(), the avatar will be removed
*/
public void setProfile(String name, Optional<File> avatar) throws IOException {
+ // TODO
+ String about = null;
+ String aboutEmoji = null;
+
try (final StreamDetails streamDetails = avatar == null
? avatarStore.retrieveProfileAvatar(getSelfAddress())
: avatar.isPresent() ? Utils.createStreamDetailsFromFile(avatar.get()) : null) {
- accountManager.setVersionedProfile(account.getUuid(), account.getProfileKey(), name, streamDetails);
+ accountManager.setVersionedProfile(account.getUuid(),
+ account.getProfileKey(),
+ name,
+ about,
+ aboutEmoji,
+ streamDetails);
}
if (avatar != null) {
avatarStore.deleteProfileAvatar(getSelfAddress());
}
}
+
+ try {
+ sendSyncMessage(SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE));
+ } catch (UntrustedIdentityException ignored) {
+ }
}
public void unregister() throws IOException {
// If this is the master device, other users can't send messages to this number anymore.
// If this is a linked device, other users can still send messages, but this device doesn't receive them anymore.
accountManager.setGcmId(Optional.absent());
+ accountManager.deleteAccount();
account.setRegistered(false);
account.save();
}
public void setRegistrationLockPin(Optional<String> pin) throws IOException, UnauthenticatedResponseException {
+ if (!account.isMasterDevice()) {
+ throw new RuntimeException("Only master device can set a PIN");
+ }
if (pin.isPresent()) {
final MasterKey masterKey = account.getPinMasterKey() != null
? account.getPinMasterKey()
Optional.absent(),
clientZkProfileOperations,
executor,
- ServiceConfig.MAX_ENVELOPE_SIZE);
+ ServiceConfig.MAX_ENVELOPE_SIZE,
+ ServiceConfig.AUTOMATIC_NETWORK_RETRY);
}
private SignalProfile getRecipientProfile(
SignalServiceAddress address
+ ) {
+ return getRecipientProfile(address, false);
+ }
+
+ private SignalProfile getRecipientProfile(
+ SignalServiceAddress address, boolean force
) {
SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address);
if (profileEntry == null) {
long now = new Date().getTime();
// Profiles are cached for 24h before retrieving them again
if (!profileEntry.isRequestPending() && (
- profileEntry.getProfile() == null || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000
+ force
+ || profileEntry.getProfile() == null
+ || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000
)) {
profileEntry.setRequestPending(true);
final SignalServiceProfile encryptedProfile;
newE164Members.add(member.getNumber().get());
}
- final List<ContactTokenDetails> contacts = accountManager.getContacts(newE164Members);
- if (contacts.size() != newE164Members.size()) {
+ final Map<String, UUID> registeredUsers = getRegisteredUsers(newE164Members);
+ if (registeredUsers.size() != newE164Members.size()) {
// Some of the new members are not registered on Signal
- for (ContactTokenDetails contact : contacts) {
- newE164Members.remove(contact.getNumber());
- }
+ newE164Members.removeAll(registeredUsers.keySet());
throw new IOException("Failed to add members "
+ String.join(", ", newE164Members)
+ " to group: Not registered on Signal");
}
}
+ void requestSyncKeys() throws IOException {
+ SignalServiceProtos.SyncMessage.Request r = SignalServiceProtos.SyncMessage.Request.newBuilder()
+ .setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS)
+ .build();
+ SignalServiceSyncMessage message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));
+ try {
+ sendSyncMessage(message);
+ } catch (UntrustedIdentityException e) {
+ throw new AssertionError(e);
+ }
+ }
+
private byte[] getSenderCertificate() {
// TODO support UUID capable sender certificates
// byte[] certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy();
private Map<String, UUID> getRegisteredUsers(final Set<String> numbersMissingUuid) throws IOException {
try {
return accountManager.getRegisteredUsers(getIasKeyStore(), numbersMissingUuid, CDS_MRENCLAVE);
- } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException e) {
+ } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) {
throw new IOException(e);
}
}
if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
org.whispersystems.libsignal.UntrustedIdentityException identityException = (org.whispersystems.libsignal.UntrustedIdentityException) e
.getCause();
- account.getSignalProtocolStore()
- .saveIdentity(resolveSignalServiceAddress(identityException.getName()),
- identityException.getUntrustedIdentity(),
- TrustLevel.UNTRUSTED);
+ final IdentityKey untrustedIdentity = identityException.getUntrustedIdentity();
+ if (untrustedIdentity != null) {
+ account.getSignalProtocolStore()
+ .saveIdentity(resolveSignalServiceAddress(identityException.getName()),
+ untrustedIdentity,
+ TrustLevel.UNTRUSTED);
+ }
throw identityException;
}
throw new AssertionError(e);
account.getStickerStore().updateSticker(sticker);
}
}
+ if (syncMessage.getFetchType().isPresent()) {
+ switch (syncMessage.getFetchType().get()) {
+ case LOCAL_PROFILE:
+ getRecipientProfile(getSelfAddress(), true);
+ case STORAGE_MANIFEST:
+ // TODO
+ }
+ }
+ if (syncMessage.getKeys().isPresent()) {
+ final KeysMessage keysMessage = syncMessage.getKeys().get();
+ if (keysMessage.getStorageService().isPresent()) {
+ final StorageKey storageKey = keysMessage.getStorageService().get();
+ account.setStorageKey(storageKey);
+ }
+ }
if (syncMessage.getConfiguration().isPresent()) {
// TODO
}
return account.getContactStore().getContacts();
}
- public ContactInfo getContact(String number) {
- return account.getContactStore().getContact(Utils.getSignalServiceAddressFromIdentifier(number));
+ public String getContactOrProfileName(String number) {
+ final SignalServiceAddress address = Utils.getSignalServiceAddressFromIdentifier(number);
+
+ final ContactInfo contact = account.getContactStore().getContact(address);
+ if (contact != null && !Util.isEmpty(contact.name)) {
+ return contact.name;
+ }
+
+ final SignalProfileEntry profileEntry = account.getProfileStore().getProfileEntry(address);
+ if (profileEntry != null && profileEntry.getProfile() != null) {
+ return profileEntry.getProfile().getName();
+ }
+
+ return null;
}
public GroupInfo getGroup(GroupId groupId) {