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.util.InvalidNumberException;
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;
return new Manager(account, pathConfig, serviceConfiguration, userAgent);
}
+ public static List<String> getAllLocalUsernames(File settingsPath) {
+ PathConfig pathConfig = PathConfig.createDefault(settingsPath);
+ final File dataPath = pathConfig.getDataPath();
+ final File[] files = dataPath.listFiles();
+
+ if (files == null) {
+ return List.of();
+ }
+
+ return Arrays.stream(files)
+ .filter(File::isFile)
+ .map(File::getName)
+ .filter(file -> PhoneNumberFormatter.isValidNumber(file, null))
+ .collect(Collectors.toList());
+ }
+
public void checkAccountState() throws IOException {
if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
refreshPreKeys();
*/
public Map<String, Boolean> areUsersRegistered(Set<String> numbers) throws IOException {
// Note "contactDetails" has no optionals. It only gives us info on users who are registered
- List<ContactTokenDetails> contactDetails = this.accountManager.getContacts(numbers);
+ Map<String, UUID> contactDetails = getRegisteredUsers(numbers);
- Set<String> registeredUsers = contactDetails.stream()
- .map(ContactTokenDetails::getNumber)
- .collect(Collectors.toSet());
+ Set<String> registeredUsers = contactDetails.keySet();
return numbers.stream().collect(Collectors.toMap(x -> x, registeredUsers::contains));
}
avatarStore.deleteProfileAvatar(getSelfAddress());
}
}
+
+ try {
+ sendSyncMessage(SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE));
+ } catch (UntrustedIdentityException ignored) {
+ }
}
public void unregister() throws IOException {
}
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()
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");
return sendMessage(messageBuilder, getSignalServiceAddresses(recipients));
}
+ public Pair<Long, SendMessageResult> sendSelfMessage(
+ String messageText, List<String> attachments
+ ) throws IOException, AttachmentInvalidException {
+ final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
+ .withBody(messageText);
+ if (attachments != null) {
+ messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments));
+ }
+ return sendSelfMessage(messageBuilder);
+ }
+
public Pair<Long, List<SendMessageResult>> sendMessageReaction(
String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List<String> recipients
) throws IOException, InvalidNumberException {
private Collection<SignalServiceAddress> getSignalServiceAddresses(Collection<String> numbers) throws InvalidNumberException {
final Set<SignalServiceAddress> signalServiceAddresses = new HashSet<>(numbers.size());
- final Set<SignalServiceAddress> missingUuids = new HashSet<>();
+ final Set<SignalServiceAddress> addressesMissingUuid = new HashSet<>();
for (String number : numbers) {
final SignalServiceAddress resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number);
if (resolvedAddress.getUuid().isPresent()) {
signalServiceAddresses.add(resolvedAddress);
} else {
- missingUuids.add(resolvedAddress);
+ addressesMissingUuid.add(resolvedAddress);
}
}
+ final Set<String> numbersMissingUuid = addressesMissingUuid.stream()
+ .map(a -> a.getNumber().get())
+ .collect(Collectors.toSet());
Map<String, UUID> registeredUsers;
try {
- registeredUsers = accountManager.getRegisteredUsers(getIasKeyStore(),
- missingUuids.stream().map(a -> a.getNumber().get()).collect(Collectors.toSet()),
- CDS_MRENCLAVE);
- } catch (IOException | Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException e) {
+ registeredUsers = getRegisteredUsers(numbersMissingUuid);
+ } catch (IOException e) {
logger.warn("Failed to resolve uuids from server, ignoring: {}", e.getMessage());
- registeredUsers = new HashMap<>();
+ registeredUsers = Map.of();
}
- for (SignalServiceAddress address : missingUuids) {
+ for (SignalServiceAddress address : addressesMissingUuid) {
final String number = address.getNumber().get();
if (registeredUsers.containsKey(number)) {
final SignalServiceAddress newAddress = resolveSignalServiceAddress(new SignalServiceAddress(
return signalServiceAddresses;
}
+ 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) {
+ throw new IOException(e);
+ }
+ }
+
private Pair<Long, List<SendMessageResult>> sendMessage(
SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients
) throws IOException {
final int expirationTime = contact != null ? contact.messageExpirationTime : 0;
messageBuilder.withExpiration(expirationTime);
message = messageBuilder.build();
- if (address.matches(account.getSelfAddress())) {
- results.add(sendSelfMessage(message));
- } else {
- results.add(sendMessage(address, message));
- }
+ results.add(sendMessage(address, message));
}
return new Pair<>(timestamp, results);
}
}
}
+ private Pair<Long, SendMessageResult> sendSelfMessage(
+ SignalServiceDataMessage.Builder messageBuilder
+ ) throws IOException {
+ final long timestamp = System.currentTimeMillis();
+ messageBuilder.withTimestamp(timestamp);
+ getOrCreateMessagePipe();
+ getOrCreateUnidentifiedMessagePipe();
+ try {
+ final SignalServiceAddress address = getSelfAddress();
+
+ final ContactInfo contact = account.getContactStore().getContact(address);
+ final int expirationTime = contact != null ? contact.messageExpirationTime : 0;
+ messageBuilder.withExpiration(expirationTime);
+
+ SignalServiceDataMessage message = messageBuilder.build();
+ final SendMessageResult result = sendSelfMessage(message);
+ return new Pair<>(timestamp, result);
+ } finally {
+ account.save();
+ }
+ }
+
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
SignalServiceMessageSender messageSender = createMessageSender();
account.getStickerStore().updateSticker(sticker);
}
}
+ if (syncMessage.getFetchType().isPresent()) {
+ switch (syncMessage.getFetchType().get()) {
+ case LOCAL_PROFILE:
+ getRecipientProfile(getSelfAddress(), true);
+ case STORAGE_MANIFEST:
+ // TODO
+ }
+ }
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) {