"name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$CallMessage$Opaque",
"fields":[
{"name":"bitField0_"},
- {"name":"data_"}
+ {"name":"data_"},
+ {"name":"urgency_"}
]}
,
{
{"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":[
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;
*
* @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 "";
}
}
}
+ @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
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;
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);
}
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);
}
Contact getContact(RecipientId recipientId);
List<Pair<RecipientId, Contact>> getContacts();
+
+ void deleteContact(RecipientId recipientId);
}
}
}
+ public void deleteIdentity(final RecipientId recipientId) {
+ synchronized (cachedIdentities) {
+ deleteIdentityLocked(recipientId);
+ }
+ }
+
/**
* @param identifier can be either a serialized uuid or a e164 phone number
*/
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;
.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);
senderKeyRecordStore.deleteAll();
}
- public void rotateSenderKeys(RecipientId recipientId) {
+ public void deleteAll(RecipientId recipientId) {
senderKeySharedStore.deleteAllFor(recipientId);
senderKeyRecordStore.deleteAllFor(recipientId);
}
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).
addCommand(new QuitGroupCommand());
addCommand(new ReceiveCommand());
addCommand(new RegisterCommand());
+ addCommand(new RemoveContactCommand());
addCommand(new RemoveDeviceCommand());
addCommand(new RemoteDeleteCommand());
addCommand(new RemovePinCommand());
--- /dev/null
+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);
+ }
+ }
+}
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