mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
Implement username links
This commit is contained in:
parent
77f284661b
commit
37c65ca6b4
10 changed files with 214 additions and 24 deletions
|
@ -173,6 +173,10 @@
|
||||||
"name":"org.signal.libsignal.usernames.CannotBeEmptyException",
|
"name":"org.signal.libsignal.usernames.CannotBeEmptyException",
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name":"org.signal.libsignal.usernames.MissingSeparatorException",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"org.signal.libsignal.usernames.NicknameTooLongException",
|
"name":"org.signal.libsignal.usernames.NicknameTooLongException",
|
||||||
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
|
||||||
|
|
|
@ -768,7 +768,7 @@
|
||||||
"allDeclaredFields":true,
|
"allDeclaredFields":true,
|
||||||
"queryAllDeclaredMethods":true,
|
"queryAllDeclaredMethods":true,
|
||||||
"queryAllDeclaredConstructors":true,
|
"queryAllDeclaredConstructors":true,
|
||||||
"methods":[{"name":"username","parameterTypes":[] }]
|
"methods":[{"name":"username","parameterTypes":[] }, {"name":"usernameLink","parameterTypes":[] }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name":"org.asamk.signal.commands.VerifyCommand$VerifyParams",
|
"name":"org.asamk.signal.commands.VerifyCommand$VerifyParams",
|
||||||
|
@ -2594,6 +2594,20 @@
|
||||||
"name":"org.whispersystems.signalservice.internal.push.SenderCertificate$ByteArrayDesieralizer",
|
"name":"org.whispersystems.signalservice.internal.push.SenderCertificate$ByteArrayDesieralizer",
|
||||||
"methods":[{"name":"<init>","parameterTypes":[] }]
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name":"org.whispersystems.signalservice.internal.push.SetUsernameLinkRequestBody",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true,
|
||||||
|
"queryAllDeclaredConstructors":true,
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }, {"name":"getUsernameLinkEncryptedValue","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"org.whispersystems.signalservice.internal.push.SetUsernameLinkResponseBody",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true,
|
||||||
|
"queryAllDeclaredConstructors":true,
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.util.UUID"] }, {"name":"<init>","parameterTypes":["java.util.UUID","int","kotlin.jvm.internal.DefaultConstructorMarker"] }]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name":"org.whispersystems.signalservice.internal.push.StaleDevices",
|
"name":"org.whispersystems.signalservice.internal.push.StaleDevices",
|
||||||
"allDeclaredFields":true,
|
"allDeclaredFields":true,
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
||||||
import org.asamk.signal.manager.api.UpdateGroup;
|
import org.asamk.signal.manager.api.UpdateGroup;
|
||||||
import org.asamk.signal.manager.api.UpdateProfile;
|
import org.asamk.signal.manager.api.UpdateProfile;
|
||||||
import org.asamk.signal.manager.api.UserStatus;
|
import org.asamk.signal.manager.api.UserStatus;
|
||||||
|
import org.asamk.signal.manager.api.UsernameLinkUrl;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
|
@ -100,11 +101,15 @@ public interface Manager extends Closeable {
|
||||||
*/
|
*/
|
||||||
void updateProfile(UpdateProfile updateProfile) throws IOException;
|
void updateProfile(UpdateProfile updateProfile) throws IOException;
|
||||||
|
|
||||||
|
String getUsername();
|
||||||
|
|
||||||
|
UsernameLinkUrl getUsernameLink();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a username for the account.
|
* Set a username for the account.
|
||||||
* If the username is null, it will be deleted.
|
* If the username is null, it will be deleted.
|
||||||
*/
|
*/
|
||||||
String setUsername(String username) throws IOException, InvalidUsernameException;
|
void setUsername(String username) throws IOException, InvalidUsernameException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a username for the account.
|
* Set a username for the account.
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package org.asamk.signal.manager.api;
|
||||||
|
|
||||||
|
import org.signal.core.util.Base64;
|
||||||
|
import org.whispersystems.signalservice.api.push.UsernameLinkComponents;
|
||||||
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public final class UsernameLinkUrl {
|
||||||
|
|
||||||
|
private static final Pattern URL_REGEX = Pattern.compile("(https://)?signal.me/?#eu/([a-zA-Z0-9+\\-_/]+)");
|
||||||
|
|
||||||
|
private static final String BASE_URL = "https://signal.me/#eu/";
|
||||||
|
|
||||||
|
private final String url;
|
||||||
|
private final UsernameLinkComponents usernameLinkComponents;
|
||||||
|
|
||||||
|
public static UsernameLinkUrl fromUri(String url) throws InvalidUsernameLinkException {
|
||||||
|
final var matcher = URL_REGEX.matcher(url);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
throw new InvalidUsernameLinkException("Invalid username link");
|
||||||
|
}
|
||||||
|
final var path = matcher.group(2);
|
||||||
|
final byte[] allBytes;
|
||||||
|
try {
|
||||||
|
allBytes = Base64.decode(path);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InvalidUsernameLinkException("Invalid base64 encoding");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allBytes.length != 48) {
|
||||||
|
throw new InvalidUsernameLinkException("Invalid username link");
|
||||||
|
}
|
||||||
|
|
||||||
|
final var entropy = Arrays.copyOfRange(allBytes, 0, 32);
|
||||||
|
final var serverId = Arrays.copyOfRange(allBytes, 32, allBytes.length);
|
||||||
|
final var serverIdUuid = UuidUtil.parseOrNull(serverId);
|
||||||
|
if (serverIdUuid == null) {
|
||||||
|
throw new InvalidUsernameLinkException("Invalid serverId");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UsernameLinkUrl(new UsernameLinkComponents(entropy, serverIdUuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public UsernameLinkUrl(UsernameLinkComponents usernameLinkComponents) {
|
||||||
|
this.usernameLinkComponents = usernameLinkComponents;
|
||||||
|
this.url = createUrl(usernameLinkComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String createUrl(UsernameLinkComponents usernameLinkComponents) {
|
||||||
|
final var entropy = usernameLinkComponents.getEntropy();
|
||||||
|
final var serverId = UuidUtil.toByteArray(usernameLinkComponents.getServerId());
|
||||||
|
|
||||||
|
final var combined = new byte[entropy.length + serverId.length];
|
||||||
|
System.arraycopy(entropy, 0, combined, 0, entropy.length);
|
||||||
|
System.arraycopy(serverId, 0, combined, entropy.length, serverId.length);
|
||||||
|
|
||||||
|
final var base64 = Base64.encodeUrlSafeWithoutPadding(combined);
|
||||||
|
return BASE_URL + base64;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UsernameLinkComponents getComponents() {
|
||||||
|
return usernameLinkComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class InvalidUsernameLinkException extends Exception {
|
||||||
|
|
||||||
|
public InvalidUsernameLinkException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidUsernameLinkException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,9 @@ import org.whispersystems.signalservice.api.push.SignedPreKeyEntity;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.AlreadyVerifiedException;
|
import org.whispersystems.signalservice.api.push.exceptions.AlreadyVerifiedException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
|
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException;
|
import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException;
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotReservedException;
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.UsernameMalformedException;
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.UsernameTakenException;
|
||||||
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
|
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
|
||||||
import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity;
|
import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity;
|
||||||
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage;
|
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage;
|
||||||
|
@ -98,6 +101,13 @@ public class AccountHelper {
|
||||||
&& account.getRegistrationLockPin() != null) {
|
&& account.getRegistrationLockPin() != null) {
|
||||||
migrateRegistrationPin();
|
migrateRegistrationPin();
|
||||||
}
|
}
|
||||||
|
if (account.getUsername() != null && account.getUsernameLink() == null) {
|
||||||
|
try {
|
||||||
|
tryToSetUsernameLink(new Username(account.getUsername()));
|
||||||
|
} catch (BaseUsernameException e) {
|
||||||
|
logger.debug("Invalid local username");
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (DeprecatedVersionException e) {
|
} catch (DeprecatedVersionException e) {
|
||||||
logger.debug("Signal-Server returned deprecated version exception", e);
|
logger.debug("Signal-Server returned deprecated version exception", e);
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -305,13 +315,13 @@ public class AccountHelper {
|
||||||
public static final int USERNAME_MIN_LENGTH = 3;
|
public static final int USERNAME_MIN_LENGTH = 3;
|
||||||
public static final int USERNAME_MAX_LENGTH = 32;
|
public static final int USERNAME_MAX_LENGTH = 32;
|
||||||
|
|
||||||
public String reserveUsername(String nickname) throws IOException, BaseUsernameException {
|
public void reserveUsername(String nickname) throws IOException, BaseUsernameException {
|
||||||
final var currentUsername = account.getUsername();
|
final var currentUsername = account.getUsername();
|
||||||
if (currentUsername != null) {
|
if (currentUsername != null) {
|
||||||
final var currentNickname = currentUsername.substring(0, currentUsername.indexOf('.'));
|
final var currentNickname = currentUsername.substring(0, currentUsername.indexOf('.'));
|
||||||
if (currentNickname.equals(nickname)) {
|
if (currentNickname.equals(nickname)) {
|
||||||
refreshCurrentUsername();
|
refreshCurrentUsername();
|
||||||
return currentUsername;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,14 +339,13 @@ public class AccountHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("[reserveUsername] Successfully reserved username.");
|
logger.debug("[reserveUsername] Successfully reserved username.");
|
||||||
final var username = candidates.get(hashIndex).getUsername();
|
final var username = candidates.get(hashIndex);
|
||||||
|
|
||||||
dependencies.getAccountManager().confirmUsername(username, response);
|
dependencies.getAccountManager().confirmUsername(username.getUsername(), response);
|
||||||
account.setUsername(username);
|
account.setUsername(username.getUsername());
|
||||||
account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress());
|
account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress());
|
||||||
logger.debug("[confirmUsername] Successfully confirmed username.");
|
logger.debug("[confirmUsername] Successfully confirmed username.");
|
||||||
|
tryToSetUsernameLink(username);
|
||||||
return username;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshCurrentUsername() throws IOException, BaseUsernameException {
|
public void refreshCurrentUsername() throws IOException, BaseUsernameException {
|
||||||
|
@ -348,7 +357,8 @@ public class AccountHelper {
|
||||||
final var whoAmIResponse = dependencies.getAccountManager().getWhoAmI();
|
final var whoAmIResponse = dependencies.getAccountManager().getWhoAmI();
|
||||||
final var serverUsernameHash = whoAmIResponse.getUsernameHash();
|
final var serverUsernameHash = whoAmIResponse.getUsernameHash();
|
||||||
final var hasServerUsername = !isEmpty(serverUsernameHash);
|
final var hasServerUsername = !isEmpty(serverUsernameHash);
|
||||||
final var localUsernameHash = Base64.encodeUrlSafeWithoutPadding(new Username(localUsername).getHash());
|
final var username = new Username(localUsername);
|
||||||
|
final var localUsernameHash = Base64.encodeUrlSafeWithoutPadding(username.getHash());
|
||||||
|
|
||||||
if (!hasServerUsername) {
|
if (!hasServerUsername) {
|
||||||
logger.debug("No remote username is set.");
|
logger.debug("No remote username is set.");
|
||||||
|
@ -360,17 +370,40 @@ public class AccountHelper {
|
||||||
|
|
||||||
if (!hasServerUsername || !Objects.equals(localUsernameHash, serverUsernameHash)) {
|
if (!hasServerUsername || !Objects.equals(localUsernameHash, serverUsernameHash)) {
|
||||||
logger.debug("Attempting to resynchronize username.");
|
logger.debug("Attempting to resynchronize username.");
|
||||||
tryReserveConfirmUsername(localUsername, localUsernameHash);
|
try {
|
||||||
|
tryReserveConfirmUsername(username);
|
||||||
|
} catch (UsernameMalformedException | UsernameTakenException | UsernameIsNotReservedException e) {
|
||||||
|
logger.debug("[confirmUsername] Failed to reserve confirm username: {} ({})",
|
||||||
|
e.getMessage(),
|
||||||
|
e.getClass().getSimpleName());
|
||||||
|
account.setUsername(null);
|
||||||
|
account.setUsernameLink(null);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Username already set, not refreshing.");
|
logger.debug("Username already set, not refreshing.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryReserveConfirmUsername(final String username, String localUsernameHash) throws IOException {
|
private void tryReserveConfirmUsername(final Username username) throws IOException {
|
||||||
final var response = dependencies.getAccountManager().reserveUsername(List.of(localUsernameHash));
|
final var response = dependencies.getAccountManager()
|
||||||
|
.reserveUsername(List.of(Base64.encodeUrlSafeWithoutPadding(username.getHash())));
|
||||||
logger.debug("[reserveUsername] Successfully reserved existing username.");
|
logger.debug("[reserveUsername] Successfully reserved existing username.");
|
||||||
dependencies.getAccountManager().confirmUsername(username, response);
|
dependencies.getAccountManager().confirmUsername(username.getUsername(), response);
|
||||||
logger.debug("[confirmUsername] Successfully confirmed existing username.");
|
logger.debug("[confirmUsername] Successfully confirmed existing username.");
|
||||||
|
tryToSetUsernameLink(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryToSetUsernameLink(Username username) {
|
||||||
|
for (var i = 1; i < 4; i++) {
|
||||||
|
try {
|
||||||
|
final var linkComponents = dependencies.getAccountManager().createUsernameLink(username);
|
||||||
|
account.setUsernameLink(linkComponents);
|
||||||
|
break;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.debug("[tryToSetUsernameLink] Failed with IOException on attempt {}/3", i, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteUsername() throws IOException {
|
public void deleteUsername() throws IOException {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
import org.asamk.signal.manager.api.RecipientIdentifier;
|
import org.asamk.signal.manager.api.RecipientIdentifier;
|
||||||
import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
||||||
|
import org.asamk.signal.manager.api.UsernameLinkUrl;
|
||||||
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
|
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
|
||||||
import org.asamk.signal.manager.internal.SignalDependencies;
|
import org.asamk.signal.manager.internal.SignalDependencies;
|
||||||
import org.asamk.signal.manager.storage.SignalAccount;
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
|
@ -93,10 +94,23 @@ public class RecipientHelper {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (recipient instanceof RecipientIdentifier.Username usernameRecipient) {
|
} else if (recipient instanceof RecipientIdentifier.Username usernameRecipient) {
|
||||||
final var username = usernameRecipient.username();
|
var username = usernameRecipient.username();
|
||||||
return account.getRecipientStore().resolveRecipientByUsername(username, () -> {
|
|
||||||
try {
|
try {
|
||||||
return getRegisteredUserByUsername(username);
|
UsernameLinkUrl usernameLinkUrl = UsernameLinkUrl.fromUri(username);
|
||||||
|
final var components = usernameLinkUrl.getComponents();
|
||||||
|
final var encryptedUsername = dependencies.getAccountManager()
|
||||||
|
.getEncryptedUsernameFromLinkServerId(components.getServerId());
|
||||||
|
final var link = new Username.UsernameLink(components.getEntropy(), encryptedUsername);
|
||||||
|
|
||||||
|
username = Username.fromLink(link).getUsername();
|
||||||
|
} catch (UsernameLinkUrl.InvalidUsernameLinkException e) {
|
||||||
|
} catch (IOException | BaseUsernameException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
final String finalUsername = username;
|
||||||
|
return account.getRecipientStore().resolveRecipientByUsername(finalUsername, () -> {
|
||||||
|
try {
|
||||||
|
return getRegisteredUserByUsername(finalUsername);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
||||||
import org.asamk.signal.manager.api.UpdateGroup;
|
import org.asamk.signal.manager.api.UpdateGroup;
|
||||||
import org.asamk.signal.manager.api.UpdateProfile;
|
import org.asamk.signal.manager.api.UpdateProfile;
|
||||||
import org.asamk.signal.manager.api.UserStatus;
|
import org.asamk.signal.manager.api.UserStatus;
|
||||||
|
import org.asamk.signal.manager.api.UsernameLinkUrl;
|
||||||
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
|
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
|
||||||
import org.asamk.signal.manager.helper.AccountFileUpdater;
|
import org.asamk.signal.manager.helper.AccountFileUpdater;
|
||||||
import org.asamk.signal.manager.helper.Context;
|
import org.asamk.signal.manager.helper.Context;
|
||||||
|
@ -332,9 +333,19 @@ public class ManagerImpl implements Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String setUsername(final String username) throws IOException, InvalidUsernameException {
|
public String getUsername() {
|
||||||
|
return account.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UsernameLinkUrl getUsernameLink() {
|
||||||
|
return new UsernameLinkUrl(account.getUsernameLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsername(final String username) throws IOException, InvalidUsernameException {
|
||||||
try {
|
try {
|
||||||
return context.getAccountHelper().reserveUsername(username);
|
context.getAccountHelper().reserveUsername(username);
|
||||||
} catch (BaseUsernameException e) {
|
} catch (BaseUsernameException e) {
|
||||||
throw new InvalidUsernameException(e.getMessage() + " (" + e.getClass().getSimpleName() + ")", e);
|
throw new InvalidUsernameException(e.getMessage() + " (" + e.getClass().getSimpleName() + ")", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ import org.whispersystems.signalservice.api.push.ServiceId.ACI;
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId.PNI;
|
import org.whispersystems.signalservice.api.push.ServiceId.PNI;
|
||||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.api.push.UsernameLinkComponents;
|
||||||
import org.whispersystems.signalservice.api.storage.SignalStorageManifest;
|
import org.whispersystems.signalservice.api.storage.SignalStorageManifest;
|
||||||
import org.whispersystems.signalservice.api.storage.StorageKey;
|
import org.whispersystems.signalservice.api.storage.StorageKey;
|
||||||
import org.whispersystems.signalservice.api.util.CredentialsProvider;
|
import org.whispersystems.signalservice.api.util.CredentialsProvider;
|
||||||
|
@ -129,6 +130,7 @@ public class SignalAccount implements Closeable {
|
||||||
private ServiceEnvironment serviceEnvironment;
|
private ServiceEnvironment serviceEnvironment;
|
||||||
private String number;
|
private String number;
|
||||||
private String username;
|
private String username;
|
||||||
|
private UsernameLinkComponents usernameLink;
|
||||||
private String encryptedDeviceName;
|
private String encryptedDeviceName;
|
||||||
private int deviceId = 0;
|
private int deviceId = 0;
|
||||||
private String password;
|
private String password;
|
||||||
|
@ -1278,6 +1280,14 @@ public class SignalAccount implements Closeable {
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UsernameLinkComponents getUsernameLink() {
|
||||||
|
return usernameLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsernameLink(final UsernameLinkComponents usernameLink) {
|
||||||
|
this.usernameLink = usernameLink;
|
||||||
|
}
|
||||||
|
|
||||||
public ServiceEnvironment getServiceEnvironment() {
|
public ServiceEnvironment getServiceEnvironment() {
|
||||||
return serviceEnvironment;
|
return serviceEnvironment;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,15 @@ public class UpdateAccountCommand implements JsonRpcLocalCommand {
|
||||||
var username = ns.getString("username");
|
var username = ns.getString("username");
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
try {
|
try {
|
||||||
final var newUsername = m.setUsername(username);
|
m.setUsername(username);
|
||||||
|
final var newUsername = m.getUsername();
|
||||||
|
final var newUsernameLink = m.getUsernameLink();
|
||||||
switch (outputWriter) {
|
switch (outputWriter) {
|
||||||
case PlainTextWriter w -> w.println("Your new username: {}", newUsername);
|
case PlainTextWriter w -> w.println("Your new username: {} ({})",
|
||||||
case JsonWriter w -> w.write(new JsonAccountResponse(newUsername));
|
newUsername,
|
||||||
|
newUsernameLink == null ? "-" : newUsernameLink.getUrl());
|
||||||
|
case JsonWriter w -> w.write(new JsonAccountResponse(newUsername,
|
||||||
|
newUsernameLink == null ? null : newUsernameLink.getUrl()));
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IOErrorException("Failed to set username: " + e.getMessage(), e);
|
throw new IOErrorException("Failed to set username: " + e.getMessage(), e);
|
||||||
|
@ -72,6 +77,7 @@ public class UpdateAccountCommand implements JsonRpcLocalCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
private record JsonAccountResponse(
|
private record JsonAccountResponse(
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL) String username
|
@JsonInclude(JsonInclude.Include.NON_NULL) String username,
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) String usernameLink
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.asamk.signal.manager.api.UnregisteredRecipientException;
|
||||||
import org.asamk.signal.manager.api.UpdateGroup;
|
import org.asamk.signal.manager.api.UpdateGroup;
|
||||||
import org.asamk.signal.manager.api.UpdateProfile;
|
import org.asamk.signal.manager.api.UpdateProfile;
|
||||||
import org.asamk.signal.manager.api.UserStatus;
|
import org.asamk.signal.manager.api.UserStatus;
|
||||||
|
import org.asamk.signal.manager.api.UsernameLinkUrl;
|
||||||
import org.freedesktop.dbus.DBusMap;
|
import org.freedesktop.dbus.DBusMap;
|
||||||
import org.freedesktop.dbus.DBusPath;
|
import org.freedesktop.dbus.DBusPath;
|
||||||
import org.freedesktop.dbus.connections.impl.DBusConnection;
|
import org.freedesktop.dbus.connections.impl.DBusConnection;
|
||||||
|
@ -164,7 +165,17 @@ public class DbusManagerImpl implements Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String setUsername(final String username) throws IOException, InvalidUsernameException {
|
public String getUsername() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UsernameLinkUrl getUsernameLink() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsername(final String username) throws IOException, InvalidUsernameException {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue