mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
parent
55dde93811
commit
22add1cbee
13 changed files with 170 additions and 9 deletions
|
@ -0,0 +1,26 @@
|
||||||
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
|
import org.asamk.signal.manager.helper.AccountFileUpdater;
|
||||||
|
import org.asamk.signal.manager.storage.accounts.AccountsStore;
|
||||||
|
import org.whispersystems.signalservice.api.push.ACI;
|
||||||
|
|
||||||
|
class AccountFileUpdaterImpl implements AccountFileUpdater {
|
||||||
|
|
||||||
|
private final AccountsStore accountsStore;
|
||||||
|
private final String accountPath;
|
||||||
|
|
||||||
|
public AccountFileUpdaterImpl(final AccountsStore accountsStore, final String accountPath) {
|
||||||
|
this.accountsStore = accountsStore;
|
||||||
|
this.accountPath = accountPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateAccountIdentifiers(final String newNumber, final ACI newAci) {
|
||||||
|
accountsStore.updateAccount(accountPath, newNumber, newAci);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAccount() {
|
||||||
|
accountsStore.removeAccount(accountPath);
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,6 +66,7 @@ import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
|
||||||
|
import org.whispersystems.signalservice.api.push.ACI;
|
||||||
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
|
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
|
||||||
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;
|
||||||
|
@ -99,6 +100,7 @@ class ManagerImpl implements Manager {
|
||||||
private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class);
|
private final static Logger logger = LoggerFactory.getLogger(ManagerImpl.class);
|
||||||
|
|
||||||
private SignalAccount account;
|
private SignalAccount account;
|
||||||
|
private AccountFileUpdater accountFileUpdater;
|
||||||
private final SignalDependencies dependencies;
|
private final SignalDependencies dependencies;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
|
@ -120,6 +122,7 @@ class ManagerImpl implements Manager {
|
||||||
String userAgent
|
String userAgent
|
||||||
) {
|
) {
|
||||||
this.account = account;
|
this.account = account;
|
||||||
|
this.accountFileUpdater = accountFileUpdater;
|
||||||
|
|
||||||
final var sessionLock = new SignalSessionLock() {
|
final var sessionLock = new SignalSessionLock() {
|
||||||
private final ReentrantLock LEGACY_LOCK = new ReentrantLock();
|
private final ReentrantLock LEGACY_LOCK = new ReentrantLock();
|
||||||
|
@ -140,10 +143,18 @@ class ManagerImpl implements Manager {
|
||||||
final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath());
|
final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath());
|
||||||
final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath());
|
final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath());
|
||||||
|
|
||||||
this.context = new Context(account, (number, aci) -> {
|
this.context = new Context(account, new AccountFileUpdater() {
|
||||||
accountFileUpdater.updateAccountIdentifiers(number, aci);
|
@Override
|
||||||
synchronized (addressChangedListeners) {
|
public void updateAccountIdentifiers(final String number, final ACI aci) {
|
||||||
addressChangedListeners.forEach(Runnable::run);
|
accountFileUpdater.updateAccountIdentifiers(number, aci);
|
||||||
|
synchronized (addressChangedListeners) {
|
||||||
|
addressChangedListeners.forEach(Runnable::run);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAccount() {
|
||||||
|
accountFileUpdater.removeAccount();
|
||||||
}
|
}
|
||||||
}, dependencies, avatarStore, attachmentStore, stickerPackStore);
|
}, dependencies, avatarStore, attachmentStore, stickerPackStore);
|
||||||
this.context.getAccountHelper().setUnregisteredListener(this::close);
|
this.context.getAccountHelper().setUnregisteredListener(this::close);
|
||||||
|
|
|
@ -153,7 +153,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
|
||||||
final var accountPathFinal = accountPath;
|
final var accountPathFinal = accountPath;
|
||||||
m = new ManagerImpl(account,
|
m = new ManagerImpl(account,
|
||||||
pathConfig,
|
pathConfig,
|
||||||
(newNumber, newAci) -> accountsStore.updateAccount(accountPathFinal, newNumber, newAci),
|
new AccountFileUpdaterImpl(accountsStore, accountPathFinal),
|
||||||
serviceEnvironmentConfig,
|
serviceEnvironmentConfig,
|
||||||
userAgent);
|
userAgent);
|
||||||
account = null;
|
account = null;
|
||||||
|
@ -220,7 +220,7 @@ class ProvisioningManagerImpl implements ProvisioningManager {
|
||||||
|
|
||||||
final var m = new ManagerImpl(signalAccount,
|
final var m = new ManagerImpl(signalAccount,
|
||||||
pathConfig,
|
pathConfig,
|
||||||
(newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci),
|
new AccountFileUpdaterImpl(accountsStore, accountPath),
|
||||||
serviceEnvironmentConfig,
|
serviceEnvironmentConfig,
|
||||||
userAgent);
|
userAgent);
|
||||||
try (m) {
|
try (m) {
|
||||||
|
|
|
@ -14,4 +14,8 @@ public interface RegistrationManager extends Closeable {
|
||||||
void verifyAccount(
|
void verifyAccount(
|
||||||
String verificationCode, String pin
|
String verificationCode, String pin
|
||||||
) throws IOException, PinLockedException, IncorrectPinException;
|
) throws IOException, PinLockedException, IncorrectPinException;
|
||||||
|
|
||||||
|
void deleteLocalAccountData() throws IOException;
|
||||||
|
|
||||||
|
boolean isRegistered();
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,18 @@ class RegistrationManagerImpl implements RegistrationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteLocalAccountData() throws IOException {
|
||||||
|
account.deleteAccountData();
|
||||||
|
accountFileUpdater.removeAccount();
|
||||||
|
account = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegistered() {
|
||||||
|
return account.isRegistered();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean attemptReactivateAccount() {
|
private boolean attemptReactivateAccount() {
|
||||||
try {
|
try {
|
||||||
final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
|
final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class SignalAccountFiles {
|
||||||
|
|
||||||
final var manager = new ManagerImpl(account,
|
final var manager = new ManagerImpl(account,
|
||||||
pathConfig,
|
pathConfig,
|
||||||
(newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci),
|
new AccountFileUpdaterImpl(accountsStore, accountPath),
|
||||||
serviceEnvironmentConfig,
|
serviceEnvironmentConfig,
|
||||||
userAgent);
|
userAgent);
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ public class SignalAccountFiles {
|
||||||
serviceEnvironmentConfig,
|
serviceEnvironmentConfig,
|
||||||
userAgent,
|
userAgent,
|
||||||
newManagerListener,
|
newManagerListener,
|
||||||
(newNumber, newAci) -> accountsStore.updateAccount(newAccountPath, newNumber, newAci));
|
new AccountFileUpdaterImpl(accountsStore, newAccountPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity);
|
var account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, trustNewIdentity);
|
||||||
|
@ -169,6 +169,6 @@ public class SignalAccountFiles {
|
||||||
serviceEnvironmentConfig,
|
serviceEnvironmentConfig,
|
||||||
userAgent,
|
userAgent,
|
||||||
newManagerListener,
|
newManagerListener,
|
||||||
(newNumber, newAci) -> accountsStore.updateAccount(accountPath, newNumber, newAci));
|
new AccountFileUpdaterImpl(accountsStore, accountPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,6 @@ import org.whispersystems.signalservice.api.push.ACI;
|
||||||
public interface AccountFileUpdater {
|
public interface AccountFileUpdater {
|
||||||
|
|
||||||
void updateAccountIdentifiers(String number, ACI aci);
|
void updateAccountIdentifiers(String number, ACI aci);
|
||||||
|
|
||||||
|
void removeAccount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,9 +70,11 @@ import java.nio.channels.Channels;
|
||||||
import java.nio.channels.ClosedChannelException;
|
import java.nio.channels.ClosedChannelException;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.channels.FileLock;
|
import java.nio.channels.FileLock;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -1330,6 +1332,17 @@ public class SignalAccount implements Closeable {
|
||||||
getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED);
|
getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteAccountData() throws IOException {
|
||||||
|
close();
|
||||||
|
try (final var files = Files.walk(getUserPath(dataPath, accountPath).toPath())
|
||||||
|
.sorted(Comparator.reverseOrder())) {
|
||||||
|
for (final var file = files.iterator(); file.hasNext(); ) {
|
||||||
|
Files.delete(file.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Files.delete(getFileName(dataPath, accountPath).toPath());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
synchronized (fileChannel) {
|
synchronized (fileChannel) {
|
||||||
|
|
|
@ -104,6 +104,10 @@ public class AccountsStore {
|
||||||
return accountPath;
|
return accountPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeAccount(final String accountPath) {
|
||||||
|
updateAccounts(accounts -> accounts.stream().filter(a -> !a.path().equals(accountPath)).toList());
|
||||||
|
}
|
||||||
|
|
||||||
private String generateNewAccountPath() {
|
private String generateNewAccountPath() {
|
||||||
return new Random().ints(100000, 1000000)
|
return new Random().ints(100000, 1000000)
|
||||||
.mapToObj(String::valueOf)
|
.mapToObj(String::valueOf)
|
||||||
|
|
|
@ -121,6 +121,16 @@ You will have to be readded to each group.
|
||||||
|
|
||||||
CAUTION: Only delete your account if you won't use this number again!
|
CAUTION: Only delete your account if you won't use this number again!
|
||||||
|
|
||||||
|
=== deleteLocalAccountData
|
||||||
|
|
||||||
|
Delete all local data for this account.
|
||||||
|
Data should only be deleted if the account is unregistered.
|
||||||
|
|
||||||
|
CAUTION: This cannot be undone.
|
||||||
|
|
||||||
|
*--ignore-registered*::
|
||||||
|
Delete the account data even though the account is still registered on the Signal servers.
|
||||||
|
|
||||||
=== updateAccount
|
=== updateAccount
|
||||||
|
|
||||||
Update the account attributes on the signal server.
|
Update the account attributes on the signal server.
|
||||||
|
|
|
@ -13,6 +13,7 @@ public class Commands {
|
||||||
addCommand(new AddDeviceCommand());
|
addCommand(new AddDeviceCommand());
|
||||||
addCommand(new BlockCommand());
|
addCommand(new BlockCommand());
|
||||||
addCommand(new DaemonCommand());
|
addCommand(new DaemonCommand());
|
||||||
|
addCommand(new DeleteLocalAccountDataCommand());
|
||||||
addCommand(new FinishLinkCommand());
|
addCommand(new FinishLinkCommand());
|
||||||
addCommand(new GetUserStatusCommand());
|
addCommand(new GetUserStatusCommand());
|
||||||
addCommand(new JoinGroupCommand());
|
addCommand(new JoinGroupCommand());
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.asamk.signal.commands;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import net.sourceforge.argparse4j.impl.Arguments;
|
||||||
|
import net.sourceforge.argparse4j.inf.Namespace;
|
||||||
|
import net.sourceforge.argparse4j.inf.Subparser;
|
||||||
|
|
||||||
|
import org.asamk.signal.OutputType;
|
||||||
|
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.RegistrationManager;
|
||||||
|
import org.asamk.signal.output.JsonWriter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DeleteLocalAccountDataCommand implements RegistrationCommand, JsonRpcRegistrationCommand<Map<String, Object>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "deleteLocalAccountData";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachToSubparser(final Subparser subparser) {
|
||||||
|
subparser.help(
|
||||||
|
"Delete all local data for this account. Data should only be deleted if the account is unregistered. CAUTION: This cannot be undone.");
|
||||||
|
subparser.addArgument("--ignore-registered")
|
||||||
|
.help("Delete the account data even though the account is still registered on the Signal servers.")
|
||||||
|
.action(Arguments.storeTrue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(final Namespace ns, final RegistrationManager m) throws CommandException {
|
||||||
|
try {
|
||||||
|
final var ignoreRegistered = Boolean.TRUE.equals(ns.getBoolean("ignore-registered"));
|
||||||
|
if (m.isRegistered() && !ignoreRegistered) {
|
||||||
|
throw new UserErrorException(
|
||||||
|
"Not deleting account, it is still registered. Use --ignore-registered to delete it anyway.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m.deleteLocalAccountData();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOErrorException("Deletion error: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeReference<Map<String, Object>> getRequestType() {
|
||||||
|
return new TypeReference<>() {};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OutputType> getSupportedOutputTypes() {
|
||||||
|
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(
|
||||||
|
Map<String, Object> request, RegistrationManager m, JsonWriter jsonWriter
|
||||||
|
) throws CommandException {
|
||||||
|
Namespace commandNamespace = new JsonRpcNamespace(request == null ? Map.of() : request);
|
||||||
|
handleCommand(commandNamespace, m);
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,16 @@ public class DbusRegistrationManagerImpl implements RegistrationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteLocalAccountData() throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegistered() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue