Refactor Manager interface

This commit is contained in:
AsamK 2021-09-18 10:19:56 +02:00
parent b91c162159
commit d72b838560
33 changed files with 416 additions and 169 deletions

View file

@ -1,6 +1,8 @@
package org.asamk.signal.manager; package org.asamk.signal.manager;
import org.asamk.signal.manager.api.Device; import org.asamk.signal.manager.api.Device;
import org.asamk.signal.manager.api.Group;
import org.asamk.signal.manager.api.Identity;
import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.Message;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendGroupMessageResults;
@ -17,12 +19,10 @@ import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.LastGroupAdminException;
import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.groups.GroupInfo;
import org.asamk.signal.manager.storage.identities.IdentityInfo;
import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
@ -51,7 +51,7 @@ import java.util.stream.Collectors;
public interface Manager extends Closeable { public interface Manager extends Closeable {
static Manager init( static Manager init(
String username, String number,
File settingsPath, File settingsPath,
ServiceEnvironment serviceEnvironment, ServiceEnvironment serviceEnvironment,
String userAgent, String userAgent,
@ -59,11 +59,11 @@ public interface Manager extends Closeable {
) throws IOException, NotRegisteredException { ) throws IOException, NotRegisteredException {
var pathConfig = PathConfig.createDefault(settingsPath); var pathConfig = PathConfig.createDefault(settingsPath);
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { if (!SignalAccount.userExists(pathConfig.getDataPath(), number)) {
throw new NotRegisteredException(); throw new NotRegisteredException();
} }
var account = SignalAccount.load(pathConfig.getDataPath(), username, true, trustNewIdentity); var account = SignalAccount.load(pathConfig.getDataPath(), number, true, trustNewIdentity);
if (!account.isRegistered()) { if (!account.isRegistered()) {
throw new NotRegisteredException(); throw new NotRegisteredException();
@ -74,7 +74,7 @@ public interface Manager extends Closeable {
return new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); return new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
} }
static List<String> getAllLocalUsernames(File settingsPath) { static List<String> getAllLocalNumbers(File settingsPath) {
var pathConfig = PathConfig.createDefault(settingsPath); var pathConfig = PathConfig.createDefault(settingsPath);
final var dataPath = pathConfig.getDataPath(); final var dataPath = pathConfig.getDataPath();
final var files = dataPath.listFiles(); final var files = dataPath.listFiles();
@ -90,11 +90,7 @@ public interface Manager extends Closeable {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
String getUsername(); String getSelfNumber();
RecipientId getSelfRecipientId();
int getDeviceId();
void checkAccountState() throws IOException; void checkAccountState() throws IOException;
@ -120,9 +116,9 @@ public interface Manager extends Closeable {
void setRegistrationLockPin(Optional<String> pin) throws IOException, UnauthenticatedResponseException; void setRegistrationLockPin(Optional<String> pin) throws IOException, UnauthenticatedResponseException;
Profile getRecipientProfile(RecipientId recipientId); Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws UnregisteredUserException;
List<GroupInfo> getGroups(); List<Group> getGroups();
SendGroupMessageResults quitGroup( SendGroupMessageResults quitGroup(
GroupId groupId, Set<RecipientIdentifier.Single> groupAdmins GroupId groupId, Set<RecipientIdentifier.Single> groupAdmins
@ -221,15 +217,15 @@ public interface Manager extends Closeable {
void sendContacts() throws IOException; void sendContacts() throws IOException;
List<Pair<RecipientId, Contact>> getContacts(); List<Pair<RecipientAddress, Contact>> getContacts();
String getContactOrProfileName(RecipientIdentifier.Single recipientIdentifier); String getContactOrProfileName(RecipientIdentifier.Single recipient);
GroupInfo getGroup(GroupId groupId); Group getGroup(GroupId groupId);
List<IdentityInfo> getIdentities(); List<Identity> getIdentities();
List<IdentityInfo> getIdentities(RecipientIdentifier.Single recipient); List<Identity> getIdentities(RecipientIdentifier.Single recipient);
boolean trustIdentityVerified(RecipientIdentifier.Single recipient, byte[] fingerprint); boolean trustIdentityVerified(RecipientIdentifier.Single recipient, byte[] fingerprint);
@ -241,14 +237,8 @@ public interface Manager extends Closeable {
String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey); String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey);
byte[] computeSafetyNumberForScanning(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey);
SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address); SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address);
SignalServiceAddress resolveSignalServiceAddress(UUID uuid);
SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId);
@Override @Override
void close() throws IOException; void close() throws IOException;

View file

@ -18,6 +18,8 @@ package org.asamk.signal.manager;
import org.asamk.signal.manager.actions.HandleAction; import org.asamk.signal.manager.actions.HandleAction;
import org.asamk.signal.manager.api.Device; import org.asamk.signal.manager.api.Device;
import org.asamk.signal.manager.api.Group;
import org.asamk.signal.manager.api.Identity;
import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.Message;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendGroupMessageResults;
@ -52,6 +54,7 @@ import org.asamk.signal.manager.storage.identities.IdentityInfo;
import org.asamk.signal.manager.storage.messageCache.CachedMessage; import org.asamk.signal.manager.storage.messageCache.CachedMessage;
import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.stickers.Sticker; import org.asamk.signal.manager.storage.stickers.Sticker;
import org.asamk.signal.manager.storage.stickers.StickerPackId; import org.asamk.signal.manager.storage.stickers.StickerPackId;
@ -196,7 +199,7 @@ public class ManagerImpl implements Manager {
this::resolveSignalServiceAddress, this::resolveSignalServiceAddress,
account.getRecipientStore(), account.getRecipientStore(),
this::handleIdentityFailure, this::handleIdentityFailure,
this::getGroup, this::getGroupInfo,
this::refreshRegisteredUser); this::refreshRegisteredUser);
this.groupHelper = new GroupHelper(account, this.groupHelper = new GroupHelper(account,
dependencies, dependencies,
@ -240,20 +243,10 @@ public class ManagerImpl implements Manager {
} }
@Override @Override
public String getUsername() { public String getSelfNumber() {
return account.getUsername(); return account.getUsername();
} }
@Override
public RecipientId getSelfRecipientId() {
return account.getSelfRecipientId();
}
@Override
public int getDeviceId() {
return account.getDeviceId();
}
@Override @Override
public void checkAccountState() throws IOException { public void checkAccountState() throws IOException {
if (account.getLastReceiveTimestamp() == 0) { if (account.getLastReceiveTimestamp() == 0) {
@ -385,7 +378,11 @@ public class ManagerImpl implements Manager {
logger.debug("Failed to decrypt device name, maybe plain text?", e); logger.debug("Failed to decrypt device name, maybe plain text?", e);
} }
} }
return new Device(d.getId(), deviceName, d.getCreated(), d.getLastSeen()); return new Device(d.getId(),
deviceName,
d.getCreated(),
d.getLastSeen(),
d.getId() == account.getDeviceId());
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
@ -442,13 +439,48 @@ public class ManagerImpl implements Manager {
} }
@Override @Override
public Profile getRecipientProfile(RecipientId recipientId) { public Profile getRecipientProfile(RecipientIdentifier.Single recipient) throws UnregisteredUserException {
return profileHelper.getRecipientProfile(resolveRecipient(recipient));
}
private Profile getRecipientProfile(RecipientId recipientId) {
return profileHelper.getRecipientProfile(recipientId); return profileHelper.getRecipientProfile(recipientId);
} }
@Override @Override
public List<GroupInfo> getGroups() { public List<Group> getGroups() {
return account.getGroupStore().getGroups(); return account.getGroupStore().getGroups().stream().map(this::toGroup).collect(Collectors.toList());
}
private Group toGroup(final GroupInfo groupInfo) {
if (groupInfo == null) {
return null;
}
return new Group(groupInfo.getGroupId(),
groupInfo.getTitle(),
groupInfo.getDescription(),
groupInfo.getGroupInviteLink(),
groupInfo.getMembers()
.stream()
.map(account.getRecipientStore()::resolveRecipientAddress)
.collect(Collectors.toSet()),
groupInfo.getPendingMembers()
.stream()
.map(account.getRecipientStore()::resolveRecipientAddress)
.collect(Collectors.toSet()),
groupInfo.getRequestingMembers()
.stream()
.map(account.getRecipientStore()::resolveRecipientAddress)
.collect(Collectors.toSet()),
groupInfo.getAdminMembers()
.stream()
.map(account.getRecipientStore()::resolveRecipientAddress)
.collect(Collectors.toSet()),
groupInfo.isBlocked(),
groupInfo.getMessageExpirationTime(),
groupInfo.isAnnouncementGroup(),
groupInfo.isMember(account.getSelfRecipientId()));
} }
@Override @Override
@ -973,15 +1005,19 @@ public class ManagerImpl implements Manager {
} }
@Override @Override
public List<Pair<RecipientId, Contact>> getContacts() { public List<Pair<RecipientAddress, Contact>> getContacts() {
return account.getContactStore().getContacts(); return account.getContactStore()
.getContacts()
.stream()
.map(p -> new Pair<>(account.getRecipientStore().resolveRecipientAddress(p.first()), p.second()))
.collect(Collectors.toList());
} }
@Override @Override
public String getContactOrProfileName(RecipientIdentifier.Single recipientIdentifier) { public String getContactOrProfileName(RecipientIdentifier.Single recipient) {
final RecipientId recipientId; final RecipientId recipientId;
try { try {
recipientId = resolveRecipient(recipientIdentifier); recipientId = resolveRecipient(recipient);
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
return null; return null;
} }
@ -1000,24 +1036,46 @@ public class ManagerImpl implements Manager {
} }
@Override @Override
public GroupInfo getGroup(GroupId groupId) { public Group getGroup(GroupId groupId) {
return toGroup(groupHelper.getGroup(groupId));
}
public GroupInfo getGroupInfo(GroupId groupId) {
return groupHelper.getGroup(groupId); return groupHelper.getGroup(groupId);
} }
@Override @Override
public List<IdentityInfo> getIdentities() { public List<Identity> getIdentities() {
return account.getIdentityKeyStore().getIdentities(); return account.getIdentityKeyStore()
.getIdentities()
.stream()
.map(this::toIdentity)
.collect(Collectors.toList());
}
private Identity toIdentity(final IdentityInfo identityInfo) {
if (identityInfo == null) {
return null;
}
final var address = account.getRecipientStore().resolveRecipientAddress(identityInfo.getRecipientId());
return new Identity(address,
identityInfo.getIdentityKey(),
computeSafetyNumber(address.toSignalServiceAddress(), identityInfo.getIdentityKey()),
computeSafetyNumberForScanning(address.toSignalServiceAddress(), identityInfo.getIdentityKey()),
identityInfo.getTrustLevel(),
identityInfo.getDateAdded());
} }
@Override @Override
public List<IdentityInfo> getIdentities(RecipientIdentifier.Single recipient) { public List<Identity> getIdentities(RecipientIdentifier.Single recipient) {
IdentityInfo identity; IdentityInfo identity;
try { try {
identity = account.getIdentityKeyStore().getIdentity(resolveRecipient(recipient)); identity = account.getIdentityKeyStore().getIdentity(resolveRecipient(recipient));
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
identity = null; identity = null;
} }
return identity == null ? List.of() : List.of(identity); return identity == null ? List.of() : List.of(toIdentity(identity));
} }
/** /**
@ -1144,8 +1202,7 @@ public class ManagerImpl implements Manager {
return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText(); return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText();
} }
@Override private byte[] computeSafetyNumberForScanning(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) {
public byte[] computeSafetyNumberForScanning(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) {
final Fingerprint fingerprint = computeSafetyNumberFingerprint(theirAddress, theirIdentityKey); final Fingerprint fingerprint = computeSafetyNumberFingerprint(theirAddress, theirIdentityKey);
return fingerprint == null ? null : fingerprint.getScannableFingerprint().getSerialized(); return fingerprint == null ? null : fingerprint.getScannableFingerprint().getSerialized();
} }
@ -1165,13 +1222,7 @@ public class ManagerImpl implements Manager {
return resolveSignalServiceAddress(resolveRecipient(address)); return resolveSignalServiceAddress(resolveRecipient(address));
} }
@Override private SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) {
public SignalServiceAddress resolveSignalServiceAddress(UUID uuid) {
return resolveSignalServiceAddress(account.getRecipientStore().resolveRecipient(uuid));
}
@Override
public SignalServiceAddress resolveSignalServiceAddress(RecipientId recipientId) {
final var address = account.getRecipientStore().resolveRecipientAddress(recipientId); final var address = account.getRecipientStore().resolveRecipientAddress(recipientId);
if (address.getUuid().isPresent()) { if (address.getUuid().isPresent()) {
return address.toSignalServiceAddress(); return address.toSignalServiceAddress();
@ -1180,13 +1231,15 @@ public class ManagerImpl implements Manager {
// Address in recipient store doesn't have a uuid, this shouldn't happen // Address in recipient store doesn't have a uuid, this shouldn't happen
// Try to retrieve the uuid from the server // Try to retrieve the uuid from the server
final var number = address.getNumber().get(); final var number = address.getNumber().get();
final UUID uuid;
try { try {
return resolveSignalServiceAddress(getRegisteredUser(number)); uuid = getRegisteredUser(number);
} catch (IOException e) { } catch (IOException e) {
logger.warn("Failed to get uuid for e164 number: {}", number, e); logger.warn("Failed to get uuid for e164 number: {}", number, e);
// Return SignalServiceAddress with unknown UUID // Return SignalServiceAddress with unknown UUID
return address.toSignalServiceAddress(); return address.toSignalServiceAddress();
} }
return resolveSignalServiceAddress(account.getRecipientStore().resolveRecipient(uuid));
} }
private Set<RecipientId> resolveRecipients(Collection<RecipientIdentifier.Single> recipients) throws UnregisteredUserException { private Set<RecipientId> resolveRecipients(Collection<RecipientIdentifier.Single> recipients) throws UnregisteredUserException {

View file

@ -91,18 +91,18 @@ public class RegistrationManager implements Closeable {
} }
public static RegistrationManager init( public static RegistrationManager init(
String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent String number, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
) throws IOException { ) throws IOException {
var pathConfig = PathConfig.createDefault(settingsPath); var pathConfig = PathConfig.createDefault(settingsPath);
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) { if (!SignalAccount.userExists(pathConfig.getDataPath(), number)) {
var identityKey = KeyUtils.generateIdentityKeyPair(); var identityKey = KeyUtils.generateIdentityKeyPair();
var registrationId = KeyHelper.generateRegistrationId(false); var registrationId = KeyHelper.generateRegistrationId(false);
var profileKey = KeyUtils.createProfileKey(); var profileKey = KeyUtils.createProfileKey();
var account = SignalAccount.create(pathConfig.getDataPath(), var account = SignalAccount.create(pathConfig.getDataPath(),
username, number,
identityKey, identityKey,
registrationId, registrationId,
profileKey, profileKey,
@ -111,7 +111,7 @@ public class RegistrationManager implements Closeable {
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
} }
var account = SignalAccount.load(pathConfig.getDataPath(), username, true, TrustNewIdentity.ON_FIRST_USE); var account = SignalAccount.load(pathConfig.getDataPath(), number, true, TrustNewIdentity.ON_FIRST_USE);
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
} }

View file

@ -4,16 +4,16 @@ import java.io.File;
public class UserAlreadyExists extends Exception { public class UserAlreadyExists extends Exception {
private final String username; private final String number;
private final File fileName; private final File fileName;
public UserAlreadyExists(String username, File fileName) { public UserAlreadyExists(String number, File fileName) {
this.username = username; this.number = number;
this.fileName = fileName; this.fileName = fileName;
} }
public String getUsername() { public String getNumber() {
return username; return number;
} }
public File getFileName() { public File getFileName() {

View file

@ -6,12 +6,14 @@ public class Device {
private final String name; private final String name;
private final long created; private final long created;
private final long lastSeen; private final long lastSeen;
private final boolean thisDevice;
public Device(long id, String name, long created, long lastSeen) { public Device(long id, String name, long created, long lastSeen, final boolean thisDevice) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.created = created; this.created = created;
this.lastSeen = lastSeen; this.lastSeen = lastSeen;
this.thisDevice = thisDevice;
} }
public long getId() { public long getId() {
@ -29,4 +31,8 @@ public class Device {
public long getLastSeen() { public long getLastSeen() {
return lastSeen; return lastSeen;
} }
public boolean isThisDevice() {
return thisDevice;
}
} }

View file

@ -0,0 +1,99 @@
package org.asamk.signal.manager.api;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupInviteLinkUrl;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import java.util.Set;
public class Group {
private final GroupId groupId;
private final String title;
private final String description;
private final GroupInviteLinkUrl groupInviteLinkUrl;
private final Set<RecipientAddress> members;
private final Set<RecipientAddress> pendingMembers;
private final Set<RecipientAddress> requestingMembers;
private final Set<RecipientAddress> adminMembers;
private final boolean isBlocked;
private final int messageExpirationTime;
private final boolean isAnnouncementGroup;
private final boolean isMember;
public Group(
final GroupId groupId,
final String title,
final String description,
final GroupInviteLinkUrl groupInviteLinkUrl,
final Set<RecipientAddress> members,
final Set<RecipientAddress> pendingMembers,
final Set<RecipientAddress> requestingMembers,
final Set<RecipientAddress> adminMembers,
final boolean isBlocked,
final int messageExpirationTime,
final boolean isAnnouncementGroup,
final boolean isMember
) {
this.groupId = groupId;
this.title = title;
this.description = description;
this.groupInviteLinkUrl = groupInviteLinkUrl;
this.members = members;
this.pendingMembers = pendingMembers;
this.requestingMembers = requestingMembers;
this.adminMembers = adminMembers;
this.isBlocked = isBlocked;
this.messageExpirationTime = messageExpirationTime;
this.isAnnouncementGroup = isAnnouncementGroup;
this.isMember = isMember;
}
public GroupId getGroupId() {
return groupId;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public GroupInviteLinkUrl getGroupInviteLinkUrl() {
return groupInviteLinkUrl;
}
public Set<RecipientAddress> getMembers() {
return members;
}
public Set<RecipientAddress> getPendingMembers() {
return pendingMembers;
}
public Set<RecipientAddress> getRequestingMembers() {
return requestingMembers;
}
public Set<RecipientAddress> getAdminMembers() {
return adminMembers;
}
public boolean isBlocked() {
return isBlocked;
}
public int getMessageExpirationTime() {
return messageExpirationTime;
}
public boolean isAnnouncementGroup() {
return isAnnouncementGroup;
}
public boolean isMember() {
return isMember;
}
}

View file

@ -0,0 +1,65 @@
package org.asamk.signal.manager.api;
import org.asamk.signal.manager.TrustLevel;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.libsignal.IdentityKey;
import java.util.Date;
public class Identity {
private final RecipientAddress recipient;
private final IdentityKey identityKey;
private final String safetyNumber;
private final byte[] scannableSafetyNumber;
private final TrustLevel trustLevel;
private final Date dateAdded;
public Identity(
final RecipientAddress recipient,
final IdentityKey identityKey,
final String safetyNumber,
final byte[] scannableSafetyNumber,
final TrustLevel trustLevel,
final Date dateAdded
) {
this.recipient = recipient;
this.identityKey = identityKey;
this.safetyNumber = safetyNumber;
this.scannableSafetyNumber = scannableSafetyNumber;
this.trustLevel = trustLevel;
this.dateAdded = dateAdded;
}
public RecipientAddress getRecipient() {
return recipient;
}
public IdentityKey getIdentityKey() {
return this.identityKey;
}
public TrustLevel getTrustLevel() {
return this.trustLevel;
}
boolean isTrusted() {
return trustLevel == TrustLevel.TRUSTED_UNVERIFIED || trustLevel == TrustLevel.TRUSTED_VERIFIED;
}
public Date getDateAdded() {
return this.dateAdded;
}
public byte[] getFingerprint() {
return identityKey.getPublicKey().serialize();
}
public String getSafetyNumber() {
return safetyNumber;
}
public byte[] getScannableSafetyNumber() {
return scannableSafetyNumber;
}
}

View file

@ -1,6 +1,7 @@
package org.asamk.signal.manager.api; package org.asamk.signal.manager.api;
import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter; import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
@ -29,6 +30,17 @@ public abstract class RecipientIdentifier {
public static Single fromAddress(SignalServiceAddress address) { public static Single fromAddress(SignalServiceAddress address) {
return new Uuid(address.getUuid()); return new Uuid(address.getUuid());
} }
public static Single fromAddress(RecipientAddress address) {
if (address.getNumber().isPresent()) {
return new Number(address.getNumber().get());
} else if (address.getUuid().isPresent()) {
return new Uuid(address.getUuid().get());
}
throw new AssertionError("RecipientAddress without identifier");
}
public abstract String getIdentifier();
} }
public static class Uuid extends Single { public static class Uuid extends Single {
@ -53,6 +65,11 @@ public abstract class RecipientIdentifier {
public int hashCode() { public int hashCode() {
return uuid.hashCode(); return uuid.hashCode();
} }
@Override
public String getIdentifier() {
return uuid.toString();
}
} }
public static class Number extends Single { public static class Number extends Single {
@ -77,6 +94,11 @@ public abstract class RecipientIdentifier {
public int hashCode() { public int hashCode() {
return number.hashCode(); return number.hashCode();
} }
@Override
public String getIdentifier() {
return number;
}
} }
public static class Group extends RecipientIdentifier { public static class Group extends RecipientIdentifier {

View file

@ -57,6 +57,16 @@ public class RecipientAddress {
} }
} }
public String getLegacyIdentifier() {
if (e164.isPresent()) {
return e164.get();
} else if (uuid.isPresent()) {
return uuid.get().toString();
} else {
throw new AssertionError("Given the checks in the constructor, this should not be possible.");
}
}
public boolean matches(RecipientAddress other) { public boolean matches(RecipientAddress other) {
return (uuid.isPresent() && other.uuid.isPresent() && uuid.get().equals(other.uuid.get())) || ( return (uuid.isPresent() && other.uuid.isPresent() && uuid.get().equals(other.uuid.get())) || (
e164.isPresent() && other.e164.isPresent() && e164.get().equals(other.e164.get()) e164.isPresent() && other.e164.isPresent() && e164.get().equals(other.e164.get())

View file

@ -13,6 +13,8 @@ import java.util.List;
*/ */
public interface Signal extends DBusInterface { public interface Signal extends DBusInterface {
String getNumber();
long sendMessage( long sendMessage(
String message, List<String> attachments, String recipient String message, List<String> attachments, String recipient
) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity;
@ -26,7 +28,7 @@ public interface Signal extends DBusInterface {
) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity; ) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity;
void sendReadReceipt( void sendReadReceipt(
String recipient, List<Long> targetSentTimestamp String recipient, List<Long> messageIds
) throws Error.Failure, Error.UntrustedIdentity; ) throws Error.Failure, Error.UntrustedIdentity;
long sendRemoteDeleteMessage( long sendRemoteDeleteMessage(

View file

@ -161,7 +161,7 @@ public class App {
} }
if (username == null) { if (username == null) {
var usernames = Manager.getAllLocalUsernames(dataPath); var usernames = Manager.getAllLocalNumbers(dataPath);
if (command instanceof MultiLocalCommand) { if (command instanceof MultiLocalCommand) {
handleMultiLocalCommand((MultiLocalCommand) command, handleMultiLocalCommand((MultiLocalCommand) command,

View file

@ -61,13 +61,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender())); final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));
writer.println( writer.println(
"Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted", "Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted",
m.getUsername(), m.getSelfNumber(),
recipientName, recipientName,
m.getUsername(), m.getSelfNumber(),
recipientName); recipientName);
writer.println( writer.println(
"If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification", "If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification",
m.getUsername(), m.getSelfNumber(),
recipientName); recipientName);
} else { } else {
writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName()); writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName());
@ -657,7 +657,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
private void printMention( private void printMention(
PlainTextWriter writer, SignalServiceDataMessage.Mention mention PlainTextWriter writer, SignalServiceDataMessage.Mention mention
) { ) {
final var address = m.resolveSignalServiceAddress(mention.getUuid()); final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid()));
writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength()); writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength());
} }

View file

@ -37,7 +37,7 @@ public class BlockCommand implements JsonRpcLocalCommand {
final Namespace ns, final Manager m, final OutputWriter outputWriter final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException { ) throws CommandException {
final var contacts = ns.<String>getList("recipient"); final var contacts = ns.<String>getList("recipient");
for (var contact : CommandUtil.getSingleRecipientIdentifiers(contacts, m.getUsername())) { for (var contact : CommandUtil.getSingleRecipientIdentifiers(contacts, m.getSelfNumber())) {
try { try {
m.setContactBlocked(contact, true); m.setContactBlocked(contact, true);
} catch (NotMasterDeviceException e) { } catch (NotMasterDeviceException e) {

View file

@ -95,7 +95,7 @@ public class DaemonCommand implements MultiLocalCommand {
try (var conn = DBusConnection.getConnection(busType)) { try (var conn = DBusConnection.getConnection(busType)) {
final var signalControl = new DbusSignalControlImpl(c, m -> { final var signalControl = new DbusSignalControlImpl(c, m -> {
try { try {
final var objectPath = DbusConfig.getObjectPath(m.getUsername()); final var objectPath = DbusConfig.getObjectPath(m.getSelfNumber());
return run(conn, objectPath, m, outputWriter, ignoreAttachments); return run(conn, objectPath, m, outputWriter, ignoreAttachments);
} catch (DBusException e) { } catch (DBusException e) {
logger.error("Failed to export object", e); logger.error("Failed to export object", e);

View file

@ -57,14 +57,14 @@ public class JoinGroupCommand implements JsonRpcLocalCommand {
var newGroupId = results.first(); var newGroupId = results.first();
if (outputWriter instanceof JsonWriter) { if (outputWriter instanceof JsonWriter) {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;
if (!m.getGroup(newGroupId).isMember(m.getSelfRecipientId())) { if (!m.getGroup(newGroupId).isMember()) {
writer.write(Map.of("groupId", newGroupId.toBase64(), "onlyRequested", true)); writer.write(Map.of("groupId", newGroupId.toBase64(), "onlyRequested", true));
} else { } else {
writer.write(Map.of("groupId", newGroupId.toBase64())); writer.write(Map.of("groupId", newGroupId.toBase64()));
} }
} else { } else {
final var writer = (PlainTextWriter) outputWriter; final var writer = (PlainTextWriter) outputWriter;
if (!m.getGroup(newGroupId).isMember(m.getSelfRecipientId())) { if (!m.getGroup(newGroupId).isMember()) {
writer.println("Requested to join group \"{}\"", newGroupId.toBase64()); writer.println("Requested to join group \"{}\"", newGroupId.toBase64());
} else { } else {
writer.println("Joined group \"{}\"", newGroupId.toBase64()); writer.println("Joined group \"{}\"", newGroupId.toBase64());

View file

@ -44,7 +44,7 @@ public class LinkCommand implements ProvisioningCommand {
try { try {
writer.println("{}", m.getDeviceLinkUri()); writer.println("{}", m.getDeviceLinkUri());
try (var manager = m.finishDeviceLink(deviceName)) { try (var manager = m.finishDeviceLink(deviceName)) {
writer.println("Associated with: {}", manager.getUsername()); writer.println("Associated with: {}", manager.getSelfNumber());
} }
} catch (TimeoutException e) { } catch (TimeoutException e) {
throw new UserErrorException("Link request timed out, please try again."); throw new UserErrorException("Link request timed out, please try again.");
@ -52,7 +52,7 @@ public class LinkCommand implements ProvisioningCommand {
throw new IOErrorException("Link request error: " + e.getMessage(), e); throw new IOErrorException("Link request error: " + e.getMessage(), e);
} catch (UserAlreadyExists e) { } catch (UserAlreadyExists e) {
throw new UserErrorException("The user " throw new UserErrorException("The user "
+ e.getUsername() + e.getNumber()
+ " already exists\nDelete \"" + " already exists\nDelete \""
+ e.getFileName() + e.getFileName()
+ "\" before trying again."); + "\" before trying again.");

View file

@ -8,10 +8,9 @@ import org.asamk.signal.OutputWriter;
import org.asamk.signal.PlainTextWriter; import org.asamk.signal.PlainTextWriter;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class ListContactsCommand implements JsonRpcLocalCommand { public class ListContactsCommand implements JsonRpcLocalCommand {
@Override @Override
@ -33,7 +32,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand {
for (var c : contacts) { for (var c : contacts) {
final var contact = c.second(); final var contact = c.second();
writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}", writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}",
getLegacyIdentifier(m.resolveSignalServiceAddress(c.first())), c.first().getLegacyIdentifier(),
contact.getName(), contact.getName(),
contact.isBlocked(), contact.isBlocked(),
contact.getMessageExpirationTime() == 0 contact.getMessageExpirationTime() == 0
@ -43,10 +42,10 @@ public class ListContactsCommand implements JsonRpcLocalCommand {
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;
final var jsonContacts = contacts.stream().map(contactPair -> { final var jsonContacts = contacts.stream().map(contactPair -> {
final var address = m.resolveSignalServiceAddress(contactPair.first()); final var address = contactPair.first();
final var contact = contactPair.second(); final var contact = contactPair.second();
return new JsonContact(address.getNumber().orNull(), return new JsonContact(address.getNumber().orElse(null),
address.getUuid().toString(), address.getUuid().map(UUID::toString).orElse(null),
contact.getName(), contact.getName(),
contact.isBlocked(), contact.isBlocked(),
contact.getMessageExpirationTime()); contact.getMessageExpirationTime());

View file

@ -46,7 +46,7 @@ public class ListDevicesCommand implements JsonRpcLocalCommand {
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter) {
final var writer = (PlainTextWriter) outputWriter; final var writer = (PlainTextWriter) outputWriter;
for (var d : devices) { for (var d : devices) {
writer.println("- Device {}{}:", d.getId(), (d.getId() == m.getDeviceId() ? " (this device)" : "")); writer.println("- Device {}{}:", d.getId(), (d.isThisDevice() ? " (this device)" : ""));
writer.indent(w -> { writer.indent(w -> {
w.println("Name: {}", d.getName()); w.println("Name: {}", d.getName());
w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated())); w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated()));

View file

@ -9,13 +9,13 @@ import org.asamk.signal.OutputWriter;
import org.asamk.signal.PlainTextWriter; import org.asamk.signal.PlainTextWriter;
import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.storage.groups.GroupInfo; import org.asamk.signal.manager.api.Group;
import org.asamk.signal.manager.storage.recipients.RecipientId; import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.util.Util;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class ListGroupsCommand implements JsonRpcLocalCommand { public class ListGroupsCommand implements JsonRpcLocalCommand {
@ -35,44 +35,41 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
.help("List the members and group invite links of each group. If output=json, then this is always set"); .help("List the members and group invite links of each group. If output=json, then this is always set");
} }
private static Set<String> resolveMembers(Manager m, Set<RecipientId> addresses) { private static Set<String> resolveMembers(Set<RecipientAddress> addresses) {
return addresses.stream() return addresses.stream().map(RecipientAddress::getLegacyIdentifier).collect(Collectors.toSet());
.map(m::resolveSignalServiceAddress)
.map(Util::getLegacyIdentifier)
.collect(Collectors.toSet());
} }
private static Set<JsonGroupMember> resolveJsonMembers(Manager m, Set<RecipientId> addresses) { private static Set<JsonGroupMember> resolveJsonMembers(Set<RecipientAddress> addresses) {
return addresses.stream() return addresses.stream()
.map(m::resolveSignalServiceAddress) .map(address -> new JsonGroupMember(address.getNumber().orElse(null),
.map(address -> new JsonGroupMember(address.getNumber().orNull(), address.getUuid().toString())) address.getUuid().map(UUID::toString).orElse(null)))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
private static void printGroupPlainText( private static void printGroupPlainText(
PlainTextWriter writer, Manager m, GroupInfo group, boolean detailed PlainTextWriter writer, Group group, boolean detailed
) { ) {
if (detailed) { if (detailed) {
final var groupInviteLink = group.getGroupInviteLink(); final var groupInviteLink = group.getGroupInviteLinkUrl();
writer.println( writer.println(
"Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}", "Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}",
group.getGroupId().toBase64(), group.getGroupId().toBase64(),
group.getTitle(), group.getTitle(),
group.getDescription(), group.getDescription(),
group.isMember(m.getSelfRecipientId()), group.isMember(),
group.isBlocked(), group.isBlocked(),
resolveMembers(m, group.getMembers()), resolveMembers(group.getMembers()),
resolveMembers(m, group.getPendingMembers()), resolveMembers(group.getPendingMembers()),
resolveMembers(m, group.getRequestingMembers()), resolveMembers(group.getRequestingMembers()),
resolveMembers(m, group.getAdminMembers()), resolveMembers(group.getAdminMembers()),
group.getMessageExpirationTime() == 0 ? "disabled" : group.getMessageExpirationTime() + "s", group.getMessageExpirationTime() == 0 ? "disabled" : group.getMessageExpirationTime() + "s",
groupInviteLink == null ? '-' : groupInviteLink.getUrl()); groupInviteLink == null ? '-' : groupInviteLink.getUrl());
} else { } else {
writer.println("Id: {} Name: {} Active: {} Blocked: {}", writer.println("Id: {} Name: {} Active: {} Blocked: {}",
group.getGroupId().toBase64(), group.getGroupId().toBase64(),
group.getTitle(), group.getTitle(),
group.isMember(m.getSelfRecipientId()), group.isMember(),
group.isBlocked()); group.isBlocked());
} }
} }
@ -87,18 +84,18 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
final var jsonWriter = (JsonWriter) outputWriter; final var jsonWriter = (JsonWriter) outputWriter;
var jsonGroups = groups.stream().map(group -> { var jsonGroups = groups.stream().map(group -> {
final var groupInviteLink = group.getGroupInviteLink(); final var groupInviteLink = group.getGroupInviteLinkUrl();
return new JsonGroup(group.getGroupId().toBase64(), return new JsonGroup(group.getGroupId().toBase64(),
group.getTitle(), group.getTitle(),
group.getDescription(), group.getDescription(),
group.isMember(m.getSelfRecipientId()), group.isMember(),
group.isBlocked(), group.isBlocked(),
group.getMessageExpirationTime(), group.getMessageExpirationTime(),
resolveJsonMembers(m, group.getMembers()), resolveJsonMembers(group.getMembers()),
resolveJsonMembers(m, group.getPendingMembers()), resolveJsonMembers(group.getPendingMembers()),
resolveJsonMembers(m, group.getRequestingMembers()), resolveJsonMembers(group.getRequestingMembers()),
resolveJsonMembers(m, group.getAdminMembers()), resolveJsonMembers(group.getAdminMembers()),
groupInviteLink == null ? null : groupInviteLink.getUrl()); groupInviteLink == null ? null : groupInviteLink.getUrl());
}).collect(Collectors.toList()); }).collect(Collectors.toList());
@ -107,7 +104,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
final var writer = (PlainTextWriter) outputWriter; final var writer = (PlainTextWriter) outputWriter;
boolean detailed = ns.getBoolean("detailed"); boolean detailed = ns.getBoolean("detailed");
for (var group : groups) { for (var group : groups) {
printGroupPlainText(writer, m, group, detailed); printGroupPlainText(writer, group, detailed);
} }
} }
} }

View file

@ -8,7 +8,7 @@ import org.asamk.signal.OutputWriter;
import org.asamk.signal.PlainTextWriter; import org.asamk.signal.PlainTextWriter;
import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.storage.identities.IdentityInfo; import org.asamk.signal.manager.api.Identity;
import org.asamk.signal.util.CommandUtil; import org.asamk.signal.util.CommandUtil;
import org.asamk.signal.util.Hex; import org.asamk.signal.util.Hex;
import org.asamk.signal.util.Util; import org.asamk.signal.util.Util;
@ -29,9 +29,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
return "listIdentities"; return "listIdentities";
} }
private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, IdentityInfo theirId) { private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, Identity theirId) {
final SignalServiceAddress address = m.resolveSignalServiceAddress(theirId.getRecipientId()); final SignalServiceAddress address = theirId.getRecipient().toSignalServiceAddress();
var digits = Util.formatSafetyNumber(m.computeSafetyNumber(address, theirId.getIdentityKey())); var digits = Util.formatSafetyNumber(theirId.getSafetyNumber());
writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}",
address.getNumber().orNull(), address.getNumber().orNull(),
theirId.getTrustLevel(), theirId.getTrustLevel(),
@ -52,11 +52,11 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
) throws CommandException { ) throws CommandException {
var number = ns.getString("number"); var number = ns.getString("number");
List<IdentityInfo> identities; List<Identity> identities;
if (number == null) { if (number == null) {
identities = m.getIdentities(); identities = m.getIdentities();
} else { } else {
identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getUsername())); identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getSelfNumber()));
} }
if (outputWriter instanceof PlainTextWriter) { if (outputWriter instanceof PlainTextWriter) {
@ -67,9 +67,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;
final var jsonIdentities = identities.stream().map(id -> { final var jsonIdentities = identities.stream().map(id -> {
final var address = m.resolveSignalServiceAddress(id.getRecipientId()); final var address = id.getRecipient().toSignalServiceAddress();
var safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(address, id.getIdentityKey())); var safetyNumber = Util.formatSafetyNumber(id.getSafetyNumber());
var scannableSafetyNumber = m.computeSafetyNumberForScanning(address, id.getIdentityKey()); var scannableSafetyNumber = id.getScannableSafetyNumber();
return new JsonIdentity(address.getNumber().orNull(), return new JsonIdentity(address.getNumber().orNull(),
address.getUuid().toString(), address.getUuid().toString(),
Hex.toString(id.getFingerprint()), Hex.toString(id.getFingerprint()),

View file

@ -50,7 +50,7 @@ public class QuitGroupCommand implements JsonRpcLocalCommand {
) throws CommandException { ) throws CommandException {
final var groupId = CommandUtil.getGroupId(ns.getString("group-id")); final var groupId = CommandUtil.getGroupId(ns.getString("group-id"));
var groupAdmins = CommandUtil.getSingleRecipientIdentifiers(ns.getList("admin"), m.getUsername()); var groupAdmins = CommandUtil.getSingleRecipientIdentifiers(ns.getList("admin"), m.getSelfNumber());
try { try {
try { try {

View file

@ -72,7 +72,7 @@ public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand {
try { try {
final var results = m.sendMessageReaction(emoji, final var results = m.sendMessageReaction(emoji,
isRemove, isRemove,
CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getUsername()), CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
targetTimestamp, targetTimestamp,
recipientIdentifiers); recipientIdentifiers);
outputResult(outputWriter, results.getTimestamp()); outputResult(outputWriter, results.getTimestamp());

View file

@ -37,7 +37,7 @@ public class SendReceiptCommand implements JsonRpcLocalCommand {
final Namespace ns, final Manager m, final OutputWriter outputWriter final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException { ) throws CommandException {
final var recipientString = ns.getString("recipient"); final var recipientString = ns.getString("recipient");
final var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getUsername()); final var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber());
final var targetTimestamps = ns.<Long>getList("target-timestamp"); final var targetTimestamps = ns.<Long>getList("target-timestamp");
final var type = ns.getString("type"); final var type = ns.getString("type");

View file

@ -45,7 +45,7 @@ public class SendTypingCommand implements JsonRpcLocalCommand {
final var recipientIdentifiers = new HashSet<RecipientIdentifier>(); final var recipientIdentifiers = new HashSet<RecipientIdentifier>();
if (recipientStrings != null) { if (recipientStrings != null) {
final var localNumber = m.getUsername(); final var localNumber = m.getSelfNumber();
recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber)); recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber));
} }
if (groupIdStrings != null) { if (groupIdStrings != null) {

View file

@ -38,7 +38,7 @@ public class TrustCommand implements JsonRpcLocalCommand {
final Namespace ns, final Manager m, final OutputWriter outputWriter final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException { ) throws CommandException {
var recipentString = ns.getString("recipient"); var recipentString = ns.getString("recipient");
var recipient = CommandUtil.getSingleRecipientIdentifier(recipentString, m.getUsername()); var recipient = CommandUtil.getSingleRecipientIdentifier(recipentString, m.getSelfNumber());
if (ns.getBoolean("trust-all-known-keys")) { if (ns.getBoolean("trust-all-known-keys")) {
boolean res = m.trustIdentityAllKeys(recipient); boolean res = m.trustIdentityAllKeys(recipient);
if (!res) { if (!res) {

View file

@ -36,7 +36,8 @@ public class UnblockCommand implements JsonRpcLocalCommand {
public void handleCommand( public void handleCommand(
final Namespace ns, final Manager m, final OutputWriter outputWriter final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException { ) throws CommandException {
for (var contactNumber : CommandUtil.getSingleRecipientIdentifiers(ns.getList("recipient"), m.getUsername())) { for (var contactNumber : CommandUtil.getSingleRecipientIdentifiers(ns.getList("recipient"),
m.getSelfNumber())) {
try { try {
m.setContactBlocked(contactNumber, false); m.setContactBlocked(contactNumber, false);
} catch (NotMasterDeviceException e) { } catch (NotMasterDeviceException e) {

View file

@ -33,7 +33,7 @@ public class UpdateContactCommand implements JsonRpcLocalCommand {
final Namespace ns, final Manager m, final OutputWriter outputWriter final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException { ) throws CommandException {
var recipientString = ns.getString("recipient"); var recipientString = ns.getString("recipient");
var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getUsername()); var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber());
try { try {
var expiration = ns.getInt("expiration"); var expiration = ns.getInt("expiration");

View file

@ -116,7 +116,7 @@ public class UpdateGroupCommand implements DbusCommand, JsonRpcLocalCommand {
final var groupIdString = ns.getString("group-id"); final var groupIdString = ns.getString("group-id");
var groupId = CommandUtil.getGroupId(groupIdString); var groupId = CommandUtil.getGroupId(groupIdString);
final var localNumber = m.getUsername(); final var localNumber = m.getSelfNumber();
var groupName = ns.getString("name"); var groupName = ns.getString("name");
var groupDescription = ns.getString("description"); var groupDescription = ns.getString("description");

View file

@ -160,7 +160,7 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl {
synchronized (receiveThreads) { synchronized (receiveThreads) {
return receiveThreads.stream() return receiveThreads.stream()
.map(Pair::first) .map(Pair::first)
.map(Manager::getUsername) .map(Manager::getSelfNumber)
.map(u -> new DBusPath(DbusConfig.getObjectPath(u))) .map(u -> new DBusPath(DbusConfig.getObjectPath(u)))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View file

@ -8,6 +8,7 @@ import org.asamk.signal.manager.NotMasterDeviceException;
import org.asamk.signal.manager.StickerPackInvalidException; import org.asamk.signal.manager.StickerPackInvalidException;
import org.asamk.signal.manager.UntrustedIdentityException; import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.manager.api.Device; import org.asamk.signal.manager.api.Device;
import org.asamk.signal.manager.api.Identity;
import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.Message;
import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.TypingAction;
@ -17,9 +18,9 @@ import org.asamk.signal.manager.groups.GroupNotFoundException;
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.LastGroupAdminException; import org.asamk.signal.manager.groups.LastGroupAdminException;
import org.asamk.signal.manager.groups.NotAGroupMemberException; import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.manager.storage.identities.IdentityInfo; import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.util.ErrorUtils; import org.asamk.signal.util.ErrorUtils;
import org.asamk.signal.util.Util;
import org.freedesktop.dbus.exceptions.DBusExecutionException; import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
@ -45,8 +46,6 @@ import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class DbusSignalImpl implements Signal { public class DbusSignalImpl implements Signal {
private final Manager m; private final Manager m;
@ -67,6 +66,11 @@ public class DbusSignalImpl implements Signal {
return objectPath; return objectPath;
} }
@Override
public String getNumber() {
return m.getSelfNumber();
}
@Override @Override
public void addDevice(String uri) { public void addDevice(String uri) {
try { try {
@ -123,7 +127,7 @@ public class DbusSignalImpl implements Signal {
public long sendMessage(final String message, final List<String> attachments, final List<String> recipients) { public long sendMessage(final String message, final List<String> attachments, final List<String> recipients) {
try { try {
final var results = m.sendMessage(new Message(message, attachments), final var results = m.sendMessage(new Message(message, attachments),
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream() getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast) .map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
@ -153,7 +157,7 @@ public class DbusSignalImpl implements Signal {
) { ) {
try { try {
final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, final var results = m.sendRemoteDeleteMessage(targetSentTimestamp,
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream() getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast) .map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.getTimestamp(), results.getResults());
@ -205,9 +209,9 @@ public class DbusSignalImpl implements Signal {
try { try {
final var results = m.sendMessageReaction(emoji, final var results = m.sendMessageReaction(emoji,
remove, remove,
getSingleRecipientIdentifier(targetAuthor, m.getUsername()), getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
targetSentTimestamp, targetSentTimestamp,
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream() getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast) .map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.getTimestamp(), results.getResults());
@ -227,7 +231,7 @@ public class DbusSignalImpl implements Signal {
var recipients = new ArrayList<String>(1); var recipients = new ArrayList<String>(1);
recipients.add(recipient); recipients.add(recipient);
m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START, m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START,
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream() getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast) .map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
} catch (IOException e) { } catch (IOException e) {
@ -241,10 +245,10 @@ public class DbusSignalImpl implements Signal {
@Override @Override
public void sendReadReceipt( public void sendReadReceipt(
final String recipient, final List<Long> timestamps final String recipient, final List<Long> messageIds
) throws Error.Failure, Error.UntrustedIdentity { ) throws Error.Failure, Error.UntrustedIdentity {
try { try {
m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getUsername()), timestamps); m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds);
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
@ -291,7 +295,7 @@ public class DbusSignalImpl implements Signal {
@Override @Override
public void sendEndSessionMessage(final List<String> recipients) { public void sendEndSessionMessage(final List<String> recipients) {
try { try {
final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getUsername())); final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getSelfNumber()));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.getTimestamp(), results.getResults());
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
@ -325,7 +329,7 @@ public class DbusSignalImpl implements Signal {
try { try {
final var results = m.sendMessageReaction(emoji, final var results = m.sendMessageReaction(emoji,
remove, remove,
getSingleRecipientIdentifier(targetAuthor, m.getUsername()), getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
targetSentTimestamp, targetSentTimestamp,
Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
checkSendMessageResults(results.getTimestamp(), results.getResults()); checkSendMessageResults(results.getTimestamp(), results.getResults());
@ -341,13 +345,13 @@ public class DbusSignalImpl implements Signal {
// the profile name // the profile name
@Override @Override
public String getContactName(final String number) { public String getContactName(final String number) {
return m.getContactOrProfileName(getSingleRecipientIdentifier(number, m.getUsername())); return m.getContactOrProfileName(getSingleRecipientIdentifier(number, m.getSelfNumber()));
} }
@Override @Override
public void setContactName(final String number, final String name) { public void setContactName(final String number, final String name) {
try { try {
m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name); m.setContactName(getSingleRecipientIdentifier(number, m.getSelfNumber()), name);
} catch (NotMasterDeviceException e) { } catch (NotMasterDeviceException e) {
throw new Error.Failure("This command doesn't work on linked devices."); throw new Error.Failure("This command doesn't work on linked devices.");
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
@ -358,7 +362,7 @@ public class DbusSignalImpl implements Signal {
@Override @Override
public void setExpirationTimer(final String number, final int expiration) { public void setExpirationTimer(final String number, final int expiration) {
try { try {
m.setExpirationTimer(getSingleRecipientIdentifier(number, m.getUsername()), expiration); m.setExpirationTimer(getSingleRecipientIdentifier(number, m.getSelfNumber()), expiration);
} catch (IOException e) { } catch (IOException e) {
throw new Error.Failure(e.getMessage()); throw new Error.Failure(e.getMessage());
} }
@ -367,7 +371,7 @@ public class DbusSignalImpl implements Signal {
@Override @Override
public void setContactBlocked(final String number, final boolean blocked) { public void setContactBlocked(final String number, final boolean blocked) {
try { try {
m.setContactBlocked(getSingleRecipientIdentifier(number, m.getUsername()), blocked); m.setContactBlocked(getSingleRecipientIdentifier(number, m.getSelfNumber()), blocked);
} catch (NotMasterDeviceException e) { } catch (NotMasterDeviceException e) {
throw new Error.Failure("This command doesn't work on linked devices."); throw new Error.Failure("This command doesn't work on linked devices.");
} catch (IOException e) { } catch (IOException e) {
@ -412,11 +416,7 @@ public class DbusSignalImpl implements Signal {
if (group == null) { if (group == null) {
return List.of(); return List.of();
} else { } else {
return group.getMembers() return group.getMembers().stream().map(RecipientAddress::getLegacyIdentifier).collect(Collectors.toList());
.stream()
.map(m::resolveSignalServiceAddress)
.map(Util::getLegacyIdentifier)
.collect(Collectors.toList());
} }
} }
@ -432,7 +432,7 @@ public class DbusSignalImpl implements Signal {
if (avatar.isEmpty()) { if (avatar.isEmpty()) {
avatar = null; avatar = null;
} }
final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getUsername()); final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getSelfNumber());
if (groupId == null) { if (groupId == null) {
final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar)); final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar));
checkSendMessageResults(results.second().getTimestamp(), results.second().getResults()); checkSendMessageResults(results.second().getTimestamp(), results.second().getResults());
@ -573,10 +573,9 @@ public class DbusSignalImpl implements Signal {
// all numbers the system knows // all numbers the system knows
@Override @Override
public List<String> listNumbers() { public List<String> listNumbers() {
return Stream.concat(m.getIdentities().stream().map(IdentityInfo::getRecipientId), return Stream.concat(m.getIdentities().stream().map(Identity::getRecipient),
m.getContacts().stream().map(Pair::first)) m.getContacts().stream().map(Pair::first))
.map(m::resolveSignalServiceAddress) .map(a -> a.getNumber().orElse(null))
.map(a -> a.getNumber().orNull())
.filter(Objects::nonNull) .filter(Objects::nonNull)
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -589,16 +588,19 @@ public class DbusSignalImpl implements Signal {
var contacts = m.getContacts(); var contacts = m.getContacts();
for (var c : contacts) { for (var c : contacts) {
if (name.equals(c.second().getName())) { if (name.equals(c.second().getName())) {
numbers.add(getLegacyIdentifier(m.resolveSignalServiceAddress(c.first()))); numbers.add(c.first().getLegacyIdentifier());
} }
} }
// Try profiles if no contact name was found // Try profiles if no contact name was found
for (var identity : m.getIdentities()) { for (var identity : m.getIdentities()) {
final var recipientId = identity.getRecipientId(); final var address = identity.getRecipient();
final var address = m.resolveSignalServiceAddress(recipientId); var number = address.getNumber().orElse(null);
var number = address.getNumber().orNull();
if (number != null) { if (number != null) {
var profile = m.getRecipientProfile(recipientId); Profile profile = null;
try {
profile = m.getRecipientProfile(RecipientIdentifier.Single.fromAddress(address));
} catch (UnregisteredUserException ignored) {
}
if (profile != null && profile.getDisplayName().equals(name)) { if (profile != null && profile.getDisplayName().equals(name)) {
numbers.add(number); numbers.add(number);
} }
@ -639,7 +641,7 @@ public class DbusSignalImpl implements Signal {
@Override @Override
public boolean isContactBlocked(final String number) { public boolean isContactBlocked(final String number) {
return m.isContactBlocked(getSingleRecipientIdentifier(number, m.getUsername())); return m.isContactBlocked(getSingleRecipientIdentifier(number, m.getSelfNumber()));
} }
@Override @Override
@ -658,7 +660,7 @@ public class DbusSignalImpl implements Signal {
if (group == null) { if (group == null) {
return false; return false;
} else { } else {
return group.isMember(m.getSelfRecipientId()); return group.isMember();
} }
} }

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import static org.asamk.signal.util.Util.getLegacyIdentifier; import static org.asamk.signal.util.Util.getLegacyIdentifier;
@ -26,7 +27,7 @@ public class JsonMention {
final int length; final int length;
JsonMention(SignalServiceDataMessage.Mention mention, Manager m) { JsonMention(SignalServiceDataMessage.Mention mention, Manager m) {
final var address = m.resolveSignalServiceAddress(mention.getUuid()); final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid()));
this.name = getLegacyIdentifier(address); this.name = getLegacyIdentifier(address);
this.number = address.getNumber().orNull(); this.number = address.getNumber().orNull();
this.uuid = address.getUuid().toString(); this.uuid = address.getUuid().toString();

View file

@ -86,7 +86,7 @@ public class JsonMessageEnvelope {
} }
String name; String name;
try { try {
name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(this.source, m.getUsername())); name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(this.source, m.getSelfNumber()));
} catch (InvalidNumberException | NullPointerException e) { } catch (InvalidNumberException | NullPointerException e) {
name = null; name = null;
} }

View file

@ -28,7 +28,7 @@ public class CommandUtil {
recipientIdentifiers.add(RecipientIdentifier.NoteToSelf.INSTANCE); recipientIdentifiers.add(RecipientIdentifier.NoteToSelf.INSTANCE);
} }
if (recipientStrings != null) { if (recipientStrings != null) {
final var localNumber = m.getUsername(); final var localNumber = m.getSelfNumber();
recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber)); recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber));
} }
if (groupIdStrings != null) { if (groupIdStrings != null) {