diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt index 188891bd..124f2d0a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt @@ -13,6 +13,8 @@ import com.github.ajalt.timberkt.e import com.zeapo.pwdstore.PasswordStore import com.zeapo.pwdstore.R import com.zeapo.pwdstore.git.config.SshjSessionFactory +import net.schmizz.sshj.common.DisconnectReason +import net.schmizz.sshj.common.SSHException import net.schmizz.sshj.userauth.UserAuthException import org.eclipse.jgit.api.CommitCommand import org.eclipse.jgit.api.GitCommand @@ -127,14 +129,34 @@ class GitAsyncTask( return rootCause } + private fun isExplicitlyUserInitiatedError(e: Exception): Boolean { + var cause: Exception? = e + while (cause != null) { + if (cause is SSHException && + cause.disconnectReason == DisconnectReason.AUTH_CANCELLED_BY_USER) + return true + cause = cause.cause as? Exception + } + return false + } + override fun onPostExecute(maybeResult: Result?) { dialog.dismiss() when (val result = maybeResult ?: Result.Err(IOException("Unexpected error"))) { is Result.Err -> { - e(result.err) - operation.onError(rootCauseException(result.err)) - if (finishWithResultOnEnd != null) { - activity?.setResult(Activity.RESULT_CANCELED) + if (isExplicitlyUserInitiatedError(result.err)) { + // Currently, this is only executed when the user cancels a password prompt + // during authentication. + if (finishWithResultOnEnd != null) { + activity?.setResult(Activity.RESULT_CANCELED) + activity?.finish() + } + } else { + e(result.err) + operation.onError(rootCauseException(result.err)) + if (finishWithResultOnEnd != null) { + activity?.setResult(Activity.RESULT_CANCELED) + } } } is Result.Ok -> { diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt b/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt index 4d961137..f643c714 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt @@ -12,6 +12,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import net.schmizz.sshj.SSHClient import net.schmizz.sshj.common.Buffer.PlainBuffer +import net.schmizz.sshj.common.DisconnectReason +import net.schmizz.sshj.common.SSHException import net.schmizz.sshj.common.SSHRuntimeException import net.schmizz.sshj.common.SecurityUtils import net.schmizz.sshj.connection.channel.direct.Session @@ -54,12 +56,10 @@ abstract class InteractivePasswordFinder : PasswordFinder { abstract fun askForPassword(cont: Continuation, isRetry: Boolean) private var isRetry = false - private var shouldRetry = true private var lastPassword: CharArray? = null fun resetForReuse() { isRetry = false - shouldRetry = true } fun clearPassword() { @@ -82,15 +82,11 @@ abstract class InteractivePasswordFinder : PasswordFinder { } } isRetry = true - return if (password != null) { - password.toCharArray().also { lastPassword = it } - } else { - shouldRetry = false - CharArray(0) - } + return password?.toCharArray()?.also { lastPassword = it } + ?: throw SSHException(DisconnectReason.AUTH_CANCELLED_BY_USER) } - final override fun shouldRetry(resource: Resource<*>?) = shouldRetry + final override fun shouldRetry(resource: Resource<*>?) = true } class SshjSessionFactory(private val username: String, private val authData: SshAuthData, private val hostKeyFile: File) : SshSessionFactory() {