Compare commits

...

4 Commits

Author SHA1 Message Date
AsamK
fca4d7459c Clear verification sessionId after registration/changeNumber
Fixes #1882
2025-12-05 21:28:36 +01:00
AsamK
ad2338b898 Add documentation for polls
Closes #1885
2025-12-05 21:20:53 +01:00
AsamK
c237f98044 Mark legacy accounts without ACI as unregistered 2025-12-05 20:08:07 +01:00
AsamK
87945ac506 Ignore authorization failed errors in multi account mode
Fixes #1884
2025-12-05 20:08:07 +01:00
7 changed files with 121 additions and 20 deletions

View File

@ -139,6 +139,10 @@
{
"name":"org.signal.libsignal.net.ChatService$ResponseAndDebugInfo"
},
{
"name":"org.signal.libsignal.net.ChatServiceException",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]
},
{
"name":"org.signal.libsignal.net.DeviceDeregisteredException",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }]

View File

@ -296,6 +296,11 @@
"allDeclaredConstructors":true,
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
},
{
"name":"java.lang.NullPointerException",
"queryAllPublicConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"java.lang.Number",
"allDeclaredFields":true,
@ -3063,7 +3068,6 @@
{
"name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord",
"allDeclaredFields":true,
"fields":[{"name":"avatarColor"}, {"name":"avatarUrlPath"}, {"name":"backupSubscriberData"}, {"name":"backupTier"}, {"name":"backupTierHistory"}, {"name":"displayBadgesOnProfile"}, {"name":"familyName"}, {"name":"givenName"}, {"name":"hasBackup"}, {"name":"hasCompletedUsernameOnboarding"}, {"name":"hasSeenGroupStoryEducationSheet"}, {"name":"hasSetMyStoriesPrivacy"}, {"name":"hasViewedOnboardingStory"}, {"name":"keepMutedChatsArchived"}, {"name":"linkPreviews"}, {"name":"noteToSelfArchived"}, {"name":"noteToSelfMarkedUnread"}, {"name":"notificationProfileManualOverride"}, {"name":"payments"}, {"name":"phoneNumberSharingMode"}, {"name":"pinnedConversations"}, {"name":"preferContactAvatars"}, {"name":"preferredReactionEmoji"}, {"name":"primarySendsSms"}, {"name":"profileKey"}, {"name":"readReceipts"}, {"name":"sealedSenderIndicators"}, {"name":"storiesDisabled"}, {"name":"storyViewReceiptsEnabled"}, {"name":"subscriberCurrencyCode"}, {"name":"subscriberId"}, {"name":"subscriptionManuallyCancelled"}, {"name":"typingIndicators"}, {"name":"universalExpireTimer"}, {"name":"unlistedPhoneNumber"}, {"name":"username"}, {"name":"usernameLink"}],
"methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }]
},
{
@ -3102,7 +3106,6 @@
{
"name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord",
"allDeclaredFields":true,
"fields":[{"name":"aci"}, {"name":"archived"}, {"name":"avatarColor"}, {"name":"blocked"}, {"name":"e164"}, {"name":"familyName"}, {"name":"givenName"}, {"name":"hidden"}, {"name":"hideStory"}, {"name":"identityKey"}, {"name":"identityState"}, {"name":"markedUnread"}, {"name":"mutedUntilTimestamp"}, {"name":"nickname"}, {"name":"note"}, {"name":"pni"}, {"name":"pniSignatureVerified"}, {"name":"profileKey"}, {"name":"systemFamilyName"}, {"name":"systemGivenName"}, {"name":"systemNickname"}, {"name":"unregisteredAtTimestamp"}, {"name":"username"}, {"name":"whitelisted"}],
"methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }]
},
{

View File

@ -64,7 +64,7 @@ public class SignalAccountFiles {
return accountsStore.getAllNumbers();
}
public MultiAccountManager initMultiAccountManager() throws IOException, AccountCheckException {
public MultiAccountManager initMultiAccountManager() throws IOException {
final var managerPairs = accountsStore.getAllAccounts().parallelStream().map(a -> {
try {
return new Pair<Manager, Throwable>(initManager(a.number(), a.path()), null);
@ -80,12 +80,13 @@ public class SignalAccountFiles {
for (final var pair : managerPairs) {
if (pair.second() instanceof IOException e) {
throw e;
} else if (pair.second() instanceof AccountCheckException e) {
throw e;
}
}
final var managers = managerPairs.stream().map(Pair::first).toList();
final var managers = managerPairs.stream()
.filter(p -> p != null && p.first() != null)
.map(Pair::first)
.toList();
return new MultiAccountManagerImpl(managers, this);
}
@ -132,7 +133,7 @@ public class SignalAccountFiles {
manager.checkAccountState();
} catch (DeprecatedVersionException e) {
manager.close();
throw new AccountCheckException("signal-cli version is too old for the Signal-Server, please update.");
throw new IOException("signal-cli version is too old for the Signal-Server, please update.");
} catch (IOException e) {
manager.close();
throw new AccountCheckException("Error while checking account " + number + ": " + e.getMessage(), e);

View File

@ -82,16 +82,9 @@ public class AccountHelper {
}
public void checkAccountState() throws IOException {
if (account.getLastReceiveTimestamp() == 0) {
logger.info("The Signal protocol expects that incoming messages are regularly received.");
} else {
var diffInMilliseconds = System.currentTimeMillis() - account.getLastReceiveTimestamp();
long days = TimeUnit.DAYS.convert(diffInMilliseconds, TimeUnit.MILLISECONDS);
if (days > 7) {
logger.warn(
"Messages have been last received {} days ago. The Signal protocol expects that incoming messages are regularly received.",
days);
}
if (account.getAci() == null) {
account.setRegistered(false);
throw new IOException("Account without ACI");
}
try {
updateAccountAttributes();
@ -100,7 +93,7 @@ public class AccountHelper {
} else {
context.getPreKeyHelper().refreshPreKeysIfNecessary();
}
if (account.getAci() == null || account.getPni() == null) {
if (account.getPni() == null) {
checkWhoAmiI();
}
if (!account.isPrimaryDevice() && account.getPniIdentityKeyPair() == null) {
@ -125,6 +118,17 @@ public class AccountHelper {
account.setRegistered(false);
throw e;
}
if (account.getLastReceiveTimestamp() == 0) {
logger.info("The Signal protocol expects that incoming messages are regularly received.");
} else {
var diffInMilliseconds = System.currentTimeMillis() - account.getLastReceiveTimestamp();
long days = TimeUnit.DAYS.convert(diffInMilliseconds, TimeUnit.MILLISECONDS);
if (days > 7) {
logger.warn(
"Messages have been last received {} days ago. The Signal protocol expects that incoming messages are regularly received.",
days);
}
}
}
public void checkWhoAmiI() throws IOException {
@ -311,6 +315,7 @@ public class AccountHelper {
Utils.mapKeys(pniRegistrationIds, Object::toString))));
});
account.clearSessionId();
final var updatePni = PNI.parseOrThrow(result.first().getPni());
if (updatePni.equals(account.getPni())) {
logger.debug("PNI is unchanged after change number");

View File

@ -376,6 +376,7 @@ public class SignalAccount implements Closeable {
trustSelfIdentity(ServiceIdType.ACI);
trustSelfIdentity(ServiceIdType.PNI);
getKeyValueStore().storeEntry(lastRecipientsRefresh, null);
clearSessionId();
}
public void initDatabase() {
@ -1485,6 +1486,12 @@ public class SignalAccount implements Closeable {
keyValueStore.storeEntry(verificationSessionId, sessionId);
}
public void clearSessionId() {
final var keyValueStore = getKeyValueStore();
keyValueStore.storeEntry(verificationSessionNumber, null);
keyValueStore.storeEntry(verificationSessionId, null);
}
public void setEncryptedDeviceName(final String encryptedDeviceName) {
this.encryptedDeviceName = encryptedDeviceName;
save();

View File

@ -385,6 +385,89 @@ Clear session state and send end session message.
*--edit-timestamp*::
Specify the timestamp of a previous message with the recipient or group to send an edited message.
=== sendPollCreate
Send a poll create message to another user or group.
RECIPIENT::
Specify the recipients.
*--note-to-self*::
Send the message to self.
*--notify-self*::
If self is part of recipients/groups send a normal message, not a sync message.
*-g* GROUP, *--group-id* GROUP::
Specify the recipient group ID in base64 encoding.
*-u* USERNAME, *--username* USERNAME::
Specify the recipient username or username link.
*-q* QUESTION, *--question* QUESTION::
Specify the poll question.
*--no-multi*::
Allow only one option to be selected by each recipient.
By default, recipients can select multiple options.
*-o* OPTION [OPTION ...], *--option* OPTION [OPTION ...]*::
The options for the poll.
=== sendPollVote
Send a poll vote message to another user or group, to vote on a previously created vote.
RECIPIENT::
Specify the recipients.
*--note-to-self*::
Send the message to self.
*--notify-self*::
If self is part of recipients/groups send a normal message, not a sync message.
*-g* GROUP, *--group-id* GROUP::
Specify the recipient group ID in base64 encoding.
*-u* USERNAME, *--username* USERNAME::
Specify the recipient username or username link.
*--poll-author* POLL_AUTHOR::
Specify the number or uuid of the author of the poll message.
*--poll-timestamp* POLL_TIMESTAMP::
Specify the timestamp of the original poll create message.
*-o* [OPTION [OPTION ...]], *--option* [OPTION [OPTION ...]]::
The option indexes of the poll to vote for.
For none multi polls, only one option may be specified.
*--vote-count: VOTE_COUNT::
Specify the number of this vote (increase by one for every time you vote).
=== sendPollTerminate
Send a poll terminate message to another user or group, to close on a previously created vote.
RECIPIENT::
Specify the recipients.
*--note-to-self*::
Send the message to self.
*--notify-self*::
If self is part of recipients/groups send a normal message, not a sync message.
*-g* GROUP, *--group-id* GROUP::
Specify the recipient group ID in base64 encoding.
*-u* USERNAME, *--username* USERNAME::
Specify the recipient username or username link.
*--poll-timestamp* POLL_TIMESTAMP::
Specify the timestamp of the original poll create message.
=== sendMessageRequestResponse
Send response to a message request to linked devices.

View File

@ -291,8 +291,6 @@ public class App {
commandHandler.handleMultiLocalCommand(command, multiAccountManager);
} catch (IOException e) {
throw new IOErrorException("Failed to load local accounts file", e);
} catch (AccountCheckException e) {
throw new UnexpectedErrorException("Failed to load account file", e);
}
}