import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Base64;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
return getRecipientProfile(address, false);
}
- private SignalProfile getRecipientProfile(
+ SignalProfile getRecipientProfile(
SignalServiceAddress address, boolean force
) {
var profileEntry = account.getProfileStore().getProfileEntry(address);
if (profileEntry == null) {
+ // retrieve profile to get identity key
+ retrieveEncryptedProfile(address);
return null;
}
var now = new Date().getTime();
profileEntry.setRequestPending(true);
final SignalServiceProfile encryptedProfile;
try {
- encryptedProfile = profileHelper.retrieveProfileSync(address, SignalServiceProfile.RequestType.PROFILE)
- .getProfile();
- } catch (IOException e) {
- logger.warn("Failed to retrieve profile, ignoring: {}", e.getMessage());
- return null;
+ encryptedProfile = retrieveEncryptedProfile(address);
} finally {
profileEntry.setRequestPending(false);
}
+ if (encryptedProfile == null) {
+ return null;
+ }
final var profileKey = profileEntry.getProfileKey();
final var profile = decryptProfileAndDownloadAvatar(address, profileKey, encryptedProfile);
return profileEntry.getProfile();
}
+ private SignalServiceProfile retrieveEncryptedProfile(SignalServiceAddress address) {
+ try {
+ final var profile = profileHelper.retrieveProfileSync(address, SignalServiceProfile.RequestType.PROFILE)
+ .getProfile();
+ try {
+ account.getIdentityKeyStore()
+ .saveIdentity(resolveRecipient(address),
+ new IdentityKey(Base64.getDecoder().decode(profile.getIdentityKey())),
+ new Date());
+ } catch (InvalidKeyException ignored) {
+ logger.warn("Got invalid identity key in profile for {}", address.getLegacyIdentifier());
+ }
+ return profile;
+ } catch (IOException e) {
+ logger.warn("Failed to retrieve profile, ignoring: {}", e.getMessage());
+ return null;
+ }
+ }
+
private ProfileKeyCredential getRecipientProfileKeyCredential(SignalServiceAddress address) {
var profileEntry = account.getProfileStore().getProfileEntry(address);
if (profileEntry == null) {
unidentifiedAccessHelper.getAccessFor(recipients),
isRecipientUpdate,
message);
+
+ for (var r : result) {
+ if (r.getIdentityFailure() != null) {
+ account.getIdentityKeyStore().
+ saveIdentity(resolveRecipient(r.getAddress()),
+ r.getIdentityFailure().getIdentityKey(),
+ new Date());
+ }
+ }
+
return new Pair<>(timestamp, result);
} catch (UntrustedIdentityException e) {
return new Pair<>(timestamp, List.of());
}
private void retryFailedReceivedMessages(ReceiveMessageHandler handler, boolean ignoreAttachments) {
+ Set<HandleAction> queuedActions = new HashSet<>();
for (var cachedMessage : account.getMessageCache().getCachedMessages()) {
- retryFailedReceivedMessage(handler, ignoreAttachments, cachedMessage);
+ var actions = retryFailedReceivedMessage(handler, ignoreAttachments, cachedMessage);
+ if (actions != null) {
+ queuedActions.addAll(actions);
+ }
+ }
+ for (var action : queuedActions) {
+ try {
+ action.execute(this);
+ } catch (Throwable e) {
+ logger.warn("Message action failed.", e);
+ }
}
}
- private void retryFailedReceivedMessage(
+ private List<HandleAction> retryFailedReceivedMessage(
final ReceiveMessageHandler handler, final boolean ignoreAttachments, final CachedMessage cachedMessage
) {
var envelope = cachedMessage.loadEnvelope();
if (envelope == null) {
- return;
+ return null;
}
SignalServiceContent content = null;
+ List<HandleAction> actions = null;
if (!envelope.isReceipt()) {
try {
content = decryptMessage(envelope);
} catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
- return;
+ if (!envelope.hasSource()) {
+ final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) e)
+ .getName());
+ try {
+ account.getMessageCache().replaceSender(cachedMessage, recipientId);
+ } catch (IOException ioException) {
+ logger.warn("Failed to move cached message to recipient folder: {}", ioException.getMessage());
+ }
+ }
+ return null;
} catch (Exception er) {
// All other errors are not recoverable, so delete the cached message
cachedMessage.delete();
- return;
- }
- var actions = handleMessage(envelope, content, ignoreAttachments);
- for (var action : actions) {
- try {
- action.execute(this);
- } catch (Throwable e) {
- logger.warn("Message action failed.", e);
- }
+ return null;
}
+ actions = handleMessage(envelope, content, ignoreAttachments);
}
account.save();
handler.handleMessage(envelope, content, null);
cachedMessage.delete();
+ return actions;
}
public void receiveMessages(
final CachedMessage[] cachedMessage = {null};
try {
var result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> {
+ final var recipientId = envelope1.hasSource()
+ ? resolveRecipient(envelope1.getSourceIdentifier())
+ : null;
// store message on disk, before acknowledging receipt to the server
- cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1);
+ cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId);
});
if (result.isPresent()) {
envelope = result.get();
if (envelope.hasSource()) {
// Store uuid if we don't have it already
- var source = envelope.getSourceAddress();
- resolveSignalServiceAddress(source);
+ resolveRecipientTrusted(envelope.getSourceAddress());
}
+ final var notAGroupMember = isNotAGroupMember(envelope, content);
if (!envelope.isReceipt()) {
try {
content = decryptMessage(envelope);
account.save();
if (isMessageBlocked(envelope, content)) {
logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
- } else if (isNotAGroupMember(envelope, content)) {
+ } else if (notAGroupMember) {
logger.info("Ignoring a message from a non group member: {}", envelope.getTimestamp());
} else {
handler.handleMessage(envelope, content, exception);
}
- if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) {
- if (cachedMessage[0] != null) {
+ if (cachedMessage[0] != null) {
+ if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
+ final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) exception)
+ .getName());
+ queuedActions.add(new RetrieveProfileAction(resolveSignalServiceAddress(recipientId)));
+ if (!envelope.hasSource()) {
+ try {
+ cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId);
+ } catch (IOException ioException) {
+ logger.warn("Failed to move cached message to recipient folder: {}",
+ ioException.getMessage());
+ }
+ }
+ } else {
cachedMessage[0].delete();
}
}
destination,
ignoreAttachments));
}
- if (syncMessage.getRequest().isPresent()) {
+ if (syncMessage.getRequest().isPresent() && account.isMasterDevice()) {
var rm = syncMessage.getRequest().get();
if (rm.isContactsRequest()) {
actions.add(SendSyncContactsAction.create());
try (var attachmentAsStream = retrieveAttachmentAsStream(contactsMessage.getContactsStream()
.asPointer(), tmpFile)) {
var s = new DeviceContactsInputStream(attachmentAsStream);
- if (contactsMessage.isComplete()) {
- account.getContactStore().clear();
- }
DeviceContact c;
while ((c = s.read()) != null) {
if (c.getAddress().matches(account.getSelfAddress()) && c.getProfileKey().isPresent()) {
var canonicalizedNumber = UuidUtil.isUuid(identifier)
? identifier
: PhoneNumberFormatter.formatNumber(identifier, account.getUsername());
- var address = Utils.getSignalServiceAddressFromIdentifier(canonicalizedNumber);
+
+ return resolveRecipient(canonicalizedNumber);
+ }
+
+ private RecipientId resolveRecipient(final String identifier) {
+ var address = Utils.getSignalServiceAddressFromIdentifier(identifier);
return resolveRecipient(address);
}