From f6227082f1c70f0443e0f5d6a5f10c2838e1b7ee Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 28 Sep 2021 01:24:09 +0200 Subject: Implement using compat-mode --- .../ui/java/de/blinkt/openvpn/fragments/Utils.kt | 261 +++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt (limited to 'main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt') diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt new file mode 100644 index 00000000..64a8d3ba --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2012-2016 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ +package de.blinkt.openvpn.fragments + +import android.annotation.TargetApi +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.provider.OpenableColumns +import android.util.Base64 +import android.webkit.MimeTypeMap +import kotlin.Throws +import de.blinkt.openvpn.VpnProfile +import de.blinkt.openvpn.core.Preferences +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.nio.charset.Charset +import java.util.* + +object Utils { + @JvmStatic + @TargetApi(Build.VERSION_CODES.KITKAT) + fun getFilePickerIntent(c: Context, fileType: FileType?): Intent? { + val i = Intent(Intent.ACTION_GET_CONTENT) + i.addCategory(Intent.CATEGORY_OPENABLE) + val supportedMimeTypes = TreeSet() + val extensions = Vector() + when (fileType) { + FileType.PKCS12 -> { + i.type = "application/x-pkcs12" + supportedMimeTypes.add("application/x-pkcs12") + extensions.add("p12") + extensions.add("pfx") + } + FileType.CLIENT_CERTIFICATE, FileType.CA_CERTIFICATE -> { + i.type = "application/x-pem-file" + supportedMimeTypes.add("application/x-x509-ca-cert") + supportedMimeTypes.add("application/x-x509-user-cert") + supportedMimeTypes.add("application/x-pem-file") + supportedMimeTypes.add("application/pkix-cert") + supportedMimeTypes.add("text/plain") + extensions.add("pem") + extensions.add("crt") + extensions.add("cer") + } + FileType.KEYFILE -> { + i.type = "application/x-pem-file" + supportedMimeTypes.add("application/x-pem-file") + supportedMimeTypes.add("application/pkcs8") + + // Google drive .... + supportedMimeTypes.add("application/x-iwork-keynote-sffkey") + extensions.add("key") + } + FileType.TLS_AUTH_FILE -> { + i.type = "text/plain" + + // Backup .... + supportedMimeTypes.add("application/pkcs8") + // Google Drive is kind of crazy ..... + supportedMimeTypes.add("application/x-iwork-keynote-sffkey") + extensions.add("txt") + extensions.add("key") + } + FileType.OVPN_CONFIG -> { + i.type = "application/x-openvpn-profile" + supportedMimeTypes.add("application/x-openvpn-profile") + supportedMimeTypes.add("application/openvpn-profile") + supportedMimeTypes.add("application/ovpn") + supportedMimeTypes.add("text/plain") + extensions.add("ovpn") + extensions.add("conf") + } + FileType.CRL_FILE -> { + supportedMimeTypes.add("application/x-pkcs7-crl") + supportedMimeTypes.add("application/pkix-crl") + extensions.add("crl") + } + FileType.USERPW_FILE -> { + i.type = "text/plain" + supportedMimeTypes.add("text/plain") + } + } + val mtm = MimeTypeMap.getSingleton() + for (ext in extensions) { + val mimeType = mtm.getMimeTypeFromExtension(ext) + if (mimeType != null) supportedMimeTypes.add(mimeType) + } + + // Always add this as fallback + supportedMimeTypes.add("application/octet-stream") + i.putExtra(Intent.EXTRA_MIME_TYPES, supportedMimeTypes.toTypedArray()) + + // People don't know that this is actually a system setting. Override it ... + // DocumentsContract.EXTRA_SHOW_ADVANCED is hidden + i.putExtra("android.content.extra.SHOW_ADVANCED", true) + + /* Samsung has decided to do something strange, on stock Android GET_CONTENT opens the document UI */ + /* fist try with documentsui */if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) i.setPackage( + "com.android.documentsui" + ) + + + /* + * Android 11 is much stricter about allowing what to query. Since the app has the + * QUERY_ALL permission we can still check on Android 11 but otherwise we would just + * assume the documents ui to be always there: + */ + + /* + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + return i; + }*/if (!isIntentAvailable(c, i)) { + i.action = Intent.ACTION_OPEN_DOCUMENT + i.setPackage(null) + + // Check for really broken devices ... :( + if (!isIntentAvailable(c, i)) { + return null + } + } + + + /* + final PackageManager packageManager = c.getPackageManager(); + ResolveInfo list = packageManager.resolveActivity(i, 0); + + Toast.makeText(c, "Starting package: "+ list.activityInfo.packageName + + "with ACTION " + i.getAction(), Toast.LENGTH_LONG).show(); + + */return i + } + + @JvmStatic + fun alwaysUseOldFileChooser(c: Context?): Boolean { + /* Android P does not allow access to the file storage anymore */ + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) return false + val prefs = Preferences.getDefaultSharedPreferences(c) + return prefs.getBoolean("useInternalFileSelector", false) + } + + fun isIntentAvailable(context: Context, i: Intent?): Boolean { + val packageManager = context.packageManager + val list = packageManager.queryIntentActivities( + i!!, + PackageManager.MATCH_DEFAULT_ONLY + ) + + // Ignore the Android TV framework app in the list + var size = list.size + for (ri in list) { + // Ignore stub apps + if ("com.google.android.tv.frameworkpackagestubs" == ri.activityInfo.packageName) { + size-- + } + } + return size > 0 + } + + @Throws(IOException::class) + private fun readBytesFromStream(input: InputStream?): ByteArray { + val buffer = ByteArrayOutputStream() + var nRead: Int + val data = ByteArray(16384) + var totalread: Long = 0 + while (input!!.read(data, 0, data.size) + .also { nRead = it } != -1 && totalread < VpnProfile.MAX_EMBED_FILE_SIZE + ) { + buffer.write(data, 0, nRead) + totalread += nRead.toLong() + } + buffer.flush() + input.close() + return buffer.toByteArray() + } + + @JvmStatic + @Throws(IOException::class, SecurityException::class) + fun getFilePickerResult(ft: FileType?, result: Intent, c: Context): String? { + val uri = result.data ?: return null + val fileData = readBytesFromStream(c.contentResolver.openInputStream(uri)) + var newData: String? = null + val cursor = c.contentResolver.query(uri, null, null, null, null) + var prefix = "" + try { + if (cursor != null && cursor.moveToFirst()) { + val cidx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + if (cidx != -1) { + val displayName = cursor.getString(cidx) + if (!displayName.contains(VpnProfile.INLINE_TAG) && !displayName.contains( + VpnProfile.DISPLAYNAME_TAG + ) + ) prefix = VpnProfile.DISPLAYNAME_TAG + displayName + } + } + } finally { + cursor?.close() + } + newData = when (ft) { + FileType.PKCS12 -> Base64.encodeToString( + fileData, + Base64.DEFAULT + ) + else -> String(fileData, Charset.forName("UTF-8")) + } + return prefix + VpnProfile.INLINE_TAG + newData + } + + enum class FileType(val value: Int) { + PKCS12(0), CLIENT_CERTIFICATE(1), CA_CERTIFICATE(2), OVPN_CONFIG(3), KEYFILE(4), TLS_AUTH_FILE( + 5 + ), + USERPW_FILE(6), CRL_FILE(7); + + companion object { + fun getFileTypeByValue(value: Int): FileType? { + return when (value) { + 0 -> PKCS12 + 1 -> CLIENT_CERTIFICATE + 2 -> CA_CERTIFICATE + 3 -> OVPN_CONFIG + 4 -> KEYFILE + 5 -> TLS_AUTH_FILE + 6 -> USERPW_FILE + 7 -> CRL_FILE + else -> null + } + } + } + } + + /* These functions make assumptions about the R.arrays.compat_mode contents */ + @JvmStatic + fun mapCompatVer(ver: Int): Int { + if (ver == 0 || ver >= 20600) + return 0 + else if (ver < 20400) + return 3 + else if (ver < 20500) + return 2 + else if (ver < 20600) + return 1 + return 0 + } + + @JvmStatic + + fun mapCompatMode(mode: Int): Int { + when (mode) { + 0 -> return 0 + 1 -> return 20500 + 2 -> return 20400 + 3 -> return 20300 + else -> return 0 + } + } +} \ No newline at end of file -- cgit v1.2.3