mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-28 04:34:21 +01:00
Add rudimentary link+sync support.
This commit is contained in:
@@ -7,7 +7,6 @@
|
||||
package org.whispersystems.signalservice.api;
|
||||
|
||||
import org.signal.core.util.StreamUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.InvalidMessageException;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.whispersystems.signalservice.api.backup.MediaRootBackupKey;
|
||||
@@ -17,6 +16,7 @@ import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil;
|
||||
import org.whispersystems.signalservice.api.crypto.ProfileCipherInputStream;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifest;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
|
||||
@@ -38,6 +38,8 @@ import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import kotlin.Unit;
|
||||
|
||||
/**
|
||||
* The primary interface for receiving Signal Service messages.
|
||||
*
|
||||
@@ -200,6 +202,16 @@ public class SignalServiceMessageReceiver {
|
||||
socket.retrieveBackup(cdnNumber, headers, cdnPath, destination, 1_000_000_000L, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a link+sync backup file. The data is written to @{code destination}.
|
||||
*/
|
||||
public @Nonnull NetworkResult<Unit> retrieveLinkAndSyncBackup(int cdn, @Nonnull String key, @Nonnull File destination, @Nullable ProgressListener listener) {
|
||||
return NetworkResult.fromFetch(() -> {
|
||||
socket.retrieveAttachment(cdn, Collections.emptyMap(), new SignalServiceAttachmentRemoteId.V4(key), destination, 1_000_000_000L, listener);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
public @Nonnull ZonedDateTime getCdnLastModifiedTime(int cdnNumber, Map<String, String> headers, String cdnPath) throws MissingConfigurationException, IOException {
|
||||
return socket.getCdnLastModifiedTime(cdnNumber, headers, cdnPath);
|
||||
}
|
||||
|
||||
@@ -212,4 +212,27 @@ class LinkDeviceApi(
|
||||
val request = WebSocketRequestMessage.put("/v1/accounts/name?deviceId=$deviceId", SetDeviceNameRequest(encryptedDeviceName))
|
||||
return NetworkResult.fromWebSocketRequest(authWebSocket, request)
|
||||
}
|
||||
|
||||
/**
|
||||
* A "long-polling" endpoint that will return once the primary device has successfully sent sync data.
|
||||
*
|
||||
* @param timeout The max amount of time to wait. Capped at 30 seconds.
|
||||
*
|
||||
* GET /v1/devices/transfer_archive?timeout=[timeout]
|
||||
*
|
||||
* - 200: Success, the primary device was sent backup sync data.
|
||||
* - 204: The primary didn't provide data before the max waiting time elapsed.
|
||||
* - 400: Invalid timeout.
|
||||
* - 429: Rate-limited.
|
||||
*/
|
||||
fun waitForPrimaryDevice(timeout: Duration = 30.seconds): NetworkResult<TransferArchiveResponse> {
|
||||
val request = WebSocketRequestMessage.get("/v1/devices/transfer_archive?timeout=${timeout.inWholeSeconds}")
|
||||
return NetworkResult
|
||||
.fromWebSocketRequest(
|
||||
signalWebSocket = authWebSocket,
|
||||
request = request,
|
||||
timeout = timeout,
|
||||
webSocketResponseConverter = NetworkResult.LongPollingWebSocketConverter(TransferArchiveResponse::class)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.link
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
/**
|
||||
* Data from primary on where to find link+sync backup file.
|
||||
*/
|
||||
data class TransferArchiveResponse @JsonCreator constructor(
|
||||
@JsonProperty val cdn: Int,
|
||||
@JsonProperty val key: String
|
||||
)
|
||||
@@ -249,7 +249,7 @@ class ProvisioningSocket<T> private constructor(
|
||||
private fun generateProvisioningUrl(deviceAddress: String): String {
|
||||
val encodedDeviceId = URLEncoder.encode(deviceAddress, "UTF-8")
|
||||
val encodedPubKey: String = URLEncoder.encode(Base64.encodeWithoutPadding(cipher.secondaryDevicePublicKey.serialize()), "UTF-8")
|
||||
return "sgnl://${mode.host}?uuid=$encodedDeviceId&pub_key=$encodedPubKey"
|
||||
return "sgnl://${mode.host}?uuid=$encodedDeviceId&pub_key=$encodedPubKey${mode.params}"
|
||||
}
|
||||
|
||||
private suspend fun keepAlive(webSocket: WebSocket) {
|
||||
@@ -288,9 +288,9 @@ class ProvisioningSocket<T> private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
enum class Mode(val host: String) {
|
||||
REREG("rereg"),
|
||||
LINK("linkdevice")
|
||||
enum class Mode(val host: String, val params: String) {
|
||||
REREG("rereg", ""),
|
||||
LINK("linkdevice", "&capabilities=backup4")
|
||||
}
|
||||
|
||||
fun interface ProvisioningSocketExceptionHandler {
|
||||
|
||||
Reference in New Issue
Block a user