import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
+import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import org.whispersystems.signalservice.api.util.SleepTimer;
public void checkAccountState() throws IOException {
if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
refreshPreKeys();
- account.save();
}
if (account.getUuid() == null) {
account.setUuid(accountManager.getOwnUuid());
- account.save();
}
updateAccountAttributes();
}
}
/**
- * @param name if null, the previous name will be kept
+ * @param givenName if null, the previous givenName will be kept
+ * @param familyName if null, the previous familyName will be kept
* @param about if null, the previous about text will be kept
* @param aboutEmoji if null, the previous about emoji will be kept
* @param avatar if avatar is null the image from the local avatar store is used (if present),
- * if it's Optional.absent(), the avatar will be removed
*/
- public void setProfile(String name, String about, String aboutEmoji, Optional<File> avatar) throws IOException {
+ public void setProfile(
+ String givenName, final String familyName, String about, String aboutEmoji, Optional<File> avatar
+ ) throws IOException {
var profile = getRecipientProfile(account.getSelfRecipientId());
var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile);
- if (name != null) {
- builder.withGivenName(name);
- builder.withFamilyName(null);
+ if (givenName != null) {
+ builder.withGivenName(givenName);
+ }
+ if (familyName != null) {
+ builder.withFamilyName(familyName);
}
if (about != null) {
builder.withAbout(about);
accountManager.setVersionedProfile(account.getUuid(),
account.getProfileKey(),
newProfile.getInternalServiceName(),
- newProfile.getAbout(),
- newProfile.getAboutEmoji(),
+ newProfile.getAbout() == null ? "" : newProfile.getAbout(),
+ newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(),
streamDetails);
}
// 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());
+
+ account.setRegistered(false);
+ }
+
+ public void deleteAccount() throws IOException {
accountManager.deleteAccount();
account.setRegistered(false);
- account.save();
}
public List<DeviceInfo> getLinkedDevices() throws IOException {
var devices = accountManager.getDevices();
account.setMultiDevice(devices.size() > 1);
- account.save();
return devices;
}
accountManager.removeDevice(deviceId);
var devices = accountManager.getDevices();
account.setMultiDevice(devices.size() > 1);
- account.save();
}
public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException {
Optional.of(account.getProfileKey().serialize()),
verificationCode);
account.setMultiDevice(true);
- account.save();
}
public void setRegistrationLockPin(Optional<String> pin) throws IOException, UnauthenticatedResponseException {
pinHelper.setRegistrationLockPin(pin.get(), masterKey);
- account.setRegistrationLockPin(pin.get());
- account.setPinMasterKey(masterKey);
+ account.setRegistrationLockPin(pin.get(), masterKey);
} else {
// Remove legacy registration lock
accountManager.removeRegistrationLockV1();
// Remove KBS Pin
pinHelper.removeRegistrationLockPin();
- account.setRegistrationLockPin(null);
- account.setPinMasterKey(null);
+ account.setRegistrationLockPin(null, null);
}
- account.save();
}
void refreshPreKeys() throws IOException {
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
}
- public Profile getRecipientProfile(
- SignalServiceAddress address
- ) {
- return getRecipientProfile(resolveRecipient(address), false);
- }
-
public Profile getRecipientProfile(
RecipientId recipientId
) {
final var profile = profileAndCredential.getProfile();
try {
- account.getIdentityKeyStore()
+ var newIdentity = account.getIdentityKeyStore()
.saveIdentity(recipientId,
new IdentityKey(Base64.getDecoder().decode(profile.getIdentityKey())),
new Date());
+
+ if (newIdentity) {
+ account.getSessionStore().archiveSessions(recipientId);
+ }
} catch (InvalidKeyException ignored) {
logger.warn("Got invalid identity key in profile for {}",
resolveSignalServiceAddress(recipientId).getLegacyIdentifier());
public Pair<Long, List<SendMessageResult>> sendGroupMessageReaction(
String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, GroupId groupId
) throws IOException, InvalidNumberException, NotAGroupMemberException, GroupNotFoundException {
+ var targetAuthorRecipientId = canonicalizeAndResolveRecipient(targetAuthor);
var reaction = new SignalServiceDataMessage.Reaction(emoji,
remove,
- canonicalizeAndResolveSignalServiceAddress(targetAuthor),
+ resolveSignalServiceAddress(targetAuthorRecipientId),
targetSentTimestamp);
final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction);
}
g = (GroupInfoV1) group;
- if (!g.isMember(resolveRecipient(recipient))) {
+ final var recipientId = resolveRecipient(recipient);
+ if (!g.isMember(recipientId)) {
throw new NotAGroupMemberException(groupId, g.name);
}
var messageBuilder = getGroupUpdateMessageBuilder(g);
// Send group message only to the recipient who requested it
- return sendMessage(messageBuilder, Set.of(resolveRecipient(recipient)));
+ return sendMessage(messageBuilder, Set.of(recipientId));
}
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException {
public Pair<Long, List<SendMessageResult>> sendMessageReaction(
String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List<String> recipients
) throws IOException, InvalidNumberException {
+ var targetAuthorRecipientId = canonicalizeAndResolveRecipient(targetAuthor);
var reaction = new SignalServiceDataMessage.Reaction(emoji,
remove,
- canonicalizeAndResolveSignalServiceAddress(targetAuthor),
+ resolveSignalServiceAddress(targetAuthorRecipientId),
targetSentTimestamp);
final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction);
return sendMessage(messageBuilder, getSignalServiceAddresses(recipients));
for (var address : signalServiceAddresses) {
handleEndSession(address);
}
- account.save();
throw e;
}
}
return contact == null || contact.getName() == null ? "" : contact.getName();
}
- public void setContactName(String number, String name) throws InvalidNumberException {
+ public void setContactName(String number, String name) throws InvalidNumberException, NotMasterDeviceException {
+ if (!account.isMasterDevice()) {
+ throw new NotMasterDeviceException();
+ }
final var recipientId = canonicalizeAndResolveRecipient(number);
var contact = account.getContactStore().getContact(recipientId);
final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
account.getContactStore().storeContact(recipientId, builder.withName(name).build());
- account.save();
}
- public void setContactBlocked(String number, boolean blocked) throws InvalidNumberException {
+ public void setContactBlocked(
+ String number, boolean blocked
+ ) throws InvalidNumberException, NotMasterDeviceException {
+ if (!account.isMasterDevice()) {
+ throw new NotMasterDeviceException();
+ }
setContactBlocked(canonicalizeAndResolveRecipient(number), blocked);
}
var contact = account.getContactStore().getContact(recipientId);
final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
account.getContactStore().storeContact(recipientId, builder.withBlocked(blocked).build());
- account.save();
}
public void setGroupBlocked(final GroupId groupId, final boolean blocked) throws GroupNotFoundException {
group.setBlocked(blocked);
account.getGroupStore().updateGroup(group);
- account.save();
}
private void setExpirationTimer(RecipientId recipientId, int messageExpirationTimer) {
var recipientId = canonicalizeAndResolveRecipient(number);
setExpirationTimer(recipientId, messageExpirationTimer);
sendExpirationTimerUpdate(recipientId);
- account.save();
}
/**
var sticker = new Sticker(StickerPackId.deserialize(Hex.fromStringCondensed(packId)), packKey);
account.getStickerStore().updateSticker(sticker);
- account.save();
try {
return new URI("https",
}
}
- void requestSyncGroups() throws IOException {
+ public void requestAllSyncData() throws IOException {
+ requestSyncGroups();
+ requestSyncContacts();
+ requestSyncBlocked();
+ requestSyncConfiguration();
+ requestSyncKeys();
+ }
+
+ private void requestSyncGroups() throws IOException {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS)
.build();
}
}
- void requestSyncContacts() throws IOException {
+ private void requestSyncContacts() throws IOException {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS)
.build();
}
}
- void requestSyncBlocked() throws IOException {
+ private void requestSyncBlocked() throws IOException {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED)
.build();
}
}
- void requestSyncConfiguration() throws IOException {
+ private void requestSyncConfiguration() throws IOException {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION)
.build();
}
}
- void requestSyncKeys() throws IOException {
+ private void requestSyncKeys() throws IOException {
var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
.setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS)
.build();
final var addressesMissingUuid = new HashSet<SignalServiceAddress>();
for (var number : numbers) {
- final var resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number);
+ final var resolvedAddress = resolveSignalServiceAddress(canonicalizeAndResolveRecipient(number));
if (resolvedAddress.getUuid().isPresent()) {
signalServiceAddresses.add(resolvedAddress);
} else {
return signalServiceAddresses.stream().map(this::resolveRecipient).collect(Collectors.toSet());
}
- private Map<String, UUID> getRegisteredUsers(final Set<String> numbersMissingUuid) throws IOException {
+ private RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException {
+ final var address = resolveSignalServiceAddress(recipientId);
+ if (!address.getNumber().isPresent()) {
+ return recipientId;
+ }
+ final var number = address.getNumber().get();
+ final var uuidMap = getRegisteredUsers(Set.of(number));
+ return resolveRecipientTrusted(new SignalServiceAddress(uuidMap.getOrDefault(number, null), number));
+ }
+
+ private Map<String, UUID> getRegisteredUsers(final Set<String> numbers) throws IOException {
try {
return accountManager.getRegisteredUsers(ServiceConfig.getIasKeyStore(),
- numbersMissingUuid,
+ numbers,
serviceEnvironmentConfig.getCdsMrenclave());
} catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) {
throw new IOException(e);
for (var r : result) {
if (r.getIdentityFailure() != null) {
- account.getIdentityKeyStore().
- saveIdentity(resolveRecipient(r.getAddress()),
- r.getIdentityFailure().getIdentityKey(),
- new Date());
+ final var recipientId = resolveRecipient(r.getAddress());
+ final var newIdentity = account.getIdentityKeyStore()
+ .saveIdentity(recipientId, r.getIdentityFailure().getIdentityKey(), new Date());
+ if (newIdentity) {
+ account.getSessionStore().archiveSessions(recipientId);
+ }
}
}
final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
messageBuilder.withExpiration(expirationTime);
message = messageBuilder.build();
- results.add(sendMessage(resolveSignalServiceAddress(recipientId), message));
+ results.add(sendMessage(recipientId, message));
}
return new Pair<>(timestamp, results);
}
handleEndSession(recipient);
}
}
- account.save();
}
}
messageBuilder.withTimestamp(timestamp);
getOrCreateMessagePipe();
getOrCreateUnidentifiedMessagePipe();
- try {
- final var recipientId = account.getSelfRecipientId();
+ final var recipientId = account.getSelfRecipientId();
- final var contact = account.getContactStore().getContact(recipientId);
- final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
- messageBuilder.withExpiration(expirationTime);
+ final var contact = account.getContactStore().getContact(recipientId);
+ final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
+ messageBuilder.withExpiration(expirationTime);
- var message = messageBuilder.build();
- final var result = sendSelfMessage(message);
- return new Pair<>(timestamp, result);
- } finally {
- account.save();
- }
+ var message = messageBuilder.build();
+ final var result = sendSelfMessage(message);
+ return new Pair<>(timestamp, result);
}
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
var messageSender = createMessageSender();
- var recipient = account.getSelfAddress();
+ var recipientId = account.getSelfRecipientId();
- final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(resolveRecipient(recipient));
+ final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(recipientId);
+ var recipient = resolveSignalServiceAddress(recipientId);
var transcript = new SentTranscriptMessage(Optional.of(recipient),
message.getTimestamp(),
message,
}
private SendMessageResult sendMessage(
- SignalServiceAddress address, SignalServiceDataMessage message
+ RecipientId recipientId, SignalServiceDataMessage message
) throws IOException {
var messageSender = createMessageSender();
+ final var address = resolveSignalServiceAddress(recipientId);
try {
- return messageSender.sendMessage(address,
- unidentifiedAccessHelper.getAccessFor(resolveRecipient(address)),
- message);
+ try {
+ return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId), message);
+ } catch (UnregisteredUserException e) {
+ final var newRecipientId = refreshRegisteredUser(recipientId);
+ return messageSender.sendMessage(resolveSignalServiceAddress(newRecipientId),
+ unidentifiedAccessHelper.getAccessFor(newRecipientId),
+ message);
+ }
} catch (UntrustedIdentityException e) {
return SendMessageResult.identityFailure(address, e.getIdentityKey());
}
private void storeProfileKeysFromMembers(final DecryptedGroup group) {
for (var member : group.getMembersList()) {
- final var address = resolveRecipient(new SignalServiceAddress(UuidUtil.parseOrThrow(member.getUuid()
- .toByteArray()), null));
+ final var uuid = UuidUtil.parseOrThrow(member.getUuid().toByteArray());
+ final var recipientId = account.getRecipientStore().resolveRecipient(uuid);
try {
account.getProfileStore()
- .storeProfileKey(address, new ProfileKey(member.getProfileKey().toByteArray()));
+ .storeProfileKey(recipientId, new ProfileKey(member.getProfileKey().toByteArray()));
} catch (InvalidInputException ignored) {
}
}
content = decryptMessage(envelope);
} catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
if (!envelope.hasSource()) {
- final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) e)
- .getName());
+ final var identifier = ((org.whispersystems.libsignal.UntrustedIdentityException) e).getName();
+ final var recipientId = resolveRecipient(identifier);
try {
account.getMessageCache().replaceSender(cachedMessage, recipientId);
} catch (IOException ioException) {
}
actions = handleMessage(envelope, content, ignoreAttachments);
}
- account.save();
handler.handleMessage(envelope, content, null);
cachedMessage.delete();
return actions;
logger.warn("Message action failed.", e);
}
}
- account.save();
queuedActions.clear();
queuedActions = null;
}
queuedActions.addAll(actions);
}
}
- account.save();
if (isMessageBlocked(envelope, content)) {
logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
} else if (notAGroupMember) {
}
if (cachedMessage[0] != null) {
if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
- final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) exception)
- .getName());
+ final var identifier = ((org.whispersystems.libsignal.UntrustedIdentityException) exception).getName();
+ final var recipientId = resolveRecipient(identifier);
queuedActions.add(new RetrieveProfileAction(recipientId));
if (!envelope.hasSource()) {
try {
theirIdentityKey);
}
- @Deprecated
- public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException {
- var canonicalizedNumber = UuidUtil.isUuid(identifier)
- ? identifier
- : PhoneNumberFormatter.formatNumber(identifier, account.getUsername());
- return resolveSignalServiceAddress(canonicalizedNumber);
- }
-
@Deprecated
public SignalServiceAddress resolveSignalServiceAddress(String identifier) {
var address = Utils.getSignalServiceAddressFromIdentifier(identifier);