mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
parent
5cd5697aea
commit
7e7e4150e1
14 changed files with 167 additions and 7 deletions
|
@ -2070,7 +2070,8 @@
|
|||
"name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Opaque",
|
||||
"fields":[
|
||||
{"name":"bitField0_"},
|
||||
{"name":"data_"}
|
||||
{"name":"data_"},
|
||||
{"name":"urgency_"}
|
||||
]}
|
||||
,
|
||||
{
|
||||
|
@ -2520,6 +2521,15 @@
|
|||
{"name":"type_"}
|
||||
]}
|
||||
,
|
||||
{
|
||||
"name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$ViewOnceOpen",
|
||||
"fields":[
|
||||
{"name":"bitField0_"},
|
||||
{"name":"senderE164_"},
|
||||
{"name":"senderUuid_"},
|
||||
{"name":"timestamp_"}
|
||||
]}
|
||||
,
|
||||
{
|
||||
"name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$SyncMessage$Viewed",
|
||||
"fields":[
|
||||
|
|
|
@ -174,6 +174,10 @@ public interface Manager extends Closeable {
|
|||
|
||||
SendMessageResults sendEndSessionMessage(Set<RecipientIdentifier.Single> recipients) throws IOException;
|
||||
|
||||
void deleteRecipient(RecipientIdentifier.Single recipient) throws IOException;
|
||||
|
||||
void deleteContact(RecipientIdentifier.Single recipient) throws IOException;
|
||||
|
||||
void setContactName(
|
||||
RecipientIdentifier.Single recipient, String name
|
||||
) throws NotMasterDeviceException, IOException;
|
||||
|
|
|
@ -279,13 +279,17 @@ public class ManagerImpl implements Manager {
|
|||
*
|
||||
* @param numbers The set of phone number in question
|
||||
* @return A map of numbers to canonicalized number and uuid. If a number is not registered the uuid is null.
|
||||
* @throws IOException if its unable to get the contacts to check if they're registered
|
||||
* @throws IOException if it's unable to get the contacts to check if they're registered
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Pair<String, UUID>> areUsersRegistered(Set<String> numbers) throws IOException {
|
||||
Map<String, String> canonicalizedNumbers = numbers.stream().collect(Collectors.toMap(n -> n, n -> {
|
||||
try {
|
||||
return PhoneNumberFormatter.formatNumber(n, account.getAccount());
|
||||
final var canonicalizedNumber = PhoneNumberFormatter.formatNumber(n, account.getAccount());
|
||||
if (!canonicalizedNumber.equals(n)) {
|
||||
logger.debug("Normalized number {} to {}.", n, canonicalizedNumber);
|
||||
}
|
||||
return canonicalizedNumber;
|
||||
} catch (InvalidNumberException e) {
|
||||
return "";
|
||||
}
|
||||
|
@ -774,6 +778,16 @@ public class ManagerImpl implements Manager {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRecipient(final RecipientIdentifier.Single recipient) throws IOException {
|
||||
account.removeRecipient(resolveRecipient(recipient));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteContact(final RecipientIdentifier.Single recipient) throws IOException {
|
||||
account.getContactStore().deleteContact(resolveRecipient(recipient));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContactName(
|
||||
RecipientIdentifier.Single recipient, String name
|
||||
|
|
|
@ -2,6 +2,8 @@ package org.asamk.signal.manager.api;
|
|||
|
||||
import org.asamk.signal.manager.groups.GroupId;
|
||||
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
|
@ -18,9 +20,16 @@ public sealed interface RecipientIdentifier {
|
|||
|
||||
static Single fromString(String identifier, String localNumber) throws InvalidNumberException {
|
||||
try {
|
||||
return UuidUtil.isUuid(identifier)
|
||||
? new Uuid(UUID.fromString(identifier))
|
||||
: new Number(PhoneNumberFormatter.formatNumber(identifier, localNumber));
|
||||
if (UuidUtil.isUuid(identifier)) {
|
||||
return new Uuid(UUID.fromString(identifier));
|
||||
}
|
||||
|
||||
final var normalizedNumber = PhoneNumberFormatter.formatNumber(identifier, localNumber);
|
||||
if (!normalizedNumber.equals(identifier)) {
|
||||
final Logger logger = LoggerFactory.getLogger(RecipientIdentifier.class);
|
||||
logger.debug("Normalized number {} to {}.", identifier, normalizedNumber);
|
||||
}
|
||||
return new Number(normalizedNumber);
|
||||
} catch (org.whispersystems.signalservice.api.util.InvalidNumberException e) {
|
||||
throw new InvalidNumberException(e.getMessage(), e);
|
||||
}
|
||||
|
|
|
@ -324,6 +324,14 @@ public class SignalAccount implements Closeable {
|
|||
senderKeyStore.mergeRecipients(recipientId, toBeMergedRecipientId);
|
||||
}
|
||||
|
||||
public void removeRecipient(final RecipientId recipientId) {
|
||||
sessionStore.deleteAllSessions(recipientId);
|
||||
identityKeyStore.deleteIdentity(recipientId);
|
||||
messageCache.deleteMessages(recipientId);
|
||||
senderKeyStore.deleteAll(recipientId);
|
||||
recipientStore.deleteRecipientData(recipientId);
|
||||
}
|
||||
|
||||
public static File getFileName(File dataPath, String account) {
|
||||
return new File(dataPath, account);
|
||||
}
|
||||
|
|
|
@ -13,4 +13,6 @@ public interface ContactsStore {
|
|||
Contact getContact(RecipientId recipientId);
|
||||
|
||||
List<Pair<RecipientId, Contact>> getContacts();
|
||||
|
||||
void deleteContact(RecipientId recipientId);
|
||||
}
|
||||
|
|
|
@ -172,6 +172,12 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
|
|||
}
|
||||
}
|
||||
|
||||
public void deleteIdentity(final RecipientId recipientId) {
|
||||
synchronized (cachedIdentities) {
|
||||
deleteIdentityLocked(recipientId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param identifier can be either a serialized uuid or a e164 phone number
|
||||
*/
|
||||
|
|
|
@ -71,6 +71,25 @@ public class MessageCache {
|
|||
return new CachedMessage(cacheFile);
|
||||
}
|
||||
|
||||
public void deleteMessages(final RecipientId recipientId) {
|
||||
final var recipientMessageCachePath = getMessageCachePath(recipientId);
|
||||
if (!recipientMessageCachePath.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var file : Objects.requireNonNull(recipientMessageCachePath.listFiles())) {
|
||||
if (!file.isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Files.delete(file.toPath());
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to delete cache file “{}”, ignoring: {}", file, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File getMessageCachePath(RecipientId recipientId) {
|
||||
if (recipientId == null) {
|
||||
return messageCachePath;
|
||||
|
|
|
@ -220,6 +220,25 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteContact(final RecipientId recipientId) {
|
||||
synchronized (recipients) {
|
||||
final var recipient = recipients.get(recipientId);
|
||||
storeRecipientLocked(recipientId, Recipient.newBuilder(recipient).withContact(null).build());
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRecipientData(final RecipientId recipientId) {
|
||||
synchronized (recipients) {
|
||||
final var recipient = recipients.get(recipientId);
|
||||
storeRecipientLocked(recipientId,
|
||||
Recipient.newBuilder()
|
||||
.withRecipientId(recipientId)
|
||||
.withAddress(new RecipientAddress(recipient.getAddress().getUuid().orElse(null)))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Profile getProfile(final RecipientId recipientId) {
|
||||
final var recipient = getRecipient(recipientId);
|
||||
|
|
|
@ -63,7 +63,7 @@ public class SenderKeyStore implements SignalServiceSenderKeyStore {
|
|||
senderKeyRecordStore.deleteAll();
|
||||
}
|
||||
|
||||
public void rotateSenderKeys(RecipientId recipientId) {
|
||||
public void deleteAll(RecipientId recipientId) {
|
||||
senderKeySharedStore.deleteAllFor(recipientId);
|
||||
senderKeyRecordStore.deleteAllFor(recipientId);
|
||||
}
|
||||
|
|
|
@ -442,6 +442,15 @@ Specify the new name for this contact.
|
|||
Set expiration time of messages (seconds).
|
||||
To disable expiration set expiration time to 0.
|
||||
|
||||
=== removeContact
|
||||
Remove the info of a given contact
|
||||
|
||||
NUMBER::
|
||||
Specify the contact phone number.
|
||||
|
||||
*--forget*::
|
||||
Delete all data associated with this contact, including identity keys and sessions.
|
||||
|
||||
=== block
|
||||
|
||||
Block the given contacts or groups (no messages will be received).
|
||||
|
|
|
@ -26,6 +26,7 @@ public class Commands {
|
|||
addCommand(new QuitGroupCommand());
|
||||
addCommand(new ReceiveCommand());
|
||||
addCommand(new RegisterCommand());
|
||||
addCommand(new RemoveContactCommand());
|
||||
addCommand(new RemoveDeviceCommand());
|
||||
addCommand(new RemoteDeleteCommand());
|
||||
addCommand(new RemovePinCommand());
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package org.asamk.signal.commands;
|
||||
|
||||
import net.sourceforge.argparse4j.impl.Arguments;
|
||||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
import net.sourceforge.argparse4j.inf.Subparser;
|
||||
|
||||
import org.asamk.signal.commands.exceptions.CommandException;
|
||||
import org.asamk.signal.commands.exceptions.IOErrorException;
|
||||
import org.asamk.signal.manager.Manager;
|
||||
import org.asamk.signal.output.OutputWriter;
|
||||
import org.asamk.signal.util.CommandUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RemoveContactCommand implements JsonRpcLocalCommand {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "removeContact";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachToSubparser(final Subparser subparser) {
|
||||
subparser.help("Remove the details of a given contact");
|
||||
subparser.addArgument("recipient").help("Contact number");
|
||||
subparser.addArgument("--forget")
|
||||
.action(Arguments.storeTrue())
|
||||
.help("Delete all data associated with this contact, including identity keys and sessions.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(
|
||||
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
||||
) throws CommandException {
|
||||
var recipientString = ns.getString("recipient");
|
||||
var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber());
|
||||
|
||||
var forget = Boolean.TRUE == ns.getBoolean("forget");
|
||||
try {
|
||||
if (forget) {
|
||||
m.deleteRecipient(recipient);
|
||||
} else {
|
||||
m.deleteContact(recipient);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOErrorException("Remove contact error: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -381,6 +381,16 @@ public class DbusManagerImpl implements Manager {
|
|||
return new SendMessageResults(0, Map.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRecipient(final RecipientIdentifier.Single recipient) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteContact(final RecipientIdentifier.Single recipient) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContactName(
|
||||
final RecipientIdentifier.Single recipient, final String name
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue