aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/com/zeapo/pwdstore/utils
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2020-12-05 06:07:18 +0530
committerGitHub <noreply@github.com>2020-12-05 06:07:18 +0530
commit5e66d99c852ea67a88b650c03b0e8d55e83eccde (patch)
treeaee323a08253fe7b863976eff92a1bd412bd1430 /app/src/main/java/com/zeapo/pwdstore/utils
parent8eb55f18a11d6b2f155c7c9d48ca833313ee13db (diff)
downloadHardenedPass-5e66d99c852ea67a88b650c03b0e8d55e83eccde.tar.gz
HardenedPass-5e66d99c852ea67a88b650c03b0e8d55e83eccde.zip
Refactor package structure (#1233)
* idea: default test runner to Gradle * Kick off package structure revamp * Reparent all classes under dev.msfjarvis.aps Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/utils')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt178
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/BiometricAuthenticator.kt78
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt89
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt36
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/FragmentViewBindingDelegate.kt65
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/Otp.kt73
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.kt86
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.kt238
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/PasswordSortOrder.kt46
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt85
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/ProxyUtils.kt66
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/TotpFinder.kt32
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/UriTotpFinder.kt66
13 files changed, 0 insertions, 1138 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt
deleted file mode 100644
index 408e9d5e..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/AndroidExtensions.kt
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package com.zeapo.pwdstore.utils
-
-import android.app.KeyguardManager
-import android.content.ClipboardManager
-import android.content.Context
-import android.content.SharedPreferences
-import android.content.pm.PackageManager
-import android.os.Build
-import android.util.Base64
-import android.util.TypedValue
-import android.view.View
-import android.view.autofill.AutofillManager
-import android.view.inputmethod.InputMethodManager
-import androidx.annotation.IdRes
-import androidx.annotation.RequiresApi
-import androidx.appcompat.app.AlertDialog
-import androidx.core.content.ContextCompat
-import androidx.core.content.getSystemService
-import androidx.fragment.app.FragmentActivity
-import androidx.preference.PreferenceManager
-import androidx.security.crypto.EncryptedSharedPreferences
-import androidx.security.crypto.MasterKey
-import com.github.ajalt.timberkt.d
-import com.github.michaelbull.result.Ok
-import com.github.michaelbull.result.Result
-import com.google.android.material.snackbar.Snackbar
-import com.zeapo.pwdstore.R
-import com.zeapo.pwdstore.git.operation.GitOperation
-
-/**
- * Extension function for [AlertDialog] that requests focus for the
- * view whose id is [id]. Solution based on a StackOverflow
- * answer: https://stackoverflow.com/a/13056259/297261
- */
-fun <T : View> AlertDialog.requestInputFocusOnView(@IdRes id: Int) {
- setOnShowListener {
- findViewById<T>(id)?.apply {
- setOnFocusChangeListener { v, _ ->
- v.post {
- context.getSystemService<InputMethodManager>()
- ?.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT)
- }
- }
- requestFocus()
- }
- }
-}
-
-/**
- * Get an instance of [AutofillManager]. Only
- * available on Android Oreo and above
- */
-val Context.autofillManager: AutofillManager?
- @RequiresApi(Build.VERSION_CODES.O)
- get() = getSystemService()
-
-/**
- * Get an instance of [ClipboardManager]
- */
-val Context.clipboard
- get() = getSystemService<ClipboardManager>()
-
-/**
- * Wrapper for [getEncryptedPrefs] to avoid open-coding the file name at
- * each call site
- */
-fun Context.getEncryptedGitPrefs() = getEncryptedPrefs("git_operation")
-
-/**
- * Wrapper for [getEncryptedPrefs] to get the encrypted preference set for the HTTP
- * proxy.
- */
-fun Context.getEncryptedProxyPrefs() = getEncryptedPrefs("http_proxy")
-
-/**
- * Get an instance of [EncryptedSharedPreferences] with the given [fileName]
- */
-private fun Context.getEncryptedPrefs(fileName: String): SharedPreferences {
- val masterKeyAlias = MasterKey.Builder(applicationContext)
- .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
- .build()
- return EncryptedSharedPreferences.create(
- applicationContext,
- fileName,
- masterKeyAlias,
- EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
- EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
- )
-}
-
-/**
- * Get an instance of [KeyguardManager]
- */
-val Context.keyguardManager: KeyguardManager
- get() = getSystemService()!!
-
-/**
- * Get the default [SharedPreferences] instance
- */
-val Context.sharedPrefs: SharedPreferences
- get() = PreferenceManager.getDefaultSharedPreferences(applicationContext)
-
-
-/**
- * Resolve [attr] from the [Context]'s theme
- */
-fun Context.resolveAttribute(attr: Int): Int {
- val typedValue = TypedValue()
- this.theme.resolveAttribute(attr, typedValue, true)
- return typedValue.data
-}
-
-/**
- * Commit changes to the store from a [FragmentActivity] using
- * a custom implementation of [GitOperation]
- */
-suspend fun FragmentActivity.commitChange(
- message: String,
-): Result<Unit, Throwable> {
- if (!PasswordRepository.isGitRepo()) {
- return Ok(Unit)
- }
- return object : GitOperation(this@commitChange) {
- override val commands = arrayOf(
- // Stage all files
- git.add().addFilepattern("."),
- // Populate the changed files count
- git.status(),
- // Commit everything! If anything changed, that is.
- git.commit().setAll(true).setMessage(message),
- )
-
- override fun preExecute(): Boolean {
- d { "Committing with message: '$message'" }
- return true
- }
- }.execute()
-}
-
-/**
- * Check if [permission] has been granted to the app.
- */
-fun FragmentActivity.isPermissionGranted(permission: String): Boolean {
- return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
-}
-
-/**
- * Show a [Snackbar] in a [FragmentActivity] and correctly
- * anchor it to a [com.google.android.material.floatingactionbutton.FloatingActionButton]
- * if one exists in the [view]
- */
-fun FragmentActivity.snackbar(
- view: View = findViewById(android.R.id.content),
- message: String,
- length: Int = Snackbar.LENGTH_SHORT,
-): Snackbar {
- val snackbar = Snackbar.make(view, message, length)
- snackbar.anchorView = findViewById(R.id.fab)
- snackbar.show()
- return snackbar
-}
-
-/**
- * Simplifies the common `getString(key, null) ?: defaultValue` case slightly
- */
-fun SharedPreferences.getString(key: String): String? = getString(key, null)
-
-/**
- * Convert this [String] to its [Base64] representation
- */
-fun String.base64(): String {
- return Base64.encodeToString(encodeToByteArray(), Base64.NO_WRAP)
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/BiometricAuthenticator.kt b/app/src/main/java/com/zeapo/pwdstore/utils/BiometricAuthenticator.kt
deleted file mode 100644
index 12ba84f1..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/BiometricAuthenticator.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.utils
-
-import android.app.KeyguardManager
-import androidx.annotation.StringRes
-import androidx.biometric.BiometricManager
-import androidx.biometric.BiometricManager.Authenticators
-import androidx.biometric.BiometricPrompt
-import androidx.core.content.ContextCompat
-import androidx.core.content.getSystemService
-import androidx.fragment.app.FragmentActivity
-import com.github.ajalt.timberkt.Timber.tag
-import com.github.ajalt.timberkt.d
-import com.zeapo.pwdstore.R
-
-object BiometricAuthenticator {
-
- private const val TAG = "BiometricAuthenticator"
- private const val validAuthenticators = Authenticators.DEVICE_CREDENTIAL or Authenticators.BIOMETRIC_WEAK
-
- sealed class Result {
- data class Success(val cryptoObject: BiometricPrompt.CryptoObject?) : Result()
- data class Failure(val code: Int?, val message: CharSequence) : Result()
- object HardwareUnavailableOrDisabled : Result()
- object Cancelled : Result()
- }
-
- fun canAuthenticate(activity: FragmentActivity): Boolean {
- return BiometricManager.from(activity).canAuthenticate(validAuthenticators) == BiometricManager.BIOMETRIC_SUCCESS
- }
-
- fun authenticate(
- activity: FragmentActivity,
- @StringRes dialogTitleRes: Int = R.string.biometric_prompt_title,
- callback: (Result) -> Unit
- ) {
- val authCallback = object : BiometricPrompt.AuthenticationCallback() {
- override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
- super.onAuthenticationError(errorCode, errString)
- tag(TAG).d { "BiometricAuthentication error: errorCode=$errorCode, msg=$errString" }
- callback(when (errorCode) {
- BiometricPrompt.ERROR_CANCELED, BiometricPrompt.ERROR_USER_CANCELED,
- BiometricPrompt.ERROR_NEGATIVE_BUTTON -> {
- Result.Cancelled
- }
- BiometricPrompt.ERROR_HW_NOT_PRESENT, BiometricPrompt.ERROR_HW_UNAVAILABLE,
- BiometricPrompt.ERROR_NO_BIOMETRICS, BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL -> {
- Result.HardwareUnavailableOrDisabled
- }
- else -> Result.Failure(errorCode, activity.getString(R.string.biometric_auth_error_reason, errString))
- })
- }
-
- override fun onAuthenticationFailed() {
- super.onAuthenticationFailed()
- callback(Result.Failure(null, activity.getString(R.string.biometric_auth_error)))
- }
-
- override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
- super.onAuthenticationSucceeded(result)
- callback(Result.Success(result.cryptoObject))
- }
- }
- val deviceHasKeyguard = activity.getSystemService<KeyguardManager>()?.isDeviceSecure == true
- if (canAuthenticate(activity) || deviceHasKeyguard) {
- val promptInfo = BiometricPrompt.PromptInfo.Builder()
- .setTitle(activity.getString(dialogTitleRes))
- .setAllowedAuthenticators(validAuthenticators)
- .build()
- BiometricPrompt(activity, ContextCompat.getMainExecutor(activity.applicationContext), authCallback).authenticate(promptInfo)
- } else {
- callback(Result.HardwareUnavailableOrDisabled)
- }
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
deleted file mode 100644
index 7a439e94..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.utils
-
-import com.github.michaelbull.result.getOrElse
-import com.github.michaelbull.result.runCatching
-import java.io.File
-import java.util.Date
-import org.eclipse.jgit.lib.ObjectId
-import org.eclipse.jgit.revwalk.RevCommit
-
-/**
- * The default OpenPGP provider for the app
- */
-const val OPENPGP_PROVIDER = "org.sufficientlysecure.keychain"
-
-/**
- * Clears the given [flag] from the value of this [Int]
- */
-fun Int.clearFlag(flag: Int): Int {
- return this and flag.inv()
-}
-
-/**
- * Checks if this [Int] contains the given [flag]
- */
-infix fun Int.hasFlag(flag: Int): Boolean {
- return this and flag == flag
-}
-
-/**
- * Checks whether this [File] is a directory that contains [other].
- */
-fun File.contains(other: File): Boolean {
- if (!isDirectory)
- return false
- if (!other.exists())
- return false
- val relativePath = runCatching {
- other.relativeTo(this)
- }.getOrElse {
- return false
- }
- // Direct containment is equivalent to the relative path being equal to the filename.
- return relativePath.path == other.name
-}
-
-/**
- * Checks if this [File] is in the password repository directory as given
- * by [PasswordRepository.getRepositoryDirectory]
- */
-fun File.isInsideRepository(): Boolean {
- return canonicalPath.contains(PasswordRepository.getRepositoryDirectory().canonicalPath)
-}
-
-/**
- * Recursively lists the files in this [File], skipping any directories it encounters.
- */
-fun File.listFilesRecursively() = walkTopDown().filter { !it.isDirectory }.toList()
-
-/**
- * Unique SHA-1 hash of this commit as hexadecimal string.
- *
- * @see RevCommit.getId
- */
-val RevCommit.hash: String
- get() = ObjectId.toString(id)
-
-/**
- * Time this commit was made with second precision.
- *
- * @see RevCommit.commitTime
- */
-val RevCommit.time: Date
- get() {
- val epochSeconds = commitTime.toLong()
- val epochMilliseconds = epochSeconds * 1000
- return Date(epochMilliseconds)
- }
-
-/**
- * Splits this [String] into an [Array] of [String]s, split on the UNIX LF line ending
- * and stripped of any empty lines.
- */
-fun String.splitLines(): Array<String> {
- return split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt
deleted file mode 100644
index 251f5259..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/FragmentExtensions.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.zeapo.pwdstore.utils
-
-import androidx.annotation.IdRes
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
-import androidx.fragment.app.commit
-import com.zeapo.pwdstore.R
-
-/**
- * Check if [permission] is granted to the app. Aliases to [isPermissionGranted] internally.
- */
-fun Fragment.isPermissionGranted(permission: String): Boolean {
- return requireActivity().isPermissionGranted(permission)
-}
-
-/**
- * Calls `finish()` on the enclosing [androidx.fragment.app.FragmentActivity]
- */
-fun Fragment.finish() = requireActivity().finish()
-
-/**
- * Perform a [commit] on this [FragmentManager] with custom animations and adding the [destinationFragment]
- * to the fragment backstack
- */
-fun FragmentManager.performTransactionWithBackStack(destinationFragment: Fragment, @IdRes containerViewId: Int = android.R.id.content) {
- commit {
- beginTransaction()
- addToBackStack(destinationFragment.tag)
- setCustomAnimations(
- R.animator.slide_in_left,
- R.animator.slide_out_left,
- R.animator.slide_in_right,
- R.animator.slide_out_right)
- replace(containerViewId, destinationFragment)
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/FragmentViewBindingDelegate.kt b/app/src/main/java/com/zeapo/pwdstore/utils/FragmentViewBindingDelegate.kt
deleted file mode 100644
index 55d654e0..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/FragmentViewBindingDelegate.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package com.zeapo.pwdstore.utils
-
-
-import android.view.LayoutInflater
-import android.view.View
-import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.DefaultLifecycleObserver
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.viewbinding.ViewBinding
-import kotlin.properties.ReadOnlyProperty
-import kotlin.reflect.KProperty
-
-/**
- * Imported from https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c
- */
-class FragmentViewBindingDelegate<T : ViewBinding>(
- val fragment: Fragment,
- val viewBindingFactory: (View) -> T
-) : ReadOnlyProperty<Fragment, T> {
-
- private var binding: T? = null
-
- init {
- fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
- override fun onCreate(owner: LifecycleOwner) {
- fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
- viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
- override fun onDestroy(owner: LifecycleOwner) {
- binding = null
- }
- })
- }
- }
- })
- }
-
- override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
- val binding = binding
- if (binding != null) {
- return binding
- }
-
- val lifecycle = fragment.viewLifecycleOwner.lifecycle
- if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
- throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
- }
-
- return viewBindingFactory(thisRef.requireView()).also { this.binding = it }
- }
-}
-
-fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
- FragmentViewBindingDelegate(this, viewBindingFactory)
-
-inline fun <T : ViewBinding> AppCompatActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) =
- lazy(LazyThreadSafetyMode.NONE) {
- bindingInflater.invoke(layoutInflater)
- }
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Otp.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Otp.kt
deleted file mode 100644
index 994b4d82..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/Otp.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package com.zeapo.pwdstore.utils
-
-import com.github.michaelbull.result.Err
-import com.github.michaelbull.result.runCatching
-import java.nio.ByteBuffer
-import java.util.Locale
-import javax.crypto.Mac
-import javax.crypto.spec.SecretKeySpec
-import kotlin.experimental.and
-import org.apache.commons.codec.binary.Base32
-
-object Otp {
-
- private val BASE_32 = Base32()
- private val STEAM_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY".toCharArray()
-
- init {
- check(STEAM_ALPHABET.size == 26)
- }
-
- fun calculateCode(secret: String, counter: Long, algorithm: String, digits: String) = runCatching {
- val algo = "Hmac${algorithm.toUpperCase(Locale.ROOT)}"
- val decodedSecret = BASE_32.decode(secret)
- val secretKey = SecretKeySpec(decodedSecret, algo)
- val digest = Mac.getInstance(algo).run {
- init(secretKey)
- doFinal(ByteBuffer.allocate(8).putLong(counter).array())
- }
- // Least significant 4 bits are used as an offset into the digest.
- val offset = (digest.last() and 0xf).toInt()
- // Extract 32 bits at the offset and clear the most significant bit.
- val code = digest.copyOfRange(offset, offset + 4)
- code[0] = (0x7f and code[0].toInt()).toByte()
- val codeInt = ByteBuffer.wrap(code).int
- check(codeInt > 0)
- if (digits == "s") {
- // Steam
- var remainingCodeInt = codeInt
- buildString {
- repeat(5) {
- append(STEAM_ALPHABET[remainingCodeInt % 26])
- remainingCodeInt /= 26
- }
- }
- } else {
- // Base 10, 6 to 10 digits
- val numDigits = digits.toIntOrNull()
- when {
- numDigits == null -> {
- return Err(IllegalArgumentException("Digits specifier has to be either 's' or numeric"))
- }
- numDigits < 6 -> {
- return Err(IllegalArgumentException("TOTP codes have to be at least 6 digits long"))
- }
- numDigits > 10 -> {
- return Err(IllegalArgumentException("TOTP codes can be at most 10 digits long"))
- }
- else -> {
- // 2^31 = 2_147_483_648, so we can extract at most 10 digits with the first one
- // always being 0, 1, or 2. Pad with leading zeroes.
- val codeStringBase10 = codeInt.toString(10).padStart(10, '0')
- check(codeStringBase10.length == 10)
- codeStringBase10.takeLast(numDigits)
- }
- }
- }
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.kt b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.kt
deleted file mode 100644
index ef31324b..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordItem.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.utils
-
-import com.zeapo.pwdstore.crypto.BasePgpActivity
-import java.io.File
-
-data class PasswordItem(
- val name: String,
- val parent: PasswordItem? = null,
- val type: Char,
- val file: File,
- val rootDir: File
-) : Comparable<PasswordItem> {
-
- val fullPathToParent = file.absolutePath
- .replace(rootDir.absolutePath, "")
- .replace(file.name, "")
-
- val longName = BasePgpActivity.getLongName(
- fullPathToParent,
- rootDir.absolutePath,
- toString())
-
- override fun equals(other: Any?): Boolean {
- return (other is PasswordItem) && (other.file == file)
- }
-
- override fun compareTo(other: PasswordItem): Int {
- return (type + name).compareTo(other.type + other.name, ignoreCase = true)
- }
-
- override fun toString(): String {
- return name.replace("\\.gpg$".toRegex(), "")
- }
-
- override fun hashCode(): Int {
- return 0
- }
-
- companion object {
-
- const val TYPE_CATEGORY = 'c'
- const val TYPE_PASSWORD = 'p'
-
- @JvmStatic
- fun newCategory(
- name: String,
- file: File,
- parent: PasswordItem,
- rootDir: File
- ): PasswordItem {
- return PasswordItem(name, parent, TYPE_CATEGORY, file, rootDir)
- }
-
- @JvmStatic
- fun newCategory(
- name: String,
- file: File,
- rootDir: File
- ): PasswordItem {
- return PasswordItem(name, null, TYPE_CATEGORY, file, rootDir)
- }
-
- @JvmStatic
- fun newPassword(
- name: String,
- file: File,
- parent: PasswordItem,
- rootDir: File
- ): PasswordItem {
- return PasswordItem(name, parent, TYPE_PASSWORD, file, rootDir)
- }
-
- @JvmStatic
- fun newPassword(
- name: String,
- file: File,
- rootDir: File
- ): PasswordItem {
- return PasswordItem(name, null, TYPE_PASSWORD, file, rootDir)
- }
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.kt b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.kt
deleted file mode 100644
index 8b403a56..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.kt
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package com.zeapo.pwdstore.utils
-
-import android.os.Build
-import androidx.annotation.RequiresApi
-import androidx.core.content.edit
-import com.github.michaelbull.result.getOrElse
-import com.github.michaelbull.result.onFailure
-import com.github.michaelbull.result.runCatching
-import com.zeapo.pwdstore.Application
-import java.io.File
-import java.io.FileFilter
-import java.nio.file.Files
-import java.nio.file.LinkOption
-import org.eclipse.jgit.api.Git
-import org.eclipse.jgit.lib.Repository
-import org.eclipse.jgit.storage.file.FileRepositoryBuilder
-import org.eclipse.jgit.transport.RefSpec
-import org.eclipse.jgit.transport.RemoteConfig
-import org.eclipse.jgit.transport.URIish
-import org.eclipse.jgit.util.FS
-import org.eclipse.jgit.util.FS_POSIX_Java6
-
-object PasswordRepository {
-
- @RequiresApi(Build.VERSION_CODES.O)
- private class FS_POSIX_Java6_with_optional_symlinks : FS_POSIX_Java6() {
-
- override fun supportsSymlinks() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-
- override fun isSymLink(file: File) = Files.isSymbolicLink(file.toPath())
-
- override fun readSymLink(file: File) = Files.readSymbolicLink(file.toPath()).toString()
-
- override fun createSymLink(source: File, target: String) {
- val sourcePath = source.toPath()
- if (Files.exists(sourcePath, LinkOption.NOFOLLOW_LINKS))
- Files.delete(sourcePath)
- Files.createSymbolicLink(sourcePath, File(target).toPath())
- }
- }
-
- @RequiresApi(Build.VERSION_CODES.O)
- private class Java7FSFactory : FS.FSFactory() {
-
- override fun detect(cygwinUsed: Boolean?): FS {
- return FS_POSIX_Java6_with_optional_symlinks()
- }
- }
-
- private var repository: Repository? = null
- private val settings by lazy(LazyThreadSafetyMode.NONE) { Application.instance.sharedPrefs }
- private val filesDir
- get() = Application.instance.filesDir
-
- /**
- * Returns the git repository
- *
- * @param localDir needed only on the creation
- * @return the git repository
- */
- @JvmStatic
- fun getRepository(localDir: File?): Repository? {
- if (repository == null && localDir != null) {
- val builder = FileRepositoryBuilder()
- repository = runCatching {
- builder.run {
- gitDir = localDir
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- fs = Java7FSFactory().detect(null)
- }
- readEnvironment()
- }.build()
- }.getOrElse { e ->
- e.printStackTrace()
- null
- }
- }
- return repository
- }
-
- @JvmStatic
- val isInitialized: Boolean
- get() = repository != null
-
- @JvmStatic
- fun isGitRepo(): Boolean {
- if (repository != null) {
- return repository!!.objectDatabase.exists()
- }
- return false
- }
-
- @JvmStatic
- @Throws(Exception::class)
- fun createRepository(localDir: File) {
- localDir.delete()
-
- Git.init().setDirectory(localDir).call()
- getRepository(localDir)
- }
-
- // TODO add multiple remotes support for pull/push
- @JvmStatic
- fun addRemote(name: String, url: String, replace: Boolean = false) {
- val storedConfig = repository!!.config
- val remotes = storedConfig.getSubsections("remote")
-
- if (!remotes.contains(name)) {
- runCatching {
- val uri = URIish(url)
- val refSpec = RefSpec("+refs/head/*:refs/remotes/$name/*")
-
- val remoteConfig = RemoteConfig(storedConfig, name)
- remoteConfig.addFetchRefSpec(refSpec)
- remoteConfig.addPushRefSpec(refSpec)
- remoteConfig.addURI(uri)
- remoteConfig.addPushURI(uri)
-
- remoteConfig.update(storedConfig)
-
- storedConfig.save()
- }.onFailure { e ->
- e.printStackTrace()
- }
- } else if (replace) {
- runCatching {
- val uri = URIish(url)
-
- val remoteConfig = RemoteConfig(storedConfig, name)
- // remove the first and eventually the only uri
- if (remoteConfig.urIs.size > 0) {
- remoteConfig.removeURI(remoteConfig.urIs[0])
- }
- if (remoteConfig.pushURIs.size > 0) {
- remoteConfig.removePushURI(remoteConfig.pushURIs[0])
- }
-
- remoteConfig.addURI(uri)
- remoteConfig.addPushURI(uri)
-
- remoteConfig.update(storedConfig)
-
- storedConfig.save()
- }.onFailure { e ->
- e.printStackTrace()
- }
- }
- }
-
- @JvmStatic
- fun closeRepository() {
- if (repository != null) repository!!.close()
- repository = null
- }
-
- @JvmStatic
- fun getRepositoryDirectory(): File {
- return if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) {
- val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
- if (externalRepo != null)
- File(externalRepo)
- else
- File(filesDir.toString(), "/store")
- } else {
- File(filesDir.toString(), "/store")
- }
- }
-
- @JvmStatic
- fun initialize(): Repository? {
- val dir = getRepositoryDirectory()
- // uninitialize the repo if the dir does not exist or is absolutely empty
- settings.edit {
- if (!dir.exists() || !dir.isDirectory || requireNotNull(dir.listFiles()).isEmpty()) {
- putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)
- } else {
- putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true)
- }
- }
-
- // create the repository static variable in PasswordRepository
- return getRepository(File(dir.absolutePath + "/.git"))
- }
-
- /**
- * Gets the .gpg files in a directory
- *
- * @param path the directory path
- * @return the list of gpg files in that directory
- */
- @JvmStatic
- fun getFilesList(path: File?): ArrayList<File> {
- if (path == null || !path.exists()) return ArrayList()
-
- val directories = (path.listFiles(FileFilter { pathname -> pathname.isDirectory })
- ?: emptyArray()).toList()
- val files = (path.listFiles(FileFilter { pathname -> pathname.extension == "gpg" })
- ?: emptyArray()).toList()
-
- val items = ArrayList<File>()
- items.addAll(directories)
- items.addAll(files)
-
- return items
- }
-
- /**
- * Gets the passwords (PasswordItem) in a directory
- *
- * @param path the directory path
- * @return a list of password items
- */
- @JvmStatic
- fun getPasswords(path: File, rootDir: File, sortOrder: PasswordSortOrder): ArrayList<PasswordItem> {
- // We need to recover the passwords then parse the files
- val passList = getFilesList(path).also { it.sortBy { f -> f.name } }
- val passwordList = ArrayList<PasswordItem>()
- val showHidden = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, false)
-
- if (passList.size == 0) return passwordList
- if (!showHidden) {
- passList.filter { !it.isHidden }.toCollection(passList.apply { clear() })
- }
- passList.forEach { file ->
- passwordList.add(if (file.isFile) {
- PasswordItem.newPassword(file.name, file, rootDir)
- } else {
- PasswordItem.newCategory(file.name, file, rootDir)
- })
- }
- passwordList.sortWith(sortOrder.comparator)
- return passwordList
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordSortOrder.kt b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordSortOrder.kt
deleted file mode 100644
index 0613c97d..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordSortOrder.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package com.zeapo.pwdstore.utils
-
-import android.content.Context
-import android.content.SharedPreferences
-import com.zeapo.pwdstore.Application
-
-enum class PasswordSortOrder(val comparator: java.util.Comparator<PasswordItem>) {
-
- FOLDER_FIRST(Comparator { p1: PasswordItem, p2: PasswordItem ->
- (p1.type + p1.name)
- .compareTo(p2.type + p2.name, ignoreCase = true)
- }),
-
- INDEPENDENT(Comparator { p1: PasswordItem, p2: PasswordItem ->
- p1.name.compareTo(p2.name, ignoreCase = true)
- }),
-
- RECENTLY_USED(Comparator { p1: PasswordItem, p2: PasswordItem ->
- val recentHistory = Application.instance.getSharedPreferences("recent_password_history", Context.MODE_PRIVATE)
- val timeP1 = recentHistory.getString(p1.file.absolutePath.base64())
- val timeP2 = recentHistory.getString(p2.file.absolutePath.base64())
- when {
- timeP1 != null && timeP2 != null -> timeP2.compareTo(timeP1)
- timeP1 != null && timeP2 == null -> return@Comparator -1
- timeP1 == null && timeP2 != null -> return@Comparator 1
- else -> p1.name.compareTo(p2.name, ignoreCase = true)
- }
- }),
-
- FILE_FIRST(Comparator { p1: PasswordItem, p2: PasswordItem ->
- (p2.type + p1.name).compareTo(p1.type + p2.name, ignoreCase = true)
- });
-
- companion object {
-
- @JvmStatic
- fun getSortOrder(settings: SharedPreferences): PasswordSortOrder {
- return valueOf(settings.getString(PreferenceKeys.SORT_ORDER) ?: FOLDER_FIRST.name)
- }
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt b/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt
deleted file mode 100644
index 70818568..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package com.zeapo.pwdstore.utils
-
-object PreferenceKeys {
-
- const val APP_THEME = "app_theme"
- const val APP_VERSION = "app_version"
- const val AUTOFILL_ENABLE = "autofill_enable"
- const val BIOMETRIC_AUTH = "biometric_auth"
- const val CLEAR_CLIPBOARD_20X = "clear_clipboard_20x"
- const val CLEAR_SAVED_PASS = "clear_saved_pass"
- const val COPY_ON_DECRYPT = "copy_on_decrypt"
- const val ENABLE_DEBUG_LOGGING = "enable_debug_logging"
- const val EXPORT_PASSWORDS = "export_passwords"
- const val FILTER_RECURSIVELY = "filter_recursively"
- const val GENERAL_SHOW_TIME = "general_show_time"
- const val GIT_CONFIG = "git_config"
- const val GIT_CONFIG_AUTHOR_EMAIL = "git_config_user_email"
- const val GIT_CONFIG_AUTHOR_NAME = "git_config_user_name"
- const val GIT_EXTERNAL = "git_external"
- const val GIT_EXTERNAL_REPO = "git_external_repo"
- const val GIT_REMOTE_AUTH = "git_remote_auth"
- const val GIT_REMOTE_KEY_TYPE = "git_remote_key_type"
-
- @Deprecated("Use GIT_REMOTE_URL instead")
- const val GIT_REMOTE_LOCATION = "git_remote_location"
- const val GIT_REMOTE_USE_MULTIPLEXING = "git_remote_use_multiplexing"
-
- @Deprecated("Use GIT_REMOTE_URL instead")
- const val GIT_REMOTE_PORT = "git_remote_port"
-
- @Deprecated("Use GIT_REMOTE_URL instead")
- const val GIT_REMOTE_PROTOCOL = "git_remote_protocol"
- const val GIT_DELETE_REPO = "git_delete_repo"
-
- @Deprecated("Use GIT_REMOTE_URL instead")
- const val GIT_REMOTE_SERVER = "git_remote_server"
- const val GIT_REMOTE_URL = "git_remote_url"
-
- @Deprecated("Use GIT_REMOTE_URL instead")
- const val GIT_REMOTE_USERNAME = "git_remote_username"
- const val GIT_SERVER_INFO = "git_server_info"
- const val GIT_BRANCH_NAME = "git_branch"
- const val HTTPS_PASSWORD = "https_password"
- const val LENGTH = "length"
- const val OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES = "oreo_autofill_custom_public_suffixes"
- const val OREO_AUTOFILL_DEFAULT_USERNAME = "oreo_autofill_default_username"
- const val OREO_AUTOFILL_DIRECTORY_STRUCTURE = "oreo_autofill_directory_structure"
- const val PREF_KEY_CUSTOM_DICT = "pref_key_custom_dict"
- const val PREF_KEY_IS_CUSTOM_DICT = "pref_key_is_custom_dict"
- const val PREF_KEY_PWGEN_TYPE = "pref_key_pwgen_type"
- const val PREF_SELECT_EXTERNAL = "pref_select_external"
- const val REPOSITORY_INITIALIZED = "repository_initialized"
- const val REPO_CHANGED = "repo_changed"
- const val SEARCH_ON_START = "search_on_start"
- const val SHOW_EXTRA_CONTENT = "show_extra_content"
-
- @Deprecated(
- message = "Use SHOW_HIDDEN_CONTENTS instead",
- replaceWith = ReplaceWith("PreferenceKeys.SHOW_HIDDEN_CONTENTS")
- )
- const val SHOW_HIDDEN_FOLDERS = "show_hidden_folders"
- const val SHOW_HIDDEN_CONTENTS = "show_hidden_contents"
- const val SORT_ORDER = "sort_order"
- const val SHOW_PASSWORD = "show_password"
- const val SSH_KEY = "ssh_key"
- const val SSH_KEYGEN = "ssh_keygen"
- const val SSH_KEY_LOCAL_PASSPHRASE = "ssh_key_local_passphrase"
- const val SSH_OPENKEYSTORE_CLEAR_KEY_ID = "ssh_openkeystore_clear_keyid"
- const val SSH_OPENKEYSTORE_KEYID = "ssh_openkeystore_keyid"
- const val SSH_SEE_KEY = "ssh_see_key"
-
- @Deprecated("To be used only in Migrations.kt")
- const val USE_GENERATED_KEY = "use_generated_key"
-
- const val PROXY_SETTINGS = "proxy_settings"
- const val PROXY_HOST = "proxy_host"
- const val PROXY_PORT = "proxy_port"
- const val PROXY_USERNAME = "proxy_username"
- const val PROXY_PASSWORD = "proxy_password"
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/ProxyUtils.kt b/app/src/main/java/com/zeapo/pwdstore/utils/ProxyUtils.kt
deleted file mode 100644
index 4bb3dfcb..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/ProxyUtils.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package com.zeapo.pwdstore.utils
-
-import com.zeapo.pwdstore.git.config.GitSettings
-import java.io.IOException
-import java.net.Authenticator
-import java.net.InetSocketAddress
-import java.net.PasswordAuthentication
-import java.net.Proxy
-import java.net.ProxySelector
-import java.net.SocketAddress
-import java.net.URI
-
-/**
- * Utility class for [Proxy] handling.
- */
-object ProxyUtils {
-
- private const val HTTP_PROXY_USER_PROPERTY = "http.proxyUser"
- private const val HTTP_PROXY_PASSWORD_PROPERTY = "http.proxyPassword"
-
- /**
- * Set the default [Proxy] and [Authenticator] for the app based on user provided settings.
- */
- fun setDefaultProxy() {
- ProxySelector.setDefault(object : ProxySelector() {
- override fun select(uri: URI?): MutableList<Proxy> {
- val host = GitSettings.proxyHost
- val port = GitSettings.proxyPort
- return if (host == null || port == -1) {
- mutableListOf(Proxy.NO_PROXY)
- } else {
- mutableListOf(Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port)))
- }
- }
-
- override fun connectFailed(uri: URI?, sa: SocketAddress?, ioe: IOException?) {
- if (uri == null || sa == null || ioe == null) {
- throw IllegalArgumentException("Arguments can't be null.")
- }
- }
- })
- val user = GitSettings.proxyUsername ?: ""
- val password = GitSettings.proxyPassword ?: ""
- if (user.isEmpty() || password.isEmpty()) {
- System.clearProperty(HTTP_PROXY_USER_PROPERTY)
- System.clearProperty(HTTP_PROXY_PASSWORD_PROPERTY)
- } else {
- System.setProperty(HTTP_PROXY_USER_PROPERTY, user)
- System.setProperty(HTTP_PROXY_PASSWORD_PROPERTY, password)
- }
- Authenticator.setDefault(object : Authenticator() {
- override fun getPasswordAuthentication(): PasswordAuthentication? {
- return if (requestorType == RequestorType.PROXY) {
- PasswordAuthentication(user, password.toCharArray())
- } else {
- null
- }
- }
- })
- }
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/TotpFinder.kt b/app/src/main/java/com/zeapo/pwdstore/utils/TotpFinder.kt
deleted file mode 100644
index 13a47543..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/TotpFinder.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package com.zeapo.pwdstore.utils
-
-/**
- * Defines a class that can extract relevant parts of a TOTP URL for use by the app.
- */
-interface TotpFinder {
-
- /**
- * Get the TOTP secret from the given extra content.
- */
- fun findSecret(content: String): String?
-
- /**
- * Get the number of digits required in the final OTP.
- */
- fun findDigits(content: String): String
-
- /**
- * Get the TOTP timeout period.
- */
- fun findPeriod(content: String): Long
-
- /**
- * Get the algorithm for the TOTP secret.
- */
- fun findAlgorithm(content: String): String
-}
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/UriTotpFinder.kt b/app/src/main/java/com/zeapo/pwdstore/utils/UriTotpFinder.kt
deleted file mode 100644
index 23101a13..00000000
--- a/app/src/main/java/com/zeapo/pwdstore/utils/UriTotpFinder.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package com.zeapo.pwdstore.utils
-
-import android.net.Uri
-
-/**
- * [Uri] backed TOTP URL parser.
- */
-class UriTotpFinder : TotpFinder {
-
- override fun findSecret(content: String): String? {
- content.split("\n".toRegex()).forEach { line ->
- if (line.startsWith(TOTP_FIELDS[0])) {
- return Uri.parse(line).getQueryParameter("secret")
- }
- if (line.startsWith(TOTP_FIELDS[1], ignoreCase = true)) {
- return line.split(": *".toRegex(), 2).toTypedArray()[1]
- }
- }
- return null
- }
-
- override fun findDigits(content: String): String {
- content.split("\n".toRegex()).forEach { line ->
- if (line.startsWith(TOTP_FIELDS[0]) &&
- Uri.parse(line).getQueryParameter("digits") != null) {
- return Uri.parse(line).getQueryParameter("digits")!!
- }
- }
- return "6"
- }
-
- override fun findPeriod(content: String): Long {
- content.split("\n".toRegex()).forEach { line ->
- if (line.startsWith(TOTP_FIELDS[0]) &&
- Uri.parse(line).getQueryParameter("period") != null) {
- val period = Uri.parse(line).getQueryParameter("period")!!.toLongOrNull()
- if (period != null && period > 0)
- return period
- }
- }
- return 30
- }
-
- override fun findAlgorithm(content: String): String {
- content.split("\n".toRegex()).forEach { line ->
- if (line.startsWith(TOTP_FIELDS[0]) &&
- Uri.parse(line).getQueryParameter("algorithm") != null) {
- return Uri.parse(line).getQueryParameter("algorithm")!!
- }
- }
- return "sha1"
- }
-
- companion object {
-
- val TOTP_FIELDS = arrayOf(
- "otpauth://totp",
- "totp:"
- )
- }
-}