mirror of
https://github.com/AsamK/signal-cli
synced 2025-09-04 05:00:39 +00:00
Merge branch 'AsamK:master' into master
This commit is contained in:
commit
bc5c54cb77
41 changed files with 2256 additions and 1475 deletions
File diff suppressed because it is too large
Load diff
1293
lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java
Normal file
1293
lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -126,9 +126,9 @@ public class ProvisioningManager {
|
||||||
profileKey,
|
profileKey,
|
||||||
TrustNewIdentity.ON_FIRST_USE);
|
TrustNewIdentity.ON_FIRST_USE);
|
||||||
|
|
||||||
Manager m = null;
|
ManagerImpl m = null;
|
||||||
try {
|
try {
|
||||||
m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
|
m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
|
||||||
|
|
||||||
logger.debug("Refreshing pre keys");
|
logger.debug("Refreshing pre keys");
|
||||||
try {
|
try {
|
||||||
|
@ -178,7 +178,7 @@ public class ProvisioningManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final var m = new Manager(signalAccount, pathConfig, serviceEnvironmentConfig, userAgent);
|
final var m = new ManagerImpl(signalAccount, pathConfig, serviceEnvironmentConfig, userAgent);
|
||||||
try (m) {
|
try (m) {
|
||||||
m.checkAccountState();
|
m.checkAccountState();
|
||||||
} catch (AuthorizationFailedException ignored) {
|
} catch (AuthorizationFailedException ignored) {
|
||||||
|
|
|
@ -91,18 +91,18 @@ public class RegistrationManager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RegistrationManager init(
|
public static RegistrationManager init(
|
||||||
String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
|
String number, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
var pathConfig = PathConfig.createDefault(settingsPath);
|
var pathConfig = PathConfig.createDefault(settingsPath);
|
||||||
|
|
||||||
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
|
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
|
||||||
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
|
if (!SignalAccount.userExists(pathConfig.getDataPath(), number)) {
|
||||||
var identityKey = KeyUtils.generateIdentityKeyPair();
|
var identityKey = KeyUtils.generateIdentityKeyPair();
|
||||||
var registrationId = KeyHelper.generateRegistrationId(false);
|
var registrationId = KeyHelper.generateRegistrationId(false);
|
||||||
|
|
||||||
var profileKey = KeyUtils.createProfileKey();
|
var profileKey = KeyUtils.createProfileKey();
|
||||||
var account = SignalAccount.create(pathConfig.getDataPath(),
|
var account = SignalAccount.create(pathConfig.getDataPath(),
|
||||||
username,
|
number,
|
||||||
identityKey,
|
identityKey,
|
||||||
registrationId,
|
registrationId,
|
||||||
profileKey,
|
profileKey,
|
||||||
|
@ -111,7 +111,7 @@ public class RegistrationManager implements Closeable {
|
||||||
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
|
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
var account = SignalAccount.load(pathConfig.getDataPath(), username, true, TrustNewIdentity.ON_FIRST_USE);
|
var account = SignalAccount.load(pathConfig.getDataPath(), number, true, TrustNewIdentity.ON_FIRST_USE);
|
||||||
|
|
||||||
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
|
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
|
||||||
}
|
}
|
||||||
|
@ -177,9 +177,9 @@ public class RegistrationManager implements Closeable {
|
||||||
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
|
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
|
||||||
account.finishRegistration(UuidUtil.parseOrNull(response.getUuid()), masterKey, pin);
|
account.finishRegistration(UuidUtil.parseOrNull(response.getUuid()), masterKey, pin);
|
||||||
|
|
||||||
Manager m = null;
|
ManagerImpl m = null;
|
||||||
try {
|
try {
|
||||||
m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
|
m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
|
||||||
account = null;
|
account = null;
|
||||||
|
|
||||||
m.refreshPreKeys();
|
m.refreshPreKeys();
|
||||||
|
|
|
@ -4,16 +4,16 @@ import java.io.File;
|
||||||
|
|
||||||
public class UserAlreadyExists extends Exception {
|
public class UserAlreadyExists extends Exception {
|
||||||
|
|
||||||
private final String username;
|
private final String number;
|
||||||
private final File fileName;
|
private final File fileName;
|
||||||
|
|
||||||
public UserAlreadyExists(String username, File fileName) {
|
public UserAlreadyExists(String number, File fileName) {
|
||||||
this.username = username;
|
this.number = number;
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getNumber() {
|
||||||
return username;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getFileName() {
|
public File getFileName() {
|
||||||
|
|
|
@ -6,12 +6,14 @@ public class Device {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final long created;
|
private final long created;
|
||||||
private final long lastSeen;
|
private final long lastSeen;
|
||||||
|
private final boolean thisDevice;
|
||||||
|
|
||||||
public Device(long id, String name, long created, long lastSeen) {
|
public Device(long id, String name, long created, long lastSeen, final boolean thisDevice) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.created = created;
|
this.created = created;
|
||||||
this.lastSeen = lastSeen;
|
this.lastSeen = lastSeen;
|
||||||
|
this.thisDevice = thisDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getId() {
|
public long getId() {
|
||||||
|
@ -29,4 +31,8 @@ public class Device {
|
||||||
public long getLastSeen() {
|
public long getLastSeen() {
|
||||||
return lastSeen;
|
return lastSeen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isThisDevice() {
|
||||||
|
return thisDevice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
99
lib/src/main/java/org/asamk/signal/manager/api/Group.java
Normal file
99
lib/src/main/java/org/asamk/signal/manager/api/Group.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
65
lib/src/main/java/org/asamk/signal/manager/api/Identity.java
Normal file
65
lib/src/main/java/org/asamk/signal/manager/api/Identity.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package org.asamk.signal.manager.api;
|
package org.asamk.signal.manager.api;
|
||||||
|
|
||||||
import org.asamk.signal.manager.groups.GroupId;
|
import org.asamk.signal.manager.groups.GroupId;
|
||||||
|
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
|
@ -12,14 +13,9 @@ public abstract class RecipientIdentifier {
|
||||||
|
|
||||||
public static class NoteToSelf extends RecipientIdentifier {
|
public static class NoteToSelf extends RecipientIdentifier {
|
||||||
|
|
||||||
@Override
|
public static NoteToSelf INSTANCE = new NoteToSelf();
|
||||||
public boolean equals(final Object obj) {
|
|
||||||
return obj instanceof NoteToSelf;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private NoteToSelf() {
|
||||||
public int hashCode() {
|
|
||||||
return 5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +30,17 @@ public abstract class RecipientIdentifier {
|
||||||
public static Single fromAddress(SignalServiceAddress address) {
|
public static Single fromAddress(SignalServiceAddress address) {
|
||||||
return new Uuid(address.getUuid());
|
return new Uuid(address.getUuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Single fromAddress(RecipientAddress address) {
|
||||||
|
if (address.getNumber().isPresent()) {
|
||||||
|
return new Number(address.getNumber().get());
|
||||||
|
} else if (address.getUuid().isPresent()) {
|
||||||
|
return new Uuid(address.getUuid().get());
|
||||||
|
}
|
||||||
|
throw new AssertionError("RecipientAddress without identifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getIdentifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Uuid extends Single {
|
public static class Uuid extends Single {
|
||||||
|
@ -58,6 +65,11 @@ public abstract class RecipientIdentifier {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return uuid.hashCode();
|
return uuid.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifier() {
|
||||||
|
return uuid.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Number extends Single {
|
public static class Number extends Single {
|
||||||
|
@ -82,6 +94,11 @@ public abstract class RecipientIdentifier {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return number.hashCode();
|
return number.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifier() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Group extends RecipientIdentifier {
|
public static class Group extends RecipientIdentifier {
|
||||||
|
|
|
@ -144,7 +144,8 @@ public final class IncomingMessageHandler {
|
||||||
final var sender = account.getRecipientStore().resolveRecipient(e.getSender());
|
final var sender = account.getRecipientStore().resolveRecipient(e.getSender());
|
||||||
final var senderProfile = profileProvider.getProfile(sender);
|
final var senderProfile = profileProvider.getProfile(sender);
|
||||||
final var selfProfile = profileProvider.getProfile(account.getSelfRecipientId());
|
final var selfProfile = profileProvider.getProfile(account.getSelfRecipientId());
|
||||||
if (senderProfile != null
|
if (e.getSenderDevice() != account.getDeviceId()
|
||||||
|
&& senderProfile != null
|
||||||
&& senderProfile.getCapabilities().contains(Profile.Capability.senderKey)
|
&& senderProfile.getCapabilities().contains(Profile.Capability.senderKey)
|
||||||
&& selfProfile != null
|
&& selfProfile != null
|
||||||
&& selfProfile.getCapabilities().contains(Profile.Capability.senderKey)) {
|
&& selfProfile.getCapabilities().contains(Profile.Capability.senderKey)) {
|
||||||
|
@ -415,7 +416,7 @@ public final class IncomingMessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
final var recipientId = recipientResolver.resolveRecipient(source);
|
final var recipientId = recipientResolver.resolveRecipient(source);
|
||||||
if (!group.isMember(recipientId)) {
|
if (!group.isMember(recipientId) && !(group.isPendingMember(recipientId) && message.isGroupV2Update())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,16 @@ public class RecipientAddress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLegacyIdentifier() {
|
||||||
|
if (e164.isPresent()) {
|
||||||
|
return e164.get();
|
||||||
|
} else if (uuid.isPresent()) {
|
||||||
|
return uuid.get().toString();
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("Given the checks in the constructor, this should not be possible.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean matches(RecipientAddress other) {
|
public boolean matches(RecipientAddress other) {
|
||||||
return (uuid.isPresent() && other.uuid.isPresent() && uuid.get().equals(other.uuid.get())) || (
|
return (uuid.isPresent() && other.uuid.isPresent() && uuid.get().equals(other.uuid.get())) || (
|
||||||
e164.isPresent() && other.e164.isPresent() && e164.get().equals(other.e164.get())
|
e164.isPresent() && other.e164.isPresent() && e164.get().equals(other.e164.get())
|
||||||
|
|
|
@ -110,12 +110,15 @@ updateGroup(groupId<ay>, newName<s>, members<as>, avatar<s>) -> groupId<ay>::
|
||||||
|
|
||||||
Exceptions: AttachmentInvalid, Failure, InvalidNumber, GroupNotFound
|
Exceptions: AttachmentInvalid, Failure, InvalidNumber, GroupNotFound
|
||||||
|
|
||||||
updateProfile(newName<s>, about <s>, aboutEmoji <s>, avatar<s>, remove<b>) -> <>::
|
updateProfile(name<s>, about<s>, aboutEmoji <s>, avatar<s>, remove<b>) -> <>::
|
||||||
* newName : New name for your own profile (empty if unchanged)
|
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)
|
* about : About message for profile (empty if unchanged)
|
||||||
* aboutEmoji : Emoji for profile (empty if unchanged)
|
* aboutEmoji : Emoji for profile (empty if unchanged)
|
||||||
* avatar : Filename of avatar picture 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
|
Exceptions: Failure
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public interface Signal extends DBusInterface {
|
public interface Signal extends DBusInterface {
|
||||||
|
|
||||||
|
String getSelfNumber();
|
||||||
|
|
||||||
long sendMessage(
|
long sendMessage(
|
||||||
String message, List<String> attachments, String recipient
|
String message, List<String> attachments, String recipient
|
||||||
) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity;
|
) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity;
|
||||||
|
@ -26,7 +28,7 @@ public interface Signal extends DBusInterface {
|
||||||
) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity;
|
) throws Error.Failure, Error.GroupNotFound, Error.UntrustedIdentity;
|
||||||
|
|
||||||
void sendReadReceipt(
|
void sendReadReceipt(
|
||||||
String recipient, List<Long> targetSentTimestamp
|
String recipient, List<Long> messageIds
|
||||||
) throws Error.Failure, Error.UntrustedIdentity;
|
) throws Error.Failure, Error.UntrustedIdentity;
|
||||||
|
|
||||||
long sendRemoteDeleteMessage(
|
long sendRemoteDeleteMessage(
|
||||||
|
@ -101,6 +103,15 @@ public interface Signal extends DBusInterface {
|
||||||
|
|
||||||
void updateDeviceName(String deviceName) throws Error.Failure;
|
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(
|
void updateProfile(
|
||||||
String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar
|
String name, String about, String aboutEmoji, String avatarPath, boolean removeAvatar
|
||||||
) throws Error.Failure;
|
) throws Error.Failure;
|
||||||
|
|
|
@ -8,7 +8,6 @@ import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import org.asamk.Signal;
|
import org.asamk.Signal;
|
||||||
import org.asamk.signal.commands.Command;
|
import org.asamk.signal.commands.Command;
|
||||||
import org.asamk.signal.commands.Commands;
|
import org.asamk.signal.commands.Commands;
|
||||||
import org.asamk.signal.commands.DbusCommand;
|
|
||||||
import org.asamk.signal.commands.ExtendedDbusCommand;
|
import org.asamk.signal.commands.ExtendedDbusCommand;
|
||||||
import org.asamk.signal.commands.LocalCommand;
|
import org.asamk.signal.commands.LocalCommand;
|
||||||
import org.asamk.signal.commands.MultiLocalCommand;
|
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.IOErrorException;
|
||||||
import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
|
import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
|
||||||
import org.asamk.signal.commands.exceptions.UserErrorException;
|
import org.asamk.signal.commands.exceptions.UserErrorException;
|
||||||
|
import org.asamk.signal.dbus.DbusManagerImpl;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.manager.NotRegisteredException;
|
import org.asamk.signal.manager.NotRegisteredException;
|
||||||
import org.asamk.signal.manager.ProvisioningManager;
|
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.asamk.signal.util.IOUtils;
|
||||||
import org.freedesktop.dbus.connections.impl.DBusConnection;
|
import org.freedesktop.dbus.connections.impl.DBusConnection;
|
||||||
import org.freedesktop.dbus.exceptions.DBusException;
|
import org.freedesktop.dbus.exceptions.DBusException;
|
||||||
|
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
|
@ -161,7 +162,7 @@ public class App {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
var usernames = Manager.getAllLocalUsernames(dataPath);
|
var usernames = Manager.getAllLocalNumbers(dataPath);
|
||||||
|
|
||||||
if (command instanceof MultiLocalCommand) {
|
if (command instanceof MultiLocalCommand) {
|
||||||
handleMultiLocalCommand((MultiLocalCommand) command,
|
handleMultiLocalCommand((MultiLocalCommand) command,
|
||||||
|
@ -346,8 +347,14 @@ public class App {
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
if (command instanceof ExtendedDbusCommand) {
|
if (command instanceof ExtendedDbusCommand) {
|
||||||
((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn, outputWriter);
|
((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn, outputWriter);
|
||||||
} else if (command instanceof DbusCommand) {
|
} else if (command instanceof LocalCommand) {
|
||||||
((DbusCommand) command).handleCommand(ns, ts, outputWriter);
|
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 {
|
} else {
|
||||||
throw new UserErrorException("Command is not yet implemented via dbus");
|
throw new UserErrorException("Command is not yet implemented via dbus");
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,13 +61,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
||||||
final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));
|
final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));
|
||||||
writer.println(
|
writer.println(
|
||||||
"Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted",
|
"Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted",
|
||||||
m.getUsername(),
|
m.getSelfNumber(),
|
||||||
recipientName,
|
recipientName,
|
||||||
m.getUsername(),
|
m.getSelfNumber(),
|
||||||
recipientName);
|
recipientName);
|
||||||
writer.println(
|
writer.println(
|
||||||
"If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification",
|
"If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification",
|
||||||
m.getUsername(),
|
m.getSelfNumber(),
|
||||||
recipientName);
|
recipientName);
|
||||||
} else {
|
} else {
|
||||||
writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName());
|
writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName());
|
||||||
|
@ -657,7 +657,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
||||||
private void printMention(
|
private void printMention(
|
||||||
PlainTextWriter writer, SignalServiceDataMessage.Mention mention
|
PlainTextWriter writer, SignalServiceDataMessage.Mention mention
|
||||||
) {
|
) {
|
||||||
final var address = m.resolveSignalServiceAddress(mention.getUuid());
|
final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid()));
|
||||||
writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength());
|
writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class BlockCommand implements JsonRpcLocalCommand {
|
||||||
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
final var contacts = ns.<String>getList("recipient");
|
final var contacts = ns.<String>getList("recipient");
|
||||||
for (var contact : CommandUtil.getSingleRecipientIdentifiers(contacts, m.getUsername())) {
|
for (var contact : CommandUtil.getSingleRecipientIdentifiers(contacts, m.getSelfNumber())) {
|
||||||
try {
|
try {
|
||||||
m.setContactBlocked(contact, true);
|
m.setContactBlocked(contact, true);
|
||||||
} catch (NotMasterDeviceException e) {
|
} catch (NotMasterDeviceException e) {
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class DaemonCommand implements MultiLocalCommand {
|
||||||
try (var conn = DBusConnection.getConnection(busType)) {
|
try (var conn = DBusConnection.getConnection(busType)) {
|
||||||
final var signalControl = new DbusSignalControlImpl(c, m -> {
|
final var signalControl = new DbusSignalControlImpl(c, m -> {
|
||||||
try {
|
try {
|
||||||
final var objectPath = DbusConfig.getObjectPath(m.getUsername());
|
final var objectPath = DbusConfig.getObjectPath(m.getSelfNumber());
|
||||||
return run(conn, objectPath, m, outputWriter, ignoreAttachments);
|
return run(conn, objectPath, m, outputWriter, ignoreAttachments);
|
||||||
} catch (DBusException e) {
|
} catch (DBusException e) {
|
||||||
logger.error("Failed to export object", e);
|
logger.error("Failed to export object", e);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -57,14 +57,14 @@ public class JoinGroupCommand implements JsonRpcLocalCommand {
|
||||||
var newGroupId = results.first();
|
var newGroupId = results.first();
|
||||||
if (outputWriter instanceof JsonWriter) {
|
if (outputWriter instanceof JsonWriter) {
|
||||||
final var writer = (JsonWriter) outputWriter;
|
final var writer = (JsonWriter) outputWriter;
|
||||||
if (!m.getGroup(newGroupId).isMember(m.getSelfRecipientId())) {
|
if (!m.getGroup(newGroupId).isMember()) {
|
||||||
writer.write(Map.of("groupId", newGroupId.toBase64(), "onlyRequested", true));
|
writer.write(Map.of("groupId", newGroupId.toBase64(), "onlyRequested", true));
|
||||||
} else {
|
} else {
|
||||||
writer.write(Map.of("groupId", newGroupId.toBase64()));
|
writer.write(Map.of("groupId", newGroupId.toBase64()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final var writer = (PlainTextWriter) outputWriter;
|
final var writer = (PlainTextWriter) outputWriter;
|
||||||
if (!m.getGroup(newGroupId).isMember(m.getSelfRecipientId())) {
|
if (!m.getGroup(newGroupId).isMember()) {
|
||||||
writer.println("Requested to join group \"{}\"", newGroupId.toBase64());
|
writer.println("Requested to join group \"{}\"", newGroupId.toBase64());
|
||||||
} else {
|
} else {
|
||||||
writer.println("Joined group \"{}\"", newGroupId.toBase64());
|
writer.println("Joined group \"{}\"", newGroupId.toBase64());
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class LinkCommand implements ProvisioningCommand {
|
||||||
try {
|
try {
|
||||||
writer.println("{}", m.getDeviceLinkUri());
|
writer.println("{}", m.getDeviceLinkUri());
|
||||||
try (var manager = m.finishDeviceLink(deviceName)) {
|
try (var manager = m.finishDeviceLink(deviceName)) {
|
||||||
writer.println("Associated with: {}", manager.getUsername());
|
writer.println("Associated with: {}", manager.getSelfNumber());
|
||||||
}
|
}
|
||||||
} catch (TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
throw new UserErrorException("Link request timed out, please try again.");
|
throw new UserErrorException("Link request timed out, please try again.");
|
||||||
|
@ -52,7 +52,7 @@ public class LinkCommand implements ProvisioningCommand {
|
||||||
throw new IOErrorException("Link request error: " + e.getMessage(), e);
|
throw new IOErrorException("Link request error: " + e.getMessage(), e);
|
||||||
} catch (UserAlreadyExists e) {
|
} catch (UserAlreadyExists e) {
|
||||||
throw new UserErrorException("The user "
|
throw new UserErrorException("The user "
|
||||||
+ e.getUsername()
|
+ e.getNumber()
|
||||||
+ " already exists\nDelete \""
|
+ " already exists\nDelete \""
|
||||||
+ e.getFileName()
|
+ e.getFileName()
|
||||||
+ "\" before trying again.");
|
+ "\" before trying again.");
|
||||||
|
|
|
@ -8,10 +8,9 @@ import org.asamk.signal.OutputWriter;
|
||||||
import org.asamk.signal.PlainTextWriter;
|
import org.asamk.signal.PlainTextWriter;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.asamk.signal.util.Util.getLegacyIdentifier;
|
|
||||||
|
|
||||||
public class ListContactsCommand implements JsonRpcLocalCommand {
|
public class ListContactsCommand implements JsonRpcLocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -33,7 +32,7 @@ public class ListContactsCommand implements JsonRpcLocalCommand {
|
||||||
for (var c : contacts) {
|
for (var c : contacts) {
|
||||||
final var contact = c.second();
|
final var contact = c.second();
|
||||||
writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}",
|
writer.println("Number: {} Name: {} Blocked: {} Message expiration: {}",
|
||||||
getLegacyIdentifier(m.resolveSignalServiceAddress(c.first())),
|
c.first().getLegacyIdentifier(),
|
||||||
contact.getName(),
|
contact.getName(),
|
||||||
contact.isBlocked(),
|
contact.isBlocked(),
|
||||||
contact.getMessageExpirationTime() == 0
|
contact.getMessageExpirationTime() == 0
|
||||||
|
@ -43,10 +42,10 @@ public class ListContactsCommand implements JsonRpcLocalCommand {
|
||||||
} else {
|
} else {
|
||||||
final var writer = (JsonWriter) outputWriter;
|
final var writer = (JsonWriter) outputWriter;
|
||||||
final var jsonContacts = contacts.stream().map(contactPair -> {
|
final var jsonContacts = contacts.stream().map(contactPair -> {
|
||||||
final var address = m.resolveSignalServiceAddress(contactPair.first());
|
final var address = contactPair.first();
|
||||||
final var contact = contactPair.second();
|
final var contact = contactPair.second();
|
||||||
return new JsonContact(address.getNumber().orNull(),
|
return new JsonContact(address.getNumber().orElse(null),
|
||||||
address.getUuid().toString(),
|
address.getUuid().map(UUID::toString).orElse(null),
|
||||||
contact.getName(),
|
contact.getName(),
|
||||||
contact.isBlocked(),
|
contact.isBlocked(),
|
||||||
contact.getMessageExpirationTime());
|
contact.getMessageExpirationTime());
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class ListDevicesCommand implements JsonRpcLocalCommand {
|
||||||
if (outputWriter instanceof PlainTextWriter) {
|
if (outputWriter instanceof PlainTextWriter) {
|
||||||
final var writer = (PlainTextWriter) outputWriter;
|
final var writer = (PlainTextWriter) outputWriter;
|
||||||
for (var d : devices) {
|
for (var d : devices) {
|
||||||
writer.println("- Device {}{}:", d.getId(), (d.getId() == m.getDeviceId() ? " (this device)" : ""));
|
writer.println("- Device {}{}:", d.getId(), (d.isThisDevice() ? " (this device)" : ""));
|
||||||
writer.indent(w -> {
|
writer.indent(w -> {
|
||||||
w.println("Name: {}", d.getName());
|
w.println("Name: {}", d.getName());
|
||||||
w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated()));
|
w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated()));
|
||||||
|
|
|
@ -9,13 +9,13 @@ import org.asamk.signal.OutputWriter;
|
||||||
import org.asamk.signal.PlainTextWriter;
|
import org.asamk.signal.PlainTextWriter;
|
||||||
import org.asamk.signal.commands.exceptions.CommandException;
|
import org.asamk.signal.commands.exceptions.CommandException;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.manager.storage.groups.GroupInfo;
|
import org.asamk.signal.manager.api.Group;
|
||||||
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
|
||||||
import org.asamk.signal.util.Util;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ListGroupsCommand implements JsonRpcLocalCommand {
|
public class ListGroupsCommand implements JsonRpcLocalCommand {
|
||||||
|
@ -35,44 +35,41 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
|
||||||
.help("List the members and group invite links of each group. If output=json, then this is always set");
|
.help("List the members and group invite links of each group. If output=json, then this is always set");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> resolveMembers(Manager m, Set<RecipientId> addresses) {
|
private static Set<String> resolveMembers(Set<RecipientAddress> addresses) {
|
||||||
return addresses.stream()
|
return addresses.stream().map(RecipientAddress::getLegacyIdentifier).collect(Collectors.toSet());
|
||||||
.map(m::resolveSignalServiceAddress)
|
|
||||||
.map(Util::getLegacyIdentifier)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<JsonGroupMember> resolveJsonMembers(Manager m, Set<RecipientId> addresses) {
|
private static Set<JsonGroupMember> resolveJsonMembers(Set<RecipientAddress> addresses) {
|
||||||
return addresses.stream()
|
return addresses.stream()
|
||||||
.map(m::resolveSignalServiceAddress)
|
.map(address -> new JsonGroupMember(address.getNumber().orElse(null),
|
||||||
.map(address -> new JsonGroupMember(address.getNumber().orNull(), address.getUuid().toString()))
|
address.getUuid().map(UUID::toString).orElse(null)))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printGroupPlainText(
|
private static void printGroupPlainText(
|
||||||
PlainTextWriter writer, Manager m, GroupInfo group, boolean detailed
|
PlainTextWriter writer, Group group, boolean detailed
|
||||||
) {
|
) {
|
||||||
if (detailed) {
|
if (detailed) {
|
||||||
final var groupInviteLink = group.getGroupInviteLink();
|
final var groupInviteLink = group.getGroupInviteLinkUrl();
|
||||||
|
|
||||||
writer.println(
|
writer.println(
|
||||||
"Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}",
|
"Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}",
|
||||||
group.getGroupId().toBase64(),
|
group.getGroupId().toBase64(),
|
||||||
group.getTitle(),
|
group.getTitle(),
|
||||||
group.getDescription(),
|
group.getDescription(),
|
||||||
group.isMember(m.getSelfRecipientId()),
|
group.isMember(),
|
||||||
group.isBlocked(),
|
group.isBlocked(),
|
||||||
resolveMembers(m, group.getMembers()),
|
resolveMembers(group.getMembers()),
|
||||||
resolveMembers(m, group.getPendingMembers()),
|
resolveMembers(group.getPendingMembers()),
|
||||||
resolveMembers(m, group.getRequestingMembers()),
|
resolveMembers(group.getRequestingMembers()),
|
||||||
resolveMembers(m, group.getAdminMembers()),
|
resolveMembers(group.getAdminMembers()),
|
||||||
group.getMessageExpirationTime() == 0 ? "disabled" : group.getMessageExpirationTime() + "s",
|
group.getMessageExpirationTime() == 0 ? "disabled" : group.getMessageExpirationTime() + "s",
|
||||||
groupInviteLink == null ? '-' : groupInviteLink.getUrl());
|
groupInviteLink == null ? '-' : groupInviteLink.getUrl());
|
||||||
} else {
|
} else {
|
||||||
writer.println("Id: {} Name: {} Active: {} Blocked: {}",
|
writer.println("Id: {} Name: {} Active: {} Blocked: {}",
|
||||||
group.getGroupId().toBase64(),
|
group.getGroupId().toBase64(),
|
||||||
group.getTitle(),
|
group.getTitle(),
|
||||||
group.isMember(m.getSelfRecipientId()),
|
group.isMember(),
|
||||||
group.isBlocked());
|
group.isBlocked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,18 +84,18 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
|
||||||
final var jsonWriter = (JsonWriter) outputWriter;
|
final var jsonWriter = (JsonWriter) outputWriter;
|
||||||
|
|
||||||
var jsonGroups = groups.stream().map(group -> {
|
var jsonGroups = groups.stream().map(group -> {
|
||||||
final var groupInviteLink = group.getGroupInviteLink();
|
final var groupInviteLink = group.getGroupInviteLinkUrl();
|
||||||
|
|
||||||
return new JsonGroup(group.getGroupId().toBase64(),
|
return new JsonGroup(group.getGroupId().toBase64(),
|
||||||
group.getTitle(),
|
group.getTitle(),
|
||||||
group.getDescription(),
|
group.getDescription(),
|
||||||
group.isMember(m.getSelfRecipientId()),
|
group.isMember(),
|
||||||
group.isBlocked(),
|
group.isBlocked(),
|
||||||
group.getMessageExpirationTime(),
|
group.getMessageExpirationTime(),
|
||||||
resolveJsonMembers(m, group.getMembers()),
|
resolveJsonMembers(group.getMembers()),
|
||||||
resolveJsonMembers(m, group.getPendingMembers()),
|
resolveJsonMembers(group.getPendingMembers()),
|
||||||
resolveJsonMembers(m, group.getRequestingMembers()),
|
resolveJsonMembers(group.getRequestingMembers()),
|
||||||
resolveJsonMembers(m, group.getAdminMembers()),
|
resolveJsonMembers(group.getAdminMembers()),
|
||||||
groupInviteLink == null ? null : groupInviteLink.getUrl());
|
groupInviteLink == null ? null : groupInviteLink.getUrl());
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
@ -107,7 +104,7 @@ public class ListGroupsCommand implements JsonRpcLocalCommand {
|
||||||
final var writer = (PlainTextWriter) outputWriter;
|
final var writer = (PlainTextWriter) outputWriter;
|
||||||
boolean detailed = ns.getBoolean("detailed");
|
boolean detailed = ns.getBoolean("detailed");
|
||||||
for (var group : groups) {
|
for (var group : groups) {
|
||||||
printGroupPlainText(writer, m, group, detailed);
|
printGroupPlainText(writer, group, detailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import org.asamk.signal.OutputWriter;
|
||||||
import org.asamk.signal.PlainTextWriter;
|
import org.asamk.signal.PlainTextWriter;
|
||||||
import org.asamk.signal.commands.exceptions.CommandException;
|
import org.asamk.signal.commands.exceptions.CommandException;
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.asamk.signal.manager.storage.identities.IdentityInfo;
|
import org.asamk.signal.manager.api.Identity;
|
||||||
import org.asamk.signal.util.CommandUtil;
|
import org.asamk.signal.util.CommandUtil;
|
||||||
import org.asamk.signal.util.Hex;
|
import org.asamk.signal.util.Hex;
|
||||||
import org.asamk.signal.util.Util;
|
import org.asamk.signal.util.Util;
|
||||||
|
@ -29,9 +29,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
|
||||||
return "listIdentities";
|
return "listIdentities";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, IdentityInfo theirId) {
|
private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, Identity theirId) {
|
||||||
final SignalServiceAddress address = m.resolveSignalServiceAddress(theirId.getRecipientId());
|
final SignalServiceAddress address = theirId.getRecipient().toSignalServiceAddress();
|
||||||
var digits = Util.formatSafetyNumber(m.computeSafetyNumber(address, theirId.getIdentityKey()));
|
var digits = Util.formatSafetyNumber(theirId.getSafetyNumber());
|
||||||
writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}",
|
writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}",
|
||||||
address.getNumber().orNull(),
|
address.getNumber().orNull(),
|
||||||
theirId.getTrustLevel(),
|
theirId.getTrustLevel(),
|
||||||
|
@ -52,11 +52,11 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
var number = ns.getString("number");
|
var number = ns.getString("number");
|
||||||
|
|
||||||
List<IdentityInfo> identities;
|
List<Identity> identities;
|
||||||
if (number == null) {
|
if (number == null) {
|
||||||
identities = m.getIdentities();
|
identities = m.getIdentities();
|
||||||
} else {
|
} else {
|
||||||
identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getUsername()));
|
identities = m.getIdentities(CommandUtil.getSingleRecipientIdentifier(number, m.getSelfNumber()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputWriter instanceof PlainTextWriter) {
|
if (outputWriter instanceof PlainTextWriter) {
|
||||||
|
@ -67,9 +67,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand {
|
||||||
} else {
|
} else {
|
||||||
final var writer = (JsonWriter) outputWriter;
|
final var writer = (JsonWriter) outputWriter;
|
||||||
final var jsonIdentities = identities.stream().map(id -> {
|
final var jsonIdentities = identities.stream().map(id -> {
|
||||||
final var address = m.resolveSignalServiceAddress(id.getRecipientId());
|
final var address = id.getRecipient().toSignalServiceAddress();
|
||||||
var safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(address, id.getIdentityKey()));
|
var safetyNumber = Util.formatSafetyNumber(id.getSafetyNumber());
|
||||||
var scannableSafetyNumber = m.computeSafetyNumberForScanning(address, id.getIdentityKey());
|
var scannableSafetyNumber = id.getScannableSafetyNumber();
|
||||||
return new JsonIdentity(address.getNumber().orNull(),
|
return new JsonIdentity(address.getNumber().orNull(),
|
||||||
address.getUuid().toString(),
|
address.getUuid().toString(),
|
||||||
Hex.toString(id.getFingerprint()),
|
Hex.toString(id.getFingerprint()),
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class QuitGroupCommand implements JsonRpcLocalCommand {
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
final var groupId = CommandUtil.getGroupId(ns.getString("group-id"));
|
final var groupId = CommandUtil.getGroupId(ns.getString("group-id"));
|
||||||
|
|
||||||
var groupAdmins = CommandUtil.getSingleRecipientIdentifiers(ns.getList("admin"), m.getUsername());
|
var groupAdmins = CommandUtil.getSingleRecipientIdentifiers(ns.getList("admin"), m.getSelfNumber());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import net.sourceforge.argparse4j.impl.Arguments;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.Signal;
|
|
||||||
import org.asamk.signal.JsonWriter;
|
import org.asamk.signal.JsonWriter;
|
||||||
import org.asamk.signal.OutputWriter;
|
import org.asamk.signal.OutputWriter;
|
||||||
import org.asamk.signal.PlainTextWriter;
|
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.manager.groups.NotAGroupMemberException;
|
||||||
import org.asamk.signal.util.CommandUtil;
|
import org.asamk.signal.util.CommandUtil;
|
||||||
import org.asamk.signal.util.ErrorUtils;
|
import org.asamk.signal.util.ErrorUtils;
|
||||||
import org.freedesktop.dbus.errors.UnknownObject;
|
|
||||||
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class RemoteDeleteCommand implements DbusCommand, JsonRpcLocalCommand {
|
public class RemoteDeleteCommand implements JsonRpcLocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
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) {
|
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
|
||||||
if (outputWriter instanceof PlainTextWriter) {
|
if (outputWriter instanceof PlainTextWriter) {
|
||||||
final var writer = (PlainTextWriter) outputWriter;
|
final var writer = (PlainTextWriter) outputWriter;
|
||||||
|
|
|
@ -4,13 +4,11 @@ import net.sourceforge.argparse4j.impl.Arguments;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.Signal;
|
|
||||||
import org.asamk.signal.JsonWriter;
|
import org.asamk.signal.JsonWriter;
|
||||||
import org.asamk.signal.OutputWriter;
|
import org.asamk.signal.OutputWriter;
|
||||||
import org.asamk.signal.PlainTextWriter;
|
import org.asamk.signal.PlainTextWriter;
|
||||||
import org.asamk.signal.commands.exceptions.CommandException;
|
import org.asamk.signal.commands.exceptions.CommandException;
|
||||||
import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
|
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.commands.exceptions.UserErrorException;
|
||||||
import org.asamk.signal.manager.AttachmentInvalidException;
|
import org.asamk.signal.manager.AttachmentInvalidException;
|
||||||
import org.asamk.signal.manager.Manager;
|
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.CommandUtil;
|
||||||
import org.asamk.signal.util.ErrorUtils;
|
import org.asamk.signal.util.ErrorUtils;
|
||||||
import org.asamk.signal.util.IOUtils;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -33,7 +29,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
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);
|
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) {
|
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
|
||||||
if (outputWriter instanceof PlainTextWriter) {
|
if (outputWriter instanceof PlainTextWriter) {
|
||||||
final var writer = (PlainTextWriter) outputWriter;
|
final var writer = (PlainTextWriter) outputWriter;
|
||||||
|
|
|
@ -4,7 +4,6 @@ import net.sourceforge.argparse4j.impl.Arguments;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.Signal;
|
|
||||||
import org.asamk.signal.JsonWriter;
|
import org.asamk.signal.JsonWriter;
|
||||||
import org.asamk.signal.OutputWriter;
|
import org.asamk.signal.OutputWriter;
|
||||||
import org.asamk.signal.PlainTextWriter;
|
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.manager.groups.NotAGroupMemberException;
|
||||||
import org.asamk.signal.util.CommandUtil;
|
import org.asamk.signal.util.CommandUtil;
|
||||||
import org.asamk.signal.util.ErrorUtils;
|
import org.asamk.signal.util.ErrorUtils;
|
||||||
import org.freedesktop.dbus.errors.UnknownObject;
|
|
||||||
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand {
|
public class SendReactionCommand implements JsonRpcLocalCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -72,7 +69,7 @@ public class SendReactionCommand implements DbusCommand, JsonRpcLocalCommand {
|
||||||
try {
|
try {
|
||||||
final var results = m.sendMessageReaction(emoji,
|
final var results = m.sendMessageReaction(emoji,
|
||||||
isRemove,
|
isRemove,
|
||||||
CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getUsername()),
|
CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
|
||||||
targetTimestamp,
|
targetTimestamp,
|
||||||
recipientIdentifiers);
|
recipientIdentifiers);
|
||||||
outputResult(outputWriter, results.getTimestamp());
|
outputResult(outputWriter, results.getTimestamp());
|
||||||
|
@ -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) {
|
private void outputResult(final OutputWriter outputWriter, final long timestamp) {
|
||||||
if (outputWriter instanceof PlainTextWriter) {
|
if (outputWriter instanceof PlainTextWriter) {
|
||||||
final var writer = (PlainTextWriter) outputWriter;
|
final var writer = (PlainTextWriter) outputWriter;
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class SendReceiptCommand implements JsonRpcLocalCommand {
|
||||||
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
final var recipientString = ns.getString("recipient");
|
final var recipientString = ns.getString("recipient");
|
||||||
final var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getUsername());
|
final var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber());
|
||||||
|
|
||||||
final var targetTimestamps = ns.<Long>getList("target-timestamp");
|
final var targetTimestamps = ns.<Long>getList("target-timestamp");
|
||||||
final var type = ns.getString("type");
|
final var type = ns.getString("type");
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class SendTypingCommand implements JsonRpcLocalCommand {
|
||||||
|
|
||||||
final var recipientIdentifiers = new HashSet<RecipientIdentifier>();
|
final var recipientIdentifiers = new HashSet<RecipientIdentifier>();
|
||||||
if (recipientStrings != null) {
|
if (recipientStrings != null) {
|
||||||
final var localNumber = m.getUsername();
|
final var localNumber = m.getSelfNumber();
|
||||||
recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber));
|
recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber));
|
||||||
}
|
}
|
||||||
if (groupIdStrings != null) {
|
if (groupIdStrings != null) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class TrustCommand implements JsonRpcLocalCommand {
|
||||||
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
var recipentString = ns.getString("recipient");
|
var recipentString = ns.getString("recipient");
|
||||||
var recipient = CommandUtil.getSingleRecipientIdentifier(recipentString, m.getUsername());
|
var recipient = CommandUtil.getSingleRecipientIdentifier(recipentString, m.getSelfNumber());
|
||||||
if (ns.getBoolean("trust-all-known-keys")) {
|
if (ns.getBoolean("trust-all-known-keys")) {
|
||||||
boolean res = m.trustIdentityAllKeys(recipient);
|
boolean res = m.trustIdentityAllKeys(recipient);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
|
|
@ -36,7 +36,8 @@ public class UnblockCommand implements JsonRpcLocalCommand {
|
||||||
public void handleCommand(
|
public void handleCommand(
|
||||||
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
for (var contactNumber : CommandUtil.getSingleRecipientIdentifiers(ns.getList("recipient"), m.getUsername())) {
|
for (var contactNumber : CommandUtil.getSingleRecipientIdentifiers(ns.getList("recipient"),
|
||||||
|
m.getSelfNumber())) {
|
||||||
try {
|
try {
|
||||||
m.setContactBlocked(contactNumber, false);
|
m.setContactBlocked(contactNumber, false);
|
||||||
} catch (NotMasterDeviceException e) {
|
} catch (NotMasterDeviceException e) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class UpdateContactCommand implements JsonRpcLocalCommand {
|
||||||
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
var recipientString = ns.getString("recipient");
|
var recipientString = ns.getString("recipient");
|
||||||
var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getUsername());
|
var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var expiration = ns.getInt("expiration");
|
var expiration = ns.getInt("expiration");
|
||||||
|
|
|
@ -4,7 +4,6 @@ import net.sourceforge.argparse4j.impl.Arguments;
|
||||||
import net.sourceforge.argparse4j.inf.Namespace;
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
import net.sourceforge.argparse4j.inf.Subparser;
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
import org.asamk.Signal;
|
|
||||||
import org.asamk.signal.JsonWriter;
|
import org.asamk.signal.JsonWriter;
|
||||||
import org.asamk.signal.OutputWriter;
|
import org.asamk.signal.OutputWriter;
|
||||||
import org.asamk.signal.PlainTextWriter;
|
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.manager.groups.NotAGroupMemberException;
|
||||||
import org.asamk.signal.util.CommandUtil;
|
import org.asamk.signal.util.CommandUtil;
|
||||||
import org.asamk.signal.util.ErrorUtils;
|
import org.asamk.signal.util.ErrorUtils;
|
||||||
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
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);
|
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");
|
final var groupIdString = ns.getString("group-id");
|
||||||
var groupId = CommandUtil.getGroupId(groupIdString);
|
var groupId = CommandUtil.getGroupId(groupIdString);
|
||||||
|
|
||||||
final var localNumber = m.getUsername();
|
final var localNumber = m.getSelfNumber();
|
||||||
|
|
||||||
var groupName = ns.getString("name");
|
var groupName = ns.getString("name");
|
||||||
var groupDescription = ns.getString("description");
|
var groupDescription = ns.getString("description");
|
||||||
|
@ -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) {
|
private void outputResult(final OutputWriter outputWriter, final Long timestamp, final GroupId groupId) {
|
||||||
if (outputWriter instanceof PlainTextWriter) {
|
if (outputWriter instanceof PlainTextWriter) {
|
||||||
final var writer = (PlainTextWriter) outputWriter;
|
final var writer = (PlainTextWriter) outputWriter;
|
||||||
|
|
|
@ -5,4 +5,8 @@ public final class UserErrorException extends CommandException {
|
||||||
public UserErrorException(final String message) {
|
public UserErrorException(final String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserErrorException(final String message, final Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
487
src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java
Normal file
487
src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -165,7 +165,7 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl {
|
||||||
synchronized (receiveThreads) {
|
synchronized (receiveThreads) {
|
||||||
return receiveThreads.stream()
|
return receiveThreads.stream()
|
||||||
.map(Pair::first)
|
.map(Pair::first)
|
||||||
.map(Manager::getUsername)
|
.map(Manager::getSelfNumber)
|
||||||
.map(u -> new DBusPath(DbusConfig.getObjectPath(u)))
|
.map(u -> new DBusPath(DbusConfig.getObjectPath(u)))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.asamk.signal.manager.NotMasterDeviceException;
|
||||||
import org.asamk.signal.manager.StickerPackInvalidException;
|
import org.asamk.signal.manager.StickerPackInvalidException;
|
||||||
import org.asamk.signal.manager.UntrustedIdentityException;
|
import org.asamk.signal.manager.UntrustedIdentityException;
|
||||||
import org.asamk.signal.manager.api.Device;
|
import org.asamk.signal.manager.api.Device;
|
||||||
|
import org.asamk.signal.manager.api.Identity;
|
||||||
import org.asamk.signal.manager.api.Message;
|
import org.asamk.signal.manager.api.Message;
|
||||||
import org.asamk.signal.manager.api.RecipientIdentifier;
|
import org.asamk.signal.manager.api.RecipientIdentifier;
|
||||||
import org.asamk.signal.manager.api.TypingAction;
|
import org.asamk.signal.manager.api.TypingAction;
|
||||||
|
@ -17,9 +18,9 @@ import org.asamk.signal.manager.groups.GroupNotFoundException;
|
||||||
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
|
import org.asamk.signal.manager.groups.GroupSendingNotAllowedException;
|
||||||
import org.asamk.signal.manager.groups.LastGroupAdminException;
|
import org.asamk.signal.manager.groups.LastGroupAdminException;
|
||||||
import org.asamk.signal.manager.groups.NotAGroupMemberException;
|
import org.asamk.signal.manager.groups.NotAGroupMemberException;
|
||||||
import org.asamk.signal.manager.storage.identities.IdentityInfo;
|
import org.asamk.signal.manager.storage.recipients.Profile;
|
||||||
|
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
|
||||||
import org.asamk.signal.util.ErrorUtils;
|
import org.asamk.signal.util.ErrorUtils;
|
||||||
import org.asamk.signal.util.Util;
|
|
||||||
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
||||||
import org.whispersystems.libsignal.InvalidKeyException;
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
|
@ -45,8 +46,6 @@ import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.asamk.signal.util.Util.getLegacyIdentifier;
|
|
||||||
|
|
||||||
public class DbusSignalImpl implements Signal {
|
public class DbusSignalImpl implements Signal {
|
||||||
|
|
||||||
private final Manager m;
|
private final Manager m;
|
||||||
|
@ -67,6 +66,11 @@ public class DbusSignalImpl implements Signal {
|
||||||
return objectPath;
|
return objectPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSelfNumber() {
|
||||||
|
return m.getSelfNumber();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addDevice(String uri) {
|
public void addDevice(String uri) {
|
||||||
try {
|
try {
|
||||||
|
@ -92,8 +96,6 @@ public class DbusSignalImpl implements Signal {
|
||||||
@Override
|
@Override
|
||||||
public List<String> listDevices() {
|
public List<String> listDevices() {
|
||||||
List<Device> devices;
|
List<Device> devices;
|
||||||
List<String> results = new ArrayList<String>();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
devices = m.getLinkedDevices();
|
devices = m.getLinkedDevices();
|
||||||
} catch (IOException | Error.Failure e) {
|
} 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) {
|
public long sendMessage(final String message, final List<String> attachments, final List<String> recipients) {
|
||||||
try {
|
try {
|
||||||
final var results = m.sendMessage(new Message(message, attachments),
|
final var results = m.sendMessage(new Message(message, attachments),
|
||||||
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
|
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
|
||||||
.map(RecipientIdentifier.class::cast)
|
.map(RecipientIdentifier.class::cast)
|
||||||
.collect(Collectors.toSet()));
|
.collect(Collectors.toSet()));
|
||||||
|
|
||||||
|
@ -153,7 +155,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
final var results = m.sendRemoteDeleteMessage(targetSentTimestamp,
|
final var results = m.sendRemoteDeleteMessage(targetSentTimestamp,
|
||||||
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
|
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
|
||||||
.map(RecipientIdentifier.class::cast)
|
.map(RecipientIdentifier.class::cast)
|
||||||
.collect(Collectors.toSet()));
|
.collect(Collectors.toSet()));
|
||||||
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
||||||
|
@ -205,9 +207,9 @@ public class DbusSignalImpl implements Signal {
|
||||||
try {
|
try {
|
||||||
final var results = m.sendMessageReaction(emoji,
|
final var results = m.sendMessageReaction(emoji,
|
||||||
remove,
|
remove,
|
||||||
getSingleRecipientIdentifier(targetAuthor, m.getUsername()),
|
getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
|
||||||
targetSentTimestamp,
|
targetSentTimestamp,
|
||||||
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
|
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
|
||||||
.map(RecipientIdentifier.class::cast)
|
.map(RecipientIdentifier.class::cast)
|
||||||
.collect(Collectors.toSet()));
|
.collect(Collectors.toSet()));
|
||||||
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
||||||
|
@ -227,7 +229,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
var recipients = new ArrayList<String>(1);
|
var recipients = new ArrayList<String>(1);
|
||||||
recipients.add(recipient);
|
recipients.add(recipient);
|
||||||
m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START,
|
m.sendTypingMessage(stop ? TypingAction.STOP : TypingAction.START,
|
||||||
getSingleRecipientIdentifiers(recipients, m.getUsername()).stream()
|
getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream()
|
||||||
.map(RecipientIdentifier.class::cast)
|
.map(RecipientIdentifier.class::cast)
|
||||||
.collect(Collectors.toSet()));
|
.collect(Collectors.toSet()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -241,10 +243,10 @@ public class DbusSignalImpl implements Signal {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendReadReceipt(
|
public void sendReadReceipt(
|
||||||
final String recipient, final List<Long> timestamps
|
final String recipient, final List<Long> messageIds
|
||||||
) throws Error.Failure, Error.UntrustedIdentity {
|
) throws Error.Failure, Error.UntrustedIdentity {
|
||||||
try {
|
try {
|
||||||
m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getUsername()), timestamps);
|
m.sendReadReceipt(getSingleRecipientIdentifier(recipient, m.getSelfNumber()), messageIds);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new Error.Failure(e.getMessage());
|
throw new Error.Failure(e.getMessage());
|
||||||
} catch (UntrustedIdentityException e) {
|
} catch (UntrustedIdentityException e) {
|
||||||
|
@ -276,7 +278,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity {
|
) throws Error.AttachmentInvalid, Error.Failure, Error.UntrustedIdentity {
|
||||||
try {
|
try {
|
||||||
final var results = m.sendMessage(new Message(message, attachments),
|
final var results = m.sendMessage(new Message(message, attachments),
|
||||||
Set.of(new RecipientIdentifier.NoteToSelf()));
|
Set.of(RecipientIdentifier.NoteToSelf.INSTANCE));
|
||||||
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
||||||
return results.getTimestamp();
|
return results.getTimestamp();
|
||||||
} catch (AttachmentInvalidException e) {
|
} catch (AttachmentInvalidException e) {
|
||||||
|
@ -291,7 +293,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
@Override
|
@Override
|
||||||
public void sendEndSessionMessage(final List<String> recipients) {
|
public void sendEndSessionMessage(final List<String> recipients) {
|
||||||
try {
|
try {
|
||||||
final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getUsername()));
|
final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getSelfNumber()));
|
||||||
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new Error.Failure(e.getMessage());
|
throw new Error.Failure(e.getMessage());
|
||||||
|
@ -325,7 +327,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
try {
|
try {
|
||||||
final var results = m.sendMessageReaction(emoji,
|
final var results = m.sendMessageReaction(emoji,
|
||||||
remove,
|
remove,
|
||||||
getSingleRecipientIdentifier(targetAuthor, m.getUsername()),
|
getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()),
|
||||||
targetSentTimestamp,
|
targetSentTimestamp,
|
||||||
Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
|
Set.of(new RecipientIdentifier.Group(getGroupId(groupId))));
|
||||||
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
checkSendMessageResults(results.getTimestamp(), results.getResults());
|
||||||
|
@ -341,13 +343,14 @@ public class DbusSignalImpl implements Signal {
|
||||||
// the profile name
|
// the profile name
|
||||||
@Override
|
@Override
|
||||||
public String getContactName(final String number) {
|
public String getContactName(final String number) {
|
||||||
return m.getContactOrProfileName(getSingleRecipientIdentifier(number, m.getUsername()));
|
final var name = m.getContactOrProfileName(getSingleRecipientIdentifier(number, m.getSelfNumber()));
|
||||||
|
return name == null ? "" : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContactName(final String number, final String name) {
|
public void setContactName(final String number, final String name) {
|
||||||
try {
|
try {
|
||||||
m.setContactName(getSingleRecipientIdentifier(number, m.getUsername()), name);
|
m.setContactName(getSingleRecipientIdentifier(number, m.getSelfNumber()), name);
|
||||||
} catch (NotMasterDeviceException e) {
|
} catch (NotMasterDeviceException e) {
|
||||||
throw new Error.Failure("This command doesn't work on linked devices.");
|
throw new Error.Failure("This command doesn't work on linked devices.");
|
||||||
} catch (UnregisteredUserException e) {
|
} catch (UnregisteredUserException e) {
|
||||||
|
@ -358,7 +361,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
@Override
|
@Override
|
||||||
public void setExpirationTimer(final String number, final int expiration) {
|
public void setExpirationTimer(final String number, final int expiration) {
|
||||||
try {
|
try {
|
||||||
m.setExpirationTimer(getSingleRecipientIdentifier(number, m.getUsername()), expiration);
|
m.setExpirationTimer(getSingleRecipientIdentifier(number, m.getSelfNumber()), expiration);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new Error.Failure(e.getMessage());
|
throw new Error.Failure(e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -367,7 +370,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
@Override
|
@Override
|
||||||
public void setContactBlocked(final String number, final boolean blocked) {
|
public void setContactBlocked(final String number, final boolean blocked) {
|
||||||
try {
|
try {
|
||||||
m.setContactBlocked(getSingleRecipientIdentifier(number, m.getUsername()), blocked);
|
m.setContactBlocked(getSingleRecipientIdentifier(number, m.getSelfNumber()), blocked);
|
||||||
} catch (NotMasterDeviceException e) {
|
} catch (NotMasterDeviceException e) {
|
||||||
throw new Error.Failure("This command doesn't work on linked devices.");
|
throw new Error.Failure("This command doesn't work on linked devices.");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -399,7 +402,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
@Override
|
@Override
|
||||||
public String getGroupName(final byte[] groupId) {
|
public String getGroupName(final byte[] groupId) {
|
||||||
var group = m.getGroup(getGroupId(groupId));
|
var group = m.getGroup(getGroupId(groupId));
|
||||||
if (group == null) {
|
if (group == null || group.getTitle() == null) {
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
return group.getTitle();
|
return group.getTitle();
|
||||||
|
@ -412,27 +415,17 @@ public class DbusSignalImpl implements Signal {
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
return List.of();
|
return List.of();
|
||||||
} else {
|
} else {
|
||||||
return group.getMembers()
|
return group.getMembers().stream().map(RecipientAddress::getLegacyIdentifier).collect(Collectors.toList());
|
||||||
.stream()
|
|
||||||
.map(m::resolveSignalServiceAddress)
|
|
||||||
.map(Util::getLegacyIdentifier)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) {
|
public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) {
|
||||||
try {
|
try {
|
||||||
if (groupId.length == 0) {
|
groupId = nullIfEmpty(groupId);
|
||||||
groupId = null;
|
name = nullIfEmpty(name);
|
||||||
}
|
avatar = nullIfEmpty(avatar);
|
||||||
if (name.isEmpty()) {
|
final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getSelfNumber());
|
||||||
name = null;
|
|
||||||
}
|
|
||||||
if (avatar.isEmpty()) {
|
|
||||||
avatar = null;
|
|
||||||
}
|
|
||||||
final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getUsername());
|
|
||||||
if (groupId == null) {
|
if (groupId == null) {
|
||||||
final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar));
|
final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar));
|
||||||
checkSendMessageResults(results.second().getTimestamp(), results.second().getResults());
|
checkSendMessageResults(results.second().getTimestamp(), results.second().getResults());
|
||||||
|
@ -497,6 +490,30 @@ public class DbusSignalImpl implements Signal {
|
||||||
}).collect(Collectors.toList());
|
}).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
|
@Override
|
||||||
public void updateProfile(
|
public void updateProfile(
|
||||||
final String name,
|
final String name,
|
||||||
|
@ -505,17 +522,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
String avatarPath,
|
String avatarPath,
|
||||||
final boolean removeAvatar
|
final boolean removeAvatar
|
||||||
) {
|
) {
|
||||||
try {
|
updateProfile(name, "", about, aboutEmoji, avatarPath, removeAvatar);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -551,10 +558,9 @@ public class DbusSignalImpl implements Signal {
|
||||||
// all numbers the system knows
|
// all numbers the system knows
|
||||||
@Override
|
@Override
|
||||||
public List<String> listNumbers() {
|
public List<String> listNumbers() {
|
||||||
return Stream.concat(m.getIdentities().stream().map(IdentityInfo::getRecipientId),
|
return Stream.concat(m.getIdentities().stream().map(Identity::getRecipient),
|
||||||
m.getContacts().stream().map(Pair::first))
|
m.getContacts().stream().map(Pair::first))
|
||||||
.map(m::resolveSignalServiceAddress)
|
.map(a -> a.getNumber().orElse(null))
|
||||||
.map(a -> a.getNumber().orNull())
|
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
@ -567,16 +573,19 @@ public class DbusSignalImpl implements Signal {
|
||||||
var contacts = m.getContacts();
|
var contacts = m.getContacts();
|
||||||
for (var c : contacts) {
|
for (var c : contacts) {
|
||||||
if (name.equals(c.second().getName())) {
|
if (name.equals(c.second().getName())) {
|
||||||
numbers.add(getLegacyIdentifier(m.resolveSignalServiceAddress(c.first())));
|
numbers.add(c.first().getLegacyIdentifier());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Try profiles if no contact name was found
|
// Try profiles if no contact name was found
|
||||||
for (var identity : m.getIdentities()) {
|
for (var identity : m.getIdentities()) {
|
||||||
final var recipientId = identity.getRecipientId();
|
final var address = identity.getRecipient();
|
||||||
final var address = m.resolveSignalServiceAddress(recipientId);
|
var number = address.getNumber().orElse(null);
|
||||||
var number = address.getNumber().orNull();
|
|
||||||
if (number != null) {
|
if (number != null) {
|
||||||
var profile = m.getRecipientProfile(recipientId);
|
Profile profile = null;
|
||||||
|
try {
|
||||||
|
profile = m.getRecipientProfile(RecipientIdentifier.Single.fromAddress(address));
|
||||||
|
} catch (UnregisteredUserException ignored) {
|
||||||
|
}
|
||||||
if (profile != null && profile.getDisplayName().equals(name)) {
|
if (profile != null && profile.getDisplayName().equals(name)) {
|
||||||
numbers.add(number);
|
numbers.add(number);
|
||||||
}
|
}
|
||||||
|
@ -617,7 +626,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isContactBlocked(final String number) {
|
public boolean isContactBlocked(final String number) {
|
||||||
return m.isContactBlocked(getSingleRecipientIdentifier(number, m.getUsername()));
|
return m.isContactBlocked(getSingleRecipientIdentifier(number, m.getSelfNumber()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -636,7 +645,7 @@ public class DbusSignalImpl implements Signal {
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return group.isMember(m.getSelfRecipientId());
|
return group.isMember();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -747,4 +756,12 @@ public class DbusSignalImpl implements Signal {
|
||||||
throw new Error.InvalidGroupId("Invalid group id: " + e.getMessage());
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import org.asamk.signal.manager.Manager;
|
import org.asamk.signal.manager.Manager;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
import static org.asamk.signal.util.Util.getLegacyIdentifier;
|
import static org.asamk.signal.util.Util.getLegacyIdentifier;
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ public class JsonMention {
|
||||||
final int length;
|
final int length;
|
||||||
|
|
||||||
JsonMention(SignalServiceDataMessage.Mention mention, Manager m) {
|
JsonMention(SignalServiceDataMessage.Mention mention, Manager m) {
|
||||||
final var address = m.resolveSignalServiceAddress(mention.getUuid());
|
final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid()));
|
||||||
this.name = getLegacyIdentifier(address);
|
this.name = getLegacyIdentifier(address);
|
||||||
this.number = address.getNumber().orNull();
|
this.number = address.getNumber().orNull();
|
||||||
this.uuid = address.getUuid().toString();
|
this.uuid = address.getUuid().toString();
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class JsonMessageEnvelope {
|
||||||
}
|
}
|
||||||
String name;
|
String name;
|
||||||
try {
|
try {
|
||||||
name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(this.source, m.getUsername()));
|
name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(this.source, m.getSelfNumber()));
|
||||||
} catch (InvalidNumberException | NullPointerException e) {
|
} catch (InvalidNumberException | NullPointerException e) {
|
||||||
name = null;
|
name = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,10 @@ public class CommandUtil {
|
||||||
) throws UserErrorException {
|
) throws UserErrorException {
|
||||||
final var recipientIdentifiers = new HashSet<RecipientIdentifier>();
|
final var recipientIdentifiers = new HashSet<RecipientIdentifier>();
|
||||||
if (isNoteToSelf) {
|
if (isNoteToSelf) {
|
||||||
recipientIdentifiers.add(new RecipientIdentifier.NoteToSelf());
|
recipientIdentifiers.add(RecipientIdentifier.NoteToSelf.INSTANCE);
|
||||||
}
|
}
|
||||||
if (recipientStrings != null) {
|
if (recipientStrings != null) {
|
||||||
final var localNumber = m.getUsername();
|
final var localNumber = m.getSelfNumber();
|
||||||
recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber));
|
recipientIdentifiers.addAll(CommandUtil.getSingleRecipientIdentifiers(recipientStrings, localNumber));
|
||||||
}
|
}
|
||||||
if (groupIdStrings != null) {
|
if (groupIdStrings != null) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue