modify DBus unlisten and unregister

add unlisten() and unregister() to this branch

move unlisten() and unregister() methods from Signal.java to SignalControl.java for consistency

update documentation
This commit is contained in:
John Freed 2021-10-06 03:20:29 +02:00
parent 690636b83d
commit 3e67d61681
9 changed files with 196 additions and 14 deletions

View file

@ -922,8 +922,12 @@ public class ManagerImpl implements Manager {
while (!Thread.interrupted()) {
SignalServiceEnvelope envelope;
final CachedMessage[] cachedMessage = {null};
account.setLastReceiveTimestamp(System.currentTimeMillis());
if (account == null) {
logger.debug("Account closed.");
break;
}
logger.debug("Checking for new message from server");
account.setLastReceiveTimestamp(System.currentTimeMillis());
try {
var result = signalWebSocket.readOrEmpty(unit.toMillis(timeout), envelope1 -> {
final var recipientId = envelope1.hasSourceUuid()
@ -955,7 +959,7 @@ public class ManagerImpl implements Manager {
} else {
throw e;
}
} catch (WebSocketUnavailableException e) {
} catch (IOException e) {
logger.debug("Pipe unexpectedly unavailable, connecting");
signalWebSocket.connect();
continue;

View file

@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.util.KeyHelper;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.SignalServiceAccountManager.NewDeviceRegistrationReturn;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -90,7 +91,13 @@ public class ProvisioningManager {
}
public Manager finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists {
var ret = accountManager.getNewDeviceRegistration(tempIdentityKey);
NewDeviceRegistrationReturn ret;
logger.info("Waiting for addDevice request...");
try {
ret = accountManager.getNewDeviceRegistration(tempIdentityKey);
} catch (IOException | TimeoutException e) {
throw new TimeoutException(e.getMessage());
}
var number = ret.getNumber();
logger.info("Received link information from {}, linking in progress ...", number);

View file

@ -70,7 +70,7 @@ Exceptions: None
listen(number<s>) -> <>::
* number : Phone number
Starting checking the Signal servers on behalf of this number, and export a DBus object path for it.
Start checking the Signal servers on behalf of this number, and export a DBus object path for it.
Fails if user is not already registered.
Exceptions: Failure
@ -90,6 +90,23 @@ Captcha strings may be obtained from `https://signalcaptchas.org/registration/ge
Exceptions: Failure, InvalidNumber, RequiresCaptcha
unlisten(number<s>) -> <>::
* number : Phone number
* keepData : true or omitted = keep files in data directory; false = delete files
Stops the current device from listening to DBus. In single-user mode, kills the daemon.
Exception: Failure
unregister(number<s>) -> <>::
unregister(number<s>, keepData<b>) -> <>::
* number : Phone number
* keepData : true or omitted = keep files in data directory; false = delete files
Unregisters the current device. In single-user mode, kills the daemon.
Exception: Failure
verify(number<s>, verificationCode<s>) -> <>::
* number : Phone number
* verificationCode : Code received from Signal after successful registration request
@ -173,7 +190,7 @@ isMember(groupId<ay>) -> active<b>::
Note that this method does not raise an Exception for a non-existing/unknown group but will simply return 0 (false)
sendEndSessionMessage(recipients<as>) -> <>::
* recipients : Array of phone numbers
* recipients : Array of phone numbers
Exceptions: Failure, InvalidNumber, UntrustedIdentity
@ -209,7 +226,7 @@ sendMessage(message<s>, attachments<as>, recipients<as>) -> timestamp<x>::
* message : Text to send (can be UTF8)
* attachments : String array of filenames to send as attachments (passed as filename, so need to be readable by the user signal-cli is running under)
* recipient : Phone number of a single recipient
* recipients : Array of phone numbers
* recipients : Array of phone numbers
* timestamp : Can be used to identify the corresponding signal reply
Depending on the type of the recipient field this sends a message to one or multiple recipients.
@ -285,7 +302,7 @@ groupList : Array of Byte arrays representing the internal group identifiers
All groups known are returned, regardless of their active or blocked status. To query that use isMember() and isGroupBlocked()
getGroupName(groupId<ay>) -> groupName<s>::
groupName : The display name of the group
groupName : The display name of the group
groupId : Byte array representing the internal group identifier
Exceptions: None, if the group name is not found an empty string is returned
@ -361,7 +378,7 @@ removeDevice(deviceId<i>) -> <>::
Exception: Failure
updateDeviceName(deviceName<s>) -> <>::
* deviceName : New name
* deviceName : New name
Set a new name for this device (main or linked).

View file

@ -8,6 +8,7 @@ import org.freedesktop.dbus.interfaces.DBusInterface;
import org.freedesktop.dbus.interfaces.Properties;
import org.freedesktop.dbus.messages.DBusSignal;
import java.io.IOException;
import java.util.List;
/**
@ -16,7 +17,7 @@ import java.util.List;
*/
public interface Signal extends DBusInterface {
String getSelfNumber();
String getSelfNumber();
long sendMessage(
String message, List<String> attachments, String recipient

View file

@ -1,5 +1,6 @@
package org.asamk;
import org.asamk.Signal.Error;
import org.freedesktop.dbus.DBusPath;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.interfaces.DBusInterface;
@ -24,12 +25,18 @@ public interface SignalControl extends DBusInterface {
void verifyWithPin(String number, String verificationCode, String pin) throws Error.Failure, Error.InvalidNumber;
void listen(String number) throws Error.Failure;
void unlisten(String number) throws Error.Failure;
void unregister(String number) throws Error.Failure;
void unregister(String number, boolean keepData) throws Error.Failure;
String link(String newDeviceName) throws Error.Failure;
public String version();
void listen(String number) throws Error.Failure;
List<DBusPath> listAccounts();
interface Error {

View file

@ -216,7 +216,10 @@ public class DaemonCommand implements MultiLocalCommand {
initThread.join();
} catch (InterruptedException ignored) {
}
signal.close();
try {
signal.close();
} catch (IOException ignored) {
}
});
thread.start();

View file

@ -10,7 +10,7 @@ import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import java.util.List;
public interface MultiLocalCommand extends LocalCommand {
void handleCommand(Namespace ns, List<Manager> m, SignalCreator c, OutputWriter outputWriter, TrustNewIdentity trustNewIdentity) throws CommandException;
void handleCommand(Namespace ns, List<Manager> managers, SignalCreator c, OutputWriter outputWriter, TrustNewIdentity trustNewIdentity) throws CommandException;
void handleCommand(Namespace ns, Manager m, SignalCreator c, OutputWriter outputWriter, TrustNewIdentity trustNewIdentity) throws CommandException;
}

View file

@ -13,11 +13,15 @@ import org.asamk.signal.PlainTextWriter;
import org.asamk.signal.commands.DaemonCommand;
import org.asamk.signal.commands.SignalCreator;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.IOErrorException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.PathConfig;
import org.asamk.signal.manager.ProvisioningManager;
import org.asamk.signal.manager.RegistrationManager;
import org.asamk.signal.manager.UserAlreadyExists;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.freedesktop.dbus.DBusPath;
@ -38,7 +42,11 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.channels.OverlappingFileLockException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@ -46,6 +54,7 @@ import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DbusSignalControlImpl implements org.asamk.SignalControl {
@ -163,6 +172,53 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl {
}
}
@Override
public void unregister(String number) {
unregister(number, true);
}
@Override
public void unregister(String number, boolean keepData) {
try {
List<Manager> managers = new ArrayList<>();
Manager manager = null;
synchronized (receiveThreads) {
managers = receiveThreads.stream()
.map(Pair::first)
.collect(Collectors.toList());
}
if (managers.size() == 0) {
throw new Error.Failure("Unregister error: no manager found.");
}
for (Manager m : managers) {
try {
m.getSelfNumber();
} catch (NullPointerException ignore) {
continue;
}
if (m.getSelfNumber().equals(number)) {
manager = m;
break;
}
}
if (manager == null) {
throw new Error.Failure("Unregister error, " + number + " is not listening.");
}
manager.unregister();
DBusConnection.DBusBusType busType = DaemonCommand.dBusType;
if (!keepData) {
removeUserData(number);
}
unlisten(number);
} catch (Exception e) {
throw new Error.Failure(e.getClass().getSimpleName() + " Unregister error: " + e.getMessage());
}
}
@Override
public String link(final String newDeviceName) throws Error.Failure {
try {
@ -242,6 +298,59 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl {
}
}
@Override
public void unlisten(String number) {
try {
List<Manager> managers = new ArrayList<>();
Manager manager = null;
synchronized (receiveThreads) {
managers = receiveThreads.stream()
.map(Pair::first)
.collect(Collectors.toList());
}
if (managers.size() == 0) {
throw new Error.Failure("Unlisten error: no manager found.");
}
for (Manager m : managers) {
try {
m.getSelfNumber();
} catch (NullPointerException ignore) {
continue;
}
if (m.getSelfNumber().equals(number)) {
manager = m;
break;
}
}
if (manager == null) {
throw new Error.Failure("Unlisten error, " + number + " is already not listening.");
}
String objectPath = DbusConfig.getObjectPath(number);
DBusConnection.DBusBusType busType = DaemonCommand.dBusType;
var conn = DBusConnection.getConnection(busType);
//if single-user mode, just close the manager because we're exiting anyway
//else unexport the object
try {
//this will generate an error if we are in anonymous mode
conn.exportObject(new DbusSignalImpl(manager, conn, objectPath));
//no error, hence single-user mode
manager.close();
logger.info("unExported dbus object: " + DbusConfig.getObjectPath());
} catch (DBusException ignore) {
//anonymous mode
conn.unExportObject(objectPath);
manager.close();
logger.info("unExported dbus object: " + objectPath);
}
} catch (IOException | DBusException e) {
throw new Error.Failure(e.getClass().getSimpleName() + " Unlisten error: " + e.getMessage());
}
}
@Override
public List<DBusPath> listAccounts() {
synchronized (receiveThreads) {
@ -252,4 +361,25 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl {
.collect(Collectors.toList());
}
}
private void removeUserData(String number) {
File dataPath = PathConfig.createDefault(c.getSettingsPath()).getDataPath();
number.replaceFirst("_", "+");
String eraseFileName = dataPath.getAbsolutePath() + File.separator + number;
File eraseFile = new File(eraseFileName);
if (eraseFile.delete()) {
logger.info("erased " + eraseFileName);
} else {
logger.error("erase failed for " + eraseFileName);
}
String erasePath = dataPath.getAbsolutePath() + File.separator + number + ".d/";
Path rootPath = Paths.get(erasePath);
try (Stream<Path> walk = Files.walk(rootPath)) {
walk.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (IOException e) {
throw new Error.Failure(e.getClass().getSimpleName() + " RemoveUserData failed. " + e.getMessage());
}
}
}

View file

@ -1,7 +1,10 @@
package org.asamk.signal.dbus;
import org.asamk.Signal;
import org.asamk.Signal.Error;
import org.asamk.signal.BaseConfig;
import org.asamk.signal.DbusConfig;
import org.asamk.signal.commands.DaemonCommand;
import org.asamk.signal.manager.AttachmentInvalidException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.NotMasterDeviceException;
@ -20,10 +23,13 @@ import org.asamk.signal.manager.groups.NotAGroupMemberException;
import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.util.ErrorUtils;
import org.freedesktop.dbus.DBusPath;
import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
@ -35,16 +41,21 @@ import org.whispersystems.signalservice.internal.contacts.crypto.Unauthenticated
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -53,6 +64,7 @@ public class DbusSignalImpl implements Signal {
private final Manager m;
private final DBusConnection connection;
private final String objectPath;
private final static Logger logger = LoggerFactory.getLogger(DbusSignalImpl.class);
private DBusPath thisDevice;
private final List<DBusPath> devices = new ArrayList<>();
@ -67,8 +79,9 @@ public class DbusSignalImpl implements Signal {
updateDevices();
}
public void close() {
public void close() throws IOException {
unExportDevices();
m.close();
}
@Override