X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/00cda598c849ac0dd5301a961ee4113093b533c8..fd92a96e1a1d8b5464772667579ee3387a46453b:/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java diff --git a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java index 466286b0..0c617646 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ProvisioningManagerImpl.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2015-2021 AsamK and contributors + Copyright (C) 2015-2022 AsamK and contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,15 +16,17 @@ */ package org.asamk.signal.manager; +import org.asamk.signal.manager.api.UserAlreadyExistsException; import org.asamk.signal.manager.config.ServiceConfig; import org.asamk.signal.manager.config.ServiceEnvironmentConfig; import org.asamk.signal.manager.storage.SignalAccount; +import org.asamk.signal.manager.storage.accounts.AccountsStore; import org.asamk.signal.manager.storage.identities.TrustNewIdentity; import org.asamk.signal.manager.util.KeyUtils; +import org.signal.libsignal.protocol.IdentityKeyPair; +import org.signal.libsignal.protocol.util.KeyHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKeyPair; -import org.whispersystems.libsignal.util.KeyHelper; import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; @@ -35,10 +37,11 @@ import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider import java.io.IOException; import java.net.URI; +import java.nio.channels.OverlappingFileLockException; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -public class ProvisioningManagerImpl implements ProvisioningManager { +class ProvisioningManagerImpl implements ProvisioningManager { private final static Logger logger = LoggerFactory.getLogger(ProvisioningManagerImpl.class); @@ -46,6 +49,7 @@ public class ProvisioningManagerImpl implements ProvisioningManager { private final ServiceEnvironmentConfig serviceEnvironmentConfig; private final String userAgent; private final Consumer newManagerListener; + private final AccountsStore accountsStore; private final SignalServiceAccountManager accountManager; private final IdentityKeyPair tempIdentityKey; @@ -56,24 +60,27 @@ public class ProvisioningManagerImpl implements ProvisioningManager { PathConfig pathConfig, ServiceEnvironmentConfig serviceEnvironmentConfig, String userAgent, - final Consumer newManagerListener + final Consumer newManagerListener, + final AccountsStore accountsStore ) { this.pathConfig = pathConfig; this.serviceEnvironmentConfig = serviceEnvironmentConfig; this.userAgent = userAgent; this.newManagerListener = newManagerListener; + this.accountsStore = accountsStore; tempIdentityKey = KeyUtils.generateIdentityKeyPair(); registrationId = KeyHelper.generateRegistrationId(false); password = KeyUtils.createPassword(); GroupsV2Operations groupsV2Operations; try { - groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())); + groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()), + ServiceConfig.GROUP_MAX_SIZE); } catch (Throwable ignored) { groupsV2Operations = null; } accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(), - new DynamicCredentialsProvider(null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID), + new DynamicCredentialsProvider(null, null, null, password, SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY); @@ -87,19 +94,32 @@ public class ProvisioningManagerImpl implements ProvisioningManager { } @Override - public String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExists { + public String finishDeviceLink(String deviceName) throws IOException, TimeoutException, UserAlreadyExistsException { var ret = accountManager.getNewDeviceRegistration(tempIdentityKey); var number = ret.getNumber(); + var aci = ret.getAci(); + var pni = ret.getPni(); logger.info("Received link information from {}, linking in progress ...", number); - if (SignalAccount.userExists(pathConfig.dataPath(), number) && !canRelinkExistingAccount(number)) { - throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.dataPath(), number)); + var accountPath = accountsStore.getPathByAci(aci); + if (accountPath == null) { + accountPath = accountsStore.getPathByNumber(number); + } + if (accountPath != null + && SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath) + && !canRelinkExistingAccount(accountPath)) { + throw new UserAlreadyExistsException(number, SignalAccount.getFileName(pathConfig.dataPath(), accountPath)); + } + if (accountPath == null) { + accountPath = accountsStore.addAccount(number, aci); + } else { + accountsStore.updateAccount(accountPath, number, aci); } var encryptedDeviceName = deviceName == null ? null - : DeviceNameUtil.encryptDeviceName(deviceName, ret.getIdentity().getPrivateKey()); + : DeviceNameUtil.encryptDeviceName(deviceName, ret.getAciIdentity().getPrivateKey()); logger.debug("Finishing new device registration"); var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(), @@ -114,19 +134,27 @@ public class ProvisioningManagerImpl implements ProvisioningManager { SignalAccount account = null; try { account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.dataPath(), + accountPath, number, - ret.getAci(), + serviceEnvironmentConfig.getType(), + aci, + pni, password, encryptedDeviceName, deviceId, - ret.getIdentity(), + ret.getAciIdentity(), + ret.getPniIdentity(), registrationId, profileKey, TrustNewIdentity.ON_FIRST_USE); ManagerImpl m = null; try { - m = new ManagerImpl(account, pathConfig, serviceEnvironmentConfig, userAgent); + m = new ManagerImpl(account, + pathConfig, + new AccountFileUpdaterImpl(accountsStore, accountPath), + serviceEnvironmentConfig, + userAgent); account = null; logger.debug("Refreshing pre keys"); @@ -161,22 +189,39 @@ public class ProvisioningManagerImpl implements ProvisioningManager { } } - private boolean canRelinkExistingAccount(final String number) throws IOException { + private boolean canRelinkExistingAccount(final String accountPath) throws IOException { final SignalAccount signalAccount; try { - signalAccount = SignalAccount.load(pathConfig.dataPath(), number, false, TrustNewIdentity.ON_FIRST_USE); + signalAccount = SignalAccount.load(pathConfig.dataPath(), + accountPath, + false, + TrustNewIdentity.ON_FIRST_USE); } catch (IOException e) { logger.debug("Account in use or failed to load.", e); return false; + } catch (OverlappingFileLockException e) { + logger.debug("Account in use.", e); + return false; } try (signalAccount) { - if (signalAccount.isMasterDevice()) { - logger.debug("Account is a master device."); + if (signalAccount.isPrimaryDevice()) { + logger.debug("Account is a primary device."); + return false; + } + if (signalAccount.isRegistered() + && signalAccount.getServiceEnvironment() != null + && signalAccount.getServiceEnvironment() != serviceEnvironmentConfig.getType()) { + logger.debug("Account is registered in another environment: {}.", + signalAccount.getServiceEnvironment()); return false; } - final var m = new ManagerImpl(signalAccount, pathConfig, serviceEnvironmentConfig, userAgent); + final var m = new ManagerImpl(signalAccount, + pathConfig, + new AccountFileUpdaterImpl(accountsStore, accountPath), + serviceEnvironmentConfig, + userAgent); try (m) { m.checkAccountState(); } catch (AuthorizationFailedException ignored) {