Merge remote-tracking branch 'refs/remotes/origin/master'

This commit is contained in:
Adimarantis 2021-08-30 19:27:45 +02:00
commit 54583161e2
12 changed files with 228 additions and 172 deletions

View file

@ -58,7 +58,6 @@ import org.asamk.signal.manager.storage.stickers.StickerPackId;
import org.asamk.signal.manager.util.KeyUtils;
import org.asamk.signal.manager.util.StickerUtils;
import org.asamk.signal.manager.util.Utils;
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey;
@ -165,8 +164,7 @@ public class Manager implements Closeable {
return LEGACY_LOCK::unlock;
}
};
this.dependencies = new SignalDependencies(account.getSelfAddress(),
serviceEnvironmentConfig,
this.dependencies = new SignalDependencies(serviceEnvironmentConfig,
userAgent,
credentialsProvider,
account.getSignalProtocolStore(),
@ -187,8 +185,6 @@ public class Manager implements Closeable {
avatarStore,
account.getProfileStore()::getProfileKey,
unidentifiedAccessHelper::getAccessFor,
dependencies::getProfileService,
dependencies::getMessageReceiver,
this::resolveSignalServiceAddress);
final GroupV2Helper groupV2Helper = new GroupV2Helper(profileHelper::getRecipientProfileKeyCredential,
this::getRecipientProfile,
@ -221,8 +217,7 @@ public class Manager implements Closeable {
this::resolveSignalServiceAddress);
this.context = new Context(account,
dependencies.getAccountManager(),
dependencies.getMessageReceiver(),
dependencies,
stickerPackStore,
sendHelper,
groupHelper,
@ -386,6 +381,13 @@ public class Manager implements Closeable {
}
public void deleteAccount() throws IOException {
try {
pinHelper.removeRegistrationLockPin();
} catch (UnauthenticatedResponseException e) {
logger.warn("Failed to remove registration lock pin");
}
account.setRegistrationLockPin(null, null);
dependencies.getAccountManager().deleteAccount();
account.setRegistered(false);
@ -818,37 +820,33 @@ public class Manager implements Closeable {
) {
var envelope = cachedMessage.loadEnvelope();
if (envelope == null) {
cachedMessage.delete();
return null;
}
SignalServiceContent content = null;
List<HandleAction> actions = null;
if (!envelope.isReceipt()) {
try {
content = dependencies.getCipher().decrypt(envelope);
} catch (ProtocolUntrustedIdentityException e) {
if (System.currentTimeMillis() - envelope.getServerDeliveredTimestamp() > 1000L * 60 * 60 * 24 * 30) {
// Envelope is more than a month old, cleaning up.
cachedMessage.delete();
return null;
}
if (!envelope.hasSourceUuid()) {
final var identifier = e.getSender();
final var recipientId = account.getRecipientStore().resolveRecipient(identifier);
try {
account.getMessageCache().replaceSender(cachedMessage, recipientId);
} catch (IOException ioException) {
logger.warn("Failed to move cached message to recipient folder: {}", ioException.getMessage());
}
}
return null;
} catch (Exception er) {
// All other errors are not recoverable, so delete the cached message
final var result = incomingMessageHandler.handleRetryEnvelope(envelope, ignoreAttachments, handler);
final var actions = result.first();
final var exception = result.second();
if (exception instanceof UntrustedIdentityException) {
if (System.currentTimeMillis() - envelope.getServerDeliveredTimestamp() > 1000L * 60 * 60 * 24 * 30) {
// Envelope is more than a month old, cleaning up.
cachedMessage.delete();
return null;
}
actions = incomingMessageHandler.handleMessage(envelope, content, ignoreAttachments);
if (!envelope.hasSourceUuid()) {
final var identifier = ((UntrustedIdentityException) exception).getSender();
final var recipientId = account.getRecipientStore().resolveRecipient(identifier);
try {
account.getMessageCache().replaceSender(cachedMessage, recipientId);
} catch (IOException ioException) {
logger.warn("Failed to move cached message to recipient folder: {}", ioException.getMessage());
}
}
return null;
}
handler.handleMessage(envelope, content, null);
// If successful and for all other errors that are not recoverable, delete the cached message
cachedMessage.delete();
return actions;
}
@ -1147,10 +1145,6 @@ public class Manager implements Closeable {
}
public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) {
if (address.matches(account.getSelfAddress())) {
return account.getSelfAddress();
}
return resolveSignalServiceAddress(resolveRecipient(address));
}

View file

@ -3,7 +3,6 @@ package org.asamk.signal.manager;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.signal.libsignal.metadata.certificate.CertificateValidator;
import org.signal.zkgroup.profiles.ClientZkProfileOperations;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.KeyBackupService;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
@ -18,32 +17,41 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.services.ProfileService;
import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.api.websocket.WebSocketFactory;
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
import org.whispersystems.signalservice.internal.websocket.WebSocketConnection;
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;
import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
public class SignalDependencies {
private final SignalServiceAccountManager accountManager;
private final GroupsV2Api groupsV2Api;
private final GroupsV2Operations groupsV2Operations;
private final Object LOCK = new Object();
private final SignalWebSocket signalWebSocket;
private final SignalServiceMessageReceiver messageReceiver;
private final SignalServiceMessageSender messageSender;
private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent;
private final DynamicCredentialsProvider credentialsProvider;
private final SignalServiceDataStore dataStore;
private final ExecutorService executor;
private final SignalSessionLock sessionLock;
private final KeyBackupService keyBackupService;
private final ProfileService profileService;
private final SignalServiceCipher cipher;
private SignalServiceAccountManager accountManager;
private GroupsV2Api groupsV2Api;
private GroupsV2Operations groupsV2Operations;
private ClientZkOperations clientZkOperations;
private SignalWebSocket signalWebSocket;
private SignalServiceMessageReceiver messageReceiver;
private SignalServiceMessageSender messageSender;
private KeyBackupService keyBackupService;
private ProfileService profileService;
private SignalServiceCipher cipher;
public SignalDependencies(
final SignalServiceAddress selfAddress,
final ServiceEnvironmentConfig serviceEnvironmentConfig,
final String userAgent,
final DynamicCredentialsProvider credentialsProvider,
@ -51,100 +59,134 @@ public class SignalDependencies {
final ExecutorService executor,
final SignalSessionLock sessionLock
) {
this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create(
serviceEnvironmentConfig.getSignalServiceConfiguration())) : null;
final SleepTimer timer = new UptimeSleepTimer();
this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
userAgent,
groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
this.groupsV2Api = accountManager.getGroupsV2Api();
this.keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(),
serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(),
serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(),
serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(),
10);
final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(
serviceEnvironmentConfig.getSignalServiceConfiguration()).getProfileOperations() : null;
this.messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
userAgent,
clientZkProfileOperations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
final var healthMonitor = new SignalWebSocketHealthMonitor(timer);
final WebSocketFactory webSocketFactory = new WebSocketFactory() {
@Override
public WebSocketConnection createWebSocket() {
return new WebSocketConnection("normal",
serviceEnvironmentConfig.getSignalServiceConfiguration(),
Optional.of(credentialsProvider),
userAgent,
healthMonitor);
}
@Override
public WebSocketConnection createUnidentifiedWebSocket() {
return new WebSocketConnection("unidentified",
serviceEnvironmentConfig.getSignalServiceConfiguration(),
Optional.absent(),
userAgent,
healthMonitor);
}
};
this.signalWebSocket = new SignalWebSocket(webSocketFactory);
healthMonitor.monitor(signalWebSocket);
this.profileService = new ProfileService(clientZkProfileOperations, messageReceiver, signalWebSocket);
final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot());
this.cipher = new SignalServiceCipher(selfAddress, dataStore, sessionLock, certificateValidator);
this.messageSender = new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
dataStore,
sessionLock,
userAgent,
signalWebSocket,
Optional.absent(),
clientZkProfileOperations,
executor,
ServiceConfig.MAX_ENVELOPE_SIZE,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent;
this.credentialsProvider = credentialsProvider;
this.dataStore = dataStore;
this.executor = executor;
this.sessionLock = sessionLock;
}
public SignalServiceAccountManager getAccountManager() {
return accountManager;
return getOrCreate(() -> accountManager,
() -> accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
userAgent,
getGroupsV2Operations(),
ServiceConfig.AUTOMATIC_NETWORK_RETRY));
}
public GroupsV2Api getGroupsV2Api() {
return groupsV2Api;
return getOrCreate(() -> groupsV2Api, () -> groupsV2Api = getAccountManager().getGroupsV2Api());
}
public GroupsV2Operations getGroupsV2Operations() {
return groupsV2Operations;
return getOrCreate(() -> groupsV2Operations,
() -> groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create(
serviceEnvironmentConfig.getSignalServiceConfiguration())) : null);
}
private ClientZkOperations getClientZkOperations() {
return getOrCreate(() -> clientZkOperations,
() -> clientZkOperations = capabilities.isGv2()
? ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())
: null);
}
public SignalWebSocket getSignalWebSocket() {
return signalWebSocket;
return getOrCreate(() -> signalWebSocket, () -> {
final var timer = new UptimeSleepTimer();
final var healthMonitor = new SignalWebSocketHealthMonitor(timer);
final var webSocketFactory = new WebSocketFactory() {
@Override
public WebSocketConnection createWebSocket() {
return new WebSocketConnection("normal",
serviceEnvironmentConfig.getSignalServiceConfiguration(),
Optional.of(credentialsProvider),
userAgent,
healthMonitor);
}
@Override
public WebSocketConnection createUnidentifiedWebSocket() {
return new WebSocketConnection("unidentified",
serviceEnvironmentConfig.getSignalServiceConfiguration(),
Optional.absent(),
userAgent,
healthMonitor);
}
};
signalWebSocket = new SignalWebSocket(webSocketFactory);
healthMonitor.monitor(signalWebSocket);
});
}
public SignalServiceMessageReceiver getMessageReceiver() {
return messageReceiver;
return getOrCreate(() -> messageReceiver,
() -> messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
userAgent,
getClientZkOperations().getProfileOperations(),
ServiceConfig.AUTOMATIC_NETWORK_RETRY));
}
public SignalServiceMessageSender getMessageSender() {
return messageSender;
return getOrCreate(() -> messageSender,
() -> messageSender = new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
dataStore,
sessionLock,
userAgent,
getSignalWebSocket(),
Optional.absent(),
getClientZkOperations().getProfileOperations(),
executor,
ServiceConfig.MAX_ENVELOPE_SIZE,
ServiceConfig.AUTOMATIC_NETWORK_RETRY));
}
public KeyBackupService getKeyBackupService() {
return keyBackupService;
return getOrCreate(() -> keyBackupService,
() -> keyBackupService = getAccountManager().getKeyBackupService(ServiceConfig.getIasKeyStore(),
serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(),
serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(),
serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(),
10));
}
public ProfileService getProfileService() {
return profileService;
return getOrCreate(() -> profileService,
() -> profileService = new ProfileService(getClientZkOperations().getProfileOperations(),
getMessageReceiver(),
getSignalWebSocket()));
}
public SignalServiceCipher getCipher() {
return cipher;
return getOrCreate(() -> cipher, () -> {
final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot());
final var address = new SignalServiceAddress(credentialsProvider.getUuid(), credentialsProvider.getE164());
cipher = new SignalServiceCipher(address, dataStore, sessionLock, certificateValidator);
});
}
private <T> T getOrCreate(Supplier<T> supplier, Callable creator) {
var value = supplier.get();
if (value != null) {
return value;
}
synchronized (LOCK) {
value = supplier.get();
if (value != null) {
return value;
}
creator.call();
return supplier.get();
}
}
private interface Callable {
void call();
}
}

View file

@ -79,6 +79,28 @@ public final class IncomingMessageHandler {
this.jobExecutor = jobExecutor;
}
public Pair<List<HandleAction>, Exception> handleRetryEnvelope(
final SignalServiceEnvelope envelope,
final boolean ignoreAttachments,
final Manager.ReceiveMessageHandler handler
) {
SignalServiceContent content = null;
if (!envelope.isReceipt()) {
try {
content = dependencies.getCipher().decrypt(envelope);
} catch (ProtocolUntrustedIdentityException e) {
final var recipientId = account.getRecipientStore().resolveRecipient(e.getSender());
final var exception = new UntrustedIdentityException(addressResolver.resolveSignalServiceAddress(
recipientId), e.getSenderDevice());
return new Pair<>(List.of(), exception);
} catch (Exception e) {
return new Pair<>(List.of(), e);
}
}
final var actions = checkAndHandleMessage(envelope, content, ignoreAttachments, handler, null);
return new Pair<>(actions, null);
}
public Pair<List<HandleAction>, Exception> handleEnvelope(
final SignalServiceEnvelope envelope,
final boolean ignoreAttachments,
@ -108,35 +130,48 @@ public final class IncomingMessageHandler {
} catch (Exception e) {
exception = e;
}
if (!envelope.hasSourceUuid() && content != null) {
// Store uuid if we don't have it already
// address/uuid is validated by unidentified sender certificate
account.getRecipientStore().resolveRecipientTrusted(content.getSender());
}
}
actions.addAll(checkAndHandleMessage(envelope, content, ignoreAttachments, handler, exception));
return new Pair<>(actions, exception);
}
private List<HandleAction> checkAndHandleMessage(
final SignalServiceEnvelope envelope,
final SignalServiceContent content,
final boolean ignoreAttachments,
final Manager.ReceiveMessageHandler handler,
final Exception exception
) {
if (!envelope.hasSourceUuid() && content != null) {
// Store uuid if we don't have it already
// address/uuid is validated by unidentified sender certificate
account.getRecipientStore().resolveRecipientTrusted(content.getSender());
}
if (isMessageBlocked(envelope, content)) {
logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
return List.of();
} else if (isNotAllowedToSendToGroup(envelope, content)) {
logger.info("Ignoring a group message from an unauthorized sender (no member or admin): {} {}",
(envelope.hasSourceUuid() ? envelope.getSourceAddress() : content.getSender()).getIdentifier(),
envelope.getTimestamp());
return List.of();
} else {
actions.addAll(handleMessage(envelope, content, ignoreAttachments));
List<HandleAction> actions;
if (content != null) {
actions = handleMessage(envelope, content, ignoreAttachments);
} else {
actions = List.of();
}
handler.handleMessage(envelope, content, exception);
return actions;
}
return new Pair<>(actions, exception);
}
public List<HandleAction> handleMessage(
SignalServiceEnvelope envelope, SignalServiceContent content, boolean ignoreAttachments
) {
var actions = new ArrayList<HandleAction>();
if (content == null) {
return actions;
}
final RecipientId sender;
if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
sender = recipientResolver.resolveRecipient(envelope.getSourceAddress());

View file

@ -1,8 +0,0 @@
package org.asamk.signal.manager.helper;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
public interface MessageReceiverProvider {
SignalServiceMessageReceiver getMessageReceiver();
}

View file

@ -45,8 +45,6 @@ public final class ProfileHelper {
private final AvatarStore avatarStore;
private final ProfileKeyProvider profileKeyProvider;
private final UnidentifiedAccessProvider unidentifiedAccessProvider;
private final ProfileServiceProvider profileServiceProvider;
private final MessageReceiverProvider messageReceiverProvider;
private final SignalServiceAddressResolver addressResolver;
public ProfileHelper(
@ -55,8 +53,6 @@ public final class ProfileHelper {
final AvatarStore avatarStore,
final ProfileKeyProvider profileKeyProvider,
final UnidentifiedAccessProvider unidentifiedAccessProvider,
final ProfileServiceProvider profileServiceProvider,
final MessageReceiverProvider messageReceiverProvider,
final SignalServiceAddressResolver addressResolver
) {
this.account = account;
@ -64,8 +60,6 @@ public final class ProfileHelper {
this.avatarStore = avatarStore;
this.profileKeyProvider = profileKeyProvider;
this.unidentifiedAccessProvider = unidentifiedAccessProvider;
this.profileServiceProvider = profileServiceProvider;
this.messageReceiverProvider = messageReceiverProvider;
this.addressResolver = addressResolver;
}
@ -218,7 +212,7 @@ public final class ProfileHelper {
}
private SignalServiceProfile retrieveProfileSync(String username) throws IOException {
return messageReceiverProvider.getMessageReceiver().retrieveProfileByUsername(username, Optional.absent());
return dependencies.getMessageReceiver().retrieveProfileByUsername(username, Optional.absent());
}
private ProfileAndCredential retrieveProfileAndCredential(
@ -287,7 +281,7 @@ public final class ProfileHelper {
Optional<UnidentifiedAccess> unidentifiedAccess,
SignalServiceProfile.RequestType requestType
) {
var profileService = profileServiceProvider.getProfileService();
var profileService = dependencies.getProfileService();
Single<ServiceResponse<ProfileAndCredential>> responseSingle;
try {

View file

@ -1,8 +0,0 @@
package org.asamk.signal.manager.helper;
import org.whispersystems.signalservice.api.services.ProfileService;
public interface ProfileServiceProvider {
ProfileService getProfileService();
}

View file

@ -222,8 +222,13 @@ public class SyncHelper {
try {
c = s.read();
} catch (IOException e) {
logger.warn("Sync contacts contained invalid contact, ignoring: {}", e.getMessage());
continue;
if (e.getMessage() != null && e.getMessage().contains("Missing contact address!")) {
logger.warn("Sync contacts contained invalid contact, ignoring: {}", e.getMessage());
continue;
} else {
logger.warn("Failed to read sync contacts", e);
break;
}
}
if (c == null) {
break;

View file

@ -1,19 +1,17 @@
package org.asamk.signal.manager.jobs;
import org.asamk.signal.manager.SignalDependencies;
import org.asamk.signal.manager.StickerPackStore;
import org.asamk.signal.manager.helper.GroupHelper;
import org.asamk.signal.manager.helper.ProfileHelper;
import org.asamk.signal.manager.helper.SendHelper;
import org.asamk.signal.manager.helper.SyncHelper;
import org.asamk.signal.manager.storage.SignalAccount;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
public class Context {
private final SignalAccount account;
private final SignalServiceAccountManager accountManager;
private final SignalServiceMessageReceiver messageReceiver;
private final SignalDependencies dependencies;
private final StickerPackStore stickerPackStore;
private final SendHelper sendHelper;
private final GroupHelper groupHelper;
@ -22,8 +20,7 @@ public class Context {
public Context(
final SignalAccount account,
final SignalServiceAccountManager accountManager,
final SignalServiceMessageReceiver messageReceiver,
final SignalDependencies dependencies,
final StickerPackStore stickerPackStore,
final SendHelper sendHelper,
final GroupHelper groupHelper,
@ -31,8 +28,7 @@ public class Context {
final ProfileHelper profileHelper
) {
this.account = account;
this.accountManager = accountManager;
this.messageReceiver = messageReceiver;
this.dependencies = dependencies;
this.stickerPackStore = stickerPackStore;
this.sendHelper = sendHelper;
this.groupHelper = groupHelper;
@ -44,12 +40,8 @@ public class Context {
return account;
}
public SignalServiceAccountManager getAccountManager() {
return accountManager;
}
public SignalServiceMessageReceiver getMessageReceiver() {
return messageReceiver;
public SignalDependencies getDependencies() {
return dependencies;
}
public StickerPackStore getStickerPackStore() {

View file

@ -32,7 +32,9 @@ public class RetrieveStickerPackJob implements Job {
}
logger.debug("Retrieving sticker pack {}.", Hex.toStringCondensed(packId.serialize()));
try {
final var manifest = context.getMessageReceiver().retrieveStickerManifest(packId.serialize(), packKey);
final var manifest = context.getDependencies()
.getMessageReceiver()
.retrieveStickerManifest(packId.serialize(), packKey);
final var stickerIds = new HashSet<Integer>();
if (manifest.getCover().isPresent()) {
@ -43,7 +45,9 @@ public class RetrieveStickerPackJob implements Job {
}
for (var id : stickerIds) {
final var inputStream = context.getMessageReceiver().retrieveSticker(packId.serialize(), packKey, id);
final var inputStream = context.getDependencies()
.getMessageReceiver()
.retrieveSticker(packId.serialize(), packKey, id);
context.getStickerPackStore().storeSticker(packId, id, o -> IOUtils.copyStream(inputStream, o));
}

View file

@ -21,6 +21,7 @@ import org.asamk.signal.manager.storage.protocol.SignalProtocolStore;
import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.LegacyRecipientStore;
import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientStore;
import org.asamk.signal.manager.storage.sessions.SessionStore;
@ -789,7 +790,7 @@ public class SignalAccount implements Closeable {
}
public RecipientId getSelfRecipientId() {
return recipientStore.resolveRecipientTrusted(getSelfAddress());
return recipientStore.resolveRecipientTrusted(new RecipientAddress(uuid, username));
}
public String getEncryptedDeviceName() {

View file

@ -5,7 +5,7 @@ public class BaseConfig {
public final static String PROJECT_NAME = BaseConfig.class.getPackage().getImplementationTitle();
public final static String PROJECT_VERSION = BaseConfig.class.getPackage().getImplementationVersion();
final static String USER_AGENT_SIGNAL_ANDROID = "Signal-Android/5.12.4";
final static String USER_AGENT_SIGNAL_ANDROID = "Signal-Android/5.22.3";
final static String USER_AGENT_SIGNAL_CLI = PROJECT_NAME == null
? "signal-cli"
: PROJECT_NAME + "/" + PROJECT_VERSION;

View file

@ -13,6 +13,7 @@ import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException;
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
import java.io.IOException;
import java.net.URI;
@ -99,6 +100,10 @@ public class DbusSignalControlImpl implements org.asamk.SignalControl {
public void registerWithCaptcha(
final String number, final boolean voiceVerification, final String captcha
) throws Error.Failure, Error.InvalidNumber {
if (!PhoneNumberFormatter.isValidNumber(number, null)) {
throw new SignalControl.Error.InvalidNumber(
"Invalid username (phone number), make sure you include the country code.");
}
try (final RegistrationManager registrationManager = c.getNewRegistrationManager(number)) {
registrationManager.register(voiceVerification, captcha);
} catch (CaptchaRequiredException e) {