Merge branch 'AsamK:master' into master

This commit is contained in:
Adimarantis 2021-09-29 09:06:29 +02:00 committed by GitHub
commit bc5c54cb77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 2256 additions and 1475 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -126,9 +126,9 @@ public class ProvisioningManager {
profileKey,
TrustNewIdentity.ON_FIRST_USE);
Manager m = null;
ManagerImpl m = null;
try {
m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
logger.debug("Refreshing pre keys");
try {
@ -178,7 +178,7 @@ public class ProvisioningManager {
return false;
}
final var m = new Manager(signalAccount, pathConfig, serviceEnvironmentConfig, userAgent);
final var m = new ManagerImpl(signalAccount, pathConfig, serviceEnvironmentConfig, userAgent);
try (m) {
m.checkAccountState();
} catch (AuthorizationFailedException ignored) {

View file

@ -91,18 +91,18 @@ public class RegistrationManager implements Closeable {
}
public static RegistrationManager init(
String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
String number, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
) throws IOException {
var pathConfig = PathConfig.createDefault(settingsPath);
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
if (!SignalAccount.userExists(pathConfig.getDataPath(), number)) {
var identityKey = KeyUtils.generateIdentityKeyPair();
var registrationId = KeyHelper.generateRegistrationId(false);
var profileKey = KeyUtils.createProfileKey();
var account = SignalAccount.create(pathConfig.getDataPath(),
username,
number,
identityKey,
registrationId,
profileKey,
@ -111,7 +111,7 @@ public class RegistrationManager implements Closeable {
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);
}
@ -177,9 +177,9 @@ public class RegistrationManager implements Closeable {
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
account.finishRegistration(UuidUtil.parseOrNull(response.getUuid()), masterKey, pin);
Manager m = null;
ManagerImpl m = null;
try {
m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
account = null;
m.refreshPreKeys();

View file

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

View file

@ -6,12 +6,14 @@ public class Device {
private final String name;
private final long created;
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.name = name;
this.created = created;
this.lastSeen = lastSeen;
this.thisDevice = thisDevice;
}
public long getId() {
@ -29,4 +31,8 @@ public class Device {
public long getLastSeen() {
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;
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.util.InvalidNumberException;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
@ -12,14 +13,9 @@ public abstract class RecipientIdentifier {
public static class NoteToSelf extends RecipientIdentifier {
@Override
public boolean equals(final Object obj) {
return obj instanceof NoteToSelf;
}
public static NoteToSelf INSTANCE = new NoteToSelf();
@Override
public int hashCode() {
return 5;
private NoteToSelf() {
}
}
@ -34,6 +30,17 @@ public abstract class RecipientIdentifier {
public static Single fromAddress(SignalServiceAddress address) {
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 {
@ -58,6 +65,11 @@ public abstract class RecipientIdentifier {
public int hashCode() {
return uuid.hashCode();
}
@Override
public String getIdentifier() {
return uuid.toString();
}
}
public static class Number extends Single {
@ -82,6 +94,11 @@ public abstract class RecipientIdentifier {
public int hashCode() {
return number.hashCode();
}
@Override
public String getIdentifier() {
return number;
}
}
public static class Group extends RecipientIdentifier {

View file

@ -144,7 +144,8 @@ public final class IncomingMessageHandler {
final var sender = account.getRecipientStore().resolveRecipient(e.getSender());
final var senderProfile = profileProvider.getProfile(sender);
final var selfProfile = profileProvider.getProfile(account.getSelfRecipientId());
if (senderProfile != null
if (e.getSenderDevice() != account.getDeviceId()
&& senderProfile != null
&& senderProfile.getCapabilities().contains(Profile.Capability.senderKey)
&& selfProfile != null
&& selfProfile.getCapabilities().contains(Profile.Capability.senderKey)) {
@ -415,7 +416,7 @@ public final class IncomingMessageHandler {
}
final var recipientId = recipientResolver.resolveRecipient(source);
if (!group.isMember(recipientId)) {
if (!group.isMember(recipientId) && !(group.isPendingMember(recipientId) && message.isGroupV2Update())) {
return true;
}

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) {
return (uuid.isPresent() && other.uuid.isPresent() && uuid.get().equals(other.uuid.get())) || (
e164.isPresent() && other.e164.isPresent() && e164.get().equals(other.e164.get())

View file

@ -110,12 +110,15 @@ updateGroup(groupId<ay>, newName<s>, members<as>, avatar<s>) -> groupId<ay>::
Exceptions: AttachmentInvalid, Failure, InvalidNumber, GroupNotFound
updateProfile(newName<s>, about <s>, aboutEmoji <s>, avatar<s>, remove<b>) -> <>::
* newName : New name for your own profile (empty if unchanged)
updateProfile(name<s>, about<s>, aboutEmoji <s>, avatar<s>, remove<b>) -> <>::
updateProfile(givenName<s>, familyName<s>, about<s>, aboutEmoji <s>, avatar<s>, remove<b>) -> <>::
* name : Name for your own profile (empty if unchanged)
* givenName : Given name for your own profile (empty if unchanged)
* familyName : Family name for your own profile (empty if unchanged)
* about : About message for profile (empty if unchanged)
* aboutEmoji : Emoji for profile (empty if unchanged)
* avatar : Filename of avatar picture for profile (empty if unchanged)
* remove : Set to 1 if the existing avatar picture should be removed
* remove : Set to true if the existing avatar picture should be removed
Exceptions: Failure

View file

@ -13,6 +13,8 @@ import java.util.List;
*/
public interface Signal extends DBusInterface {
String getSelfNumber();
long sendMessage(
String message, List<String> attachments, String recipient
) 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;
void sendReadReceipt(
String recipient, List<Long> targetSentTimestamp
String recipient, List<Long> messageIds
) throws Error.Failure, Error.UntrustedIdentity;
long sendRemoteDeleteMessage(
@ -101,6 +103,15 @@ public interface Signal extends DBusInterface {
void updateDeviceName(String deviceName) throws Error.Failure;
void updateProfile(
String givenName,
String familyName,
String about,
String aboutEmoji,
String avatarPath,
boolean removeAvatar
) throws Error.Failure;
void updateProfile(
String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar
) throws Error.Failure;

View file

@ -8,7 +8,6 @@ import net.sourceforge.argparse4j.inf.Namespace;
import org.asamk.Signal;
import org.asamk.signal.commands.Command;
import org.asamk.signal.commands.Commands;
import org.asamk.signal.commands.DbusCommand;
import org.asamk.signal.commands.ExtendedDbusCommand;
import org.asamk.signal.commands.LocalCommand;
import org.asamk.signal.commands.MultiLocalCommand;
@ -19,6 +18,7 @@ import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.IOErrorException;
import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.dbus.DbusManagerImpl;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.NotRegisteredException;
import org.asamk.signal.manager.ProvisioningManager;
@ -29,6 +29,7 @@ import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.util.IOUtils;
import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
@ -161,7 +162,7 @@ public class App {
}
if (username == null) {
var usernames = Manager.getAllLocalUsernames(dataPath);
var usernames = Manager.getAllLocalNumbers(dataPath);
if (command instanceof MultiLocalCommand) {
handleMultiLocalCommand((MultiLocalCommand) command,
@ -346,8 +347,14 @@ public class App {
) throws CommandException {
if (command instanceof ExtendedDbusCommand) {
((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn, outputWriter);
} else if (command instanceof DbusCommand) {
((DbusCommand) command).handleCommand(ns, ts, outputWriter);
} else if (command instanceof LocalCommand) {
try {
((LocalCommand) command).handleCommand(ns, new DbusManagerImpl(ts), outputWriter);
} catch (UnsupportedOperationException e) {
throw new UserErrorException("Command is not yet implemented via dbus", e);
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException(e.getMessage(), e);
}
} else {
throw new UserErrorException("Command is not yet implemented via dbus");
}

View file

@ -61,13 +61,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));
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",
m.getUsername(),
m.getSelfNumber(),
recipientName,
m.getUsername(),
m.getSelfNumber(),
recipientName);
writer.println(
"If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification",
m.getUsername(),
m.getSelfNumber(),
recipientName);
} else {
writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName());
@ -657,7 +657,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
private void printMention(
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());
}

View file

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

View file

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

View file

@ -1,20 +0,0 @@
package org.asamk.signal.commands;
import net.sourceforge.argparse4j.inf.Namespace;
import org.asamk.Signal;
import org.asamk.signal.OutputWriter;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.dbus.DbusSignalImpl;
import org.asamk.signal.manager.Manager;
public interface DbusCommand extends LocalCommand {
void handleCommand(Namespace ns, Signal signal, OutputWriter outputWriter) throws CommandException;
default void handleCommand(
final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException {
handleCommand(ns, new DbusSignalImpl(m, null), outputWriter);
}
}

View file

@ -57,14 +57,14 @@ public class JoinGroupCommand implements JsonRpcLocalCommand {
var newGroupId = results.first();
if (outputWriter instanceof JsonWriter) {
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));
} else {
writer.write(Map.of("groupId", newGroupId.toBase64()));
}
} else {
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());
} else {
writer.println("Joined group \"{}\"", newGroupId.toBase64());

View file

@ -44,7 +44,7 @@ public class LinkCommand implements ProvisioningCommand {
try {
writer.println("{}", m.getDeviceLinkUri());
try (var manager = m.finishDeviceLink(deviceName)) {
writer.println("Associated with: {}", manager.getUsername());
writer.println("Associated with: {}", manager.getSelfNumber());
}
} catch (TimeoutException e) {
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);
} catch (UserAlreadyExists e) {
throw new UserErrorException("The user "
+ e.getUsername()
+ e.getNumber()
+ " already exists\nDelete \""
+ e.getFileName()
+ "\" before trying again.");

View file

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

View file

@ -46,7 +46,7 @@ public class ListDevicesCommand implements JsonRpcLocalCommand {
if (outputWriter instanceof PlainTextWriter) {
final var writer = (PlainTextWriter) outputWriter;
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 -> {
w.println("Name: {}", d.getName());
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.commands.exceptions.CommandException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.storage.groups.GroupInfo;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.util.Util;
import org.asamk.signal.manager.api.Group;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
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");
}
private static Set<String> resolveMembers(Manager m, Set<RecipientId> addresses) {
return addresses.stream()
.map(m::resolveSignalServiceAddress)
.map(Util::getLegacyIdentifier)
.collect(Collectors.toSet());
private static Set<String> resolveMembers(Set<RecipientAddress> addresses) {
return addresses.stream().map(RecipientAddress::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()
.map(m::resolveSignalServiceAddress)
.map(address -> new JsonGroupMember(address.getNumber().orNull(), address.getUuid().toString()))
.map(address -> new JsonGroupMember(address.getNumber().orElse(null),
address.getUuid().map(UUID::toString).orElse(null)))
.collect(Collectors.toSet());
}
private static void printGroupPlainText(
PlainTextWriter writer, Manager m, GroupInfo group, boolean detailed
PlainTextWriter writer, Group group, boolean detailed
) {
if (detailed) {
final var groupInviteLink = group.getGroupInviteLink();
final var groupInviteLink = group.getGroupInviteLinkUrl();
writer.println(
"Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}",
group.getGroupId().toBase64(),
group.getTitle(),
group.getDescription(),
group.isMember(m.getSelfRecipientId()),
group.isMember(),
group.isBlocked(),
resolveMembers(m, group.getMembers()),
resolveMembers(m, group.getPendingMembers()),
resolveMembers(m, group.getRequestingMembers()),
resolveMembers(m, group.getAdminMembers()),
resolveMembers(group.getMembers()),
resolveMembers(group.getPendingMembers()),
resolveMembers(group.getRequestingMembers()),
resolveMembers(group.getAdminMembers()),
group.getMessageExpirationTime() == 0 ? "disabled" : group.getMessageExpirationTime() + "s",
groupInviteLink == null ? '-' : groupInviteLink.getUrl());
} else {
writer.println("Id: {} Name: {} Active: {} Blocked: {}",
group.getGroupId().toBase64(),
group.getTitle(),
group.isMember(m.getSelfRecipientId()),
group.isMember(),
group.isBlocked());
}
}
@ -87,18 +84,18 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
final var jsonWriter = (JsonWriter) outputWriter;
var jsonGroups = groups.stream().map(group -> {
final var groupInviteLink = group.getGroupInviteLink();
final var groupInviteLink = group.getGroupInviteLinkUrl();
return new JsonGroup(group.getGroupId().toBase64(),
group.getTitle(),
group.getDescription(),
group.isMember(m.getSelfRecipientId()),
group.isMember(),
group.isBlocked(),
group.getMessageExpirationTime(),
resolveJsonMembers(m, group.getMembers()),
resolveJsonMembers(m, group.getPendingMembers()),
resolveJsonMembers(m, group.getRequestingMembers()),
resolveJsonMembers(m, group.getAdminMembers()),
resolveJsonMembers(group.getMembers()),
resolveJsonMembers(group.getPendingMembers()),
resolveJsonMembers(group.getRequestingMembers()),
resolveJsonMembers(group.getAdminMembers()),
groupInviteLink == null ? null : groupInviteLink.getUrl());
}).collect(Collectors.toList());
@ -107,7 +104,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
final var writer = (PlainTextWriter) outputWriter;
boolean detailed = ns.getBoolean("detailed");
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.commands.exceptions.CommandException;
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.Hex;
import org.asamk.signal.util.Util;
@ -29,9 +29,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
return "listIdentities";
}
private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, IdentityInfo theirId) {
final SignalServiceAddress address = m.resolveSignalServiceAddress(theirId.getRecipientId());
var digits = Util.formatSafetyNumber(m.computeSafetyNumber(address, theirId.getIdentityKey()));
private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, Identity theirId) {
final SignalServiceAddress address = theirId.getRecipient().toSignalServiceAddress();
var digits = Util.formatSafetyNumber(theirId.getSafetyNumber());
writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}",
address.getNumber().orNull(),
theirId.getTrustLevel(),
@ -52,11 +52,11 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
) throws CommandException {
var number = ns.getString("number");
List<IdentityInfo> identities;
List<Identity> identities;
if (number == null) {
identities = m.getIdentities();
} else {
identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getUsername()));
identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getSelfNumber()));
}
if (outputWriter instanceof PlainTextWriter) {
@ -67,9 +67,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
} else {
final var writer = (JsonWriter) outputWriter;
final var jsonIdentities = identities.stream().map(id -> {
final var address = m.resolveSignalServiceAddress(id.getRecipientId());
var safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(address, id.getIdentityKey()));
var scannableSafetyNumber = m.computeSafetyNumberForScanning(address, id.getIdentityKey());
final var address = id.getRecipient().toSignalServiceAddress();
var safetyNumber = Util.formatSafetyNumber(id.getSafetyNumber());
var scannableSafetyNumber = id.getScannableSafetyNumber();
return new JsonIdentity(address.getNumber().orNull(),
address.getUuid().toString(),
Hex.toString(id.getFingerprint()),

View file

@ -50,7 +50,7 @@ public class QuitGroupCommand implements JsonRpcLocalCommand {
) throws CommandException {
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 {

View file

@ -4,7 +4,6 @@ import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.Signal;
import org.asamk.signal.JsonWriter;
import org.asamk.signal.OutputWriter;
import org.asamk.signal.PlainTextWriter;
@ -17,13 +16,11 @@ import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.util.CommandUtil;
import org.asamk.signal.util.ErrorUtils;
import org.freedesktop.dbus.errors.UnknownObject;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import java.io.IOException;
import java.util.Map;
public class RemoteDeleteCommand implements DbusCommand, JsonRpcLocalCommand {
public class RemoteDeleteCommand implements JsonRpcLocalCommand {
@Override
public String getName() {
@ -69,47 +66,6 @@ public class RemoteDeleteCommand implements DbusCommand, JsonRpcLocalCommand {
}
}
@Override
public void handleCommand(
final Namespace ns, final Signal signal, final OutputWriter outputWriter
) throws CommandException {
final var recipients = ns.<String>getList("recipient");
final var groupIdStrings = ns.<String>getList("group-id");
final var noRecipients = recipients == null || recipients.isEmpty();
final var noGroups = groupIdStrings == null || groupIdStrings.isEmpty();
if (noRecipients && noGroups) {
throw new UserErrorException("No recipients given");
}
if (!noRecipients && !noGroups) {
throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time");
}
final long targetTimestamp = ns.getLong("target-timestamp");
try {
long timestamp = 0;
if (!noGroups) {
final var groupIds = CommandUtil.getGroupIds(groupIdStrings);
for (final var groupId : groupIds) {
timestamp = signal.sendGroupRemoteDeleteMessage(targetTimestamp, groupId.serialize());
}
} else {
timestamp = signal.sendRemoteDeleteMessage(targetTimestamp, recipients);
}
outputResult(outputWriter, timestamp);
} catch (UnknownObject e) {
throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage());
} catch (Signal.Error.InvalidNumber e) {
throw new UserErrorException("Invalid number: " + e.getMessage());
} catch (Signal.Error.GroupNotFound e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage());
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")", e);
}
}
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
if (outputWriter instanceof PlainTextWriter) {
final var writer = (PlainTextWriter) outputWriter;

View file

@ -4,13 +4,11 @@ import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.Signal;
import org.asamk.signal.JsonWriter;
import org.asamk.signal.OutputWriter;
import org.asamk.signal.PlainTextWriter;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
import org.asamk.signal.commands.exceptions.UntrustedKeyErrorException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.AttachmentInvalidException;
import org.asamk.signal.manager.Manager;
@ -22,8 +20,6 @@ import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.util.CommandUtil;
import org.asamk.signal.util.ErrorUtils;
import org.asamk.signal.util.IOUtils;
import org.freedesktop.dbus.errors.UnknownObject;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -33,7 +29,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class SendCommand implements DbusCommand, JsonRpcLocalCommand {
public class SendCommand implements JsonRpcLocalCommand {
private final static Logger logger = LoggerFactory.getLogger(SendCommand.class);
@ -116,97 +112,6 @@ public class SendCommand implements DbusCommand, JsonRpcLocalCommand {
}
}
@Override
public void handleCommand(
final Namespace ns, final Signal signal, final OutputWriter outputWriter
) throws CommandException {
final var recipients = ns.<String>getList("recipient");
final var isEndSession = ns.getBoolean("end-session");
final var groupIdStrings = ns.<String>getList("group-id");
final var isNoteToSelf = ns.getBoolean("note-to-self");
final var noRecipients = recipients == null || recipients.isEmpty();
final var noGroups = groupIdStrings == null || groupIdStrings.isEmpty();
if ((noRecipients && isEndSession) || (noRecipients && noGroups && !isNoteToSelf)) {
throw new UserErrorException("No recipients given");
}
if (!noRecipients && !noGroups) {
throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time");
}
if (!noRecipients && isNoteToSelf) {
throw new UserErrorException(
"You cannot specify recipients by phone number and note to self at the same time");
}
if (isEndSession) {
try {
signal.sendEndSessionMessage(recipients);
return;
} catch (Signal.Error.UntrustedIdentity e) {
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")", e);
}
}
var messageText = ns.getString("message");
if (messageText == null) {
try {
messageText = IOUtils.readAll(System.in, Charset.defaultCharset());
} catch (IOException e) {
throw new UserErrorException("Failed to read message from stdin: " + e.getMessage());
}
}
List<String> attachments = ns.getList("attachment");
if (attachments == null) {
attachments = List.of();
}
if (!noGroups) {
final var groupIds = CommandUtil.getGroupIds(groupIdStrings);
try {
long timestamp = 0;
for (final var groupId : groupIds) {
timestamp = signal.sendGroupMessage(messageText, attachments, groupId.serialize());
}
outputResult(outputWriter, timestamp);
return;
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send group message: " + e.getMessage(), e);
}
}
if (isNoteToSelf) {
try {
var timestamp = signal.sendNoteToSelfMessage(messageText, attachments);
outputResult(outputWriter, timestamp);
return;
} catch (Signal.Error.UntrustedIdentity e) {
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send note to self message: " + e.getMessage(), e);
}
}
try {
var timestamp = signal.sendMessage(messageText, attachments, recipients);
outputResult(outputWriter, timestamp);
} catch (UnknownObject e) {
throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage());
} catch (Signal.Error.UntrustedIdentity e) {
throw new UntrustedKeyErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")");
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")", e);
}
}
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
if (outputWriter instanceof PlainTextWriter) {
final var writer = (PlainTextWriter) outputWriter;

View file

@ -4,7 +4,6 @@ import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.Signal;
import org.asamk.signal.JsonWriter;
import org.asamk.signal.OutputWriter;
import org.asamk.signal.PlainTextWriter;
@ -17,13 +16,11 @@ import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.util.CommandUtil;
import org.asamk.signal.util.ErrorUtils;
import org.freedesktop.dbus.errors.UnknownObject;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import java.io.IOException;
import java.util.Map;
public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand {
public class SendReactionCommand implements JsonRpcLocalCommand {
@Override
public String getName() {
@ -72,7 +69,7 @@ public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand {
try {
final var results = m.sendMessageReaction(emoji,
isRemove,
CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getUsername()),
CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
targetTimestamp,
recipientIdentifiers);
outputResult(outputWriter, results.getTimestamp());
@ -85,54 +82,6 @@ public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand {
}
}
@Override
public void handleCommand(
final Namespace ns, final Signal signal, final OutputWriter outputWriter
) throws CommandException {
final var recipients = ns.<String>getList("recipient");
final var groupIdStrings = ns.<String>getList("group-id");
final var noRecipients = recipients == null || recipients.isEmpty();
final var noGroups = groupIdStrings == null || groupIdStrings.isEmpty();
if (noRecipients && noGroups) {
throw new UserErrorException("No recipients given");
}
if (!noRecipients && !noGroups) {
throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time");
}
final var emoji = ns.getString("emoji");
final var isRemove = ns.getBoolean("remove");
final var targetAuthor = ns.getString("target-author");
final var targetTimestamp = ns.getLong("target-timestamp");
try {
long timestamp = 0;
if (!noGroups) {
final var groupIds = CommandUtil.getGroupIds(groupIdStrings);
for (final var groupId : groupIds) {
timestamp = signal.sendGroupMessageReaction(emoji,
isRemove,
targetAuthor,
targetTimestamp,
groupId.serialize());
}
} else {
timestamp = signal.sendMessageReaction(emoji, isRemove, targetAuthor, targetTimestamp, recipients);
}
outputResult(outputWriter, timestamp);
} catch (UnknownObject e) {
throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage());
} catch (Signal.Error.InvalidNumber e) {
throw new UserErrorException("Invalid number: " + e.getMessage());
} catch (Signal.Error.GroupNotFound e) {
throw new UserErrorException("Failed to send to group: " + e.getMessage());
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")", e);
}
}
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
if (outputWriter instanceof PlainTextWriter) {
final var writer = (PlainTextWriter) outputWriter;

View file

@ -37,7 +37,7 @@ public class SendReceiptCommand implements JsonRpcLocalCommand {
final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException {
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 type = ns.getString("type");

View file

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

View file

@ -38,7 +38,7 @@ public class TrustCommand implements JsonRpcLocalCommand {
final Namespace ns, final Manager m, final OutputWriter outputWriter
) throws CommandException {
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")) {
boolean res = m.trustIdentityAllKeys(recipient);
if (!res) {

View file

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

View file

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

View file

@ -4,7 +4,6 @@ import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.Signal;
import org.asamk.signal.JsonWriter;
import org.asamk.signal.OutputWriter;
import org.asamk.signal.PlainTextWriter;
@ -21,17 +20,14 @@ import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.util.CommandUtil;
import org.asamk.signal.util.ErrorUtils;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class UpdateGroupCommand implements DbusCommand, JsonRpcLocalCommand {
public class UpdateGroupCommand implements JsonRpcLocalCommand {
private final static Logger logger = LoggerFactory.getLogger(UpdateGroupCommand.class);
@ -116,7 +112,7 @@ public class UpdateGroupCommand implements DbusCommand, JsonRpcLocalCommand {
final var groupIdString = ns.getString("group-id");
var groupId = CommandUtil.getGroupId(groupIdString);
final var localNumber = m.getUsername();
final var localNumber = m.getSelfNumber();
var groupName = ns.getString("name");
var groupDescription = ns.getString("description");
@ -179,43 +175,6 @@ public class UpdateGroupCommand implements DbusCommand, JsonRpcLocalCommand {
}
}
@Override
public void handleCommand(
final Namespace ns, final Signal signal, final OutputWriter outputWriter
) throws CommandException {
var groupId = CommandUtil.getGroupId(ns.getString("group-id"));
var groupName = ns.getString("name");
if (groupName == null) {
groupName = "";
}
List<String> groupMembers = ns.getList("member");
if (groupMembers == null) {
groupMembers = new ArrayList<>();
}
var groupAvatar = ns.getString("avatar");
if (groupAvatar == null) {
groupAvatar = "";
}
try {
var newGroupId = signal.updateGroup(groupId == null ? new byte[0] : groupId.serialize(),
groupName,
groupMembers,
groupAvatar);
if (groupId == null) {
outputResult(outputWriter, null, GroupId.unknownVersion(newGroupId));
}
} catch (Signal.Error.AttachmentInvalid e) {
throw new UserErrorException("Failed to add avatar attachment for group\": " + e.getMessage());
} catch (DBusExecutionException e) {
throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass()
.getSimpleName() + ")", e);
}
}
private void outputResult(final OutputWriter outputWriter, final Long timestamp, final GroupId groupId) {
if (outputWriter instanceof PlainTextWriter) {
final var writer = (PlainTextWriter) outputWriter;

View file

@ -5,4 +5,8 @@ public final class UserErrorException extends CommandException {
public UserErrorException(final String message) {
super(message);
}
public UserErrorException(final String message, final Throwable cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,487 @@
package org.asamk.signal.dbus;
import org.asamk.Signal;
import org.asamk.signal.manager.AttachmentInvalidException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.NotMasterDeviceException;
import org.asamk.signal.manager.StickerPackInvalidException;
import org.asamk.signal.manager.UntrustedIdentityException;
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.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults;
import org.asamk.signal.manager.api.SendMessageResults;
import org.asamk.signal.manager.api.TypingAction;
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.Contact;
import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* This class implements the Manager interface using the DBus Signal interface, where possible.
* It's used for the signal-cli dbus client mode (--dbus, --dbus-system)
*/
public class DbusManagerImpl implements Manager {
private final Signal signal;
public DbusManagerImpl(final Signal signal) {
this.signal = signal;
}
@Override
public String getSelfNumber() {
return signal.getSelfNumber();
}
@Override
public void checkAccountState() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public Map<String, Pair<String, UUID>> areUsersRegistered(final Set<String> numbers) throws IOException {
final var numbersList = new ArrayList<>(numbers);
final var registered = signal.isRegistered(numbersList);
final var result = new HashMap<String, Pair<String, UUID>>();
for (var i = 0; i < numbersList.size(); i++) {
result.put(numbersList.get(i),
new Pair<>(numbersList.get(i), registered.get(i) ? UuidUtil.UNKNOWN_UUID : null));
}
return result;
}
@Override
public void updateAccountAttributes(final String deviceName) throws IOException {
if (deviceName != null) {
signal.updateDeviceName(deviceName);
}
}
@Override
public void setProfile(
final String givenName,
final String familyName,
final String about,
final String aboutEmoji,
final Optional<File> avatar
) throws IOException {
signal.updateProfile(emptyIfNull(givenName),
emptyIfNull(familyName),
emptyIfNull(about),
emptyIfNull(aboutEmoji),
avatar == null ? "" : avatar.transform(File::getPath).or(""),
avatar != null && !avatar.isPresent());
}
@Override
public void unregister() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void deleteAccount() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void submitRateLimitRecaptchaChallenge(final String challenge, final String captcha) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public List<Device> getLinkedDevices() throws IOException {
return signal.listDevices()
.stream()
.map(name -> new Device(-1, name, 0, 0, false))
.collect(Collectors.toList());
}
@Override
public void removeLinkedDevices(final int deviceId) throws IOException {
signal.removeDevice(deviceId);
}
@Override
public void addDeviceLink(final URI linkUri) throws IOException, InvalidKeyException {
signal.addDevice(linkUri.toString());
}
@Override
public void setRegistrationLockPin(final Optional<String> pin) throws IOException, UnauthenticatedResponseException {
if (pin.isPresent()) {
signal.setPin(pin.get());
} else {
signal.removePin();
}
}
@Override
public Profile getRecipientProfile(final RecipientIdentifier.Single recipient) throws UnregisteredUserException {
throw new UnsupportedOperationException();
}
@Override
public List<Group> getGroups() {
final var groupIds = signal.getGroupIds();
return groupIds.stream().map(id -> getGroup(GroupId.unknownVersion(id))).collect(Collectors.toList());
}
@Override
public SendGroupMessageResults quitGroup(
final GroupId groupId, final Set<RecipientIdentifier.Single> groupAdmins
) throws GroupNotFoundException, IOException, NotAGroupMemberException, LastGroupAdminException {
if (groupAdmins.size() > 0) {
throw new UnsupportedOperationException();
}
signal.quitGroup(groupId.serialize());
return new SendGroupMessageResults(0, List.of());
}
@Override
public void deleteGroup(final GroupId groupId) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public Pair<GroupId, SendGroupMessageResults> createGroup(
final String name, final Set<RecipientIdentifier.Single> members, final File avatarFile
) throws IOException, AttachmentInvalidException {
final var newGroupId = signal.updateGroup(new byte[0],
emptyIfNull(name),
members.stream().map(RecipientIdentifier.Single::getIdentifier).collect(Collectors.toList()),
avatarFile == null ? "" : avatarFile.getPath());
return new Pair<>(GroupId.unknownVersion(newGroupId), new SendGroupMessageResults(0, List.of()));
}
@Override
public SendGroupMessageResults updateGroup(
final GroupId groupId,
final String name,
final String description,
final Set<RecipientIdentifier.Single> members,
final Set<RecipientIdentifier.Single> removeMembers,
final Set<RecipientIdentifier.Single> admins,
final Set<RecipientIdentifier.Single> removeAdmins,
final boolean resetGroupLink,
final GroupLinkState groupLinkState,
final GroupPermission addMemberPermission,
final GroupPermission editDetailsPermission,
final File avatarFile,
final Integer expirationTimer,
final Boolean isAnnouncementGroup
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException, GroupSendingNotAllowedException {
signal.updateGroup(groupId.serialize(),
emptyIfNull(name),
members.stream().map(RecipientIdentifier.Single::getIdentifier).collect(Collectors.toList()),
avatarFile == null ? "" : avatarFile.getPath());
return new SendGroupMessageResults(0, List.of());
}
@Override
public Pair<GroupId, SendGroupMessageResults> joinGroup(final GroupInviteLinkUrl inviteLinkUrl) throws IOException, GroupLinkNotActiveException {
final var newGroupId = signal.joinGroup(inviteLinkUrl.getUrl());
return new Pair<>(GroupId.unknownVersion(newGroupId), new SendGroupMessageResults(0, List.of()));
}
@Override
public void sendTypingMessage(
final TypingAction action, final Set<RecipientIdentifier> recipients
) throws IOException, UntrustedIdentityException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
for (final var recipient : recipients) {
if (recipient instanceof RecipientIdentifier.Single) {
signal.sendTyping(((RecipientIdentifier.Single) recipient).getIdentifier(),
action == TypingAction.STOP);
} else if (recipient instanceof RecipientIdentifier.Group) {
throw new UnsupportedOperationException();
}
}
}
@Override
public void sendReadReceipt(
final RecipientIdentifier.Single sender, final List<Long> messageIds
) throws IOException, UntrustedIdentityException {
signal.sendReadReceipt(sender.getIdentifier(), messageIds);
}
@Override
public void sendViewedReceipt(
final RecipientIdentifier.Single sender, final List<Long> messageIds
) throws IOException, UntrustedIdentityException {
throw new UnsupportedOperationException();
}
@Override
public SendMessageResults sendMessage(
final Message message, final Set<RecipientIdentifier> recipients
) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
return handleMessage(recipients,
numbers -> signal.sendMessage(message.getMessageText(), message.getAttachments(), numbers),
() -> signal.sendNoteToSelfMessage(message.getMessageText(), message.getAttachments()),
groupId -> signal.sendGroupMessage(message.getMessageText(), message.getAttachments(), groupId));
}
@Override
public SendMessageResults sendRemoteDeleteMessage(
final long targetSentTimestamp, final Set<RecipientIdentifier> recipients
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
return handleMessage(recipients,
numbers -> signal.sendRemoteDeleteMessage(targetSentTimestamp, numbers),
() -> signal.sendRemoteDeleteMessage(targetSentTimestamp, signal.getSelfNumber()),
groupId -> signal.sendGroupRemoteDeleteMessage(targetSentTimestamp, groupId));
}
@Override
public SendMessageResults sendMessageReaction(
final String emoji,
final boolean remove,
final RecipientIdentifier.Single targetAuthor,
final long targetSentTimestamp,
final Set<RecipientIdentifier> recipients
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
return handleMessage(recipients,
numbers -> signal.sendMessageReaction(emoji,
remove,
targetAuthor.getIdentifier(),
targetSentTimestamp,
numbers),
() -> signal.sendMessageReaction(emoji,
remove,
targetAuthor.getIdentifier(),
targetSentTimestamp,
signal.getSelfNumber()),
groupId -> signal.sendGroupMessageReaction(emoji,
remove,
targetAuthor.getIdentifier(),
targetSentTimestamp,
groupId));
}
@Override
public SendMessageResults sendEndSessionMessage(final Set<RecipientIdentifier.Single> recipients) throws IOException {
signal.sendEndSessionMessage(recipients.stream()
.map(RecipientIdentifier.Single::getIdentifier)
.collect(Collectors.toList()));
return new SendMessageResults(0, Map.of());
}
@Override
public void setContactName(
final RecipientIdentifier.Single recipient, final String name
) throws NotMasterDeviceException, UnregisteredUserException {
signal.setContactName(recipient.getIdentifier(), name);
}
@Override
public void setContactBlocked(
final RecipientIdentifier.Single recipient, final boolean blocked
) throws NotMasterDeviceException, IOException {
signal.setContactBlocked(recipient.getIdentifier(), blocked);
}
@Override
public void setGroupBlocked(
final GroupId groupId, final boolean blocked
) throws GroupNotFoundException, IOException {
signal.setGroupBlocked(groupId.serialize(), blocked);
}
@Override
public void setExpirationTimer(
final RecipientIdentifier.Single recipient, final int messageExpirationTimer
) throws IOException {
signal.setExpirationTimer(recipient.getIdentifier(), messageExpirationTimer);
}
@Override
public URI uploadStickerPack(final File path) throws IOException, StickerPackInvalidException {
try {
return new URI(signal.uploadStickerPack(path.getPath()));
} catch (URISyntaxException e) {
throw new AssertionError(e);
}
}
@Override
public void requestAllSyncData() throws IOException {
signal.sendSyncRequest();
}
@Override
public void receiveMessages(
final long timeout,
final TimeUnit unit,
final boolean returnOnTimeout,
final boolean ignoreAttachments,
final ReceiveMessageHandler handler
) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public boolean hasCaughtUpWithOldMessages() {
throw new UnsupportedOperationException();
}
@Override
public boolean isContactBlocked(final RecipientIdentifier.Single recipient) {
return signal.isContactBlocked(recipient.getIdentifier());
}
@Override
public File getAttachmentFile(final SignalServiceAttachmentRemoteId attachmentId) {
throw new UnsupportedOperationException();
}
@Override
public void sendContacts() throws IOException {
signal.sendContacts();
}
@Override
public List<Pair<RecipientAddress, Contact>> getContacts() {
throw new UnsupportedOperationException();
}
@Override
public String getContactOrProfileName(final RecipientIdentifier.Single recipient) {
return signal.getContactName(recipient.getIdentifier());
}
@Override
public Group getGroup(final GroupId groupId) {
final var id = groupId.serialize();
return new Group(groupId,
signal.getGroupName(id),
null,
null,
signal.getGroupMembers(id).stream().map(m -> new RecipientAddress(null, m)).collect(Collectors.toSet()),
Set.of(),
Set.of(),
Set.of(),
signal.isGroupBlocked(id),
0,
false,
signal.isMember(id));
}
@Override
public List<Identity> getIdentities() {
throw new UnsupportedOperationException();
}
@Override
public List<Identity> getIdentities(final RecipientIdentifier.Single recipient) {
throw new UnsupportedOperationException();
}
@Override
public boolean trustIdentityVerified(final RecipientIdentifier.Single recipient, final byte[] fingerprint) {
throw new UnsupportedOperationException();
}
@Override
public boolean trustIdentityVerifiedSafetyNumber(
final RecipientIdentifier.Single recipient, final String safetyNumber
) {
throw new UnsupportedOperationException();
}
@Override
public boolean trustIdentityVerifiedSafetyNumber(
final RecipientIdentifier.Single recipient, final byte[] safetyNumber
) {
throw new UnsupportedOperationException();
}
@Override
public boolean trustIdentityAllKeys(final RecipientIdentifier.Single recipient) {
throw new UnsupportedOperationException();
}
@Override
public String computeSafetyNumber(
final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey
) {
throw new UnsupportedOperationException();
}
@Override
public SignalServiceAddress resolveSignalServiceAddress(final SignalServiceAddress address) {
return address;
}
@Override
public void close() throws IOException {
}
private SendMessageResults handleMessage(
Set<RecipientIdentifier> recipients,
Function<List<String>, Long> recipientsHandler,
Supplier<Long> noteToSelfHandler,
Function<byte[], Long> groupHandler
) {
long timestamp = 0;
final var singleRecipients = recipients.stream()
.filter(r -> r instanceof RecipientIdentifier.Single)
.map(RecipientIdentifier.Single.class::cast)
.map(RecipientIdentifier.Single::getIdentifier)
.collect(Collectors.toList());
if (singleRecipients.size() > 0) {
timestamp = recipientsHandler.apply(singleRecipients);
}
if (recipients.contains(RecipientIdentifier.NoteToSelf.INSTANCE)) {
timestamp = noteToSelfHandler.get();
}
final var groupRecipients = recipients.stream()
.filter(r -> r instanceof RecipientIdentifier.Group)
.map(RecipientIdentifier.Group.class::cast)
.map(g -> g.groupId)
.collect(Collectors.toList());
for (final var groupId : groupRecipients) {
timestamp = groupHandler.apply(groupId.serialize());
}
return new SendMessageResults(timestamp, Map.of());
}
private String emptyIfNull(final String string) {
return string == null ? "" : string;
}
}

View file

@ -165,7 +165,7 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl {
synchronized (receiveThreads) {
return receiveThreads.stream()
.map(Pair::first)
.map(Manager::getUsername)
.map(Manager::getSelfNumber)
.map(u -> new DBusPath(DbusConfig.getObjectPath(u)))
.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.UntrustedIdentityException;
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.RecipientIdentifier;
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.LastGroupAdminException;
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.Util;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair;
@ -45,8 +46,6 @@ import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
public class DbusSignalImpl implements Signal {
private final Manager m;
@ -67,6 +66,11 @@ public class DbusSignalImpl implements Signal {
return objectPath;
}
@Override
public String getSelfNumber() {
return m.getSelfNumber();
}
@Override
public void addDevice(String uri) {
try {
@ -92,8 +96,6 @@ public class DbusSignalImpl implements Signal {
@Override
public List<String> listDevices() {
List<Device> devices;
List<String> results = new ArrayList<String>();
try {
devices = m.getLinkedDevices();
} catch (IOException | Error.Failure e) {
@ -123,7 +125,7 @@ public class DbusSignalImpl implements Signal {
public long sendMessage(final String message, final List<String> attachments, final List<String> recipients) {
try {
final var results = m.sendMessage(new Message(message, attachments),
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet()));
@ -153,7 +155,7 @@ public class DbusSignalImpl implements Signal {
) {
try {
final var results = m.sendRemoteDeleteMessage(targetSentTimestamp,
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet()));
checkSendMessageResults(results.getTimestamp(), results.getResults());
@ -205,9 +207,9 @@ public class DbusSignalImpl implements Signal {
try {
final var results = m.sendMessageReaction(emoji,
remove,
getSingleRecipientIdentifier(targetAuthor, m.getUsername()),
getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
targetSentTimestamp,
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet()));
checkSendMessageResults(results.getTimestamp(), results.getResults());
@ -227,7 +229,7 @@ public class DbusSignalImpl implements Signal {
var recipients = new ArrayList<String>(1);
recipients.add(recipient);
m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START,
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
.map(RecipientIdentifier.class::cast)
.collect(Collectors.toSet()));
} catch (IOException e) {
@ -241,10 +243,10 @@ public class DbusSignalImpl implements Signal {
@Override
public void sendReadReceipt(
final String recipient, final List<Long> timestamps
final String recipient, final List<Long> messageIds
) throws Error.Failure, Error.UntrustedIdentity {
try {
m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getUsername()), timestamps);
m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds);
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
} catch (UntrustedIdentityException e) {
@ -276,7 +278,7 @@ public class DbusSignalImpl implements Signal {
) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity {
try {
final var results = m.sendMessage(new Message(message, attachments),
Set.of(new RecipientIdentifier.NoteToSelf()));
Set.of(RecipientIdentifier.NoteToSelf.INSTANCE));
checkSendMessageResults(results.getTimestamp(), results.getResults());
return results.getTimestamp();
} catch (AttachmentInvalidException e) {
@ -291,7 +293,7 @@ public class DbusSignalImpl implements Signal {
@Override
public void sendEndSessionMessage(final List<String> recipients) {
try {
final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getUsername()));
final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getSelfNumber()));
checkSendMessageResults(results.getTimestamp(), results.getResults());
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
@ -325,7 +327,7 @@ public class DbusSignalImpl implements Signal {
try {
final var results = m.sendMessageReaction(emoji,
remove,
getSingleRecipientIdentifier(targetAuthor, m.getUsername()),
getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
targetSentTimestamp,
Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
checkSendMessageResults(results.getTimestamp(), results.getResults());
@ -341,13 +343,14 @@ public class DbusSignalImpl implements Signal {
// the profile name
@Override
public String getContactName(final String number) {
return m.getContactOrProfileName(getSingleRecipientIdentifier(number, m.getUsername()));
final var name = m.getContactOrProfileName(getSingleRecipientIdentifier(number, m.getSelfNumber()));
return name == null ? "" : name;
}
@Override
public void setContactName(final String number, final String name) {
try {
m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name);
m.setContactName(getSingleRecipientIdentifier(number, m.getSelfNumber()), name);
} catch (NotMasterDeviceException e) {
throw new Error.Failure("This command doesn't work on linked devices.");
} catch (UnregisteredUserException e) {
@ -358,7 +361,7 @@ public class DbusSignalImpl implements Signal {
@Override
public void setExpirationTimer(final String number, final int expiration) {
try {
m.setExpirationTimer(getSingleRecipientIdentifier(number, m.getUsername()), expiration);
m.setExpirationTimer(getSingleRecipientIdentifier(number, m.getSelfNumber()), expiration);
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
}
@ -367,7 +370,7 @@ public class DbusSignalImpl implements Signal {
@Override
public void setContactBlocked(final String number, final boolean blocked) {
try {
m.setContactBlocked(getSingleRecipientIdentifier(number, m.getUsername()), blocked);
m.setContactBlocked(getSingleRecipientIdentifier(number, m.getSelfNumber()), blocked);
} catch (NotMasterDeviceException e) {
throw new Error.Failure("This command doesn't work on linked devices.");
} catch (IOException e) {
@ -399,7 +402,7 @@ public class DbusSignalImpl implements Signal {
@Override
public String getGroupName(final byte[] groupId) {
var group = m.getGroup(getGroupId(groupId));
if (group == null) {
if (group == null || group.getTitle() == null) {
return "";
} else {
return group.getTitle();
@ -412,27 +415,17 @@ public class DbusSignalImpl implements Signal {
if (group == null) {
return List.of();
} else {
return group.getMembers()
.stream()
.map(m::resolveSignalServiceAddress)
.map(Util::getLegacyIdentifier)
.collect(Collectors.toList());
return group.getMembers().stream().map(RecipientAddress::getLegacyIdentifier).collect(Collectors.toList());
}
}
@Override
public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) {
try {
if (groupId.length == 0) {
groupId = null;
}
if (name.isEmpty()) {
name = null;
}
if (avatar.isEmpty()) {
avatar = null;
}
final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getUsername());
groupId = nullIfEmpty(groupId);
name = nullIfEmpty(name);
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));
checkSendMessageResults(results.second().getTimestamp(), results.second().getResults());
@ -497,6 +490,30 @@ public class DbusSignalImpl implements Signal {
}).collect(Collectors.toList());
}
@Override
public void updateProfile(
String givenName,
String familyName,
String about,
String aboutEmoji,
String avatarPath,
final boolean removeAvatar
) {
try {
givenName = nullIfEmpty(givenName);
familyName = nullIfEmpty(familyName);
about = nullIfEmpty(about);
aboutEmoji = nullIfEmpty(aboutEmoji);
avatarPath = nullIfEmpty(avatarPath);
Optional<File> avatarFile = removeAvatar
? Optional.absent()
: avatarPath == null ? null : Optional.of(new File(avatarPath));
m.setProfile(givenName, familyName, about, aboutEmoji, avatarFile);
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
}
}
@Override
public void updateProfile(
final String name,
@ -505,17 +522,7 @@ public class DbusSignalImpl implements Signal {
String avatarPath,
final boolean removeAvatar
) {
try {
if (avatarPath.isEmpty()) {
avatarPath = null;
}
Optional<File> avatarFile = removeAvatar
? Optional.absent()
: avatarPath == null ? null : Optional.of(new File(avatarPath));
m.setProfile(name, null, about, aboutEmoji, avatarFile);
} catch (IOException e) {
throw new Error.Failure(e.getMessage());
}
updateProfile(name, "", about, aboutEmoji, avatarPath, removeAvatar);
}
@Override
@ -551,10 +558,9 @@ public class DbusSignalImpl implements Signal {
// all numbers the system knows
@Override
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))
.map(m::resolveSignalServiceAddress)
.map(a -> a.getNumber().orNull())
.map(a -> a.getNumber().orElse(null))
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
@ -567,16 +573,19 @@ public class DbusSignalImpl implements Signal {
var contacts = m.getContacts();
for (var c : contacts) {
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
for (var identity : m.getIdentities()) {
final var recipientId = identity.getRecipientId();
final var address = m.resolveSignalServiceAddress(recipientId);
var number = address.getNumber().orNull();
final var address = identity.getRecipient();
var number = address.getNumber().orElse(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)) {
numbers.add(number);
}
@ -617,7 +626,7 @@ public class DbusSignalImpl implements Signal {
@Override
public boolean isContactBlocked(final String number) {
return m.isContactBlocked(getSingleRecipientIdentifier(number, m.getUsername()));
return m.isContactBlocked(getSingleRecipientIdentifier(number, m.getSelfNumber()));
}
@Override
@ -636,7 +645,7 @@ public class DbusSignalImpl implements Signal {
if (group == null) {
return false;
} else {
return group.isMember(m.getSelfRecipientId());
return group.isMember();
}
}
@ -747,4 +756,12 @@ public class DbusSignalImpl implements Signal {
throw new Error.InvalidGroupId("Invalid group id: " + e.getMessage());
}
}
private byte[] nullIfEmpty(final byte[] array) {
return array.length == 0 ? null : array;
}
private String nullIfEmpty(final String name) {
return name.isEmpty() ? null : name;
}
}

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.asamk.signal.manager.Manager;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import static org.asamk.signal.util.Util.getLegacyIdentifier;
@ -26,7 +27,7 @@ public class JsonMention {
final int length;
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.number = address.getNumber().orNull();
this.uuid = address.getUuid().toString();

View file

@ -86,7 +86,7 @@ public class JsonMessageEnvelope {
}
String name;
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) {
name = null;
}

View file

@ -25,10 +25,10 @@ public class CommandUtil {
) throws UserErrorException {
final var recipientIdentifiers = new HashSet<RecipientIdentifier>();
if (isNoteToSelf) {
recipientIdentifiers.add(new RecipientIdentifier.NoteToSelf());
recipientIdentifiers.add(RecipientIdentifier.NoteToSelf.INSTANCE);
}
if (recipientStrings != null) {
final var localNumber = m.getUsername();
final var localNumber = m.getSelfNumber();
recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber));
}
if (groupIdStrings != null) {