summaryrefslogtreecommitdiff
path: root/libs/samba/src/main/java
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2024-08-19 00:34:03 +0200
committerJoel Klinghed <the_jk@spawned.biz>2024-08-19 00:34:03 +0200
commitd372cdcea3b3a0ba4b49180695c4e6b0e2d074a5 (patch)
treef3442d2a56afd362d172cef096c878a5e7311066 /libs/samba/src/main/java
parentb0d90f32974f6473552d8b1bf5387f9fc4995970 (diff)
Increase the samba implemetation
With the exception of openDir, largely untested.
Diffstat (limited to 'libs/samba/src/main/java')
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt68
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt25
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt79
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt40
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt57
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaTree.kt2
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/samba/SambaTreeFactory.kt3
7 files changed, 235 insertions, 39 deletions
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt
index e48268d..1863e22 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/NativeSamba.kt
@@ -4,6 +4,7 @@
package org.the_jk.cleversync.io.samba
import androidx.annotation.Keep
+import java.time.Instant
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
@@ -21,14 +22,35 @@ internal object NativeSamba {
fun connect(url: Url, credentials: SambaCredentials): Boolean
fun lastError(): String
fun openDir(path: String): Dir?
+ fun entry(path: String): DirEntry?
+ fun makeDir(path: String): Boolean
+ fun removeDir(path: String): Boolean
+ fun unlink(path: String): Boolean
+ fun readLink(path: String): String?
}
interface Url : Object {
fun path(): String
}
+ enum class DirEntryType {
+ DIR,
+ FILE,
+ LINK,
+ }
+
+ @Keep
+ data class DirEntry(
+ val name: String,
+ val type: DirEntryType,
+ val size: ULong,
+ val lastModified: Instant,
+ )
+
interface Dir : Object {
val path: String
+
+ fun list(): Array<DirEntry>
}
private class NativeContext(private var ptr: Long): Context {
@@ -55,6 +77,26 @@ internal object NativeSamba {
val dir = nativeContextOpenDir(ptr, path)
return if (dir != 0L) NativeDir(path, dir) else null
}
+
+ override fun entry(path: String): DirEntry? {
+ return nativeContextEntry(ptr, path)
+ }
+
+ override fun makeDir(path: String): Boolean {
+ return nativeContextMakeDir(ptr, path)
+ }
+
+ override fun removeDir(path: String): Boolean {
+ return nativeContextRemoveDir(ptr, path)
+ }
+
+ override fun unlink(path: String): Boolean {
+ return nativeContextUnlink(ptr, path)
+ }
+
+ override fun readLink(path: String): String? {
+ return nativeContextReadLink(ptr, path)
+ }
}
private class NativeUrl(private var ptr: Long): Url {
@@ -75,21 +117,47 @@ internal object NativeSamba {
nativeDirDestroy(ptr)
ptr = 0L
}
+
+ override fun list(): Array<DirEntry> {
+ return nativeDirList(ptr)
+ }
}
init {
System.loadLibrary("samba")
}
+ @JvmStatic
+ @Keep
+ @Suppress("UnusedPrivateMember")
+ private fun createDirEntry(name: String, type: Int, size: Long, lastModified: Long) =
+ DirEntry(
+ name = name,
+ when (type) {
+ 0 -> DirEntryType.DIR
+ 1 -> DirEntryType.FILE
+ 2 -> DirEntryType.LINK
+ else -> throw IllegalArgumentException("Unknown type: $type")
+ },
+ size = size.toULong(),
+ lastModified = Instant.ofEpochMilli(lastModified),
+ )
+
private external fun nativeContextNew(timeoutSeconds: Int): Long
private external fun nativeContextDestroy(ptr: Long)
private external fun nativeContextParseUrl(ptr: Long, url: String): Long
private external fun nativeContextConnect(ptr: Long, url: Long, username: String?, password: String?): Boolean
private external fun nativeContextGetError(ptr: Long): String
private external fun nativeContextOpenDir(ptr: Long, path: String): Long
+ private external fun nativeContextEntry(ptr: Long, path: String): DirEntry?
+ private external fun nativeContextMakeDir(ptr: Long, path: String): Boolean
+ private external fun nativeContextRemoveDir(ptr: Long, path: String): Boolean
+ private external fun nativeContextUnlink(ptr: Long, path: String): Boolean
+ private external fun nativeContextReadLink(otr: Long, path: String): String?
private external fun nativeUrlDestroy(ptr: Long)
private external fun nativeUrlPath(ptr: Long): String
private external fun nativeDirDestroy(ptr: Long)
+ private external fun nativeDirList(ptr: Long): Array<DirEntry>
}
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
index 05cc831..04aff44 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
@@ -12,21 +12,26 @@ internal class SambaConnection(uri: String, credentials: SambaCredentials) {
fun openDir(path: String): NativeSamba.Dir? =
if (connected) context.openDir(join(url!!.path(), path)) else null
+ fun entry(path: String): NativeSamba.DirEntry? =
+ if (connected) context.entry(join(url!!.path(), path)) else null
+
+ fun makeDir(path: String): Boolean =
+ connected && context.makeDir(join(url!!.path(), path))
+
+ fun removeDir(path: String): Boolean =
+ connected && context.removeDir(join(url!!.path(), path))
+
+ fun unlink(path: String): Boolean =
+ connected && context.unlink(join(url!!.path(), path))
+
+ fun readLink(path: String): String? =
+ if (connected) context.readLink(join(url!!.path(), path)) else null
+
companion object {
fun join(a: String, b: String): String {
if (a.isEmpty() || b.startsWith("/")) return b
if (b.isEmpty()) return a
return if (a.endsWith("/")) a + b else "${a}/${b}"
}
-
- fun dirname(path: String): String {
- val last = path.lastIndexOf('/')
- return if (last == -1) path else path.substring(last + 1)
- }
-
- fun basename(path: String): String {
- val last = path.lastIndexOf('/')
- return if (last < 1) "" else path.substring(0, last - 1)
- }
}
}
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt
index b82e745..d9ec6aa 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt
@@ -3,30 +3,61 @@ package org.the_jk.cleversync.io.samba
import androidx.lifecycle.LiveData
import org.the_jk.cleversync.io.Directory
import org.the_jk.cleversync.io.File
-import org.the_jk.cleversync.io.Link
import org.the_jk.cleversync.io.ModifiableDirectory
import org.the_jk.cleversync.io.ModifiableFile
import org.the_jk.cleversync.io.ModifiableLink
+import java.io.IOException
+import java.time.Instant
internal open class SambaDirectory(
private val conn: SambaConnection,
- private val dir: NativeSamba.Dir,
+ private val path: String,
+ override val name: String,
) : ModifiableDirectory {
override fun modifiableOpenDir(name: String): ModifiableDirectory? {
- val dir = conn.openDir(SambaConnection.join(dir.path, name)) ?: return null
- return SambaDirectory(conn, dir)
+ val newPath = SambaConnection.join(path, name)
+ val entry = conn.entry(newPath) ?: return null
+ if (entry.type != NativeSamba.DirEntryType.DIR) return null
+ return SambaDirectory(conn, newPath, name)
}
override fun modifiableOpenFile(name: String): ModifiableFile? {
- TODO("Not yet implemented")
+ val newPath = SambaConnection.join(path, name)
+ val entry = conn.entry(newPath) ?: return null
+ if (entry.type != NativeSamba.DirEntryType.FILE) return null
+ return SambaFile(conn, newPath, name, entry.size, entry.lastModified)
}
override fun modifiableOpenLink(name: String): ModifiableLink? {
- TODO("Not yet implemented")
+ val newPath = SambaConnection.join(path, name)
+ val entry = conn.entry(newPath) ?: return null
+ if (entry.type != NativeSamba.DirEntryType.LINK) return null
+ return SambaLink(conn, newPath, name)
}
override fun modifiableList(): ModifiableDirectory.Content {
- TODO("Not yet implemented")
+ val directories = mutableListOf<ModifiableDirectory>()
+ val files = mutableListOf<ModifiableFile>()
+ val links = mutableListOf<ModifiableLink>()
+ val dir = conn.openDir(path)
+ if (dir != null) {
+ dir.list().forEach { entry ->
+ val entryPath = SambaConnection.join(path, entry.name)
+ when (entry.type) {
+ NativeSamba.DirEntryType.DIR -> {
+ directories.add(SambaDirectory(conn, entryPath, entry.name))
+ }
+ NativeSamba.DirEntryType.FILE -> {
+ files.add(SambaFile(conn, entryPath, entry.name, entry.size, entry.lastModified))
+ }
+ NativeSamba.DirEntryType.LINK -> {
+ links.add(SambaLink(conn, entryPath, entry.name))
+ }
+ }
+ }
+ dir.destroy()
+ }
+ return ModifiableDirectory.Content(directories, files, links)
}
override fun modifiableLiveList(): LiveData<ModifiableDirectory.Content> {
@@ -34,52 +65,48 @@ internal open class SambaDirectory(
}
override fun createDirectory(name: String): ModifiableDirectory {
- TODO("Not yet implemented")
+ val newPath = SambaConnection.join(path, name)
+ if (!conn.makeDir(newPath)) throw IOException(conn.error)
+ return SambaDirectory(conn, newPath, name)
}
override fun createFile(name: String): ModifiableFile {
- TODO("Not yet implemented")
+ val newPath = SambaConnection.join(path, name)
+ return SambaFile(conn, newPath, name, 0UL, Instant.EPOCH, Instant.EPOCH)
}
override fun createLink(name: String, target: Directory): ModifiableLink {
- TODO("Not yet implemented")
+ throw IOException("Unsupported operation")
}
override fun createLink(name: String, target: File): ModifiableLink {
- TODO("Not yet implemented")
+ throw IOException("Unsupported operation")
}
override fun createLink(name: String, target: String): ModifiableLink {
- TODO("Not yet implemented")
+ throw IOException("Unsupported operation")
}
override fun removeDirectory(name: String): Boolean {
- TODO("Not yet implemented")
+ return conn.removeDir(SambaConnection.join(path, name))
}
override fun removeFile(name: String): Boolean {
- TODO("Not yet implemented")
+ return conn.unlink(SambaConnection.join(path, name))
}
override fun removeLink(name: String): Boolean {
- TODO("Not yet implemented")
+ return conn.unlink(SambaConnection.join(path, name))
}
- override val name: String
- get() = SambaConnection.dirname(dir.path)
-
override fun openDir(name: String) = modifiableOpenDir(name)
- override fun openFile(name: String): File? {
- TODO("Not yet implemented")
- }
+ override fun openFile(name: String) = modifiableOpenFile(name)
- override fun openLink(name: String): Link? {
- TODO("Not yet implemented")
- }
+ override fun openLink(name: String) = modifiableOpenLink(name)
- override fun list(): Directory.Content {
- TODO("Not yet implemented")
+ override fun list() = with(modifiableList()) {
+ Directory.Content(directories, files, links)
}
override fun liveList(): LiveData<Directory.Content> {
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt
new file mode 100644
index 0000000..817c1bf
--- /dev/null
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt
@@ -0,0 +1,40 @@
+package org.the_jk.cleversync.io.samba
+
+import org.the_jk.cleversync.io.ModifiableFile
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+import java.time.Instant
+
+internal class SambaFile(
+ private val conn: SambaConnection,
+ private val path: String,
+ override val name: String,
+ private val cachedSize: ULong,
+ private val cachedLastModified: Instant,
+ private var cacheEndOfLife: Instant = Instant.now().plusSeconds(60),
+) : ModifiableFile {
+ override fun write(): OutputStream {
+ TODO("Not yet implemented")
+ }
+
+ override fun read(): InputStream {
+ TODO("Not yet implemented")
+ }
+
+ override val size: ULong get() {
+ if (useCached()) return cachedSize
+ val entry = conn.entry(path) ?: throw IOException(conn.error)
+ return entry.size
+ }
+
+ override val lastModified: Instant get() {
+ if (useCached()) return cachedLastModified
+ val entry = conn.entry(path) ?: throw IOException(conn.error)
+ return entry.lastModified
+ }
+
+ private fun useCached(): Boolean {
+ return Instant.now().isBefore(cacheEndOfLife)
+ }
+}
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt
new file mode 100644
index 0000000..9d9fd1d
--- /dev/null
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt
@@ -0,0 +1,57 @@
+package org.the_jk.cleversync.io.samba
+
+import org.the_jk.cleversync.io.Directory
+import org.the_jk.cleversync.io.File
+import org.the_jk.cleversync.io.Link
+import org.the_jk.cleversync.io.ModifiableLink
+import java.io.IOException
+
+internal class SambaLink(
+ private val conn: SambaConnection,
+ private val path: String,
+ override val name: String,
+) : ModifiableLink {
+ override fun modifiableResolve(): ModifiableLink.ModifiableLinkTarget {
+ val (newPath, entry) = doResolve()
+ if (entry == null) return ModifiableLink.NoTarget
+ return when (entry.type) {
+ NativeSamba.DirEntryType.DIR -> ModifiableLink.ModifiableDirectoryTarget(SambaDirectory(conn, newPath, entry.name))
+ NativeSamba.DirEntryType.FILE -> ModifiableLink.ModifiableFileTarget(SambaFile(conn, newPath, entry.name, entry.size, entry.lastModified))
+ NativeSamba.DirEntryType.LINK -> ModifiableLink.NoTarget
+ }
+ }
+
+ override fun target(directory: Directory) {
+ throw IOException("Unsupported operation")
+ }
+
+ override fun target(file: File) {
+ throw IOException("Unsupported operation")
+ }
+
+ override fun target(name: String) {
+ throw IOException("Unsupported operation")
+ }
+
+ override fun resolve(): Link.LinkTarget {
+ val (newPath, entry) = doResolve()
+ if (entry == null) return Link.NoTarget
+ return when (entry.type) {
+ NativeSamba.DirEntryType.DIR -> Link.DirectoryTarget(SambaDirectory(conn, newPath, entry.name))
+ NativeSamba.DirEntryType.FILE -> Link.FileTarget(SambaFile(conn, newPath, entry.name, entry.size, entry.lastModified))
+ NativeSamba.DirEntryType.LINK -> Link.NoTarget
+ }
+ }
+
+ private fun doResolve(): Pair<String, NativeSamba.DirEntry?> {
+ var linkPath = path
+ var entry: NativeSamba.DirEntry? = null
+ while (true) {
+ val target = conn.readLink(linkPath) ?: break
+ linkPath = SambaConnection.join(linkPath, target)
+ entry = conn.entry(linkPath) ?: break
+ if (entry.type != NativeSamba.DirEntryType.LINK) break
+ }
+ return linkPath to entry
+ }
+}
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaTree.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaTree.kt
index 8b4a86d..762a61f 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaTree.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaTree.kt
@@ -3,7 +3,7 @@ package org.the_jk.cleversync.io.samba
import android.content.res.Resources
import org.the_jk.cleversync.io.ModifiableTree
-internal class SambaTree(conn: SambaConnection, root: NativeSamba.Dir) : SambaDirectory(conn, root), ModifiableTree {
+internal class SambaTree(conn: SambaConnection, root: String) : SambaDirectory(conn, root, ""), ModifiableTree {
override fun description(resources: Resources): CharSequence {
TODO("Not yet implemented")
}
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/samba/SambaTreeFactory.kt b/libs/samba/src/main/java/org/the_jk/cleversync/samba/SambaTreeFactory.kt
index 5c9bc4f..ebf47d2 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/samba/SambaTreeFactory.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/samba/SambaTreeFactory.kt
@@ -12,7 +12,6 @@ object SambaTreeFactory {
fun modifiableTree(uri: String, credentials: SambaCredentials): Result<ModifiableTree> {
val connection = SambaConnection(uri, credentials)
if (!connection.connected) return Result.failure(Exception(connection.error))
- val root = connection.openDir("") ?: return Result.failure(Exception(connection.error))
- return Result.success(SambaTree(connection, root))
+ return Result.success(SambaTree(connection, ""))
}
}