Skip to content

Commit

Permalink
Merge pull request #4174 from TranceLove/bugfix/3656
Browse files Browse the repository at this point in the history
Add explicit TLS option for FTPSClient
  • Loading branch information
VishalNehra committed May 19, 2024
2 parents 64c6a34 + 1ec0a31 commit 734a49f
Show file tree
Hide file tree
Showing 22 changed files with 776 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ public LoadFilesListTask(
case SMB:
list = listSmb(hFile, mainActivityViewModel, mainFragment);
break;
case FTP:
case SFTP:
list = listSftp(mainActivityViewModel);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class FtpAuthenticationTask(
private val certInfo: JSONObject?,
private val username: String,
private val password: String?,
private val explicitTls: Boolean = false,
) : Task<FTPClient, FtpAuthenticationTaskCallable> {
override fun getTask(): FtpAuthenticationTaskCallable {
return if (protocol == FTP_URI_PREFIX) {
Expand All @@ -54,6 +55,7 @@ class FtpAuthenticationTask(
certInfo!!,
username,
password ?: "",
explicitTls,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ package com.amaze.filemanager.asynchronous.asynctasks.ftp.auth

import com.amaze.filemanager.application.AppConfig
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl.Companion.ARG_TLS
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl.Companion.TLS_EXPLICIT
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTPS_URI_PREFIX
import com.amaze.filemanager.utils.PasswordUtil
Expand All @@ -39,6 +41,7 @@ class FtpsAuthenticationTaskCallable(
private val certInfo: JSONObject,
username: String,
password: String,
private val explicitTls: Boolean,
) : FtpAuthenticationTaskCallable(hostname, port, username, password) {
override fun call(): FTPClient {
val ftpClient = createFTPClient() as FTPSClient
Expand Down Expand Up @@ -71,8 +74,15 @@ class FtpsAuthenticationTaskCallable(

@Suppress("LabeledExpression")
override fun createFTPClient(): FTPClient {
val uri =
buildString {
append(FTPS_URI_PREFIX)
if (explicitTls) {
append("?$ARG_TLS=$TLS_EXPLICIT")
}
}
return (
NetCopyClientConnectionPool.ftpClientFactory.create(FTPS_URI_PREFIX)
NetCopyClientConnectionPool.ftpClientFactory.create(uri.toString())
as FTPSClient
).apply {
this.hostnameVerifier =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ import java.lang.ref.WeakReference
class FtpsGetHostCertificateTask(
private val host: String,
private val port: Int,
private val explicitTls: Boolean = false,
context: Context,
callback: (JSONObject) -> Unit,
) : AbstractGetHostInfoTask<JSONObject, FtpsGetHostCertificateTaskCallable>(host, port, callback) {
val ctx: WeakReference<Context> = WeakReference(context)

override fun getTask(): FtpsGetHostCertificateTaskCallable = FtpsGetHostCertificateTaskCallable(host, port)
override fun getTask(): FtpsGetHostCertificateTaskCallable = FtpsGetHostCertificateTaskCallable(host, port, explicitTls)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
package com.amaze.filemanager.asynchronous.asynctasks.ftp.hostcert

import androidx.annotation.WorkerThread
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl.Companion.ARG_TLS
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl.Companion.TLS_EXPLICIT
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.CONNECT_TIMEOUT
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTPS_URI_PREFIX
Expand All @@ -34,6 +36,7 @@ import javax.net.ssl.HostnameVerifier
open class FtpsGetHostCertificateTaskCallable(
private val hostname: String,
private val port: Int,
private val explicitTls: Boolean = false,
) : Callable<JSONObject> {
@WorkerThread
override fun call(): JSONObject? {
Expand All @@ -57,5 +60,12 @@ open class FtpsGetHostCertificateTaskCallable(
return result
}

protected open fun createFTPClient(): FTPSClient = NetCopyClientConnectionPool.ftpClientFactory.create(FTPS_URI_PREFIX) as FTPSClient
protected open fun createFTPClient(): FTPSClient =
NetCopyClientConnectionPool.ftpClientFactory.create(
if (explicitTls) {
"$FTPS_URI_PREFIX?$ARG_TLS=$TLS_EXPLICIT"
} else {
FTPS_URI_PREFIX
},
) as FTPSClient
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ class FTPClientImpl(private val ftpClient: FTPClient) : NetCopyClient<FTPClient>
@JvmStatic
private val logger: Logger = LoggerFactory.getLogger(FTPClientImpl::class.java)

@JvmStatic
val ANONYMOUS = "anonymous"
const val ANONYMOUS = "anonymous"

const val ARG_TLS = "tls"

const val TLS_EXPLICIT = "explicit"

private const val ALPHABET = "abcdefghijklmnopqrstuvwxyz1234567890"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ import com.amaze.filemanager.application.AppConfig
import com.amaze.filemanager.asynchronous.asynctasks.ftp.auth.FtpAuthenticationTask
import com.amaze.filemanager.asynchronous.asynctasks.ssh.PemToKeyPairObservable
import com.amaze.filemanager.asynchronous.asynctasks.ssh.SshAuthenticationTask
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl.Companion.ARG_TLS
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl.Companion.TLS_EXPLICIT
import com.amaze.filemanager.filesystem.ftp.NetCopyClientUtils.extractBaseUriFrom
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.QUESTION_MARK
import io.reactivex.Flowable
import io.reactivex.Maybe
import io.reactivex.Observable.create
Expand Down Expand Up @@ -131,6 +134,7 @@ object NetCopyClientConnectionPool {
username: String,
password: String? = null,
keyPair: KeyPair? = null,
explicitTls: Boolean = false,
): NetCopyClient<*>? {
val url =
NetCopyClientUtils.deriveUriFrom(
Expand All @@ -140,6 +144,7 @@ object NetCopyClientConnectionPool {
"",
username,
password,
explicitTls,
)
var client = connections[url]
if (client == null) {
Expand All @@ -152,6 +157,7 @@ object NetCopyClientConnectionPool {
username,
password,
keyPair,
explicitTls,
)
if (client != null) connections[url] = client
} else {
Expand Down Expand Up @@ -182,7 +188,8 @@ object NetCopyClientConnectionPool {
String,
String?,
KeyPair?,
) -> NetCopyClient<*>? = { protocol, host, port, hostFingerprint, username, password, keyPair ->
Boolean,
) -> NetCopyClient<*>? = { protocol, host, port, hostFingerprint, username, password, keyPair, explicitTls ->
if (protocol == SSH_URI_PREFIX) {
createSshClient(host, port, hostFingerprint!!, username, password, keyPair)
} else {
Expand All @@ -193,6 +200,7 @@ object NetCopyClientConnectionPool {
hostFingerprint?.let { JSONObject(it) },
username,
password,
explicitTls,
)
}
}
Expand Down Expand Up @@ -354,6 +362,8 @@ object NetCopyClientConnectionPool {
certInfo?.let { JSONObject(it) },
username,
password,
true == arguments?.containsKey(ARG_TLS) &&
TLS_EXPLICIT == arguments?.get(ARG_TLS),
)
}
}
Expand All @@ -366,6 +376,7 @@ object NetCopyClientConnectionPool {
certInfo: JSONObject?,
username: String,
password: String?,
explicitTls: Boolean = false,
): NetCopyClient<FTPClient>? {
val task =
FtpAuthenticationTask(
Expand All @@ -375,6 +386,7 @@ object NetCopyClientConnectionPool {
certInfo,
username,
password,
explicitTls,
)
val latch = CountDownLatch(1)
var result: FTPClient? = null
Expand Down Expand Up @@ -445,7 +457,11 @@ object NetCopyClientConnectionPool {
override fun create(uri: String): FTPClient {
return (
if (uri.startsWith(FTPS_URI_PREFIX)) {
FTPSClient("TLS", true)
FTPSClient(
"TLS",
!uri.contains(QUESTION_MARK) ||
!uri.substringAfter(QUESTION_MARK).contains("$ARG_TLS=$TLS_EXPLICIT"),
)
} else {
FTPClient()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ import com.amaze.filemanager.application.AppConfig
import com.amaze.filemanager.fileoperations.filesystem.DOESNT_EXIST
import com.amaze.filemanager.fileoperations.filesystem.FolderState
import com.amaze.filemanager.fileoperations.filesystem.WRITABLE_ON_REMOTE
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl.Companion.ARG_TLS
import com.amaze.filemanager.filesystem.ftp.FTPClientImpl.Companion.TLS_EXPLICIT
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTPS_DEFAULT_PORT
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTPS_URI_PREFIX
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTP_DEFAULT_PORT
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTP_URI_PREFIX
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.SSH_DEFAULT_PORT
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.SSH_URI_PREFIX
import com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.getConnection
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.AND
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.AT
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.COLON
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.QUESTION_MARK
import com.amaze.filemanager.filesystem.ftp.NetCopyConnectionInfo.Companion.SLASH
import com.amaze.filemanager.filesystem.smb.CifsContexts.SMB_URI_PREFIX
import com.amaze.filemanager.filesystem.ssh.SFtpClientTemplate
Expand Down Expand Up @@ -177,6 +181,10 @@ object NetCopyClientUtils {
if (it.port > 0) {
append(COLON).append(it.port)
}
if (!it.arguments.isNullOrEmpty()) {
append(QUESTION_MARK)
.append(it.arguments?.entries?.joinToString(AND.toString()))
}
}
}
}
Expand Down Expand Up @@ -230,11 +238,13 @@ object NetCopyClientUtils {
defaultPath: String? = null,
username: String,
password: String? = null,
explicitTls: Boolean = false,
edit: Boolean = false,
): String {
// FIXME: should be caller's responsibility
var pathSuffix = defaultPath
if (pathSuffix == null) pathSuffix = SLASH.toString()
if (explicitTls) pathSuffix = "$pathSuffix?$ARG_TLS=$TLS_EXPLICIT"
val thisPassword =
if (password == "" || password == null) {
""
Expand All @@ -245,7 +255,7 @@ object NetCopyClientUtils {
password.urlEncoded()
}}"
}
return if (username == "" && (true == password?.isEmpty())) {
return if (username == "") {
"$prefix$hostname:$port$pathSuffix"
} else {
"$prefix$username$thisPassword@$hostname:$port$pathSuffix"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class NetCopyConnectionInfo(url: String) {
const val AT = '@'
const val SLASH = '/'
const val COLON = ':'
const val QUESTION_MARK = '?'
}

init {
Expand Down Expand Up @@ -157,7 +158,7 @@ class NetCopyConnectionInfo(url: String) {
}

override fun toString(): String {
return if (username.isNotEmpty()) {
return if (username.isNotBlank() && username.isNotEmpty()) {
"$prefix$username@$host${if (port == 0) "" else ":$port"}${defaultPath ?: ""}"
} else {
"$prefix$host${if (port == 0) "" else ":$port"}${defaultPath ?: ""}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
import static com.amaze.filemanager.fileoperations.filesystem.OperationTypeKt.RENAME;
import static com.amaze.filemanager.fileoperations.filesystem.OperationTypeKt.SAVE_FILE;
import static com.amaze.filemanager.fileoperations.filesystem.OperationTypeKt.UNDEFINED;
import static com.amaze.filemanager.filesystem.ftp.FTPClientImpl.ARG_TLS;
import static com.amaze.filemanager.filesystem.ftp.FTPClientImpl.TLS_EXPLICIT;
import static com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTPS_URI_PREFIX;
import static com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.FTP_URI_PREFIX;
import static com.amaze.filemanager.filesystem.ftp.NetCopyClientConnectionPool.SSH_URI_PREFIX;
import static com.amaze.filemanager.ui.dialogs.SftpConnectDialog.ARG_ADDRESS;
import static com.amaze.filemanager.ui.dialogs.SftpConnectDialog.ARG_DEFAULT_PATH;
import static com.amaze.filemanager.ui.dialogs.SftpConnectDialog.ARG_EDIT;
Expand Down Expand Up @@ -2114,24 +2119,36 @@ public void showSftpDialog(String name, String path, boolean edit) {
(Function1<String, String>)
s -> GenericExtKt.urlDecoded(s, Charsets.UTF_8)));
}
retval.putString(ARG_USERNAME, connectionInfo.getUsername());
if (!TextUtils.isEmpty(connectionInfo.getUsername())) {
retval.putString(ARG_USERNAME, connectionInfo.getUsername());
}

if (connectionInfo.getPassword() == null) {
retval.putBoolean(ARG_HAS_PASSWORD, false);
retval.putString(ARG_KEYPAIR_NAME, utilsHandler.getSshAuthPrivateKeyName(path));
if (SSH_URI_PREFIX.equals(connectionInfo.getPrefix())) {
retval.putString(ARG_KEYPAIR_NAME, utilsHandler.getSshAuthPrivateKeyName(path));
}
} else {
retval.putBoolean(ARG_HAS_PASSWORD, true);
retval.putString(ARG_PASSWORD, connectionInfo.getPassword());
}
retval.putBoolean(ARG_EDIT, edit);

if ((FTP_URI_PREFIX.equals(connectionInfo.getPrefix())
|| FTPS_URI_PREFIX.equals(connectionInfo.getPrefix()))
&& connectionInfo.getArguments() != null
&& TLS_EXPLICIT.equals(connectionInfo.getArguments().get(ARG_TLS))) {
retval.putString(ARG_TLS, TLS_EXPLICIT);
}

return Flowable.just(retval);
})
.subscribeOn(Schedulers.computation())
.subscribe(
bundle -> {
sftpConnectDialog.setArguments(bundle);
sftpConnectDialog.setCancelable(true);
sftpConnectDialog.show(getSupportFragmentManager(), "sftpdialog");
sftpConnectDialog.show(getSupportFragmentManager(), SftpConnectDialog.TAG);
});
}

Expand Down

0 comments on commit 734a49f

Please sign in to comment.