Skip to content

Commit

Permalink
Merge branch 'update-kotlin-api'
Browse files Browse the repository at this point in the history
  • Loading branch information
madadam committed Feb 6, 2024
2 parents f9bd4b8 + 7582b0c commit ee35ac3
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.equalitie.ouisync

import org.msgpack.core.MessagePacker

/**
*
* A Ouisync repository.
Expand Down Expand Up @@ -93,27 +95,6 @@ class Repository private constructor(internal val handle: Long, internal val cli

return Repository(handle, client)
}

/**
* Reopen a repo using a *reopen token*.
*
* This is useful when one wants to move the repo to a different location while the repo is
* currently open without requiring the user to input the local password again.
* To do that:
*
* 1. Obtain the reopen token with [createReopenToken].
* 2. [Close][close] the repo.
* 3. Move the repo file.
* 4. Reopen the repo with [reopen], passing it the reopen token from step 1.
*
* @see createReopenToken
*/
suspend fun reopen(session: Session, path: String, token: ByteArray): Repository {
val client = session.client
val handle = client.invoke(RepositoryReopen(path, token)) as Long

return Repository(handle, client)
}
}

/**
Expand Down Expand Up @@ -145,14 +126,6 @@ class Repository private constructor(internal val handle: Long, internal val cli
*/
suspend fun databaseId() = client.invoke(RepositoryDatabaseId(handle)) as ByteArray

/**
* Returns the access mode (*blind*, *read* or *write*) the repo is opened in.
*/
suspend fun accessMode(): AccessMode {
val raw = client.invoke(RepositoryAccessMode(handle)) as Byte
return AccessMode.decode(raw)
}

/**
* Creates a *share token* to share this repository with other devices.
*
Expand All @@ -177,14 +150,6 @@ class Repository private constructor(internal val handle: Long, internal val cli
return ShareToken(raw, client)
}

/**
* Creates a token for reopening the repo after it's been renamed/moved.
*
* @see reopen
*/
suspend fun createReopenToken() =
client.invoke(RepositoryCreateReopenToken(handle)) as ByteArray

/**
* Is local password required to read this repo?
*/
Expand All @@ -198,50 +163,41 @@ class Repository private constructor(internal val handle: Long, internal val cli
client.invoke(RepositoryRequiresLocalPasswordForWriting(handle)) as Boolean

/**
* Changes the local read password.
*
* The repo must be either opened in at least read mode or the `shareToken` must be set to a
* token with at least read access, otherwise [Error] with [ErrorCode.PERMISSION_DENIED] is
* thrown.
*
* @param password new password to set. If null, local read password is removed (the repo
* becomes readable without a password).
* @param shareToken share token to use in case the repo is currently not opened in at least
* read mode. Can be used to escalate access mode from blind to read.
* Returns the access mode (*blind*, *read* or *write*) the repo is opened in.
*/
suspend fun setReadAccess(password: String?, shareToken: ShareToken? = null) =
client.invoke(RepositorySetReadAccess(handle, password, shareToken?.toString()))
suspend fun accessMode(): AccessMode {
val raw = client.invoke(RepositoryAccessMode(handle)) as Byte
return AccessMode.decode(raw)
}

/**
* Changes the local read and write password.
*
* The repo must be either opened in write mode or the `shareToken` must be set to a token with
* write mode, otherwise [Error] with [ErrorCode.PERMISSION_DENIED] is thrown.
*
* To set different read and write passwords, call first this function and then [setReadAccess].
* Switches the repository to the given access mode.
*/
suspend fun setAccessMode(
accessMode: AccessMode,
password: String?,
) =
client.invoke(RepositorySetAccessMode(handle, accessMode, password))

/**
* Gets the current credentials of this repository. Can be used to restore access after
* closing and reopening the repository.
*
* @param oldPassword previous local write password. This is optional and only needed if one
* wants to preserve the *writer id* of this replica. If null, this
* repository effectively becomes a different replica from the one it was
* before this function was called. This currently has no user observable
* effect apart from slight performance impact.
* @param newPassword new local read and write password to set. If null, password access is
* removed.
* @param shareToken share token to use in case the repo is currently not opened in write mode.
* Can be used to escalate access mode from blind or read to write.
* @see setCredentials
*/
suspend fun setReadAndWriteAccess(oldPassword: String?, newPassword: String?, shareToken: ShareToken? = null) =
client.invoke(RepositorySetReadAndWriteAccess(handle, oldPassword, newPassword, shareToken?.toString()))
suspend fun credentials(): ByteArray = client.invoke(RepositoryCredentials(handle)) as ByteArray

/**
* Removes read access to the repository using the local read password.
* Sets the current credentials of the repository.
*/
suspend fun removeReadKey() = client.invoke(RepositoryRemoveReadKey(handle))
suspend fun setCredentials(credentials: ByteArray) = client.invoke(RepositorySetCredentials(handle, credentials))

/**
* Removes write access to the repository using the local write password.
* Sets, unsets or changes local passwords for accessing the repository or disables the given
* access mode.
*/
suspend fun removeWriteKey() = client.invoke(RepositoryRemoveWriteKey(handle))
suspend fun setAccess(read: AccessChange? = null, write: AccessChange? = null) =
client.invoke(RepositorySetAccess(handle, read, write))

/**
* Is Bittorrent DHT enabled?
Expand All @@ -254,7 +210,7 @@ class Repository private constructor(internal val handle: Long, internal val cli
* Enables/disabled Bittorrent DHT (for peer discovery).
*
* @see isDhtEnabled
* @see [infoHash]
* @see infoHash
*/
suspend fun setDhtEnabled(enabled: Boolean) =
client.invoke(RepositorySetDhtEnabled(handle, enabled))
Expand Down Expand Up @@ -310,3 +266,42 @@ class Repository private constructor(internal val handle: Long, internal val cli
suspend fun moveEntry(src: String, dst: String) =
client.invoke(RepositoryMoveEntry(handle, src, dst))
}

/**
* How to change access to a repository.
*
* @see [Repository.setAccess]
*/
sealed class AccessChange {
fun pack(packer: MessagePacker) {
when (this) {
is EnableAccess -> {
packer.packMapHeader(1)
packer.packString("enable")

if (password != null) {
packer.packString(password)
} else {
packer.packNil()
}
}
is DiableAccess -> {
packer.packString("disabled")
}
}
}
}

/**
* Enable read or write access, optionally with local password
*
* @see [Repository.setAccess]
*/
class EnableAccess(val password: String?) : AccessChange()

/**
* Disable access
*
* @see [Repository.setAccess]
*/
class DiableAccess() : AccessChange()
Original file line number Diff line number Diff line change
Expand Up @@ -78,49 +78,59 @@ internal class RepositoryClose : ValueRequest<Long> {
constructor(value: Long) : super(value)
}

internal class RepositoryCreateReopenToken : ValueRequest<Long> {
internal class RepositorySubscribe : ValueRequest<Long> {
constructor(value: Long) : super(value)
}

internal class RepositoryReopen(val path: String, val token: ByteArray) : Request() {
internal class RepositorySetAccess(
val repository: Long,
val read: AccessChange?,
val write: AccessChange?,
) : Request() {
override fun packContent(packer: MessagePacker) {
packer.packMap(mapOf("path" to path, "token" to token))
packer.packMap(
mapOf(
"repository" to repository,
"read" to read,
"write" to write,
),
)
}
}

internal class RepositorySubscribe : ValueRequest<Long> {
internal class RepositoryAccessMode : ValueRequest<Long> {
constructor(value: Long) : super(value)
}

internal class RepositorySetReadAccess(
internal class RepositorySetAccessMode(
val repository: Long,
val accessMode: AccessMode,
val password: String?,
val shareToken: String?,
) : Request() {
override fun packContent(packer: MessagePacker) {
packer.packMap(
mapOf(
"repository" to repository,
"access_mode" to accessMode,
"password" to password,
"share_token" to shareToken,
),
)
}
}

internal class RepositorySetReadAndWriteAccess(
internal class RepositoryCredentials : ValueRequest<Long> {
constructor(value: Long) : super(value)
}

internal class RepositorySetCredentials(
val repository: Long,
val oldPassword: String?,
val newPassword: String?,
val shareToken: String?,
val credentials: ByteArray,
) : Request() {
override fun packContent(packer: MessagePacker) {
packer.packMap(
mapOf(
"repository" to repository,
"old_password" to oldPassword,
"new_password" to newPassword,
"share_token" to shareToken,
"credentials" to credentials,
),
)
}
Expand Down Expand Up @@ -206,10 +216,6 @@ internal class RepositoryCreateShareToken(
)
}

internal class RepositoryAccessMode : ValueRequest<Long> {
constructor(value: Long) : super(value)
}

internal class RepositorySyncProgress : ValueRequest<Long> {
constructor(value: Long) : super(value)
}
Expand Down Expand Up @@ -421,6 +427,7 @@ private fun MessagePacker.packAny(value: Any) {
is Long -> packLong(value)
is Short -> packShort(value)
is String -> packString(value)
is AccessChange -> value.pack(this)
else -> throw IllegalArgumentException("can't pack ${value::class.qualifiedName}")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.sun.jna.Pointer
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import java.io.Closeable

/**
* The entry point to the ouisync library.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ class RepositoryTest {
var repo = createRepo()

try {
val token = repo.createReopenToken()
val credentials = repo.credentials()
repo.close()
repo = Repository.reopen(session, repoPath, token)
repo = Repository.open(session, repoPath)
repo.setCredentials(credentials)
} finally {
repo.close()
}
Expand All @@ -95,15 +96,15 @@ class RepositoryTest {
assertFalse(it.requiresLocalPasswordForReading())
assertFalse(it.requiresLocalPasswordForWriting())

it.setReadAndWriteAccess(oldPassword = null, newPassword = "banana")
it.setAccess(read = EnableAccess("banana"), write = EnableAccess("banana"))
assertTrue(it.requiresLocalPasswordForReading())
assertTrue(it.requiresLocalPasswordForWriting())

it.setReadAccess(password = null)
it.setAccess(read = EnableAccess(null))
assertFalse(it.requiresLocalPasswordForReading())
assertTrue(it.requiresLocalPasswordForWriting())

it.setReadAndWriteAccess(oldPassword = null, newPassword = null)
it.setAccess(write = EnableAccess(null))
assertFalse(it.requiresLocalPasswordForReading())
assertFalse(it.requiresLocalPasswordForWriting())
}
Expand Down

0 comments on commit ee35ac3

Please sign in to comment.