Compare commits

...

13 Commits

Author SHA1 Message Date
AsamK
14297986f2 Use SequencedCollection instead of List 2026-01-24 17:24:27 +01:00
AsamK
0bd4d554d8 Use virtual threads 2026-01-24 17:24:27 +01:00
AsamK
32c8d4f801 Update to java 25 2026-01-24 15:38:02 +01:00
AsamK
82abc20871 Remove deprecated functionality 2026-01-24 15:37:00 +01:00
AsamK
e8ab01f665 Fix use of deprecated API in plugin 2026-01-24 15:20:55 +01:00
AsamK
dee557a9ad Prepare next release 2026-01-24 15:09:23 +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
53 changed files with 9763 additions and 405 deletions

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
java: [ '21', '25' ] java: [ '25' ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -58,8 +58,8 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1 - uses: graalvm/setup-graalvm@v1
with: with:
version: 'latest' distribution: 'graalvm'
java-version: '21' java-version: '25'
cache: 'gradle' cache: 'gradle'
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build with Gradle - name: Build with Gradle

View File

@ -24,7 +24,7 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: 'zulu' distribution: 'zulu'
java-version: 21 java-version: 25
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@ -2,6 +2,31 @@
## [Unreleased] ## [Unreleased]
**Attention**: Now requires Java 25
### Breaking changes
- Remove isRegistered method without parameters from Signal dbus interface, which always returned `true`
- Remove `sandbox` value for --service-environment parameter, use `staging` instead
- The `daemon` command now requires at least one channel parameter (`--socket`, `--dbus`, ...) and no longer defaults to dbus
## [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
## [0.13.22] - 2025-11-14 ## [0.13.22] - 2025-11-14
Requires libsignal-client version 0.86.1. Requires libsignal-client version 0.86.1.

View File

@ -1,4 +1,4 @@
FROM docker.io/azul/zulu-openjdk:21-jre-headless FROM docker.io/azul/zulu-openjdk:25-jre-headless
LABEL org.opencontainers.image.source=https://github.com/AsamK/signal-cli LABEL org.opencontainers.image.source=https://github.com/AsamK/signal-cli
LABEL org.opencontainers.image.description="signal-cli provides an unofficial commandline, dbus and JSON-RPC interface for the Signal messenger." LABEL org.opencontainers.image.description="signal-cli provides an unofficial commandline, dbus and JSON-RPC interface for the Signal messenger."

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. 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)) 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. 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. signal-cli needs to be kept up-to-date to keep up with Signal-Server changes.
@ -23,7 +23,7 @@ Windows. There's also a [docker image and some Linux packages](https://github.co
System requirements: System requirements:
- at least Java Runtime Environment (JRE) 21 - at least Java Runtime Environment (JRE) 25
- native library: libsignal-client - native library: libsignal-client
The native libs are bundled for x86_64 Linux (with recent enough glibc), Windows and MacOS. For other The native libs are bundled for x86_64 Linux (with recent enough glibc), Windows and MacOS. For other

View File

@ -3,17 +3,17 @@ plugins {
application application
eclipse eclipse
`check-lib-versions` `check-lib-versions`
id("org.graalvm.buildtools.native") version "0.11.3" id("org.graalvm.buildtools.native") version "0.11.4"
} }
allprojects { allprojects {
group = "org.asamk" group = "org.asamk"
version = "0.13.23-SNAPSHOT" version = "0.14.0-SNAPSHOT"
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_21 sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_25
if (!JavaVersion.current().isCompatibleWith(targetCompatibility)) { if (!JavaVersion.current().isCompatibleWith(targetCompatibility)) {
toolchain { toolchain {
@ -39,7 +39,7 @@ graalvmNative {
if (System.getenv("GRAALVM_HOME") == null) { if (System.getenv("GRAALVM_HOME") == null) {
toolchainDetection.set(true) toolchainDetection.set(true)
javaLauncher.set(javaToolchains.launcherFor { javaLauncher.set(javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(21)) languageVersion.set(JavaLanguageVersion.of(25))
}) })
} else { } else {
toolchainDetection.set(false) toolchainDetection.set(false)

View File

@ -7,11 +7,11 @@ plugins {
} }
tasks.named<KotlinCompilationTask<KotlinJvmCompilerOptions>>("compileKotlin").configure { tasks.named<KotlinCompilationTask<KotlinJvmCompilerOptions>>("compileKotlin").configure {
compilerOptions.jvmTarget.set(JvmTarget.JVM_17) compilerOptions.jvmTarget.set(JvmTarget.JVM_24)
} }
java { java {
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_24
} }
repositories { repositories {

View File

@ -1,5 +1,3 @@
@file:Suppress("DEPRECATION")
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
@ -8,7 +6,7 @@ import javax.xml.parsers.DocumentBuilderFactory
class CheckLibVersionsPlugin : Plugin<Project> { class CheckLibVersionsPlugin : Plugin<Project> {
override fun apply(project: Project) { override fun apply(project: Project) {
project.task("checkLibVersions") { project.tasks.register("checkLibVersions") {
description = description =
"Find any 3rd party libraries which have released new versions to the central Maven repo since we last upgraded." "Find any 3rd party libraries which have released new versions to the central Maven repo since we last upgraded."
doLast { doLast {
@ -28,7 +26,7 @@ class CheckLibVersionsPlugin : Plugin<Project> {
try { try {
val dbf = DocumentBuilderFactory.newInstance() val dbf = DocumentBuilderFactory.newInstance()
val db = dbf.newDocumentBuilder() val db = dbf.newDocumentBuilder()
val doc = db.parse(metaDataUrl); val doc = db.parse(metaDataUrl)
val newest = doc.getElementsByTagName("latest").item(0).textContent val newest = doc.getElementsByTagName("latest").item(0).textContent
if (version != newest.toString()) { if (version != newest.toString()) {
println("UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}") println("UPGRADE {\"group\": \"$group\", \"name\": \"$name\", \"current\": \"$version\", \"latest\": \"$newest\"}")

View File

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

View File

@ -121,7 +121,7 @@
}, },
{ {
"name":"org.signal.libsignal.net.ChatConnection$ListenerBridge", "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", "name":"org.signal.libsignal.net.ChatConnection$Response",

View File

@ -1,26 +1 @@
[ [{"interfaces":["java.sql.Connection"]},{"interfaces":["org.asamk.Signal"]},{"interfaces":["org.asamk.Signal$Configuration"]},{"interfaces":["org.asamk.Signal$Device"]},{"interfaces":["org.asamk.Signal$Group"]},{"interfaces":["org.asamk.Signal$Identity"]},{"interfaces":["org.asamk.SignalControl"]},{"interfaces":["org.freedesktop.dbus.interfaces.DBus"]}]
{
"interfaces":["java.sql.Connection"]
},
{
"interfaces":["org.asamk.Signal"]
},
{
"interfaces":["org.asamk.Signal$Configuration"]
},
{
"interfaces":["org.asamk.Signal$Device"]
},
{
"interfaces":["org.asamk.Signal$Group"]
},
{
"interfaces":["org.asamk.Signal$Identity"]
},
{
"interfaces":["org.asamk.SignalControl"]
},
{
"interfaces":["org.freedesktop.dbus.interfaces.DBus"]
}
]

File diff suppressed because it is too large Load Diff

View File

@ -1832,6 +1832,15 @@
"name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings", "name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }] "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", "name":"org.bouncycastle.jcajce.provider.keystore.BC$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }] "methods":[{"name":"<init>","parameterTypes":[] }]
@ -1919,6 +1928,10 @@
"name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings", "name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }] "methods":[{"name":"<init>","parameterTypes":[] }]
}, },
{
"name":"org.bouncycastle.jcajce.provider.symmetric.HKDF$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{ {
"name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings", "name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }] "methods":[{"name":"<init>","parameterTypes":[] }]
@ -2172,199 +2185,199 @@
"queryAllDeclaredConstructors":true "queryAllDeclaredConstructors":true
}, },
{ {
"name":"org.signal.storageservice.protos.groups.AccessControl", "name":"org.signal.storageservice.storage.protos.groups.AccessControl",
"fields":[{"name":"addFromInviteLink_"}, {"name":"attributes_"}, {"name":"members_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "fields":[{"name":"role_"}, {"name":"userId_"}]
}, },
{ {
"name":"org.signal.storageservice.protos.groups.GroupChanges", "name":"org.signal.storageservice.storage.protos.groups.GroupChanges",
"fields":[{"name":"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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "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_"}] "fields":[{"name":"value_"}]
}, },
{ {
"name":"org.signal.storageservice.protos.groups.local.DecryptedTimer", "name":"org.signal.storageservice.storage.protos.groups.local.DecryptedTimer",
"fields":[{"name":"duration_"}] "fields":[{"name":"duration_"}]
}, },
{ {
@ -2426,6 +2439,13 @@
"queryAllDeclaredConstructors":true, "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"] }] "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", "name":"org.whispersystems.signalservice.api.messages.calls.HangupMessage",
"allDeclaredFields":true, "allDeclaredFields":true,
@ -3068,6 +3088,7 @@
{ {
"name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord", "name":"org.whispersystems.signalservice.internal.storage.protos.AccountRecord",
"allDeclaredFields":true, "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":[] }] "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }]
}, },
{ {
@ -3106,6 +3127,7 @@
{ {
"name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord", "name":"org.whispersystems.signalservice.internal.storage.protos.ContactRecord",
"allDeclaredFields":true, "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":[] }] "methods":[{"name":"adapter","parameterTypes":[] }, {"name":"unknownFields","parameterTypes":[] }]
}, },
{ {

File diff suppressed because one or more lines are too long

View File

@ -1,18 +1,18 @@
[versions] [versions]
slf4j = "2.0.17" slf4j = "2.0.17"
junit = "6.0.1" junit = "6.0.2"
[libraries] [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" jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.20.1"
argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0" argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0"
dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0" dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0"
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
slf4j-jul = { module = "org.slf4j:jul-to-slf4j", 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" signalservice = "com.github.turasa:signal-service-java:2.15.3_unofficial_136"
sqlite = "org.xerial:sqlite-jdbc:3.51.0.0" sqlite = "org.xerial:sqlite-jdbc:3.51.1.0"
hikari = "com.zaxxer:HikariCP:7.0.2" hikari = "com.zaxxer:HikariCP:7.0.2"
junit-jupiter-bom = { module = "org.junit:junit-bom", version.ref = "junit" } junit-jupiter-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@ -4,8 +4,8 @@ plugins {
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_21 sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_25
if (!JavaVersion.current().isCompatibleWith(targetCompatibility)) { if (!JavaVersion.current().isCompatibleWith(targetCompatibility)) {
toolchain { toolchain {

View File

@ -154,6 +154,8 @@ public interface Manager extends Closeable {
List<Device> getLinkedDevices() throws IOException; List<Device> getLinkedDevices() throws IOException;
void updateLinkedDevice(int deviceId, String name) throws IOException, NotPrimaryDeviceException;
void removeLinkedDevices(int deviceId) throws IOException, NotPrimaryDeviceException; void removeLinkedDevices(int deviceId) throws IOException, NotPrimaryDeviceException;
void addDeviceLink(DeviceLinkUrl linkUri) throws IOException, InvalidDeviceLinkException, NotPrimaryDeviceException, DeviceLimitExceededException; 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.core.util.Base64;
import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.storageservice.protos.groups.GroupInviteLink; import org.signal.storageservice.storage.protos.groups.GroupInviteLink;
import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
@ -52,8 +52,8 @@ public final class GroupInviteLinkUrl {
var bytes = Base64.decode(encoding); var bytes = Base64.decode(encoding);
GroupInviteLink groupInviteLink = GroupInviteLink.ADAPTER.decode(bytes); GroupInviteLink groupInviteLink = GroupInviteLink.ADAPTER.decode(bytes);
if (groupInviteLink.v1Contents != null) { if (groupInviteLink.contentsV1 != null) {
var groupInviteLinkContentsV1 = groupInviteLink.v1Contents; var groupInviteLinkContentsV1 = groupInviteLink.contentsV1;
var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.groupMasterKey.toByteArray()); var groupMasterKey = new GroupMasterKey(groupInviteLinkContentsV1.groupMasterKey.toByteArray());
var password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.inviteLinkPassword.toByteArray()); var password = GroupLinkPassword.fromBytes(groupInviteLinkContentsV1.inviteLinkPassword.toByteArray());
@ -90,7 +90,7 @@ public final class GroupInviteLinkUrl {
} }
private static String createUrl(GroupMasterKey groupMasterKey, GroupLinkPassword password) { 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())) ByteString.of(groupMasterKey.serialize()))
.inviteLinkPassword(ByteString.of(password.serialize())) .inviteLinkPassword(ByteString.of(password.serialize()))
.build()).build(); .build()).build();

View File

@ -522,6 +522,22 @@ public class AccountHelper {
account.setEncryptedDeviceName(encryptedDeviceName); 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 { public void updateAccountAttributes() throws IOException {
handleResponseException(dependencies.getAccountApi().setAccountAttributes(account.getAccountAttributes(null))); 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.groups.GroupSecretParams;
import org.signal.libsignal.zkgroup.groupsend.GroupSendEndorsementsResponse; import org.signal.libsignal.zkgroup.groupsend.GroupSendEndorsementsResponse;
import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.signal.storageservice.protos.groups.GroupChangeResponse; import org.signal.storageservice.storage.protos.groups.GroupChangeResponse;
import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupJoinInfo;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupChangeLog; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupChangeLog;
@ -192,9 +192,9 @@ public class GroupHelper {
final GroupInfoV2 groupInfoV2, final GroupInfoV2 groupInfoV2,
final GroupChangeResponse groupChangeResponse final GroupChangeResponse groupChangeResponse
) { ) {
if (groupChangeResponse.groupSendEndorsementsResponse.size() > 0) { if (groupChangeResponse.group_send_endorsements_response.size() > 0) {
try { try {
final var groupSendEndorsementsResponse = new GroupSendEndorsementsResponse(groupChangeResponse.groupSendEndorsementsResponse.toByteArray()); final var groupSendEndorsementsResponse = new GroupSendEndorsementsResponse(groupChangeResponse.group_send_endorsements_response.toByteArray());
updateGroupEndorsements(groupInfoV2.getGroupId(), updateGroupEndorsements(groupInfoV2.getGroupId(),
groupInfoV2.getMasterKey(), groupInfoV2.getMasterKey(),
@ -391,7 +391,7 @@ public class GroupHelper {
.joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo); .joinGroup(inviteLinkUrl.getGroupMasterKey(), inviteLinkUrl.getPassword(), groupJoinInfo);
final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(), final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(),
groupJoinInfo.revision + 1, groupJoinInfo.revision + 1,
changeResponse.groupChange == null ? null : changeResponse.groupChange.encode()); changeResponse.group_change == null ? null : changeResponse.group_change.encode());
if (group.getGroup() == null) { if (group.getGroup() == null) {
// Only requested member, can't send update to group members // Only requested member, can't send update to group members
@ -873,10 +873,10 @@ public class GroupHelper {
final var groupChangeResponse = groupGroupChangePair.second(); final var groupChangeResponse = groupGroupChangePair.second();
handleGroupChangeResponse(groupInfoV2, groupChangeResponse); handleGroupChangeResponse(groupInfoV2, groupChangeResponse);
if (groupChangeResponse.groupChange == null) { if (groupChangeResponse.group_change == null) {
throw new AssertionError("groupChange is 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, return sendGroupMessage(messageBuilder,
groupInfoV2.getMembersIncludingPendingWithout(account.getSelfRecipientId()), groupInfoV2.getMembersIncludingPendingWithout(account.getSelfRecipientId()),
groupInfoV2); groupInfoV2);
@ -924,10 +924,10 @@ public class GroupHelper {
members.addAll(group.getMembersIncludingPendingWithout(selfRecipientId)); members.addAll(group.getMembersIncludingPendingWithout(selfRecipientId));
account.getGroupStore().updateGroup(group); account.getGroupStore().updateGroup(group);
if (groupChangeResponse.groupChange == null) { if (groupChangeResponse.group_change == null) {
throw new AssertionError("groupChange is 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); 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.GroupSecretParams;
import org.signal.libsignal.zkgroup.groups.UuidCiphertext; import org.signal.libsignal.zkgroup.groups.UuidCiphertext;
import org.signal.libsignal.zkgroup.profiles.ProfileKey; import org.signal.libsignal.zkgroup.profiles.ProfileKey;
import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.storage.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.GroupChange; import org.signal.storageservice.storage.protos.groups.GroupChange;
import org.signal.storageservice.protos.groups.GroupChangeResponse; import org.signal.storageservice.storage.protos.groups.GroupChangeResponse;
import org.signal.storageservice.protos.groups.Member; import org.signal.storageservice.storage.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupChange;
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo; import org.signal.storageservice.storage.protos.groups.local.DecryptedGroupJoinInfo;
import org.signal.storageservice.protos.groups.local.DecryptedMember; import org.signal.storageservice.storage.protos.groups.local.DecryptedMember;
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember; import org.signal.storageservice.storage.protos.groups.local.DecryptedPendingMember;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.groupsv2.DecryptChangeVerificationMode; import org.whispersystems.signalservice.api.groupsv2.DecryptChangeVerificationMode;
@ -225,7 +225,7 @@ class GroupV2Helper {
change.modifyAvatar(new GroupChange.Actions.ModifyAvatarAction.Builder().avatar(avatarCdnKey).build()); change.modifyAvatar(new GroupChange.Actions.ModifyAvatarAction.Builder().avatar(avatarCdnKey).build());
} }
change.sourceServiceId(getSelfAci().toByteString()); change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
} }
@ -252,7 +252,7 @@ class GroupV2Helper {
final var aci = getSelfAci(); final var aci = getSelfAci();
final var change = groupOperations.createModifyGroupMembershipChange(candidates, bannedUuids, aci); final var change = groupOperations.createModifyGroupMembershipChange(candidates, bannedUuids, aci);
change.sourceServiceId(getSelfAci().toByteString()); change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
} }
@ -343,7 +343,7 @@ class GroupV2Helper {
false, false,
groupInfoV2.getGroup().bannedMembers); groupInfoV2.getGroup().bannedMembers);
change.sourceServiceId(getSelfAci().toByteString()); change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
} }
@ -360,7 +360,7 @@ class GroupV2Helper {
final var change = groupOperations.createUnbanServiceIdsChange(serviceIds); final var change = groupOperations.createUnbanServiceIdsChange(serviceIds);
change.sourceServiceId(getSelfAci().toByteString()); change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
} }
@ -436,7 +436,7 @@ class GroupV2Helper {
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2); final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var change = groupOperations.createUpdateProfileKeyCredentialChange(profileKeyCredential); final var change = groupOperations.createUpdateProfileKeyCredentialChange(profileKeyCredential);
change.sourceServiceId(getSelfAci().toByteString()); change.sourceUserId(getSelfAci().toByteString());
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
} }
@ -459,7 +459,7 @@ class GroupV2Helper {
? groupOperations.createGroupJoinRequest(profileKeyCredential) ? groupOperations.createGroupJoinRequest(profileKeyCredential)
: groupOperations.createGroupJoinDirect(profileKeyCredential); : groupOperations.createGroupJoinDirect(profileKeyCredential);
change.sourceServiceId(context.getRecipientHelper() change.sourceUserId(context.getRecipientHelper()
.resolveSignalServiceAddress(selfRecipientId) .resolveSignalServiceAddress(selfRecipientId)
.getServiceId() .getServiceId()
.toByteString()); .toByteString());
@ -479,7 +479,7 @@ class GroupV2Helper {
final var change = groupOperations.createAcceptInviteChange(profileKeyCredential); final var change = groupOperations.createAcceptInviteChange(profileKeyCredential);
final var aci = context.getRecipientHelper().resolveSignalServiceAddress(selfRecipientId).getServiceId(); final var aci = context.getRecipientHelper().resolveSignalServiceAddress(selfRecipientId).getServiceId();
change.sourceServiceId(aci.toByteString()); change.sourceUserId(aci.toByteString());
return commitChange(groupInfoV2, change); return commitChange(groupInfoV2, change);
} }
@ -585,7 +585,7 @@ class GroupV2Helper {
final var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams); final var groupOperations = dependencies.getGroupsV2Operations().forGroup(groupSecretParams);
final var previousGroupState = groupInfoV2.getGroup(); final var previousGroupState = groupInfoV2.getGroup();
final var nextRevision = previousGroupState.revision + 1; final var nextRevision = previousGroupState.revision + 1;
final var changeActions = change.revision(nextRevision).build(); final var changeActions = change.version(nextRevision).build();
final DecryptedGroupChange decryptedChange; final DecryptedGroupChange decryptedChange;
final DecryptedGroup decryptedGroupState; final DecryptedGroup decryptedGroupState;
@ -611,7 +611,7 @@ class GroupV2Helper {
GroupLinkPassword password GroupLinkPassword password
) throws IOException { ) throws IOException {
final var nextRevision = currentRevision + 1; final var nextRevision = currentRevision + 1;
final var changeActions = change.revision(nextRevision).build(); final var changeActions = change.version(nextRevision).build();
return dependencies.getGroupsV2Api() return dependencies.getGroupsV2Api()
.patchGroup(changeActions, .patchGroup(changeActions,
@ -621,6 +621,9 @@ class GroupV2Helper {
Pair<ServiceId, ProfileKey> getAuthoritativeProfileKeyFromChange(final DecryptedGroupChange change) { Pair<ServiceId, ProfileKey> getAuthoritativeProfileKeyFromChange(final DecryptedGroupChange change) {
UUID editor = UuidUtil.fromByteStringOrNull(change.editorServiceIdBytes); UUID editor = UuidUtil.fromByteStringOrNull(change.editorServiceIdBytes);
if (editor == null) {
return null;
}
final var editorProfileKeyBytes = Stream.concat(Stream.of(change.newMembers.stream(), final var editorProfileKeyBytes = Stream.concat(Stream.of(change.newMembers.stream(),
change.promotePendingMembers.stream(), change.promotePendingMembers.stream(),
change.modifiedProfileKeys.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.RefreshPreKeysAction;
import org.asamk.signal.manager.actions.RenewSessionAction; import org.asamk.signal.manager.actions.RenewSessionAction;
import org.asamk.signal.manager.actions.ResendMessageAction; 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.RetrieveProfileAction;
import org.asamk.signal.manager.actions.SendGroupInfoAction; import org.asamk.signal.manager.actions.SendGroupInfoAction;
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction; import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
@ -633,6 +634,12 @@ public final class IncomingMessageHandler {
context.getAccountHelper().handlePniChangeNumberMessage(pniChangeNumber, updatedPni); 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; return actions;
} }

View File

@ -52,6 +52,8 @@ import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import okio.ByteString;
public class SyncHelper { public class SyncHelper {
private static final Logger logger = LoggerFactory.getLogger(SyncHelper.class); private static final Logger logger = LoggerFactory.getLogger(SyncHelper.class);
@ -406,6 +408,11 @@ public class SyncHelper {
return context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forMessageRequestResponse(response)); 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) { private SendMessageResult requestSyncData(final SyncMessage.Request.Type type) {
var r = new SyncMessage.Request.Builder().type(type).build(); var r = new SyncMessage.Request.Builder().type(type).build();
var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r)); var message = SignalServiceSyncMessage.forRequest(new RequestMessage(r));

View File

@ -20,7 +20,7 @@ public class JobExecutor implements AutoCloseable {
public JobExecutor(final Context context) { public JobExecutor(final Context context) {
this.context = context; this.context = context;
this.executorService = Executors.newCachedThreadPool(); this.executorService = Executors.newVirtualThreadPerTaskExecutor();
} }
public void enqueueJob(Job job) { public void enqueueJob(Job job) {

View File

@ -157,7 +157,7 @@ public class ManagerImpl implements Manager {
private final SignalDependencies dependencies; private final SignalDependencies dependencies;
private final Context context; private final Context context;
private final ExecutorService executor = Executors.newCachedThreadPool(); private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
private Thread receiveThread; private Thread receiveThread;
private boolean isReceivingSynchronous; private boolean isReceivingSynchronous;
@ -487,6 +487,21 @@ public class ManagerImpl implements Manager {
}).toList(); }).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) { private Long getPlaintextCreatedAt(DeviceInfo d) {
final var DECRYPTION_INFO = "deviceCreatedAt"; final var DECRYPTION_INFO = "deviceCreatedAt";
var identityKey = account.getAciIdentityKeyPair().getPrivateKey(); 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.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.signal.core.models.ServiceId; import org.signal.core.models.ServiceId;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey; import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
import org.signal.storageservice.protos.groups.AccessControl; import org.signal.storageservice.storage.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.Member; import org.signal.storageservice.storage.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.EnabledState; import org.signal.storageservice.storage.protos.groups.local.EnabledState;
import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.DistributionId;
import java.util.Set; 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.GroupMasterKey;
import org.signal.libsignal.zkgroup.groups.GroupSecretParams; import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
import org.signal.libsignal.zkgroup.groupsend.GroupSendEndorsement; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.DistributionId; 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.core.util.Hex;
import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.libsignal.zkgroup.groups.GroupMasterKey; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.DistributionId;

View File

@ -32,7 +32,7 @@ fi
run() { run() {
# To update graalvm config, set GRAALVM_HOME, e.g: # To update graalvm config, set GRAALVM_HOME, e.g:
# export GRAALVM_HOME=/usr/lib/jvm/java-21-graalvm # export GRAALVM_HOME=/usr/lib/jvm/java-25-graalvm
if [ ! -z "$GRAALVM_HOME" ]; then if [ ! -z "$GRAALVM_HOME" ]; then
export JAVA_HOME=$GRAALVM_HOME export JAVA_HOME=$GRAALVM_HOME
export SIGNAL_CLI_OPTS="-agentlib:native-image-agent=config-merge-dir=graalvm-config-dir-${SIGNAL_CLI_AGENT_ID}/" export SIGNAL_CLI_OPTS="-agentlib:native-image-agent=config-merge-dir=graalvm-config-dir-${SIGNAL_CLI_AGENT_ID}/"

View File

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

View File

@ -143,9 +143,6 @@ public interface Signal extends DBusInterface {
String avatar String avatar
) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound, Error.InvalidGroupId; ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.GroupNotFound, Error.InvalidGroupId;
@Deprecated
boolean isRegistered() throws Error.Failure, Error.InvalidNumber;
boolean isRegistered(String number) throws Error.Failure, Error.InvalidNumber; boolean isRegistered(String number) throws Error.Failure, Error.InvalidNumber;
List<Boolean> isRegistered(List<String> numbers) throws Error.Failure, Error.InvalidNumber; List<Boolean> isRegistered(List<String> numbers) throws Error.Failure, Error.InvalidNumber;

View File

@ -13,10 +13,4 @@ public enum ServiceEnvironmentCli {
return "staging"; return "staging";
} }
}, },
@Deprecated SANDBOX {
@Override
public String toString() {
return "sandbox";
}
},
} }

View File

@ -3,12 +3,13 @@ package org.asamk.signal.commands;
import org.asamk.signal.OutputType; import org.asamk.signal.OutputType;
import java.util.List; import java.util.List;
import java.util.SequencedCollection;
public interface Command { public interface Command {
String getName(); String getName();
default List<OutputType> getSupportedOutputTypes() { default SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT); return List.of(OutputType.PLAIN_TEXT);
} }
} }

View File

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

View File

@ -10,6 +10,7 @@ import org.asamk.signal.ReceiveMessageHandler;
import org.asamk.signal.Shutdown; import org.asamk.signal.Shutdown;
import org.asamk.signal.commands.exceptions.CommandException; import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.IOErrorException; import org.asamk.signal.commands.exceptions.IOErrorException;
import org.asamk.signal.commands.exceptions.UserErrorException;
import org.asamk.signal.dbus.DbusHandler; import org.asamk.signal.dbus.DbusHandler;
import org.asamk.signal.http.HttpServerHandler; import org.asamk.signal.http.HttpServerHandler;
import org.asamk.signal.json.JsonReceiveMessageHandler; import org.asamk.signal.json.JsonReceiveMessageHandler;
@ -31,6 +32,7 @@ import java.nio.channels.Channel;
import java.nio.channels.ServerSocketChannel; import java.nio.channels.ServerSocketChannel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.SequencedCollection;
import static org.asamk.signal.util.CommandUtil.getReceiveConfig; import static org.asamk.signal.util.CommandUtil.getReceiveConfig;
@ -86,7 +88,7 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand {
} }
@Override @Override
public List<OutputType> getSupportedOutputTypes() { public SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
} }
@ -201,9 +203,7 @@ public class DaemonCommand implements MultiLocalCommand, LocalCommand {
&& tcpAddress == null && tcpAddress == null
&& httpAddress == null && httpAddress == null
&& inheritedChannel == null) { && inheritedChannel == null) {
logger.warn( throw new UserErrorException("At least one channel parameter is required, e.g. --socket or --dbus.");
"Running daemon command without explicit mode is deprecated. Use 'daemon --dbus' to use the dbus interface.");
daemonHandler.runDbus(false, DbusConfig.getBusname());
} }
} }

View File

@ -16,6 +16,7 @@ import org.asamk.signal.output.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SequencedCollection;
public class DeleteLocalAccountDataCommand implements RegistrationCommand, JsonRpcRegistrationCommand<Map<String, Object>> { public class DeleteLocalAccountDataCommand implements RegistrationCommand, JsonRpcRegistrationCommand<Map<String, Object>> {
@ -54,7 +55,7 @@ public class DeleteLocalAccountDataCommand implements RegistrationCommand, JsonR
} }
@Override @Override
public List<OutputType> getSupportedOutputTypes() { public SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
} }

View File

@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import org.asamk.signal.OutputType; import org.asamk.signal.OutputType;
import java.util.List; import java.util.List;
import java.util.SequencedCollection;
public interface JsonRpcCommand<T> extends Command { public interface JsonRpcCommand<T> extends Command {
@ -12,7 +13,7 @@ public interface JsonRpcCommand<T> extends Command {
return null; return null;
} }
default List<OutputType> getSupportedOutputTypes() { default SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.JSON); return List.of(OutputType.JSON);
} }
} }

View File

@ -21,6 +21,7 @@ import java.io.FileInputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.util.List; import java.util.List;
import java.util.SequencedCollection;
import java.util.function.Supplier; import java.util.function.Supplier;
import static org.asamk.signal.util.CommandUtil.getReceiveConfig; import static org.asamk.signal.util.CommandUtil.getReceiveConfig;
@ -53,7 +54,7 @@ public class JsonRpcDispatcherCommand implements LocalCommand, MultiLocalCommand
} }
@Override @Override
public List<OutputType> getSupportedOutputTypes() { public SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.JSON); return List.of(OutputType.JSON);
} }

View File

@ -11,6 +11,7 @@ import org.asamk.signal.output.JsonWriter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SequencedCollection;
public interface JsonRpcLocalCommand extends JsonRpcSingleCommand<Map<String, Object>>, LocalCommand { public interface JsonRpcLocalCommand extends JsonRpcSingleCommand<Map<String, Object>>, LocalCommand {
@ -23,7 +24,7 @@ public interface JsonRpcLocalCommand extends JsonRpcSingleCommand<Map<String, Ob
handleCommand(commandNamespace, m, jsonWriter); handleCommand(commandNamespace, m, jsonWriter);
} }
default List<OutputType> getSupportedOutputTypes() { default SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
} }
} }

View File

@ -11,6 +11,7 @@ import org.asamk.signal.output.JsonWriter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SequencedCollection;
public interface JsonRpcMultiLocalCommand extends JsonRpcMultiCommand<Map<String, Object>>, MultiLocalCommand { public interface JsonRpcMultiLocalCommand extends JsonRpcMultiCommand<Map<String, Object>>, MultiLocalCommand {
@ -27,7 +28,7 @@ public interface JsonRpcMultiLocalCommand extends JsonRpcMultiCommand<Map<String
handleCommand(commandNamespace, c, jsonWriter); handleCommand(commandNamespace, c, jsonWriter);
} }
default List<OutputType> getSupportedOutputTypes() { default SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
} }
} }

View File

@ -27,6 +27,7 @@ import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.SequencedCollection;
public class ReceiveCommand implements LocalCommand, JsonRpcSingleCommand<ReceiveCommand.ReceiveParams> { public class ReceiveCommand implements LocalCommand, JsonRpcSingleCommand<ReceiveCommand.ReceiveParams> {
@ -60,7 +61,7 @@ public class ReceiveCommand implements LocalCommand, JsonRpcSingleCommand<Receiv
} }
@Override @Override
public List<OutputType> getSupportedOutputTypes() { public SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
} }

View File

@ -21,6 +21,7 @@ import org.asamk.signal.util.CommandUtil;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.SequencedCollection;
public class RegisterCommand implements RegistrationCommand, JsonRpcRegistrationCommand<RegisterCommand.RegistrationParams> { public class RegisterCommand implements RegistrationCommand, JsonRpcRegistrationCommand<RegisterCommand.RegistrationParams> {
@ -57,7 +58,7 @@ public class RegisterCommand implements RegistrationCommand, JsonRpcRegistration
} }
@Override @Override
public List<OutputType> getSupportedOutputTypes() { public SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
} }

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

@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.SequencedCollection;
public class VerifyCommand implements RegistrationCommand, JsonRpcRegistrationCommand<VerifyCommand.VerifyParams> { public class VerifyCommand implements RegistrationCommand, JsonRpcRegistrationCommand<VerifyCommand.VerifyParams> {
@ -50,7 +51,7 @@ public class VerifyCommand implements RegistrationCommand, JsonRpcRegistrationCo
} }
@Override @Override
public List<OutputType> getSupportedOutputTypes() { public SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
} }

View File

@ -16,6 +16,7 @@ import org.asamk.signal.output.PlainTextWriter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SequencedCollection;
public class VersionCommand implements JsonRpcLocalCommand, JsonRpcMultiLocalCommand { public class VersionCommand implements JsonRpcLocalCommand, JsonRpcMultiLocalCommand {
@ -25,7 +26,7 @@ public class VersionCommand implements JsonRpcLocalCommand, JsonRpcMultiLocalCom
} }
@Override @Override
public List<OutputType> getSupportedOutputTypes() { public SequencedCollection<OutputType> getSupportedOutputTypes() {
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON); return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
} }

View File

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

View File

@ -675,12 +675,6 @@ public class DbusSignalImpl implements Signal, AutoCloseable {
} }
} }
@Override
@Deprecated
public boolean isRegistered() {
return true;
}
@Override @Override
public boolean isRegistered(String number) { public boolean isRegistered(String number) {
var result = isRegistered(List.of(number)); var result = isRegistered(List.of(number));

View File

@ -59,7 +59,7 @@ public class HttpServerHandler implements AutoCloseable {
logger.debug("Starting HTTP server on {}", address); logger.debug("Starting HTTP server on {}", address);
server = HttpServer.create(address, 0); server = HttpServer.create(address, 0);
server.setExecutor(Executors.newCachedThreadPool()); server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
server.createContext("/api/v1/rpc", this::handleRpcEndpoint); server.createContext("/api/v1/rpc", this::handleRpcEndpoint);
server.createContext("/api/v1/events", this::handleEventsEndpoint); server.createContext("/api/v1/events", this::handleEventsEndpoint);

View File

@ -55,7 +55,7 @@ public class JsonRpcReader {
return; return;
} }
try (final var executor = Executors.newCachedThreadPool()) { try (final var executor = Executors.newVirtualThreadPerTaskExecutor()) {
while (!Thread.interrupted()) { while (!Thread.interrupted()) {
final var input = lineSupplier.get(); final var input = lineSupplier.get();
if (input == null) { if (input == null) {
@ -91,7 +91,7 @@ public class JsonRpcReader {
case JsonRpcBatchMessage jsonRpcBatchMessage -> { case JsonRpcBatchMessage jsonRpcBatchMessage -> {
final var messages = jsonRpcBatchMessage.getMessages(); final var messages = jsonRpcBatchMessage.getMessages();
final var responseList = new ArrayList<JsonRpcResponse>(messages.size()); final var responseList = new ArrayList<JsonRpcResponse>(messages.size());
try (final var executor = Executors.newCachedThreadPool()) { try (final var executor = Executors.newVirtualThreadPerTaskExecutor()) {
final var lock = new ReentrantLock(); final var lock = new ReentrantLock();
messages.forEach(jsonNode -> { messages.forEach(jsonNode -> {
final JsonRpcRequest request; final JsonRpcRequest request;

View File

@ -63,7 +63,7 @@ public class SocketHandler implements AutoCloseable {
logger.debug("Starting JSON-RPC server on {}", address); logger.debug("Starting JSON-RPC server on {}", address);
listenerThread = Thread.ofPlatform().name("daemon-listener").start(() -> { listenerThread = Thread.ofPlatform().name("daemon-listener").start(() -> {
try (final var executor = Executors.newCachedThreadPool()) { try (final var executor = Executors.newVirtualThreadPerTaskExecutor()) {
logger.info("Started JSON-RPC server on {}", address); logger.info("Started JSON-RPC server on {}", address);
while (true) { while (true) {
final var connectionId = threadNumber.getAndIncrement(); final var connectionId = threadNumber.getAndIncrement();