diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.kt index aa2259f8..c1c18a00 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/SimpleSftpServer.kt @@ -7,6 +7,7 @@ package org.kde.kdeconnect.Plugins.SftpPlugin import android.content.Context +import android.net.Uri import android.os.Build import android.util.Log import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory @@ -26,6 +27,7 @@ import org.apache.sshd.sftp.server.SftpFileSystemAccessor import org.apache.sshd.sftp.server.SftpSubsystemFactory import org.apache.sshd.sftp.server.SftpSubsystemProxy import org.kde.kdeconnect.Device +import org.kde.kdeconnect.Helpers.MediaStoreHelper import org.kde.kdeconnect.Helpers.RandomHelper import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper import org.kde.kdeconnect.Helpers.SecurityHelpers.constantTimeCompare @@ -33,10 +35,13 @@ import org.kde.kdeconnect.Plugins.SftpPlugin.saf.SafFileSystemFactory import org.kde.kdeconnect.Plugins.SftpPlugin.saf.SafPath import org.slf4j.impl.HandroidLoggerAdapter import java.io.IOException +import java.nio.channels.Channel import java.nio.channels.SeekableByteChannel import java.nio.charset.StandardCharsets +import java.nio.file.CopyOption import java.nio.file.OpenOption import java.nio.file.Path +import java.nio.file.StandardOpenOption import java.nio.file.attribute.FileAttribute import java.security.GeneralSecurityException import java.security.KeyPair @@ -103,6 +108,17 @@ internal class SimpleSftpServer { sshd.subsystemFactories = listOf(SftpSubsystemFactory.Builder().apply { withFileSystemAccessor(object : SftpFileSystemAccessor { + fun notifyMediaStore(path: Path) { + kotlin.runCatching { + val uri = Uri.parse(path.toUri().toString()) + MediaStoreHelper.indexFile(context, uri) + uri + }.fold( + onSuccess = { Log.i(TAG, "Notified media store: $path, $it") }, + onFailure = { Log.w(TAG, "Failed to notify media store: $path", it) } + ) + } + override fun openFile( subsystem: SftpSubsystemProxy?, fileHandle: FileHandle?, @@ -116,6 +132,61 @@ internal class SimpleSftpServer { } return super.openFile(subsystem, fileHandle, file, handle, options, *attrs) } + + override fun removeFile( + subsystem: SftpSubsystemProxy?, + path: Path?, + isDirectory: Boolean + ) { + super.removeFile(subsystem, path, isDirectory) + path?.let { notifyMediaStore(it) } + } + + override fun copyFile( + subsystem: SftpSubsystemProxy?, + src: Path?, + dst: Path?, + opts: MutableCollection? + ) { + super.copyFile(subsystem, src, dst, opts) + dst?.let { notifyMediaStore(it) } + } + + override fun renameFile( + subsystem: SftpSubsystemProxy?, + oldPath: Path?, + newPath: Path?, + opts: MutableCollection? + ) { + super.renameFile(subsystem, oldPath, newPath, opts) + oldPath?.let { notifyMediaStore(it) } + newPath?.let { notifyMediaStore(it) } + } + + override fun createLink( + subsystem: SftpSubsystemProxy?, + link: Path?, + existing: Path?, + symLink: Boolean + ) { + super.createLink(subsystem, link, existing, symLink) + link?.let { notifyMediaStore(it) } + existing?.let { notifyMediaStore(it) } + } + + override fun closeFile( + subsystem: SftpSubsystemProxy?, + fileHandle: FileHandle?, + file: Path?, + handle: String?, + channel: Channel?, + options: MutableSet? + ) { + super.closeFile(subsystem, fileHandle, file, handle, channel, options) + if (options?.contains(StandardOpenOption.WRITE) == true) { + file?.let { notifyMediaStore(it) } + } + } }) }.build()) @@ -189,8 +260,9 @@ internal class SimpleSftpServer { } companion object { - private val PORT_RANGE = 1739..1764 + private const val TAG = "SimpleSftpServer" + private val PORT_RANGE = 1739..1764 const val USER: String = "kdeconnect" init { diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystemProvider.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystemProvider.kt index 452481fd..a92e0b52 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystemProvider.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafFileSystemProvider.kt @@ -98,7 +98,6 @@ class SafFileSystemProvider( val docFile = parent.createFile(Files.probeContentType(path), path.names.last()) ?: throw IOException("Failed to create $path") val uri = docFile.uri - MediaStoreHelper.indexFile(context, uri) path.safUri = uri return uri } @@ -226,7 +225,6 @@ class SafFileSystemProvider( if (!docFile.delete()) { throw IOException("Failed to delete $path") } - MediaStoreHelper.indexFile(context, docFile.uri) } override fun copy(source: Path, target: Path, vararg options: CopyOption) { @@ -272,8 +270,6 @@ class SafFileSystemProvider( if (newUri == null) { // renameDocument returns null on failure return@firstStep } - MediaStoreHelper.indexFile(context, sourceUri) - MediaStoreHelper.indexFile(context, newUri) source.safUri = newUri return } catch (ignored: FileNotFoundException) { @@ -295,8 +291,6 @@ class SafFileSystemProvider( parentUri, destParentUri ) - MediaStoreHelper.indexFile(context, sourceUri) - MediaStoreHelper.indexFile(context, newUri) source.safUri = newUri!! return } diff --git a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafPath.kt b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafPath.kt index 317f638f..bc38d4f8 100644 --- a/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafPath.kt +++ b/src/org/kde/kdeconnect/Plugins/SftpPlugin/saf/SafPath.kt @@ -7,9 +7,9 @@ package org.kde.kdeconnect.Plugins.SftpPlugin.saf import android.content.Context import android.net.Uri -import android.util.Log import androidx.documentfile.provider.DocumentFile import org.apache.sshd.common.file.util.BasePath +import java.net.URI import java.nio.file.LinkOption class SafPath( @@ -21,6 +21,10 @@ class SafPath( return this.normalize() } + override fun toUri(): URI { + return URI.create(safUri.toString()) ?: throw IllegalStateException("SafUri is null") + } + fun getDocumentFile(ctx: Context): DocumentFile? { if (safUri == null) return null return DocumentFile.fromTreeUri(ctx, safUri!!)