X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/d0d3e207135f8625d20461b4444e12d6baa1b6b6..c0aa338d7c8e40874dbc453b3fc3916701762029:/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index b1d64886..2921533e 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -11,6 +11,7 @@ import org.asamk.signal.manager.api.GroupLinkState; import org.asamk.signal.manager.api.GroupNotFoundException; import org.asamk.signal.manager.api.GroupPermission; import org.asamk.signal.manager.api.GroupSendingNotAllowedException; +import org.asamk.signal.manager.api.IdentityVerificationCode; import org.asamk.signal.manager.api.InactiveGroupLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidNumberException; @@ -20,6 +21,7 @@ import org.asamk.signal.manager.api.Message; import org.asamk.signal.manager.api.NotAGroupMemberException; import org.asamk.signal.manager.api.NotPrimaryDeviceException; import org.asamk.signal.manager.api.PendingAdminApprovalException; +import org.asamk.signal.manager.api.RateLimitException; import org.asamk.signal.manager.api.RecipientAddress; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendMessageResult; @@ -30,6 +32,7 @@ import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UpdateGroup; import org.asamk.signal.manager.api.UpdateProfile; import org.asamk.signal.manager.api.UserStatus; +import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.SendMessageResultUtils; import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; @@ -54,11 +57,12 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; import static org.asamk.signal.dbus.DbusUtils.makeValidObjectPathElement; -public class DbusSignalImpl implements Signal { +public class DbusSignalImpl implements Signal, AutoCloseable { private final Manager m; private final DBusConnection connection; @@ -68,10 +72,11 @@ public class DbusSignalImpl implements Signal { private DBusPath thisDevice; private final List devices = new ArrayList<>(); private final List groups = new ArrayList<>(); + private final List identities = new ArrayList<>(); private DbusReceiveMessageHandler dbusMessageHandler; private int subscriberCount; - private final static Logger logger = LoggerFactory.getLogger(DbusSignalImpl.class); + private static final Logger logger = LoggerFactory.getLogger(DbusSignalImpl.class); public DbusSignalImpl( final Manager m, DBusConnection connection, final String objectPath, final boolean noReceiveOnStart @@ -100,8 +105,10 @@ public class DbusSignalImpl implements Signal { updateDevices(); updateGroups(); updateConfiguration(); + updateIdentities(); } + @Override public void close() { if (dbusMessageHandler != null) { m.removeReceiveHandler(dbusMessageHandler); @@ -114,6 +121,7 @@ public class DbusSignalImpl implements Signal { unExportDevices(); unExportGroups(); unExportConfiguration(); + unExportIdentities(); connection.unExportObject(this.objectPath); } @@ -180,6 +188,8 @@ public class DbusSignalImpl implements Signal { m.addDeviceLink(deviceLinkUrl); } catch (IOException | InvalidDeviceLinkException e) { throw new Error.Failure(e.getClass().getSimpleName() + " Add device link failed. " + e.getMessage()); + } catch (NotPrimaryDeviceException e) { + throw new Error.Failure("This command doesn't work on linked devices."); } catch (URISyntaxException e) { throw new Error.InvalidUri(e.getClass().getSimpleName() + " Device link uri has invalid format: " @@ -342,26 +352,16 @@ public class DbusSignalImpl implements Signal { public void sendReadReceipt( final String recipient, final List messageIds ) throws Error.Failure, Error.UntrustedIdentity { - try { - final var results = m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), - messageIds); - checkSendMessageResults(results); - } catch (IOException e) { - throw new Error.Failure(e.getMessage()); - } + final var results = m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds); + checkSendMessageResults(results); } @Override public void sendViewedReceipt( final String recipient, final List messageIds ) throws Error.Failure, Error.UntrustedIdentity { - try { - final var results = m.sendViewedReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), - messageIds); - checkSendMessageResults(results); - } catch (IOException e) { - throw new Error.Failure(e.getMessage()); - } + final var results = m.sendViewedReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds); + checkSendMessageResults(results); } @Override @@ -523,8 +523,6 @@ public class DbusSignalImpl implements Signal { m.setContactName(getSingleRecipientIdentifier(number, m.getSelfNumber()), name, ""); } catch (NotPrimaryDeviceException e) { throw new Error.Failure("This command doesn't work on linked devices."); - } catch (IOException e) { - throw new Error.Failure("Contact is not registered."); } catch (UnregisteredRecipientException e) { throw new Error.UntrustedIdentity(e.getSender().getIdentifier() + " is not registered."); } @@ -555,6 +553,7 @@ public class DbusSignalImpl implements Signal { } @Override + @Deprecated public void setGroupBlocked(final byte[] groupId, final boolean blocked) { try { m.setGroupsBlocked(List.of(getGroupId(groupId)), blocked); @@ -568,6 +567,7 @@ public class DbusSignalImpl implements Signal { } @Override + @Deprecated public List getGroupIds() { var groups = m.getGroups(); return groups.stream().map(g -> g.groupId().serialize()).toList(); @@ -590,6 +590,7 @@ public class DbusSignalImpl implements Signal { } @Override + @Deprecated public String getGroupName(final byte[] groupId) { var group = m.getGroup(getGroupId(groupId)); if (group == null || group.title() == null) { @@ -600,6 +601,7 @@ public class DbusSignalImpl implements Signal { } @Override + @Deprecated public List getGroupMembers(final byte[] groupId) { var group = m.getGroup(getGroupId(groupId)); if (group == null) { @@ -614,11 +616,16 @@ public class DbusSignalImpl implements Signal { public byte[] createGroup( final String name, final List members, final String avatar ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber { - return updateGroup(new byte[0], name, members, avatar); + return updateGroupInternal(new byte[0], name, members, avatar); } @Override + @Deprecated public byte[] updateGroup(byte[] groupId, String name, List members, String avatar) { + return updateGroupInternal(groupId, name, members, avatar); + } + + public byte[] updateGroupInternal(byte[] groupId, String name, List members, String avatar) { try { groupId = nullIfEmpty(groupId); name = nullIfEmpty(name); @@ -653,6 +660,7 @@ public class DbusSignalImpl implements Signal { } @Override + @Deprecated public boolean isRegistered() { return true; } @@ -674,6 +682,10 @@ public class DbusSignalImpl implements Signal { registered = m.getUserStatus(new HashSet<>(numbers)); } catch (IOException e) { throw new Error.Failure(e.getMessage()); + } catch (RateLimitException e) { + throw new Error.Failure(e.getMessage() + + ", retry at " + + DateUtils.formatTimestamp(e.getNextAttemptTimestamp())); } return numbers.stream().map(number -> registered.get(number).uuid() != null).toList(); @@ -769,6 +781,7 @@ public class DbusSignalImpl implements Signal { } @Override + @Deprecated public void quitGroup(final byte[] groupId) { var group = getGroupId(groupId); try { @@ -808,6 +821,7 @@ public class DbusSignalImpl implements Signal { } @Override + @Deprecated public boolean isGroupBlocked(final byte[] groupId) { var group = m.getGroup(getGroupId(groupId)); if (group == null) { @@ -818,6 +832,7 @@ public class DbusSignalImpl implements Signal { } @Override + @Deprecated public boolean isMember(final byte[] groupId) { var group = m.getGroup(getGroupId(groupId)); if (group == null) { @@ -886,7 +901,7 @@ public class DbusSignalImpl implements Signal { } var errors = SendMessageResultUtils.getErrorMessagesFromSendMessageResults(results); - if (errors.size() == 0 || errors.size() < results.size()) { + if (errors.isEmpty() || errors.size() < results.size()) { return; } @@ -1025,7 +1040,114 @@ public class DbusSignalImpl implements Signal { connection.exportObject(object); logger.debug("Exported dbus object: " + object.getObjectPath()); } catch (DBusException e) { - e.printStackTrace(); + logger.warn("Failed to export dbus object (" + object.getObjectPath() + "): " + e.getMessage()); + } + } + + private void updateIdentities() { + List identities; + identities = m.getIdentities(); + + unExportIdentities(); + + identities.forEach(i -> { + final var object = new DbusSignalIdentityImpl(i); + exportObject(object); + this.identities.add(new StructIdentity(new DBusPath(object.getObjectPath()), + i.recipient().uuid().map(UUID::toString).orElse(""), + i.recipient().number().orElse(""))); + }); + } + + private static String getIdentityObjectPath(String basePath, String id) { + return basePath + "/Identities/" + makeValidObjectPathElement(id); + } + + private void unExportIdentities() { + this.identities.stream() + .map(StructIdentity::getObjectPath) + .map(DBusPath::getPath) + .forEach(connection::unExportObject); + this.identities.clear(); + } + + @Override + public DBusPath getIdentity(String number) throws Error.Failure { + final var found = identities.stream() + .filter(identity -> identity.getNumber().equals(number) || identity.getUuid().equals(number)) + .findFirst(); + + if (found.isEmpty()) { + throw new Error.Failure("Identity for " + number + " unknown"); + } + return found.get().getObjectPath(); + } + + @Override + public List listIdentities() { + updateIdentities(); + return this.identities; + } + + public class DbusSignalIdentityImpl extends DbusProperties implements Signal.Identity { + + private final org.asamk.signal.manager.api.Identity identity; + + public DbusSignalIdentityImpl(final org.asamk.signal.manager.api.Identity identity) { + this.identity = identity; + super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Identity", + List.of(new DbusProperty<>("Number", () -> identity.recipient().number().orElse("")), + new DbusProperty<>("Uuid", + () -> identity.recipient().uuid().map(UUID::toString).orElse("")), + new DbusProperty<>("Fingerprint", identity::getFingerprint), + new DbusProperty<>("SafetyNumber", identity::safetyNumber), + new DbusProperty<>("ScannableSafetyNumber", identity::scannableSafetyNumber), + new DbusProperty<>("TrustLevel", identity::trustLevel), + new DbusProperty<>("AddedDate", identity::dateAddedTimestamp)))); + } + + @Override + public String getObjectPath() { + return getIdentityObjectPath(objectPath, + identity.recipient().getLegacyIdentifier() + "_" + identity.recipient().getIdentifier()); + } + + @Override + public void trust() throws Error.Failure { + var recipient = RecipientIdentifier.Single.fromAddress(identity.recipient()); + try { + m.trustIdentityAllKeys(recipient); + } catch (UnregisteredRecipientException e) { + throw new Error.Failure("The user " + e.getSender().getIdentifier() + " is not registered."); + } + updateIdentities(); + } + + @Override + public void trustVerified(String safetyNumber) throws Error.Failure { + var recipient = RecipientIdentifier.Single.fromAddress(identity.recipient()); + + if (safetyNumber == null) { + throw new Error.Failure("You need to specify a fingerprint/safety number"); + } + final IdentityVerificationCode verificationCode; + try { + verificationCode = IdentityVerificationCode.parse(safetyNumber); + } catch (Exception e) { + throw new Error.Failure( + "Safety number has invalid format, either specify the old hex fingerprint or the new safety number"); + } + + try { + final var res = m.trustIdentityVerified(recipient, verificationCode); + if (!res) { + throw new Error.Failure( + "Failed to set the trust for this number, make sure the number and the fingerprint/safety number are correct."); + } + } catch (UnregisteredRecipientException e) { + throw new Error.Failure("The user " + e.getSender().getIdentifier() + " is not registered."); + } + updateIdentities(); } } @@ -1073,8 +1195,7 @@ public class DbusSignalImpl implements Signal { public class DbusSignalConfigurationImpl extends DbusProperties implements Signal.Configuration { - public DbusSignalConfigurationImpl( - ) { + public DbusSignalConfigurationImpl() { super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Configuration", List.of(new DbusProperty<>("ReadReceipts", this::getReadReceipts, this::setReadReceipts), new DbusProperty<>("UnidentifiedDeliveryIndicators", @@ -1119,8 +1240,6 @@ public class DbusSignalImpl implements Signal { Optional.ofNullable(unidentifiedDeliveryIndicators), Optional.ofNullable(typingIndicators), Optional.ofNullable(linkPreviews))); - } catch (IOException e) { - throw new Error.Failure("UpdateAccount error: " + e.getMessage()); } catch (NotPrimaryDeviceException e) { throw new Error.Failure("This command doesn't work on linked devices."); }