aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.kt
diff options
context:
space:
mode:
authorHarsh Shandilya <msfjarvis@gmail.com>2020-04-17 18:36:07 +0530
committerGitHub <noreply@github.com>2020-04-17 18:36:07 +0530
commitb94b52a42ddc2325b539d0956fd0adcf35308b52 (patch)
tree21f35526cfc444db82e1b4b08d083e49a3c1843b /app/src/main/java/com/zeapo/pwdstore/git/GitActivity.kt
parent4ffd7ed9bffa5139277ffb91de5a69f2b714222c (diff)
downloadHardenedPass-b94b52a42ddc2325b539d0956fd0adcf35308b52.tar.gz
HardenedPass-b94b52a42ddc2325b539d0956fd0adcf35308b52.zip
Refactor Git related activities (#685)
* Refactor git logic into separate parts * Extract hardcoded strings * Add KDoc to updateHostname, remove unused field * Cleanups * Fix dialog message * Wire in repository clone flow * spotless * Remove unused method * Cleanup GitActivity - Rename to GitOperationActivity. - Ensure identityBuilder is always closed regardless of what fragment uses it. - Remove hardcoded "Operation" strings and replace with REQUEST_ARG_OP. - Apply a transparent theme to GitOperationActivity make the UI less jarring. * Tweak some stupidly worded dialog messages As pointed out in #629, these strings are shoddily worded and do not express any clear intent to the user, leaving them confused and angry. * GitOperationActivity: wrap Context to ensure right theme is used * spotless * undo build.gradle change * Use correct parent theme, remove now useless wrapping * GitServerConfigActivity: fix repository clone flow * temp: disable leakcanary framework leaks on Samsung are pissing me off * Make system bars transparent in git activity * Tweak HTTPS password layout * Unhardcode wrong passphrase string * Store SSH passphrase in EncryptedSharedPreferences Also revamp the dialog to look a bit better * Implement support for remembering HTTPS password Fixes #521 * Try to patch HTTPS remote creation logic * Update security-crypto * Clear saved passphrase/password on auth failure * Revert "Update security-crypto" Broken on R DP2.1 This reverts commit 4b20371dd42c512a3dd3b759859abb6c1ffd2961. * Revert "temp: disable leakcanary" This reverts commit 2db7d41bd67b79c6dc8c5b359a7b27100379f45f. * Update CHANGELOG * Remove spacer * Remove useless override * Wrap git server activity in a ScrollView * GitOperation: always finish calling activity when dialogs are dismissed * Wipe saved password/passphrase when hostname changes * Don't commit prefs updates * Don't call listFiles excessively * Finish activity after saving configuration * Make ConnectionMode and Protocol enum classes * Change SSH key passphrase key, don't wipe on host change * Reimplement BaseGitActivity.updateUrl (was updateHostname) * Use SharedPreferences.edit KTX extension * Disable inapplicable connection modes depending on scheme * BaseGitActivity: annotate onDestroy with CallSuper We'll leak the identityBuilder connection otherwise * Move input hack for AlertDialog into an extension function We re-use this in many places * Fix protocol/mode toggle issue and consistenly name options * Fix a crash when opening GitServerConfigActivity without a repo * Fix OpenKeychain callbacks by moving onActivityResult to BaseGitActivity * Run spotlessApply Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> Co-authored-by: Fabian Henneke <fabian@henneke.me>
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/git/GitActivity.kt')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/GitActivity.kt670
1 files changed, 0 insertions, 670 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.kt
deleted file mode 100644
index 6341fbe1..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/git/GitActivity.kt
+++ /dev/null
@@ -1,670 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.git
-
-import android.content.Context
-import android.content.Intent
-import android.content.SharedPreferences
-import android.os.Bundle
-import android.text.Editable
-import android.text.TextWatcher
-import android.view.Menu
-import android.view.MenuItem
-import android.view.View
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import android.widget.Spinner
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.AppCompatTextView
-import androidx.preference.PreferenceManager
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.textfield.TextInputEditText
-import com.zeapo.pwdstore.R
-import com.zeapo.pwdstore.UserPreference
-import com.zeapo.pwdstore.git.config.SshApiSessionFactory
-import com.zeapo.pwdstore.utils.PasswordRepository
-import java.io.File
-import java.io.IOException
-import java.util.regex.Pattern
-import org.apache.commons.io.FileUtils
-import org.eclipse.jgit.lib.Constants
-import timber.log.Timber
-
-open class GitActivity : AppCompatActivity() {
- private lateinit var context: Context
- private lateinit var settings: SharedPreferences
- private lateinit var protocol: String
- private lateinit var connectionMode: String
- private lateinit var hostname: String
- private var identityBuilder: SshApiSessionFactory.IdentityBuilder? = null
- private var identity: SshApiSessionFactory.ApiIdentity? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- context = requireNotNull(this)
-
- settings = PreferenceManager.getDefaultSharedPreferences(this)
-
- protocol = settings.getString("git_remote_protocol", null) ?: "ssh://"
- connectionMode = settings.getString("git_remote_auth", null) ?: "ssh-key"
- hostname = settings.getString("git_remote_location", null) ?: ""
- val operationCode = intent.extras!!.getInt("Operation")
-
- supportActionBar!!.setDisplayHomeAsUpEnabled(true)
-
- when (operationCode) {
- REQUEST_CLONE, EDIT_SERVER -> {
- setContentView(R.layout.activity_git_clone)
- setTitle(R.string.title_activity_git_clone)
-
- val protcolSpinner = findViewById<Spinner>(R.id.clone_protocol)
- val connectionModeSpinner = findViewById<Spinner>(R.id.connection_mode)
-
- // init the spinner for connection modes
- val connectionModeAdapter = ArrayAdapter.createFromResource(this,
- R.array.connection_modes, android.R.layout.simple_spinner_item)
- connectionModeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
- connectionModeSpinner.adapter = connectionModeAdapter
- connectionModeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) {
- val selection = (findViewById<View>(R.id.connection_mode) as Spinner).selectedItem.toString()
- connectionMode = selection
- settings.edit().putString("git_remote_auth", selection).apply()
- }
-
- override fun onNothingSelected(adapterView: AdapterView<*>) {
- }
- }
-
- // init the spinner for protocols
- val protocolAdapter = ArrayAdapter.createFromResource(this,
- R.array.clone_protocols, android.R.layout.simple_spinner_item)
- protocolAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
- protcolSpinner.adapter = protocolAdapter
- protcolSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) {
- protocol = (findViewById<View>(R.id.clone_protocol) as Spinner).selectedItem.toString()
- if (protocol == "ssh://") {
-
- // select ssh-key auth mode as default and enable the spinner in case it was disabled
- connectionModeSpinner.setSelection(0)
- connectionModeSpinner.isEnabled = true
-
- // however, if we have some saved that, that's more important!
- when {
- connectionMode.equals("ssh-key", ignoreCase = true) -> connectionModeSpinner.setSelection(0)
- connectionMode.equals("OpenKeychain", ignoreCase = true) -> connectionModeSpinner.setSelection(2)
- else -> connectionModeSpinner.setSelection(1)
- }
- } else {
- // select user/pwd auth-mode and disable the spinner
- connectionModeSpinner.setSelection(1)
- connectionModeSpinner.isEnabled = false
- }
-
- updateURI()
- }
-
- override fun onNothingSelected(adapterView: AdapterView<*>) {
- }
- }
-
- if (protocol == "ssh://") {
- protcolSpinner.setSelection(0)
- } else {
- protcolSpinner.setSelection(1)
- }
-
- // init the server information
- val serverUrl = findViewById<TextInputEditText>(R.id.server_url)
- val serverPort = findViewById<TextInputEditText>(R.id.server_port)
- val serverPath = findViewById<TextInputEditText>(R.id.server_path)
- val serverUser = findViewById<TextInputEditText>(R.id.server_user)
- val serverUri = findViewById<TextInputEditText>(R.id.clone_uri)
-
- serverUrl.setText(settings.getString("git_remote_server", ""))
- serverPort.setText(settings.getString("git_remote_port", ""))
- serverUser.setText(settings.getString("git_remote_username", ""))
- serverPath.setText(settings.getString("git_remote_location", ""))
-
- serverUrl.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {}
-
- override fun onTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {
- if (serverUrl.isFocused)
- updateURI()
- }
-
- override fun afterTextChanged(editable: Editable) {}
- })
- serverPort.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {}
-
- override fun onTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {
- if (serverPort.isFocused)
- updateURI()
- }
-
- override fun afterTextChanged(editable: Editable) {}
- })
- serverUser.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {}
-
- override fun onTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {
- if (serverUser.isFocused)
- updateURI()
- }
-
- override fun afterTextChanged(editable: Editable) {}
- })
- serverPath.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {}
-
- override fun onTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {
- if (serverPath.isFocused)
- updateURI()
- }
-
- override fun afterTextChanged(editable: Editable) {}
- })
-
- serverUri.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {}
-
- override fun onTextChanged(charSequence: CharSequence, i: Int, i2: Int, i3: Int) {
- if (serverUri.isFocused)
- splitURI()
- }
-
- override fun afterTextChanged(editable: Editable) {}
- })
-
- if (operationCode == EDIT_SERVER) {
- findViewById<View>(R.id.clone_button).visibility = View.INVISIBLE
- findViewById<View>(R.id.save_button).visibility = View.VISIBLE
- } else {
- findViewById<View>(R.id.clone_button).visibility = View.VISIBLE
- findViewById<View>(R.id.save_button).visibility = View.INVISIBLE
- }
-
- updateURI()
- }
- EDIT_GIT_CONFIG -> {
- setContentView(R.layout.activity_git_config)
- setTitle(R.string.title_activity_git_config)
-
- showGitConfig()
- }
- REQUEST_PULL -> syncRepository(REQUEST_PULL)
-
- REQUEST_PUSH -> syncRepository(REQUEST_PUSH)
-
- REQUEST_SYNC -> syncRepository(REQUEST_SYNC)
- }
- }
-
- /**
- * Fills in the server_uri field with the information coming from other fields
- */
- private fun updateURI() {
- val uri = findViewById<TextInputEditText>(R.id.clone_uri)
- val serverUrl = findViewById<TextInputEditText>(R.id.server_url)
- val serverPort = findViewById<TextInputEditText>(R.id.server_port)
- val serverPath = findViewById<TextInputEditText>(R.id.server_path)
- val serverUser = findViewById<TextInputEditText>(R.id.server_user)
-
- if (uri != null) {
- when (protocol) {
- "ssh://" -> {
- var hostname = (serverUser.text.toString() +
- "@" +
- serverUrl.text.toString().trim { it <= ' ' } +
- ":")
- if (serverPort.text.toString() == "22") {
- hostname += serverPath.text.toString()
-
- findViewById<View>(R.id.warn_url).visibility = View.GONE
- } else {
- val warnUrl = findViewById<AppCompatTextView>(R.id.warn_url)
- if (!serverPath.text.toString().matches("/.*".toRegex()) && serverPort.text.toString().isNotEmpty()) {
- warnUrl.setText(R.string.warn_malformed_url_port)
- warnUrl.visibility = View.VISIBLE
- } else {
- warnUrl.visibility = View.GONE
- }
- hostname += serverPort.text.toString() + serverPath.text.toString()
- }
-
- if (hostname != "@:") uri.setText(hostname)
- }
- "https://" -> {
- val hostname = StringBuilder()
- hostname.append(serverUrl.text.toString().trim { it <= ' ' })
-
- if (serverPort.text.toString() == "443") {
- hostname.append(serverPath.text.toString())
-
- findViewById<View>(R.id.warn_url).visibility = View.GONE
- } else {
- hostname.append("/")
- hostname.append(serverPort.text.toString())
- .append(serverPath.text.toString())
- }
-
- if (hostname.toString() != "@/") uri.setText(hostname)
- }
- else -> {
- }
- }
- }
- }
-
- /**
- * Splits the information in server_uri into the other fields
- */
- private fun splitURI() {
- val serverUri = findViewById<TextInputEditText>(R.id.clone_uri)
- val serverUrl = findViewById<TextInputEditText>(R.id.server_url)
- val serverPort = findViewById<TextInputEditText>(R.id.server_port)
- val serverPath = findViewById<TextInputEditText>(R.id.server_path)
- val serverUser = findViewById<TextInputEditText>(R.id.server_user)
-
- val uri = serverUri.text.toString()
- val pattern = Pattern.compile("(.+)@([\\w\\d.]+):([\\d]+)*(.*)")
- val matcher = pattern.matcher(uri)
- if (matcher.find()) {
- val count = matcher.groupCount()
- if (count > 1) {
- serverUser.setText(matcher.group(1))
- serverUrl.setText(matcher.group(2))
- }
- if (count == 4) {
- serverPort.setText(matcher.group(3))
- serverPath.setText(matcher.group(4))
-
- val warnUrl = findViewById<AppCompatTextView>(R.id.warn_url)
- if (!serverPath.text.toString().matches("/.*".toRegex()) && serverPort.text.toString().isNotEmpty()) {
- warnUrl.setText(R.string.warn_malformed_url_port)
- warnUrl.visibility = View.VISIBLE
- } else {
- warnUrl.visibility = View.GONE
- }
- }
- }
- }
-
- public override fun onResume() {
- super.onResume()
- updateURI()
- }
-
- override fun onDestroy() {
- // Do not leak the service connection
- if (identityBuilder != null) {
- identityBuilder!!.close()
- identityBuilder = null
- }
- super.onDestroy()
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- // Inflate the menu; this adds items to the action bar if it is present.
- menuInflater.inflate(R.menu.git_clone, menu)
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- when (item.itemId) {
- R.id.user_pref -> try {
- val intent = Intent(this, UserPreference::class.java)
- startActivity(intent)
- return true
- } catch (e: Exception) {
- println("Exception caught :(")
- e.printStackTrace()
- }
-
- android.R.id.home -> {
- finish()
- return true
- }
- }
- return super.onOptionsItemSelected(item)
- }
-
- /**
- * Saves the configuration found in the form
- */
- private fun saveConfiguration(): Boolean {
- // remember the settings
- val editor = settings.edit()
-
- editor.putString("git_remote_server", (findViewById<View>(R.id.server_url) as TextInputEditText).text.toString())
- editor.putString("git_remote_location", (findViewById<View>(R.id.server_path) as TextInputEditText).text.toString())
- editor.putString("git_remote_username", (findViewById<View>(R.id.server_user) as TextInputEditText).text.toString())
- editor.putString("git_remote_protocol", protocol)
- editor.putString("git_remote_auth", connectionMode)
- editor.putString("git_remote_port", (findViewById<View>(R.id.server_port) as TextInputEditText).text.toString())
- editor.putString("git_remote_uri", (findViewById<View>(R.id.clone_uri) as TextInputEditText).text.toString())
-
- // 'save' hostname variable for use by addRemote() either here or later
- // in syncRepository()
- hostname = (findViewById<View>(R.id.clone_uri) as TextInputEditText).text.toString()
- val port = (findViewById<View>(R.id.server_port) as TextInputEditText).text.toString()
- // don't ask the user, take off the protocol that he puts in
- hostname = hostname.replaceFirst("^.+://".toRegex(), "")
- (findViewById<View>(R.id.clone_uri) as TextInputEditText).setText(hostname)
-
- if (protocol != "ssh://") {
- hostname = protocol + hostname
- } else {
- // if the port is explicitly given, jgit requires the ssh://
- if (port.isNotEmpty() && port != "22")
- hostname = protocol + hostname
-
- // did he forget the username?
- if (!hostname.matches("^.+@.+".toRegex())) {
- MaterialAlertDialogBuilder(this)
- .setMessage(context.getString(R.string.forget_username_dialog_text))
- .setPositiveButton(context.getString(R.string.dialog_oops), null)
- .show()
- return false
- }
- }
- if (PasswordRepository.isInitialized && settings.getBoolean("repository_initialized", false)) {
- // don't just use the clone_uri text, need to use hostname which has
- // had the proper protocol prepended
- PasswordRepository.addRemote("origin", hostname, true)
- }
-
- editor.apply()
- return true
- }
-
- /**
- * Save the repository information to the shared preferences settings
- */
- @Suppress("UNUSED_PARAMETER")
- fun saveConfiguration(view: View) {
- if (!saveConfiguration())
- return
- finish()
- }
-
- private fun showGitConfig() {
- // init the server information
- val username = findViewById<TextInputEditText>(R.id.git_user_name)
- val email = findViewById<TextInputEditText>(R.id.git_user_email)
- val abort = findViewById<MaterialButton>(R.id.git_abort_rebase)
-
- username.setText(settings.getString("git_config_user_name", ""))
- email.setText(settings.getString("git_config_user_email", ""))
-
- // git status
- val repo = PasswordRepository.getRepository(PasswordRepository.getRepositoryDirectory(context))
- if (repo != null) {
- val commitHash = findViewById<AppCompatTextView>(R.id.git_commit_hash)
- try {
- val objectId = repo.resolve(Constants.HEAD)
- val ref = repo.getRef("refs/heads/master")
- val head = if (ref.objectId.equals(objectId)) ref.name else "DETACHED"
- commitHash.text = String.format("%s (%s)", objectId.abbreviate(8).name(), head)
-
- // enable the abort button only if we're rebasing
- val isRebasing = repo.repositoryState.isRebasing
- abort.isEnabled = isRebasing
- abort.alpha = if (isRebasing) 1.0f else 0.5f
- } catch (e: Exception) {
- // ignore
- }
- }
- }
-
- private fun saveGitConfigs(): Boolean {
- // remember the settings
- val editor = settings.edit()
-
- val email = (findViewById<View>(R.id.git_user_email) as TextInputEditText).text!!.toString()
- editor.putString("git_config_user_email", email)
- editor.putString("git_config_user_name", (findViewById<View>(R.id.git_user_name) as TextInputEditText).text.toString())
-
- if (!email.matches(emailPattern.toRegex())) {
- MaterialAlertDialogBuilder(this)
- .setMessage(context.getString(R.string.invalid_email_dialog_text))
- .setPositiveButton(context.getString(R.string.dialog_oops), null)
- .show()
- return false
- }
-
- editor.apply()
- return true
- }
-
- @Suppress("UNUSED_PARAMETER")
- fun applyGitConfigs(view: View) {
- if (!saveGitConfigs())
- return
- PasswordRepository.setUserName(settings.getString("git_config_user_name", null) ?: "")
- PasswordRepository.setUserEmail(settings.getString("git_config_user_email", null) ?: "")
- finish()
- }
-
- @Suppress("UNUSED_PARAMETER")
- fun abortRebase(view: View) {
- launchGitOperation(BREAK_OUT_OF_DETACHED)
- }
-
- @Suppress("UNUSED_PARAMETER")
- fun resetToRemote(view: View) {
- launchGitOperation(REQUEST_RESET)
- }
-
- /**
- * Clones the repository, the directory exists, deletes it
- */
- @Suppress("UNUSED_PARAMETER")
- fun cloneRepository(view: View) {
- if (PasswordRepository.getRepository(null) == null) {
- PasswordRepository.initialize(this)
- }
- val localDir = requireNotNull(PasswordRepository.getRepositoryDirectory(context))
-
- if (!saveConfiguration())
- return
-
- // Warn if non-empty folder unless it's a just-initialized store that has just a .git folder
- if (localDir.exists() && localDir.listFiles()!!.isNotEmpty() &&
- !(localDir.listFiles()!!.size == 1 && localDir.listFiles()!![0].name == ".git")) {
- MaterialAlertDialogBuilder(this)
- .setTitle(R.string.dialog_delete_title)
- .setMessage(resources.getString(R.string.dialog_delete_msg) + " " + localDir.toString())
- .setCancelable(false)
- .setPositiveButton(R.string.dialog_delete
- ) { dialog, _ ->
- try {
- FileUtils.deleteDirectory(localDir)
- launchGitOperation(REQUEST_CLONE)
- } catch (e: IOException) {
- // TODO Handle the exception correctly if we are unable to delete the directory...
- e.printStackTrace()
- MaterialAlertDialogBuilder(this).setMessage(e.message).show()
- }
-
- dialog.cancel()
- }
- .setNegativeButton(R.string.dialog_do_not_delete
- ) { dialog, _ -> dialog.cancel() }
- .show()
- } else {
- try {
- // Silently delete & replace the lone .git folder if it exists
- if (localDir.exists() && localDir.listFiles()!!.size == 1 && localDir.listFiles()!![0].name == ".git") {
- try {
- FileUtils.deleteDirectory(localDir)
- } catch (e: IOException) {
- e.printStackTrace()
- MaterialAlertDialogBuilder(this).setMessage(e.message).show()
- }
- }
- } catch (e: Exception) {
- // This is what happens when jgit fails :(
- // TODO Handle the diffent cases of exceptions
- e.printStackTrace()
- MaterialAlertDialogBuilder(this).setMessage(e.message).show()
- }
-
- launchGitOperation(REQUEST_CLONE)
- }
- }
-
- /**
- * Syncs the local repository with the remote one (either pull or push)
- *
- * @param operation the operation to execute can be REQUEST_PULL or REQUEST_PUSH
- */
- private fun syncRepository(operation: Int) {
- if (settings.getString("git_remote_username", "")!!.isEmpty() ||
- settings.getString("git_remote_server", "")!!.isEmpty() ||
- settings.getString("git_remote_location", "")!!.isEmpty())
- MaterialAlertDialogBuilder(this)
- .setMessage(context.getString(R.string.set_information_dialog_text))
- .setPositiveButton(context.getString(R.string.dialog_positive)) { _, _ ->
- val intent = Intent(context, UserPreference::class.java)
- startActivityForResult(intent, REQUEST_PULL)
- }
- .setNegativeButton(context.getString(R.string.dialog_negative)) { _, _ ->
- // do nothing :(
- setResult(AppCompatActivity.RESULT_OK)
- finish()
- }
- .show()
- else {
- // check that the remote origin is here, else add it
- PasswordRepository.addRemote("origin", hostname, false)
- launchGitOperation(operation)
- }
- }
-
- /**
- * Attempt to launch the requested GIT operation. Depending on the configured auth, it may not
- * be possible to launch the operation immediately. In that case, this function may launch an
- * intermediate activity instead, which will gather necessary information and post it back via
- * onActivityResult, which will then re-call this function. This may happen multiple times,
- * until either an error is encountered or the operation is successfully launched.
- *
- * @param operation The type of GIT operation to launch
- */
- private fun launchGitOperation(operation: Int) {
- val op: GitOperation
- val localDir = requireNotNull(PasswordRepository.getRepositoryDirectory(context))
-
- try {
-
- // Before launching the operation with OpenKeychain auth, we need to issue several requests
- // to the OpenKeychain API. IdentityBuild will take care of launching the relevant intents,
- // we just need to keep calling it until it returns a completed ApiIdentity.
- if (connectionMode.equals("OpenKeychain", ignoreCase = true) && identity == null) {
- // Lazy initialization of the IdentityBuilder
- if (identityBuilder == null) {
- identityBuilder = SshApiSessionFactory.IdentityBuilder(this)
- }
-
- // Try to get an ApiIdentity and bail if one is not ready yet. The builder will ensure
- // that onActivityResult is called with operation again, which will re-invoke us here
- identity = identityBuilder!!.tryBuild(operation)
- if (identity == null)
- return
- }
-
- when (operation) {
- REQUEST_CLONE, GitOperation.GET_SSH_KEY_FROM_CLONE -> op = CloneOperation(localDir, this).setCommand(hostname)
-
- REQUEST_PULL -> op = PullOperation(localDir, this).setCommand()
-
- REQUEST_PUSH -> op = PushOperation(localDir, this).setCommand()
-
- REQUEST_SYNC -> op = SyncOperation(localDir, this).setCommands()
-
- BREAK_OUT_OF_DETACHED -> op = BreakOutOfDetached(localDir, this).setCommands()
-
- REQUEST_RESET -> op = ResetToRemoteOperation(localDir, this).setCommands()
-
- SshApiSessionFactory.POST_SIGNATURE -> return
-
- else -> {
- Timber.tag(TAG).e("Operation not recognized : $operation")
- setResult(AppCompatActivity.RESULT_CANCELED)
- finish()
- return
- }
- }
-
- op.executeAfterAuthentication(connectionMode,
- settings.getString("git_remote_username", "git")!!,
- File("$filesDir/.ssh_key"),
- identity)
- } catch (e: Exception) {
- e.printStackTrace()
- MaterialAlertDialogBuilder(this).setMessage(e.message).show()
- }
- }
-
- public override fun onActivityResult(
- requestCode: Int,
- resultCode: Int,
- data: Intent?
- ) {
-
- // In addition to the pre-operation-launch series of intents for OpenKeychain auth
- // that will pass through here and back to launchGitOperation, there is one
- // synchronous operation that happens /after/ the operation has been launched in the
- // background thread - the actual signing of the SSH challenge. We pass through the
- // completed signature to the ApiIdentity, which will be blocked in the other thread
- // waiting for it.
- if (requestCode == SshApiSessionFactory.POST_SIGNATURE && identity != null) {
- identity!!.postSignature(data)
-
- // If the signature failed (usually because it was cancelled), reset state
- if (data == null) {
- identity = null
- identityBuilder = null
- }
- return
- }
-
- if (resultCode == AppCompatActivity.RESULT_CANCELED) {
- setResult(AppCompatActivity.RESULT_CANCELED)
- finish()
- } else if (resultCode == AppCompatActivity.RESULT_OK) {
- // If an operation has been re-queued via this mechanism, let the
- // IdentityBuilder attempt to extract some updated state from the intent before
- // trying to re-launch the operation.
- if (identityBuilder != null) {
- identityBuilder!!.consume(data)
- }
- launchGitOperation(requestCode)
- }
- super.onActivityResult(requestCode, resultCode, data)
- }
-
- companion object {
- const val REQUEST_PULL = 101
- const val REQUEST_PUSH = 102
- const val REQUEST_CLONE = 103
- const val REQUEST_INIT = 104
- const val EDIT_SERVER = 105
- const val REQUEST_SYNC = 106
-
- @Suppress("Unused")
- const val REQUEST_CREATE = 107
- const val EDIT_GIT_CONFIG = 108
- const val BREAK_OUT_OF_DETACHED = 109
- const val REQUEST_RESET = 110
- private const val TAG = "GitAct"
- private const val emailPattern = "^[^@]+@[^@]+$"
- }
-}