X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/489fb2ac2272f2ca1b06c18d4a951e4d502fdd13..bc3bdbbf215c21084c5109209a9a4d6a6c345771:/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 4b5b3ba8..608ed581 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -4,13 +4,24 @@ import org.asamk.Signal; import org.asamk.signal.BaseConfig; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.api.AttachmentInvalidException; +import org.asamk.signal.manager.api.DeviceLinkUrl; +import org.asamk.signal.manager.api.GroupId; +import org.asamk.signal.manager.api.GroupInviteLinkUrl; +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; import org.asamk.signal.manager.api.InvalidStickerException; +import org.asamk.signal.manager.api.LastGroupAdminException; 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.RecipientAddress; import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.api.SendMessageResult; import org.asamk.signal.manager.api.SendMessageResults; @@ -20,15 +31,6 @@ 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.manager.groups.GroupId; -import org.asamk.signal.manager.groups.GroupInviteLinkUrl; -import org.asamk.signal.manager.groups.GroupLinkState; -import org.asamk.signal.manager.groups.GroupNotFoundException; -import org.asamk.signal.manager.groups.GroupPermission; -import org.asamk.signal.manager.groups.GroupSendingNotAllowedException; -import org.asamk.signal.manager.groups.LastGroupAdminException; -import org.asamk.signal.manager.groups.NotAGroupMemberException; -import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.util.SendMessageResultUtils; import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; @@ -53,8 +55,11 @@ 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 { private final Manager m; @@ -65,6 +70,7 @@ 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; @@ -97,6 +103,7 @@ public class DbusSignalImpl implements Signal { updateDevices(); updateGroups(); updateConfiguration(); + updateIdentities(); } public void close() { @@ -111,6 +118,7 @@ public class DbusSignalImpl implements Signal { unExportDevices(); unExportGroups(); unExportConfiguration(); + unExportIdentities(); connection.unExportObject(this.objectPath); } @@ -173,9 +181,12 @@ public class DbusSignalImpl implements Signal { @Override public void addDevice(String uri) { try { - m.addDeviceLink(new URI(uri)); + var deviceLinkUrl = DeviceLinkUrl.parseDeviceLinkUri(new URI(uri)); + 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: " @@ -218,6 +229,8 @@ public class DbusSignalImpl implements Signal { List.of(), Optional.empty(), Optional.empty(), + List.of(), + Optional.empty(), List.of()), getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() .map(RecipientIdentifier.class::cast) @@ -287,7 +300,8 @@ public class DbusSignalImpl implements Signal { targetSentTimestamp, getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() .map(RecipientIdentifier.class::cast) - .collect(Collectors.toSet())); + .collect(Collectors.toSet()), + false); checkSendMessageResults(results); return results.timestamp(); } catch (IOException e) { @@ -385,6 +399,8 @@ public class DbusSignalImpl implements Signal { List.of(), Optional.empty(), Optional.empty(), + List.of(), + Optional.empty(), List.of()), Set.of(RecipientIdentifier.NoteToSelf.INSTANCE)); checkSendMessageResults(results); return results.timestamp(); @@ -427,6 +443,8 @@ public class DbusSignalImpl implements Signal { List.of(), Optional.empty(), Optional.empty(), + List.of(), + Optional.empty(), List.of()), Set.of(getGroupRecipientIdentifier(groupId))); checkSendMessageResults(results); return results.timestamp(); @@ -485,7 +503,8 @@ public class DbusSignalImpl implements Signal { remove, getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()), targetSentTimestamp, - Set.of(getGroupRecipientIdentifier(groupId))); + Set.of(getGroupRecipientIdentifier(groupId)), + false); checkSendMessageResults(results); return results.timestamp(); } catch (IOException e) { @@ -613,7 +632,7 @@ public class DbusSignalImpl implements Signal { avatar = nullIfEmpty(avatar); final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getSelfNumber()); if (groupId == null) { - final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar)); + final var results = m.createGroup(name, memberIdentifiers, avatar); updateGroups(); checkGroupSendMessageResults(results.second().timestamp(), results.second().results()); return results.first().serialize(); @@ -622,7 +641,7 @@ public class DbusSignalImpl implements Signal { UpdateGroup.newBuilder() .withName(name) .withMembers(memberIdentifiers) - .withAvatarFile(avatar == null ? null : new File(avatar)) + .withAvatarFile(avatar) .build()); if (results != null) { checkGroupSendMessageResults(results.timestamp(), results.results()); @@ -682,7 +701,7 @@ public class DbusSignalImpl implements Signal { about = nullIfEmpty(about); aboutEmoji = nullIfEmpty(aboutEmoji); avatarPath = nullIfEmpty(avatarPath); - File avatarFile = removeAvatar || avatarPath == null ? null : new File(avatarPath); + final var avatarFile = removeAvatar || avatarPath == null ? null : avatarPath; m.updateProfile(UpdateProfile.newBuilder() .withGivenName(givenName) .withFamilyName(familyName) @@ -970,11 +989,7 @@ public class DbusSignalImpl implements Signal { } private static String getGroupObjectPath(String basePath, byte[] groupId) { - return basePath + "/Groups/" + Base64.getEncoder() - .encodeToString(groupId) - .replace("+", "_") - .replace("/", "_") - .replace("=", "_"); + return basePath + "/Groups/" + makeValidObjectPathElement(Base64.getEncoder().encodeToString(groupId)); } private void updateGroups() { @@ -1017,7 +1032,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(); } } @@ -1065,8 +1187,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", @@ -1188,8 +1309,10 @@ public class DbusSignalImpl implements Signal { public void quitGroup() throws Error.Failure { try { m.quitGroup(groupId, Set.of()); - } catch (GroupNotFoundException | NotAGroupMemberException e) { + } catch (GroupNotFoundException e) { throw new Error.GroupNotFound(e.getMessage()); + } catch (NotAGroupMemberException e) { + throw new Error.NotAGroupMember(e.getMessage()); } catch (IOException e) { throw new Error.Failure(e.getMessage()); } catch (LastGroupAdminException e) { @@ -1265,7 +1388,7 @@ public class DbusSignalImpl implements Signal { } private void setGroupAvatar(final String avatar) { - updateGroup(UpdateGroup.newBuilder().withAvatarFile(new File(avatar)).build()); + updateGroup(UpdateGroup.newBuilder().withAvatarFile(avatar).build()); } private void setMessageExpirationTime(final int expirationTime) {