Compare commits

...

8 Commits

Author SHA1 Message Date
Brian Exelbierd
fe2ef88ff1
Merge 6a12e3d0af30e18dc232e3c9218aa2e80c6a4454 into f1fa2eba1db105579ca73e7b54fe0edd7abca49a 2026-01-24 15:26:37 +01:00
AsamK
f1fa2eba1d Bump version to 0.13.23 2026-01-24 14:21:40 +01:00
AsamK
ccd58bbf23 Use graalvm-community 2026-01-24 14:21:40 +01:00
AsamK
54700d9cd0 Update libsignal-service 2026-01-24 12:36:29 +01:00
AsamK
984ea47f9d Update gradle 2026-01-24 12:36:29 +01:00
AsamK
9458972d15 Update dependencies 2026-01-24 12:36:29 +01:00
AsamK
8eb9662694 Implement new updateDevice command to update device names
Fixes #1906
2026-01-23 20:00:25 +01:00
Benjamin Loison
a9be1aa608 Remove an unnecessary space in README.md 2026-01-23 19:37:26 +01:00
27 changed files with 9516 additions and 102 deletions

View File

@ -58,7 +58,8 @@ jobs:
- uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1
with:
version: 'latest'
distribution: 'graalvm-community'
version: '21.0.2'
java-version: '21'
cache: 'gradle'
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,6 +1,21 @@
# Changelog
## [Unreleased]
## [0.13.23] - 2026-01-24
Requires libsignal-client version 0.86.12.
### Added
- Add sendPollCreate, sendPollVote, sendPollTerminate commands for polls
- Add updateDevice command to set device name of linked devices
### Changed
- Allow updating contact names from linked devices
### Fixed
- Start multi account mode even if some accounts have authorization failures
### Added

View File

@ -8,7 +8,7 @@ For registering you need a phone number where you can receive SMS or incoming ca
signal-cli is primarily intended to be used on servers to notify admins of important events.
For this use-case, it has a daemon mode with JSON-RPC interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-jsonrpc.5.adoc))
and D-BUS interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)) .
and D-BUS interface ([man page](https://github.com/AsamK/signal-cli/blob/master/man/signal-cli-dbus.5.adoc)).
For the JSON-RPC interface there's also a simple [example client](https://github.com/AsamK/signal-cli/tree/master/client), written in Rust.
signal-cli needs to be kept up-to-date to keep up with Signal-Server changes.

View File

@ -3,12 +3,12 @@ plugins {
application
eclipse
`check-lib-versions`
id("org.graalvm.buildtools.native") version "0.11.3"
id("org.graalvm.buildtools.native") version "0.11.4"
}
allprojects {
group = "org.asamk"
version = "0.13.23-SNAPSHOT"
version = "0.13.23"
}
java {

View File

@ -45,6 +45,9 @@
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
<releases>
<release version="0.13.23" date="2026-01-24">
<url type="details">https://github.com/AsamK/signal-cli/releases/tag/v0.13.23</url>
</release>
<release version="0.13.22" date="2025-11-14">
<url type="details">https://github.com/AsamK/signal-cli/releases/tag/v0.13.22</url>
</release>

View File

@ -121,7 +121,7 @@
},
{
"name":"org.signal.libsignal.net.ChatConnection$ListenerBridge",
"methods":[{"name":"onConnectionInterrupted","parameterTypes":["java.lang.Throwable"] }, {"name":"onIncomingMessage","parameterTypes":["byte[]","long","long"] }, {"name":"onQueueEmpty","parameterTypes":[] }, {"name":"onReceivedAlerts","parameterTypes":["java.lang.String[]"] }]
"methods":[{"name":"connectionInterrupted","parameterTypes":["java.lang.Throwable"] }, {"name":"onConnectionInterrupted","parameterTypes":["java.lang.Throwable"] }, {"name":"onIncomingMessage","parameterTypes":["byte[]","long","long"] }, {"name":"onQueueEmpty","parameterTypes":[] }, {"name":"onReceivedAlerts","parameterTypes":["java.lang.String[]"] }, {"name":"receivedAlerts","parameterTypes":["java.lang.String[]"] }, {"name":"receivedIncomingMessage","parameterTypes":["byte[]","long","long"] }, {"name":"receivedQueueEmpty","parameterTypes":[] }]
},
{
"name":"org.signal.libsignal.net.ChatConnection$Response",

File diff suppressed because it is too large Load Diff

View File

@ -1832,6 +1832,15 @@
"name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.kdf.HKDF$Mappings"
},
{
"name":"org.bouncycastle.jcajce.provider.kdf.PBEPBKDF2$Mappings"
},
{
"name":"org.bouncycastle.jcajce.provider.kdf.SCRYPT$Mappings"
},
{
"name":"org.bouncycastle.jcajce.provider.keystore.BC$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
@ -1919,6 +1928,10 @@
"name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.HKDF$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
@ -2172,199 +2185,199 @@
"queryAllDeclaredConstructors":true
},
{
"name":"org.signal.storageservice.protos.groups.AccessControl",
"name":"org.signal.storageservice.storage.protos.groups.AccessControl",
"fields":[{"name":"addFromInviteLink_"}, {"name":"attributes_"}, {"name":"members_"}]
},
{
"name":"org.signal.storageservice.protos.groups.AvatarUploadAttributes",
"name":"org.signal.storageservice.storage.protos.groups.AvatarUploadAttributes",
"fields":[{"name":"acl_"}, {"name":"algorithm_"}, {"name":"credential_"}, {"name":"date_"}, {"name":"key_"}, {"name":"policy_"}, {"name":"signature_"}]
},
{
"name":"org.signal.storageservice.protos.groups.BannedMember",
"name":"org.signal.storageservice.storage.protos.groups.BannedMember",
"fields":[{"name":"timestamp_"}, {"name":"userId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.Group",
"name":"org.signal.storageservice.storage.protos.groups.Group",
"fields":[{"name":"accessControl_"}, {"name":"announcementsOnly_"}, {"name":"avatar_"}, {"name":"bannedMembers_"}, {"name":"description_"}, {"name":"disappearingMessagesTimer_"}, {"name":"inviteLinkPassword_"}, {"name":"members_"}, {"name":"pendingMembers_"}, {"name":"publicKey_"}, {"name":"requestingMembers_"}, {"name":"revision_"}, {"name":"title_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupAttributeBlob",
"name":"org.signal.storageservice.storage.protos.groups.GroupAttributeBlob",
"fields":[{"name":"contentCase_"}, {"name":"content_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange",
"fields":[{"name":"actions_"}, {"name":"changeEpoch_"}, {"name":"serverSignature_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions",
"fields":[{"name":"addBannedMembers_"}, {"name":"addMembers_"}, {"name":"addPendingMembers_"}, {"name":"addRequestingMembers_"}, {"name":"deleteBannedMembers_"}, {"name":"deleteMembers_"}, {"name":"deletePendingMembers_"}, {"name":"deleteRequestingMembers_"}, {"name":"modifyAddFromInviteLinkAccess_"}, {"name":"modifyAnnouncementsOnly_"}, {"name":"modifyAttributesAccess_"}, {"name":"modifyAvatar_"}, {"name":"modifyDescription_"}, {"name":"modifyDisappearingMessagesTimer_"}, {"name":"modifyInviteLinkPassword_"}, {"name":"modifyMemberAccess_"}, {"name":"modifyMemberProfileKeys_"}, {"name":"modifyMemberRoles_"}, {"name":"modifyTitle_"}, {"name":"promotePendingMembers_"}, {"name":"promotePendingPniAciMembers_"}, {"name":"promoteRequestingMembers_"}, {"name":"revision_"}, {"name":"sourceServiceId_"}, {"name":"sourceUuid_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddBannedMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$AddBannedMemberAction",
"fields":[{"name":"added_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$AddMemberAction",
"fields":[{"name":"added_"}, {"name":"joinFromInviteLink_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddPendingMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$AddPendingMemberAction",
"fields":[{"name":"added_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$AddRequestingMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$AddRequestingMemberAction",
"fields":[{"name":"added_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteBannedMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$DeleteBannedMemberAction",
"fields":[{"name":"deletedUserId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$DeleteMemberAction",
"fields":[{"name":"deletedUserId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeletePendingMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$DeletePendingMemberAction",
"fields":[{"name":"deletedUserId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$DeleteRequestingMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$DeleteRequestingMemberAction",
"fields":[{"name":"deletedUserId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAddFromInviteLinkAccessControlAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyAddFromInviteLinkAccessControlAction",
"fields":[{"name":"addFromInviteLinkAccess_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAnnouncementsOnlyAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyAnnouncementsOnlyAction",
"fields":[{"name":"announcementsOnly_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAttributesAccessControlAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyAttributesAccessControlAction",
"fields":[{"name":"attributesAccess_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyAvatarAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyAvatarAction",
"fields":[{"name":"avatar_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyDescriptionAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyDescriptionAction",
"fields":[{"name":"description_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyDisappearingMessagesTimerAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyDisappearingMessagesTimerAction",
"fields":[{"name":"timer_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyInviteLinkPasswordAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyInviteLinkPasswordAction",
"fields":[{"name":"inviteLinkPassword_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberProfileKeyAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyMemberProfileKeyAction",
"fields":[{"name":"presentation_"}, {"name":"profileKey_"}, {"name":"userId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMemberRoleAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyMemberRoleAction",
"fields":[{"name":"role_"}, {"name":"userId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyMembersAccessControlAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyMembersAccessControlAction",
"fields":[{"name":"membersAccess_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$ModifyTitleAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$ModifyTitleAction",
"fields":[{"name":"title_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$PromotePendingMemberAction",
"fields":[{"name":"presentation_"}, {"name":"profileKey_"}, {"name":"userId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromotePendingPniAciMemberProfileKeyAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$PromotePendingPniAciMemberProfileKeyAction",
"fields":[{"name":"pni_"}, {"name":"presentation_"}, {"name":"profileKey_"}, {"name":"userId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChange$Actions$PromoteRequestingMemberAction",
"name":"org.signal.storageservice.storage.protos.groups.GroupChange$Actions$PromoteRequestingMemberAction",
"fields":[{"name":"role_"}, {"name":"userId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChanges",
"name":"org.signal.storageservice.storage.protos.groups.GroupChanges",
"fields":[{"name":"groupChanges_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupChanges$GroupChangeState",
"name":"org.signal.storageservice.storage.protos.groups.GroupChanges$GroupChangeState",
"fields":[{"name":"groupChange_"}, {"name":"groupState_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupInviteLink",
"name":"org.signal.storageservice.storage.protos.groups.GroupInviteLink",
"fields":[{"name":"contentsCase_"}, {"name":"contents_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupInviteLink$GroupInviteLinkContentsV1",
"name":"org.signal.storageservice.storage.protos.groups.GroupInviteLink$GroupInviteLinkContentsV1",
"fields":[{"name":"groupMasterKey_"}, {"name":"inviteLinkPassword_"}]
},
{
"name":"org.signal.storageservice.protos.groups.GroupJoinInfo",
"name":"org.signal.storageservice.storage.protos.groups.GroupJoinInfo",
"fields":[{"name":"addFromInviteLink_"}, {"name":"avatar_"}, {"name":"description_"}, {"name":"memberCount_"}, {"name":"pendingAdminApproval_"}, {"name":"publicKey_"}, {"name":"revision_"}, {"name":"title_"}]
},
{
"name":"org.signal.storageservice.protos.groups.Member",
"name":"org.signal.storageservice.storage.protos.groups.Member",
"fields":[{"name":"joinedAtRevision_"}, {"name":"presentation_"}, {"name":"profileKey_"}, {"name":"role_"}, {"name":"userId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.PendingMember",
"name":"org.signal.storageservice.storage.protos.groups.PendingMember",
"fields":[{"name":"addedByUserId_"}, {"name":"member_"}, {"name":"timestamp_"}]
},
{
"name":"org.signal.storageservice.protos.groups.RequestingMember",
"name":"org.signal.storageservice.storage.protos.groups.RequestingMember",
"fields":[{"name":"presentation_"}, {"name":"profileKey_"}, {"name":"timestamp_"}, {"name":"userId_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedApproveMember",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedApproveMember",
"fields":[{"name":"role_"}, {"name":"uuid_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedBannedMember",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedBannedMember",
"fields":[{"name":"serviceIdBinary_"}, {"name":"serviceIdBytes_"}, {"name":"timestamp_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedGroup",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedGroup",
"fields":[{"name":"accessControl_"}, {"name":"avatar_"}, {"name":"bannedMembers_"}, {"name":"description_"}, {"name":"disappearingMessagesTimer_"}, {"name":"inviteLinkPassword_"}, {"name":"isAnnouncementGroup_"}, {"name":"members_"}, {"name":"pendingMembers_"}, {"name":"requestingMembers_"}, {"name":"revision_"}, {"name":"title_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedGroupChange",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedGroupChange",
"fields":[{"name":"deleteBannedMembers_"}, {"name":"deleteMembers_"}, {"name":"deletePendingMembers_"}, {"name":"deleteRequestingMembers_"}, {"name":"editorServiceIdBytes_"}, {"name":"editor_"}, {"name":"modifiedProfileKeys_"}, {"name":"modifyMemberRoles_"}, {"name":"newAttributeAccess_"}, {"name":"newAvatar_"}, {"name":"newBannedMembers_"}, {"name":"newDescription_"}, {"name":"newInviteLinkAccess_"}, {"name":"newInviteLinkPassword_"}, {"name":"newIsAnnouncementGroup_"}, {"name":"newMemberAccess_"}, {"name":"newMembers_"}, {"name":"newPendingMembers_"}, {"name":"newRequestingMembers_"}, {"name":"newTimer_"}, {"name":"newTitle_"}, {"name":"promotePendingMembers_"}, {"name":"promotePendingPniAciMembers_"}, {"name":"promoteRequestingMembers_"}, {"name":"revision_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedGroupJoinInfo",
"fields":[{"name":"addFromInviteLink_"}, {"name":"avatar_"}, {"name":"description_"}, {"name":"isAnnouncementGroup_"}, {"name":"memberCount_"}, {"name":"pendingAdminApproval_"}, {"name":"revision_"}, {"name":"title_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedMember",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedMember",
"fields":[{"name":"aciBytes_"}, {"name":"joinedAtRevision_"}, {"name":"pniBytes_"}, {"name":"pni_"}, {"name":"profileKey_"}, {"name":"role_"}, {"name":"uuid_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedModifyMemberRole",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedModifyMemberRole",
"fields":[{"name":"role_"}, {"name":"uuid_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedPendingMember",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedPendingMember",
"fields":[{"name":"addedByAci_"}, {"name":"addedByUuid_"}, {"name":"role_"}, {"name":"serviceIdBinary_"}, {"name":"serviceIdBytes_"}, {"name":"serviceIdCipherText_"}, {"name":"timestamp_"}, {"name":"uuidCipherText_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedPendingMemberRemoval",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedPendingMemberRemoval",
"fields":[{"name":"uuidCipherText_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedRequestingMember",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedRequestingMember",
"fields":[{"name":"aciBytes_"}, {"name":"profileKey_"}, {"name":"timestamp_"}, {"name":"uuid_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedString",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedString",
"fields":[{"name":"value_"}]
},
{
"name":"org.signal.storageservice.protos.groups.local.DecryptedTimer",
"name":"org.signal.storageservice.storage.protos.groups.local.DecryptedTimer",
"fields":[{"name":"duration_"}]
},
{
@ -2426,6 +2439,13 @@
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"<init>","parameterTypes":["java.lang.String","java.lang.String","int","kotlin.jvm.internal.DefaultConstructorMarker"] }]
},
{
"name":"org.whispersystems.signalservice.api.link.SetDeviceNameRequest",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":["java.lang.String"] }, {"name":"getDeviceName","parameterTypes":[] }]
},
{
"name":"org.whispersystems.signalservice.api.messages.calls.HangupMessage",
"allDeclaredFields":true,
@ -3068,6 +3088,7 @@
{
"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":[] }]
},
{
@ -3106,6 +3127,7 @@
{
"name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord",
"allDeclaredFields":true,
"fields":[{"name":"aci"}, {"name":"aciBinary"}, {"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":"pniBinary"}, {"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

@ -1,18 +1,18 @@
[versions]
slf4j = "2.0.17"
junit = "6.0.1"
junit = "6.0.2"
[libraries]
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.82"
bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.83"
jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.20.1"
argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0"
dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0"
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" }
logback = "ch.qos.logback:logback-classic:1.5.21"
logback = "ch.qos.logback:logback-classic:1.5.25"
signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_135"
sqlite = "org.xerial:sqlite-jdbc:3.51.0.0"
signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_136"
sqlite = "org.xerial:sqlite-jdbc:3.51.1.0"
hikari = "com.zaxxer:HikariCP:7.0.2"
junit-jupiter-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -154,6 +154,8 @@ public interface Manager extends Closeable {
List<Device> getLinkedDevices() throws IOException;
void updateLinkedDevice(int deviceId, String name) throws IOException, NotPrimaryDeviceException;
void removeLinkedDevices(int deviceId) throws IOException, NotPrimaryDeviceException;
void addDeviceLink(DeviceLinkUrl linkUri) throws IOException, InvalidDeviceLinkException, NotPrimaryDeviceException, DeviceLimitExceededException;

View File

@ -0,0 +1,20 @@
package org.asamk.signal.manager.actions;
import org.asamk.signal.manager.helper.Context;
public class RetrieveDeviceNameAction implements HandleAction {
private static final RetrieveDeviceNameAction INSTANCE = new RetrieveDeviceNameAction();
public static RetrieveDeviceNameAction create() {
return INSTANCE;
}
private RetrieveDeviceNameAction() {
}
@Override
public void execute(Context context) throws Throwable {
context.getAccountHelper().refreshDeviceName();
}
}

View File

@ -4,8 +4,8 @@ import org.asamk.signal.manager.groups.GroupLinkPassword;
import org.signal.core.util.Base64;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.storageservice.protos.groups.GroupInviteLink;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.storage.protos.groups.GroupInviteLink;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import java.io.IOException;
import java.net.URI;
@ -52,8 +52,8 @@ public final class GroupInviteLinkUrl {
var bytes = Base64.decode(encoding);
GroupInviteLink groupInviteLink = GroupInviteLink.ADAPTER.decode(bytes);
if (groupInviteLink.v1Contents != null) {
var groupInviteLinkContentsV1 = groupInviteLink.v1Contents;
if (groupInviteLink.contentsV1 != null) {
var groupInviteLinkContentsV1 = groupInviteLink.contentsV1;
var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.groupMasterKey.toByteArray());
var password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.inviteLinkPassword.toByteArray());
@ -90,7 +90,7 @@ public final class GroupInviteLinkUrl {
}
private static String createUrl(GroupMasterKey groupMasterKey, GroupLinkPassword password) {
var groupInviteLink = new GroupInviteLink.Builder().v1Contents(new GroupInviteLink.GroupInviteLinkContentsV1.Builder().groupMasterKey(
var groupInviteLink = new GroupInviteLink.Builder().contentsV1(new GroupInviteLink.GroupInviteLinkContentsV1.Builder().groupMasterKey(
ByteString.of(groupMasterKey.serialize()))
.inviteLinkPassword(ByteString.of(password.serialize()))
.build()).build();

View File

@ -522,6 +522,22 @@ public class AccountHelper {
account.setEncryptedDeviceName(encryptedDeviceName);
}
public void setDeviceName(int deviceId, String deviceName) throws IOException {
final var privateKey = account.getAciIdentityKeyPair().getPrivateKey();
final var encryptedDeviceName = DeviceNameUtil.encryptDeviceName(deviceName, privateKey);
handleResponseException(dependencies.getLinkDeviceApi().setDeviceName(encryptedDeviceName, deviceId));
context.getSyncHelper().sendDeviceNameChange(deviceId);
}
public void refreshDeviceName() throws IOException {
final var devices = handleResponseException(dependencies.getLinkDeviceApi().getDevices());
final var deviceId = account.getDeviceId();
final var device = devices.stream().filter(d -> d.id == deviceId).findFirst();
if (device.isPresent()) {
account.setEncryptedDeviceName(device.get().name);
}
}
public void updateAccountAttributes() throws IOException {
handleResponseException(dependencies.getAccountApi().setAccountAttributes(account.getAccountAttributes(null)));
}

View File

@ -34,10 +34,10 @@ import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
import org.signal.libsignal.zkgroup.groupsend.GroupSendEndorsementsResponse;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.signal.storageservice.protos.groups.GroupChangeResponse;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo;
import org.signal.storageservice.storage.protos.groups.GroupChangeResponse;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupJoinInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupChangeLog;
@ -195,9 +195,9 @@ public class GroupHelper {
final GroupInfoV2 groupInfoV2,
final GroupChangeResponse groupChangeResponse
) {
if (groupChangeResponse.groupSendEndorsementsResponse.size() > 0) {
if (groupChangeResponse.group_send_endorsements_response.size() > 0) {
try {
final var groupSendEndorsementsResponse = new GroupSendEndorsementsResponse(groupChangeResponse.groupSendEndorsementsResponse.toByteArray());
final var groupSendEndorsementsResponse = new GroupSendEndorsementsResponse(groupChangeResponse.group_send_endorsements_response.toByteArray());
updateGroupEndorsements(groupInfoV2.getGroupId(),
groupInfoV2.getMasterKey(),
@ -394,7 +394,7 @@ public class GroupHelper {
.joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo);
final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(),
groupJoinInfo.revision + 1,
changeResponse.groupChange == null ? null : changeResponse.groupChange.encode());
changeResponse.group_change == null ? null : changeResponse.group_change.encode());
if (group.getGroup() == null) {
// Only requested member, can't send update to group members
@ -876,10 +876,10 @@ public class GroupHelper {
final var groupChangeResponse = groupGroupChangePair.second();
handleGroupChangeResponse(groupInfoV2, groupChangeResponse);
if (groupChangeResponse.groupChange == null) {
if (groupChangeResponse.group_change == null) {
throw new AssertionError("groupChange is null");
}
var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupChangeResponse.groupChange.encode());
var messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupChangeResponse.group_change.encode());
return sendGroupMessage(messageBuilder,
groupInfoV2.getMembersIncludingPendingWithout(account.getSelfRecipientId()),
groupInfoV2);
@ -927,10 +927,10 @@ public class GroupHelper {
members.addAll(group.getMembersIncludingPendingWithout(selfRecipientId));
account.getGroupStore().updateGroup(group);
if (groupChangeResponse.groupChange == null) {
if (groupChangeResponse.group_change == null) {
throw new AssertionError("groupChange is null");
}
final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChangeResponse.groupChange.encode());
final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChangeResponse.group_change.encode());
return sendGroupMessage(messageBuilder, members, group);
}

View File

@ -21,15 +21,15 @@ import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
import org.signal.libsignal.zkgroup.groups.UuidCiphertext;
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.signal.storageservice.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.GroupChange;
import org.signal.storageservice.protos.groups.GroupChangeResponse;
import org.signal.storageservice.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo;
import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
import org.signal.storageservice.storage.protos.groups.AccessControl;
import org.signal.storageservice.storage.protos.groups.GroupChange;
import org.signal.storageservice.storage.protos.groups.GroupChangeResponse;
import org.signal.storageservice.storage.protos.groups.Member;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupJoinInfo;
import org.signal.storageservice.storage.protos.groups.local.DecryptedMember;
import org.signal.storageservice.storage.protos.groups.local.DecryptedPendingMember;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.groupsv2.DecryptChangeVerificationMode;
@ -225,7 +225,7 @@ class GroupV2Helper {
change.modifyAvatar(new GroupChange.Actions.ModifyAvatarAction.Builder().avatar(avatarCdnKey).build());
}
change.sourceServiceId(getSelfAci().toByteString());
change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change);
}
@ -252,7 +252,7 @@ class GroupV2Helper {
final var aci = getSelfAci();
final var change = groupOperations.createModifyGroupMembershipChange(candidates, bannedUuids, aci);
change.sourceServiceId(getSelfAci().toByteString());
change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change);
}
@ -343,7 +343,7 @@ class GroupV2Helper {
false,
groupInfoV2.getGroup().bannedMembers);
change.sourceServiceId(getSelfAci().toByteString());
change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change);
}
@ -360,7 +360,7 @@ class GroupV2Helper {
final var change = groupOperations.createUnbanServiceIdsChange(serviceIds);
change.sourceServiceId(getSelfAci().toByteString());
change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change);
}
@ -436,7 +436,7 @@ class GroupV2Helper {
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var change = groupOperations.createUpdateProfileKeyCredentialChange(profileKeyCredential);
change.sourceServiceId(getSelfAci().toByteString());
change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change);
}
@ -459,7 +459,7 @@ class GroupV2Helper {
? groupOperations.createGroupJoinRequest(profileKeyCredential)
: groupOperations.createGroupJoinDirect(profileKeyCredential);
change.sourceServiceId(context.getRecipientHelper()
change.sourceUserId(context.getRecipientHelper()
.resolveSignalServiceAddress(selfRecipientId)
.getServiceId()
.toByteString());
@ -479,7 +479,7 @@ class GroupV2Helper {
final var change = groupOperations.createAcceptInviteChange(profileKeyCredential);
final var aci = context.getRecipientHelper().resolveSignalServiceAddress(selfRecipientId).getServiceId();
change.sourceServiceId(aci.toByteString());
change.sourceUserId(aci.toByteString());
return commitChange(groupInfoV2, change);
}
@ -585,7 +585,7 @@ class GroupV2Helper {
final var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams);
final var previousGroupState = groupInfoV2.getGroup();
final var nextRevision = previousGroupState.revision + 1;
final var changeActions = change.revision(nextRevision).build();
final var changeActions = change.version(nextRevision).build();
final DecryptedGroupChange decryptedChange;
final DecryptedGroup decryptedGroupState;
@ -611,7 +611,7 @@ class GroupV2Helper {
GroupLinkPassword password
) throws IOException {
final var nextRevision = currentRevision + 1;
final var changeActions = change.revision(nextRevision).build();
final var changeActions = change.version(nextRevision).build();
return dependencies.getGroupsV2Api()
.patchGroup(changeActions,
@ -621,6 +621,9 @@ class GroupV2Helper {
Pair<ServiceId, ProfileKey> getAuthoritativeProfileKeyFromChange(final DecryptedGroupChange change) {
UUID editor = UuidUtil.fromByteStringOrNull(change.editorServiceIdBytes);
if (editor == null) {
return null;
}
final var editorProfileKeyBytes = Stream.concat(Stream.of(change.newMembers.stream(),
change.promotePendingMembers.stream(),
change.modifiedProfileKeys.stream())

View File

@ -5,6 +5,7 @@ import org.asamk.signal.manager.actions.HandleAction;
import org.asamk.signal.manager.actions.RefreshPreKeysAction;
import org.asamk.signal.manager.actions.RenewSessionAction;
import org.asamk.signal.manager.actions.ResendMessageAction;
import org.asamk.signal.manager.actions.RetrieveDeviceNameAction;
import org.asamk.signal.manager.actions.RetrieveProfileAction;
import org.asamk.signal.manager.actions.SendGroupInfoAction;
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
@ -633,6 +634,12 @@ public final class IncomingMessageHandler {
context.getAccountHelper().handlePniChangeNumberMessage(pniChangeNumber, updatedPni);
}
}
if (syncMessage.getDeviceNameChange().isPresent()) {
final var deviceNameChange = syncMessage.getDeviceNameChange().get();
if (deviceNameChange.deviceId != null && deviceNameChange.deviceId == account.getDeviceId()) {
actions.add(RetrieveDeviceNameAction.create());
}
}
return actions;
}

View File

@ -52,6 +52,8 @@ import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import okio.ByteString;
public class SyncHelper {
private static final Logger logger = LoggerFactory.getLogger(SyncHelper.class);
@ -406,6 +408,11 @@ public class SyncHelper {
return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forMessageRequestResponse(response));
}
public SendMessageResult sendDeviceNameChange(final int deviceId) {
final var deviceNameChange = new SyncMessage.DeviceNameChange(deviceId, ByteString.EMPTY);
return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forDeviceNameChange(deviceNameChange));
}
private SendMessageResult requestSyncData(final SyncMessage.Request.Type type) {
var r = new SyncMessage.Request.Builder().type(type).build();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));

View File

@ -487,6 +487,21 @@ public class ManagerImpl implements Manager {
}).toList();
}
@Override
public void updateLinkedDevice(
final int deviceId,
final String name
) throws IOException, NotPrimaryDeviceException {
if (deviceId == account.getDeviceId()) {
context.getAccountHelper().setDeviceName(name);
} else {
if (!account.isPrimaryDevice()) {
throw new NotPrimaryDeviceException();
}
context.getAccountHelper().setDeviceName(deviceId, name);
}
}
private Long getPlaintextCreatedAt(DeviceInfo d) {
final var DECRYPTION_INFO = "deviceCreatedAt";
var identityKey = account.getAciIdentityKeyPair().getPrivateKey();

View File

@ -8,10 +8,10 @@ import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.signal.core.models.ServiceId;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.storageservice.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.EnabledState;
import org.signal.storageservice.storage.protos.groups.AccessControl;
import org.signal.storageservice.storage.protos.groups.Member;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.storage.protos.groups.local.EnabledState;
import org.whispersystems.signalservice.api.push.DistributionId;
import java.util.Set;

View File

@ -16,7 +16,7 @@ import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
import org.signal.libsignal.zkgroup.groupsend.GroupSendEndorsement;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.DistributionId;

View File

@ -16,7 +16,7 @@ import org.signal.core.models.ServiceId;
import org.signal.core.util.Hex;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.DistributionId;

View File

@ -1,7 +1,14 @@
dependencyResolutionManagement {
repositories {
mavenLocal()
mavenCentral()
mavenLocal()
maven {
name = "SignalBuildArtifacts"
url = uri("https://build-artifacts.signal.org/libraries/maven/")
content {
includeGroupByRegex("org\\.signal.*")
}
}
}
}

View File

@ -58,6 +58,7 @@ public class Commands {
addCommand(new UpdateAccountCommand());
addCommand(new UpdateConfigurationCommand());
addCommand(new UpdateContactCommand());
addCommand(new UpdateDeviceCommand());
addCommand(new UpdateGroupCommand());
addCommand(new UpdateProfileCommand());
addCommand(new UploadStickerPackCommand());

View File

@ -0,0 +1,50 @@
package org.asamk.signal.commands;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.IOErrorException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.api.NotPrimaryDeviceException;
import org.asamk.signal.output.OutputWriter;
import java.io.IOException;
public class UpdateDeviceCommand implements JsonRpcLocalCommand {
@Override
public String getName() {
return "updateDevice";
}
@Override
public void attachToSubparser(final Subparser subparser) {
subparser.help("Update a linked device.");
subparser.addArgument("-d", "--device-id", "--deviceId")
.type(int.class)
.required(true)
.help("Specify the device you want to update. Use listDevices to see the deviceIds.");
subparser.addArgument("-n", "--device-name")
.required(true)
.help("Specify a name to describe the given device.");
}
@Override
public void handleCommand(
final Namespace ns,
final Manager m,
final OutputWriter outputWriter
) throws CommandException {
try {
final var deviceId = ns.getInt("device-id");
final var deviceName = ns.getString("device-name");
m.updateLinkedDevice(deviceId, deviceName);
} catch (NotPrimaryDeviceException e) {
throw new UserErrorException("This command doesn't work on linked devices.");
} catch (IOException e) {
throw new IOErrorException("Error while updating device: " + e.getMessage(), e);
}
}
}

View File

@ -246,6 +246,14 @@ public class DbusManagerImpl implements Manager {
}).toList();
}
@Override
public void updateLinkedDevice(
final int deviceId,
final String name
) throws IOException, NotPrimaryDeviceException {
throw new UnsupportedOperationException();
}
@Override
public void removeLinkedDevices(final int deviceId) throws IOException {
final var devicePath = signal.getDevice(deviceId);