summaryrefslogtreecommitdiff
path: root/main/src/ui
diff options
context:
space:
mode:
authorArne Schwabe <arne@rfc2549.org>2021-08-24 19:25:59 +0200
committerArne Schwabe <arne@rfc2549.org>2021-08-24 19:25:59 +0200
commit09c5cb0a0d33c58897026b7b4b1901c1ddc088f1 (patch)
tree2a881c94bd517b145178a1bd08f71d69914115d4 /main/src/ui
parentd1eb15b8dd6b4cb599ea7a084e61e24dfdbc74f4 (diff)
Implement support of openvpn://import-profile/ support
For details about the protocol see https://github.com/OpenVPN/openvpn3/blob/master/doc/webauth.md
Diffstat (limited to 'main/src/ui')
-rw-r--r--main/src/ui/AndroidManifest.xml7
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java34
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/fragments/ImportRemoteConfig.kt (renamed from main/src/ui/java/de/blinkt/openvpn/fragments/ImportASConfig.kt)82
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java14
4 files changed, 104 insertions, 33 deletions
diff --git a/main/src/ui/AndroidManifest.xml b/main/src/ui/AndroidManifest.xml
index a9dfa380..0caccd5d 100644
--- a/main/src/ui/AndroidManifest.xml
+++ b/main/src/ui/AndroidManifest.xml
@@ -31,6 +31,13 @@
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="openvpn" />
+ <data android:host="import-profile" />
+ </intent-filter>
</activity>
<activity android:name=".activities.InternalWebView" />
<activity
diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java
index 58698ea3..fe98cf77 100644
--- a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java
+++ b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.java
@@ -5,11 +5,11 @@
package de.blinkt.openvpn.activities;
-import android.annotation.TargetApi;
import android.content.Intent;
-import android.os.Build;
+import android.net.Uri;
import android.view.Menu;
import android.view.MenuItem;
+import android.widget.Toast;
import androidx.appcompat.app.ActionBar;
import androidx.viewpager.widget.ViewPager;
@@ -21,6 +21,7 @@ import de.blinkt.openvpn.fragments.AboutFragment;
import de.blinkt.openvpn.fragments.FaqFragment;
import de.blinkt.openvpn.fragments.GeneralSettings;
import de.blinkt.openvpn.fragments.GraphFragment;
+import de.blinkt.openvpn.fragments.ImportRemoteConfig;
import de.blinkt.openvpn.fragments.LogFragment;
import de.blinkt.openvpn.fragments.SendDumpFragment;
import de.blinkt.openvpn.fragments.VPNProfileList;
@@ -76,8 +77,14 @@ public class MainActivity extends BaseActivity {
@Override
protected void onResume() {
super.onResume();
- if (getIntent() != null) {
- String page = getIntent().getStringExtra("PAGE");
+ Intent intent = getIntent();
+ if (intent != null) {
+ if (intent.getAction().equals(Intent.ACTION_VIEW))
+ {
+ Uri uri = intent.getData();
+ checkUriForProfileImport(uri);
+ }
+ String page = intent.getStringExtra("PAGE");
if ("graph".equals(page)) {
mPager.setCurrentItem(1);
}
@@ -85,6 +92,25 @@ public class MainActivity extends BaseActivity {
}
}
+ private void checkUriForProfileImport(Uri uri) {
+ if ("openvpn".equals(uri.getScheme()) && "import-profile".equals(uri.getHost()))
+ {
+ String realUrl = uri.getEncodedPath() + "?" + uri.getEncodedQuery();
+ if (!realUrl.startsWith("/https://"))
+ {
+ Toast.makeText(this, "Cannot use openvpn://import-profile/ URL that does not use https://", Toast.LENGTH_LONG).show();
+ return;
+ }
+ realUrl = realUrl.substring(1);
+ startOpenVPNUrlImport(realUrl);
+ }
+ }
+
+ private void startOpenVPNUrlImport(String url) {
+ ImportRemoteConfig asImportFrag = ImportRemoteConfig.newInstance(url);
+ asImportFrag.show(getSupportFragmentManager(), "dialog");
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/ImportASConfig.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/ImportRemoteConfig.kt
index 71d1ee7c..6cd322ca 100644
--- a/main/src/ui/java/de/blinkt/openvpn/fragments/ImportASConfig.kt
+++ b/main/src/ui/java/de/blinkt/openvpn/fragments/ImportRemoteConfig.kt
@@ -19,9 +19,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.CheckBox
-import android.widget.EditText
-import android.widget.Toast
+import android.widget.*
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import de.blinkt.openvpn.R
@@ -86,13 +84,16 @@ fun getCompositeSSLSocketFactory(certPin: CertificatePinner, hostname: String):
}
-class ImportASConfig : DialogFragment() {
- private lateinit var asUseAutlogin: CheckBox
+class ImportRemoteConfig : DialogFragment() {
+ private lateinit var asUseAutologin: CheckBox
private lateinit var asServername: EditText
private lateinit var asUsername: EditText
private lateinit var asPassword: EditText
private lateinit var dialogView: View
+ private lateinit var importChoiceGroup: RadioGroup
+ private lateinit var importChoiceAS: RadioButton
+
internal fun getHostNameVerifier(prefs: SharedPreferences): HostnameVerifier {
@@ -131,8 +132,10 @@ class ImportASConfig : DialogFragment() {
val pinnedHosts: Set<String> = prefs.getStringSet("pinnedHosts", emptySet())!!
val okHttpClient = OkHttpClient.Builder()
- .addInterceptor(BasicAuthInterceptor(user, password))
- .connectTimeout(15, TimeUnit.SECONDS)
+ if (user.isNotBlank() && password.isNotBlank()) {
+ okHttpClient.addInterceptor(BasicAuthInterceptor(user, password))
+ }
+ okHttpClient.connectTimeout(15, TimeUnit.SECONDS)
/* Rely on system certificates if we do not have the host pinned */
if (pinnedHosts.contains(hostname)) {
@@ -164,6 +167,7 @@ class ImportASConfig : DialogFragment() {
val prefs = c.getSharedPreferences("pinnedCerts", Context.MODE_PRIVATE)
val pedit = prefs.edit()
val pinnedHosts: MutableSet<String> = prefs.getStringSet("pinnedHosts", mutableSetOf<String>())!!
+ .toMutableSet()
pinnedHosts.add(host)
@@ -177,7 +181,7 @@ class ImportASConfig : DialogFragment() {
internal fun removedPinnedCert(c: Context, host: String) {
val prefs = c.getSharedPreferences("pinnedCerts", Context.MODE_PRIVATE)
val pedit = prefs.edit()
- val pinnedHosts: MutableSet<String> = prefs.getStringSet("pinnedHosts", mutableSetOf<String>())!!
+ val pinnedHosts: MutableSet<String> = prefs.getStringSet("pinnedHosts", mutableSetOf<String>())!!.toMutableSet()
pinnedHosts.remove(host)
@@ -223,7 +227,7 @@ class ImportASConfig : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val inflater = requireActivity().layoutInflater
- dialogView = inflater.inflate(R.layout.import_as_config, null);
+ dialogView = inflater.inflate(R.layout.import_remote_config, null);
val builder = AlertDialog.Builder(requireContext())
@@ -233,11 +237,27 @@ class ImportASConfig : DialogFragment() {
asServername = dialogView.findViewById(R.id.as_servername)
asUsername = dialogView.findViewById(R.id.username)
asPassword = dialogView.findViewById(R.id.password)
- asUseAutlogin = dialogView.findViewById(R.id.request_autologin)
+ asUseAutologin = dialogView.findViewById(R.id.request_autologin)
+
+ importChoiceGroup = dialogView.findViewById(R.id.import_source_group)
+ importChoiceAS = dialogView.findViewById(R.id.import_choice_as)
+
+ importChoiceGroup.setOnCheckedChangeListener { group, checkedId ->
+ if (checkedId == R.id.import_choice_as)
+ asServername.setHint(R.string.as_servername)
+ else
+ asServername.setHint(R.string.server_url)
+ }
builder.setPositiveButton(R.string.import_config, null)
builder.setNegativeButton(android.R.string.cancel) { _, _ -> }
+ if (arguments?.getString("url") != null)
+ {
+ asServername.setText(arguments?.getString("url"))
+ importChoiceGroup.check(R.id.import_choice_url)
+ }
+
val dialog = builder.create()
return dialog
@@ -276,8 +296,11 @@ class ImportASConfig : DialogFragment() {
Toast.makeText(context, "Downloading profile", Toast.LENGTH_LONG).show()
}
-
- val asProfileUri = getAsUrl(asServername.text.toString(), asUseAutlogin.isChecked)
+ val asProfileUri:HttpUrl
+ if (importChoiceAS.isChecked)
+ asProfileUri = getAsUrl(asServername.text.toString(), asUseAutologin.isChecked)
+ else
+ asProfileUri = HttpUrl.parse(asServername.text.toString())
var e: Exception? = null
try {
@@ -400,22 +423,43 @@ class ImportASConfig : DialogFragment() {
override fun onResume() {
super.onResume()
- asServername.setText(Preferences.getDefaultSharedPreferences(activity).getString("as-hostname", ""))
- asUsername.setText(Preferences.getDefaultSharedPreferences(activity).getString("as-username", ""))
+ if (arguments == null) {
+ asServername.setText(
+ Preferences.getDefaultSharedPreferences(activity).getString("as-hostname", "")
+ )
+ asUsername.setText(
+ Preferences.getDefaultSharedPreferences(activity).getString("as-username", "")
+ )
+ if (Preferences.getDefaultSharedPreferences(activity).getBoolean("as-selected", true)) {
+ importChoiceGroup.check(R.id.import_choice_as)
+ } else {
+ importChoiceGroup.check(R.id.import_choice_url)
+ }
+
+ }
}
override fun onPause() {
super.onPause()
val prefs = Preferences.getDefaultSharedPreferences(activity)
- prefs.edit().putString("as-hostname", asServername.text.toString()).apply()
- prefs.edit().putString("as-username", asUsername.text.toString()).apply()
+ val editor = prefs.edit()
+ editor.putString("as-hostname", asServername.text.toString())
+ editor.putString("as-username", asUsername.text.toString())
+ editor.putBoolean("as-selected", importChoiceAS.isChecked)
+ editor.apply()
}
companion object {
@JvmStatic
- fun newInstance(): ImportASConfig {
- return ImportASConfig();
+ fun newInstance(url:String? = null): ImportRemoteConfig {
+ val frag = ImportRemoteConfig()
+ if (url != null)
+ {
+ val extras = Bundle()
+ extras.putString("url", url)
+ frag.arguments = extras
+ }
+ return frag
}
}
-
}
diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java
index 3804926f..ce6fa7f1 100644
--- a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java
+++ b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java
@@ -21,7 +21,6 @@ import android.os.Bundle;
import android.os.PersistableBundle;
import androidx.annotation.RequiresApi;
-import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.ListFragment;
import android.text.Html;
@@ -95,7 +94,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn
private boolean showUserRequestDialogIfNeeded(ConnectionStatus level, Intent intent) {
if (level == LEVEL_WAITING_FOR_USER_INPUT) {
- if (intent.getStringExtra(EXTRA_CHALLENGE_TXT) != null) {
+ if (intent != null && intent.getStringExtra(EXTRA_CHALLENGE_TXT) != null) {
PasswordDialogFragment pwInputFrag = PasswordDialogFragment.Companion.newInstance(intent, false);
pwInputFrag.show(getParentFragmentManager(), "dialog");
@@ -126,6 +125,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
+ setListAdapter();
}
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
@@ -277,12 +277,6 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn
}
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- setListAdapter();
- }
-
private void setListAdapter() {
if (mArrayadapter == null) {
mArrayadapter = new VPNArrayAdapter(getActivity(), R.layout.vpn_list_item, R.id.vpn_item_title);
@@ -354,8 +348,8 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn
}
private boolean startASProfileImport() {
- ImportASConfig asImportFrag = ImportASConfig.newInstance();
- asImportFrag.show(requireFragmentManager(), "dialog");
+ ImportRemoteConfig asImportFrag = ImportRemoteConfig.newInstance(null);
+ asImportFrag.show(getParentFragmentManager(), "dialog");
return true;
}