*/
package org.asamk.signal.manager;
+import org.asamk.signal.manager.api.CaptchaRequiredException;
+import org.asamk.signal.manager.api.IncorrectPinException;
+import org.asamk.signal.manager.api.PinLockedException;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironment;
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.util.KeyUtils;
+import org.asamk.signal.manager.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.util.KeyHelper;
import org.whispersystems.libsignal.util.guava.Optional;
+import org.whispersystems.signalservice.api.KbsPinData;
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.kbs.MasterKey;
+import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.ServiceResponse;
import org.whispersystems.signalservice.internal.push.LockedException;
import org.whispersystems.signalservice.internal.push.RequestVerificationCodeResponse;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
-import java.util.Locale;
+import java.util.function.Consumer;
+
+import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
public class RegistrationManager implements Closeable {
private final PathConfig pathConfig;
private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent;
+ private final Consumer<Manager> newManagerListener;
private final SignalServiceAccountManager accountManager;
private final PinHelper pinHelper;
SignalAccount account,
PathConfig pathConfig,
ServiceEnvironmentConfig serviceEnvironmentConfig,
- String userAgent
+ String userAgent,
+ Consumer<Manager> newManagerListener
) {
this.account = account;
this.pathConfig = pathConfig;
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent;
+ this.newManagerListener = newManagerListener;
GroupsV2Operations groupsV2Operations;
try {
this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
new DynamicCredentialsProvider(
// Using empty UUID, because registering doesn't work otherwise
- null, account.getUsername(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID),
+ null, account.getAccount(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID),
userAgent,
groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
}
public static RegistrationManager init(
- String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
+ String number, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
+ ) throws IOException {
+ return init(number, settingsPath, serviceEnvironment, userAgent, null);
+ }
+
+ public static RegistrationManager init(
+ String number,
+ File settingsPath,
+ ServiceEnvironment serviceEnvironment,
+ String userAgent,
+ Consumer<Manager> newManagerListener
) throws IOException {
var pathConfig = PathConfig.createDefault(settingsPath);
final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
- if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
+ if (!SignalAccount.userExists(pathConfig.dataPath(), number)) {
var identityKey = KeyUtils.generateIdentityKeyPair();
var registrationId = KeyHelper.generateRegistrationId(false);
var profileKey = KeyUtils.createProfileKey();
- var account = SignalAccount.create(pathConfig.getDataPath(),
- username,
+ var account = SignalAccount.create(pathConfig.dataPath(),
+ number,
identityKey,
registrationId,
profileKey,
TrustNewIdentity.ON_FIRST_USE);
- return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
+ return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent, newManagerListener);
}
- var account = SignalAccount.load(pathConfig.getDataPath(), username, true, TrustNewIdentity.ON_FIRST_USE);
+ var account = SignalAccount.load(pathConfig.dataPath(), number, true, TrustNewIdentity.ON_FIRST_USE);
- return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
+ return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent, newManagerListener);
}
- public void register(boolean voiceVerification, String captcha) throws IOException {
+ public void register(boolean voiceVerification, String captcha) throws IOException, CaptchaRequiredException {
+ captcha = captcha == null ? null : captcha.replace("signalcaptcha://", "");
+ if (account.getAci() != null) {
+ try {
+ final var accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
+ new DynamicCredentialsProvider(account.getAci(),
+ account.getAccount(),
+ account.getPassword(),
+ account.getDeviceId()),
+ userAgent,
+ null,
+ ServiceConfig.AUTOMATIC_NETWORK_RETRY);
+ accountManager.setAccountAttributes(account.getEncryptedDeviceName(),
+ null,
+ account.getLocalRegistrationId(),
+ true,
+ null,
+ account.getPinMasterKey() == null ? null : account.getPinMasterKey().deriveRegistrationLock(),
+ account.getSelfUnidentifiedAccessKey(),
+ account.isUnrestrictedUnidentifiedAccess(),
+ capabilities,
+ account.isDiscoverableByPhoneNumber());
+ account.setRegistered(true);
+ logger.info("Reactivated existing account, verify is not necessary.");
+ if (newManagerListener != null) {
+ final var m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
+ account = null;
+ newManagerListener.accept(m);
+ }
+ return;
+ } catch (IOException e) {
+ logger.debug("Failed to reactivate account");
+ }
+ }
final ServiceResponse<RequestVerificationCodeResponse> response;
if (voiceVerification) {
- response = accountManager.requestVoiceVerificationCode(getDefaultLocale(),
+ response = accountManager.requestVoiceVerificationCode(Utils.getDefaultLocale(),
Optional.fromNullable(captcha),
Optional.absent(),
Optional.absent());
Optional.absent(),
Optional.absent());
}
- handleResponseException(response);
- }
-
- private Locale getDefaultLocale() {
- final var locale = Locale.getDefault();
try {
- Locale.LanguageRange.parse(locale.getLanguage() + "-" + locale.getCountry());
- } catch (IllegalArgumentException e) {
- logger.debug("Invalid locale, ignoring: {}", locale);
- return null;
+ handleResponseException(response);
+ } catch (org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredException e) {
+ throw new CaptchaRequiredException(e.getMessage(), e);
}
-
- return locale;
}
- public Manager verifyAccount(
+ public void verifyAccount(
String verificationCode, String pin
- ) throws IOException, LockedException, KeyBackupSystemNoDataException, KeyBackupServicePinException {
+ ) throws IOException, PinLockedException, IncorrectPinException {
verificationCode = verificationCode.replace("-", "");
VerifyAccountResponse response;
MasterKey masterKey;
pin = null;
} catch (LockedException e) {
if (pin == null) {
- throw e;
+ throw new PinLockedException(e.getTimeRemaining());
}
- var registrationLockData = pinHelper.getRegistrationLockData(pin, e);
+ KbsPinData registrationLockData;
+ try {
+ registrationLockData = pinHelper.getRegistrationLockData(pin, e);
+ } catch (KeyBackupSystemNoDataException ex) {
+ throw new IOException(e);
+ } catch (KeyBackupServicePinException ex) {
+ throw new IncorrectPinException(ex.getTriesRemaining());
+ }
if (registrationLockData == null) {
throw e;
}
}
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
- account.finishRegistration(UuidUtil.parseOrNull(response.getUuid()), masterKey, pin);
+ account.finishRegistration(ACI.parseOrNull(response.getUuid()), masterKey, pin);
- Manager m = null;
+ ManagerImpl m = null;
try {
- m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
+ m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent);
account = null;
m.refreshPreKeys();
+ if (response.isStorageCapable()) {
+ m.retrieveRemoteStorage();
+ }
// Set an initial empty profile so user can be added to groups
try {
m.setProfile(null, null, null, null, null);
} catch (NoClassDefFoundError e) {
logger.warn("Failed to set default profile: {}", e.getMessage());
}
- if (response.isStorageCapable()) {
- m.retrieveRemoteStorage();
- }
- final var result = m;
- m = null;
-
- return result;
+ if (newManagerListener != null) {
+ newManagerListener.accept(m);
+ m = null;
+ }
} finally {
if (m != null) {
m.close();