summaryrefslogtreecommitdiff
path: root/main/src/ui/java
diff options
context:
space:
mode:
authorArne Schwabe <arne@rfc2549.org>2019-11-12 11:23:13 +0100
committerArne Schwabe <arne@rfc2549.org>2019-11-17 10:16:02 +0100
commit81d7c76b94335e699b2885cd74f3e364eba60cc3 (patch)
tree6bdfb768e5ab6edb4c12f9feaf4dbd4b21d384ee /main/src/ui/java
parent05ae5866281283cc9dd22579da28a4040d9c24c5 (diff)
Convert ConfigConvert to Kotlin
Diffstat (limited to 'main/src/ui/java')
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.java846
-rw-r--r--main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt797
2 files changed, 797 insertions, 846 deletions
diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.java b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.java
deleted file mode 100644
index cd6133be..00000000
--- a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.java
+++ /dev/null
@@ -1,846 +0,0 @@
-
-/*
- * 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.activities;
-
-import android.Manifest;
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.provider.OpenableColumns;
-import android.security.KeyChain;
-import android.security.KeyChainAliasCallback;
-import androidx.annotation.NonNull;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Pair;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
-
-import de.blinkt.openvpn.R;
-import de.blinkt.openvpn.VpnProfile;
-import de.blinkt.openvpn.core.ConfigParser;
-import de.blinkt.openvpn.core.ConfigParser.ConfigParseError;
-import de.blinkt.openvpn.core.ProfileManager;
-import de.blinkt.openvpn.fragments.Utils;
-import de.blinkt.openvpn.views.FileSelectLayout;
-
-import static de.blinkt.openvpn.views.FileSelectLayout.FileSelectCallback;
-
-public class ConfigConverter extends BaseActivity implements FileSelectCallback, View.OnClickListener {
-
- public static final String IMPORT_PROFILE = "de.blinkt.openvpn.IMPORT_PROFILE";
- private static final int RESULT_INSTALLPKCS12 = 7;
- private static final int CHOOSE_FILE_OFFSET = 1000;
- public static final String VPNPROFILE = "vpnProfile";
- private static final int PERMISSION_REQUEST_EMBED_FILES = 37231;
- private static final int PERMISSION_REQUEST_READ_URL = PERMISSION_REQUEST_EMBED_FILES + 1;
-
- private VpnProfile mResult;
-
- private transient List<String> mPathsegments;
-
- private String mAliasName = null;
-
-
- private Map<Utils.FileType, FileSelectLayout> fileSelectMap = new HashMap<>();
- private String mEmbeddedPwFile;
- private Vector<String> mLogEntries = new Vector<>();
- private Uri mSourceUri;
- private EditText mProfilename;
- private AsyncTask<Void, Void, Integer> mImportTask;
- private LinearLayout mLogLayout;
- private TextView mProfilenameLabel;
-
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.fab_save)
- userActionSaveProfile();
- if (v.getId() == R.id.permssion_hint && Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
- doRequestSDCardPermission(PERMISSION_REQUEST_EMBED_FILES);
-
- }
-
- @TargetApi(Build.VERSION_CODES.M)
- private void doRequestSDCardPermission(int requestCode) {
- requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, requestCode);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- // Permission declined, do nothing
- if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED)
- return;
-
- // Reset file select dialogs
- findViewById(R.id.files_missing_hint).setVisibility(View.GONE);
- findViewById(R.id.permssion_hint).setVisibility(View.GONE);
- LinearLayout fileroot = (LinearLayout) findViewById(R.id.config_convert_root);
- for (int i = 0; i < fileroot.getChildCount(); ) {
- if (fileroot.getChildAt(i) instanceof FileSelectLayout)
- fileroot.removeViewAt(i);
- else
- i++;
- }
-
- if (requestCode == PERMISSION_REQUEST_EMBED_FILES)
- embedFiles(null);
-
- else if (requestCode == PERMISSION_REQUEST_READ_URL) {
- if (mSourceUri != null)
- doImportUri(mSourceUri);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.cancel) {
- setResult(Activity.RESULT_CANCELED);
- finish();
- } else if (item.getItemId() == R.id.ok) {
- return userActionSaveProfile();
- }
-
- return super.onOptionsItemSelected(item);
-
- }
-
- private boolean userActionSaveProfile() {
- if (mResult == null) {
- log(R.string.import_config_error);
- Toast.makeText(this, R.string.import_config_error, Toast.LENGTH_LONG).show();
- return true;
- }
-
- mResult.mName = mProfilename.getText().toString();
- ProfileManager vpl = ProfileManager.getInstance(this);
- if (vpl.getProfileByName(mResult.mName) != null) {
- mProfilename.setError(getString(R.string.duplicate_profile_name));
- return true;
- }
-
- Intent in = installPKCS12();
-
- if (in != null)
- startActivityForResult(in, RESULT_INSTALLPKCS12);
- else
- saveProfile();
-
- return true;
- }
-
- @Override
- protected void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- if (mResult != null)
- outState.putSerializable(VPNPROFILE, mResult);
- outState.putString("mAliasName", mAliasName);
-
-
- String[] logentries = mLogEntries.toArray(new String[mLogEntries.size()]);
-
- outState.putStringArray("logentries", logentries);
-
- int[] fileselects = new int[fileSelectMap.size()];
- int k = 0;
- for (Utils.FileType key : fileSelectMap.keySet()) {
- fileselects[k] = key.getValue();
- k++;
- }
- outState.putIntArray("fileselects", fileselects);
- outState.putString("pwfile", mEmbeddedPwFile);
- outState.putParcelable("mSourceUri", mSourceUri);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent result) {
- if (requestCode == RESULT_INSTALLPKCS12 && resultCode == Activity.RESULT_OK) {
- showCertDialog();
- }
-
- if (resultCode == Activity.RESULT_OK && requestCode >= CHOOSE_FILE_OFFSET) {
- Utils.FileType type = Utils.FileType.getFileTypeByValue(requestCode - CHOOSE_FILE_OFFSET);
-
-
- FileSelectLayout fs = fileSelectMap.get(type);
- fs.parseResponse(result, this);
-
- String data = fs.getData();
-
- switch (type) {
- case USERPW_FILE:
- mEmbeddedPwFile = data;
- break;
- case PKCS12:
- mResult.mPKCS12Filename = data;
- break;
- case TLS_AUTH_FILE:
- mResult.mTLSAuthFilename = data;
- break;
- case CA_CERTIFICATE:
- mResult.mCaFilename = data;
- break;
- case CLIENT_CERTIFICATE:
- mResult.mClientCertFilename = data;
- break;
- case KEYFILE:
- mResult.mClientKeyFilename = data;
- break;
- case CRL_FILE:
- mResult.mCrlFilename = data;
- break;
- default:
- throw new RuntimeException("Type is wrong somehow?");
- }
- }
-
- super.onActivityResult(requestCode, resultCode, result);
- }
-
- private void saveProfile() {
- Intent result = new Intent();
- ProfileManager vpl = ProfileManager.getInstance(this);
-
- if (!TextUtils.isEmpty(mEmbeddedPwFile))
- ConfigParser.useEmbbedUserAuth(mResult, mEmbeddedPwFile);
-
- vpl.addProfile(mResult);
- vpl.saveProfile(this, mResult);
- vpl.saveProfileList(this);
- result.putExtra(VpnProfile.EXTRA_PROFILEUUID, mResult.getUUID().toString());
- setResult(Activity.RESULT_OK, result);
- finish();
- }
-
- public void showCertDialog() {
- try {
- //noinspection WrongConstant
- KeyChain.choosePrivateKeyAlias(this,
- new KeyChainAliasCallback() {
-
- public void alias(String alias) {
- // Credential alias selected. Remember the alias selection for future use.
- mResult.mAlias = alias;
- saveProfile();
- }
-
-
- },
- new String[]{"RSA", "EC"}, // List of acceptable key types. null for any
- null, // issuer, null for any
- mResult.mServerName, // host name of server requesting the cert, null if unavailable
- -1, // port of server requesting the cert, -1 if unavailable
- mAliasName); // alias to preselect, null if unavailable
- } catch (ActivityNotFoundException anf) {
- Builder ab = new AlertDialog.Builder(this);
- ab.setTitle(R.string.broken_image_cert_title);
- ab.setMessage(R.string.broken_image_cert);
- ab.setPositiveButton(android.R.string.ok, null);
- ab.show();
- }
- }
-
-
- private Intent installPKCS12() {
-
- if (!((CheckBox) findViewById(R.id.importpkcs12)).isChecked()) {
- setAuthTypeToEmbeddedPKCS12();
- return null;
-
- }
- String pkcs12datastr = mResult.mPKCS12Filename;
- if (VpnProfile.isEmbedded(pkcs12datastr)) {
- Intent inkeyIntent = KeyChain.createInstallIntent();
-
- pkcs12datastr = VpnProfile.getEmbeddedContent(pkcs12datastr);
-
-
- byte[] pkcs12data = Base64.decode(pkcs12datastr, Base64.DEFAULT);
-
-
- inkeyIntent.putExtra(KeyChain.EXTRA_PKCS12, pkcs12data);
-
- if (mAliasName.equals(""))
- mAliasName = null;
-
- if (mAliasName != null) {
- inkeyIntent.putExtra(KeyChain.EXTRA_NAME, mAliasName);
- }
- return inkeyIntent;
-
- }
- return null;
- }
-
-
- private void setAuthTypeToEmbeddedPKCS12() {
- if (VpnProfile.isEmbedded(mResult.mPKCS12Filename)) {
- if (mResult.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE)
- mResult.mAuthenticationType = VpnProfile.TYPE_USERPASS_PKCS12;
-
- if (mResult.mAuthenticationType == VpnProfile.TYPE_KEYSTORE)
- mResult.mAuthenticationType = VpnProfile.TYPE_PKCS12;
-
- }
- }
-
-
- private String getUniqueProfileName(String possibleName) {
-
- int i = 0;
-
- ProfileManager vpl = ProfileManager.getInstance(this);
-
- String newname = possibleName;
-
- // Default to
- if (mResult.mName != null && !ConfigParser.CONVERTED_PROFILE.equals(mResult.mName))
- newname = mResult.mName;
-
- while (newname == null || vpl.getProfileByName(newname) != null) {
- i++;
- if (i == 1)
- newname = getString(R.string.converted_profile);
- else
- newname = getString(R.string.converted_profile_i, i);
- }
-
- return newname;
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.import_menu, menu);
- return true;
- }
-
- private String embedFile(String filename, Utils.FileType type, boolean onlyFindFileAndNullonNotFound) {
- if (filename == null)
- return null;
-
- // Already embedded, nothing to do
- if (VpnProfile.isEmbedded(filename))
- return filename;
-
- File possibleFile = findFile(filename, type);
- if (possibleFile == null)
- if (onlyFindFileAndNullonNotFound)
- return null;
- else
- return filename;
- else if (onlyFindFileAndNullonNotFound)
- return possibleFile.getAbsolutePath();
- else
- return readFileContent(possibleFile, type == Utils.FileType.PKCS12);
-
- }
-
-
- private Pair<Integer, String> getFileDialogInfo(Utils.FileType type) {
- int titleRes = 0;
- String value = null;
- switch (type) {
- case KEYFILE:
- titleRes = R.string.client_key_title;
- if (mResult != null)
- value = mResult.mClientKeyFilename;
- break;
- case CLIENT_CERTIFICATE:
- titleRes = R.string.client_certificate_title;
- if (mResult != null)
- value = mResult.mClientCertFilename;
- break;
- case CA_CERTIFICATE:
- titleRes = R.string.ca_title;
- if (mResult != null)
- value = mResult.mCaFilename;
- break;
- case TLS_AUTH_FILE:
- titleRes = R.string.tls_auth_file;
- if (mResult != null)
- value = mResult.mTLSAuthFilename;
- break;
- case PKCS12:
- titleRes = R.string.client_pkcs12_title;
- if (mResult != null)
- value = mResult.mPKCS12Filename;
- break;
-
- case USERPW_FILE:
- titleRes = R.string.userpw_file;
- value = mEmbeddedPwFile;
- break;
-
- case CRL_FILE:
- titleRes = R.string.crl_file;
- value = mResult.mCrlFilename;
- break;
- }
-
- return Pair.create(titleRes, value);
-
- }
-
- private File findFile(String filename, Utils.FileType fileType) {
- File foundfile = findFileRaw(filename);
-
- if (foundfile == null && filename != null && !filename.equals("")) {
- log(R.string.import_could_not_open, filename);
- }
- fileSelectMap.put(fileType, null);
-
- return foundfile;
- }
-
- private void addMissingFileDialogs()
- {
- for (Map.Entry<Utils.FileType, FileSelectLayout> item: fileSelectMap.entrySet()) {
- if (item.getValue()==null)
- addFileSelectDialog(item.getKey());
- }
- }
-
- private void addFileSelectDialog(Utils.FileType type) {
-
- Pair<Integer, String> fileDialogInfo = getFileDialogInfo(type);
-
- boolean isCert = type == Utils.FileType.CA_CERTIFICATE || type == Utils.FileType.CLIENT_CERTIFICATE;
- FileSelectLayout fl = new FileSelectLayout(this, getString(fileDialogInfo.first), isCert, false);
- fileSelectMap.put(type, fl);
- fl.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-
- ((LinearLayout) findViewById(R.id.config_convert_root)).addView(fl, 2);
- findViewById(R.id.files_missing_hint).setVisibility(View.VISIBLE);
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
- checkPermission();
-
- fl.setData(fileDialogInfo.second, this);
- int i = getFileLayoutOffset(type);
- fl.setCaller(this, i, type);
-
- }
-
- @TargetApi(Build.VERSION_CODES.M)
- private void checkPermission() {
- if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- findViewById(R.id.permssion_hint).setVisibility(View.VISIBLE);
- findViewById(R.id.permssion_hint).setOnClickListener(this);
- }
- }
-
- private int getFileLayoutOffset(Utils.FileType type) {
- return CHOOSE_FILE_OFFSET + type.getValue();
- }
-
-
- private File findFileRaw(String filename) {
- if (filename == null || filename.equals(""))
- return null;
-
- // Try diffent path relative to /mnt/sdcard
- File sdcard = Environment.getExternalStorageDirectory();
- File root = new File("/");
-
- HashSet<File> dirlist = new HashSet<>();
-
- for (int i = mPathsegments.size() - 1; i >= 0; i--) {
- String path = "";
- for (int j = 0; j <= i; j++) {
- path += "/" + mPathsegments.get(j);
- }
- // Do a little hackish dance for the Android File Importer
- // /document/primary:ovpn/openvpn-imt.conf
-
-
- if (path.indexOf(':') != -1 && path.lastIndexOf('/') > path.indexOf(':')) {
- String possibleDir = path.substring(path.indexOf(':') + 1, path.length());
- // Unquote chars in the path
- try {
- possibleDir = URLDecoder.decode(possibleDir, "UTF-8");
- } catch (UnsupportedEncodingException ignored) {}
-
- possibleDir = possibleDir.substring(0, possibleDir.lastIndexOf('/'));
-
-
-
-
- dirlist.add(new File(sdcard, possibleDir));
-
- }
- dirlist.add(new File(path));
-
-
- }
- dirlist.add(sdcard);
- dirlist.add(root);
-
-
- String[] fileparts = filename.split("/");
- for (File rootdir : dirlist) {
- String suffix = "";
- for (int i = fileparts.length - 1; i >= 0; i--) {
- if (i == fileparts.length - 1)
- suffix = fileparts[i];
- else
- suffix = fileparts[i] + "/" + suffix;
-
- File possibleFile = new File(rootdir, suffix);
- if (possibleFile.canRead())
- return possibleFile;
-
- }
- }
- return null;
- }
-
- String readFileContent(File possibleFile, boolean base64encode) {
- byte[] filedata;
- try {
- filedata = readBytesFromFile(possibleFile);
- } catch (IOException e) {
- log(e.getLocalizedMessage());
- return null;
- }
-
- String data;
- if (base64encode) {
- data = Base64.encodeToString(filedata, Base64.DEFAULT);
- } else {
- data = new String(filedata);
-
- }
-
- return VpnProfile.DISPLAYNAME_TAG + possibleFile.getName() + VpnProfile.INLINE_TAG + data;
-
- }
-
-
- private byte[] readBytesFromFile(File file) throws IOException {
- InputStream input = new FileInputStream(file);
-
- long len = file.length();
- if (len > VpnProfile.MAX_EMBED_FILE_SIZE)
- throw new IOException("File size of file to import too large.");
-
- // Create the byte array to hold the data
- byte[] bytes = new byte[(int) len];
-
- // Read in the bytes
- int offset = 0;
- int bytesRead;
- while (offset < bytes.length
- && (bytesRead = input.read(bytes, offset, bytes.length - offset)) >= 0) {
- offset += bytesRead;
- }
-
- input.close();
- return bytes;
- }
-
- void embedFiles(ConfigParser cp) {
- // This where I would like to have a c++ style
- // void embedFile(std::string & option)
-
- if (mResult.mPKCS12Filename != null) {
- File pkcs12file = findFileRaw(mResult.mPKCS12Filename);
- if (pkcs12file != null) {
- mAliasName = pkcs12file.getName().replace(".p12", "");
- } else {
- mAliasName = "Imported PKCS12";
- }
- }
-
-
- mResult.mCaFilename = embedFile(mResult.mCaFilename, Utils.FileType.CA_CERTIFICATE, false);
- mResult.mClientCertFilename = embedFile(mResult.mClientCertFilename, Utils.FileType.CLIENT_CERTIFICATE, false);
- mResult.mClientKeyFilename = embedFile(mResult.mClientKeyFilename, Utils.FileType.KEYFILE, false);
- mResult.mTLSAuthFilename = embedFile(mResult.mTLSAuthFilename, Utils.FileType.TLS_AUTH_FILE, false);
- mResult.mPKCS12Filename = embedFile(mResult.mPKCS12Filename, Utils.FileType.PKCS12, false);
- mResult.mCrlFilename = embedFile(mResult.mCrlFilename, Utils.FileType.CRL_FILE, true);
- if (cp != null) {
- mEmbeddedPwFile = cp.getAuthUserPassFile();
- mEmbeddedPwFile = embedFile(cp.getAuthUserPassFile(), Utils.FileType.USERPW_FILE, false);
- }
-
- }
-
- private void updateFileSelectDialogs() {
- for (Map.Entry<Utils.FileType, FileSelectLayout> fl : fileSelectMap.entrySet()) {
- fl.getValue().setData(getFileDialogInfo(fl.getKey()).second, this);
- }
- }
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.config_converter);
-
- ImageButton fab_button = (ImageButton) findViewById(R.id.fab_save);
- if (fab_button != null) {
- fab_button.setOnClickListener(this);
- findViewById(R.id.fab_footerspace).setVisibility(View.VISIBLE);
- }
-
- mLogLayout = (LinearLayout) findViewById(R.id.config_convert_root);
-
-
- mProfilename = (EditText) findViewById(R.id.profilename);
- mProfilenameLabel = (TextView) findViewById(R.id.profilename_label);
-
- if (savedInstanceState != null && savedInstanceState.containsKey(VPNPROFILE)) {
- mResult = (VpnProfile) savedInstanceState.getSerializable(VPNPROFILE);
- mAliasName = savedInstanceState.getString("mAliasName");
- mEmbeddedPwFile = savedInstanceState.getString("pwfile");
- mSourceUri = savedInstanceState.getParcelable("mSourceUri");
- mProfilename.setText(mResult.mName);
-
- if (savedInstanceState.containsKey("logentries")) {
- //noinspection ConstantConditions
- for (String logItem : savedInstanceState.getStringArray("logentries"))
- log(logItem);
- }
- if (savedInstanceState.containsKey("fileselects")) {
- //noinspection ConstantConditions
- for (int k : savedInstanceState.getIntArray("fileselects")) {
- addFileSelectDialog(Utils.FileType.getFileTypeByValue(k));
- }
- }
- return;
- }
-
-
- final android.content.Intent intent = getIntent();
-
- if (intent != null) {
- doImportIntent(intent);
-
- // We parsed the intent, relay on saved instance for restoring
- setIntent(null);
- }
-
-
- }
-
- private void doImportIntent(Intent intent) {
- final Uri data = intent.getData();
- if (data != null) {
- mSourceUri = data;
- doImportUri(data);
- }
- }
-
- private void doImportUri(Uri data) {
- //log(R.string.import_experimental);
- log(R.string.importing_config, data.toString());
- String possibleName = null;
- if ((data.getScheme() != null && data.getScheme().equals("file")) ||
- (data.getLastPathSegment() != null &&
- (data.getLastPathSegment().endsWith(".ovpn") ||
- data.getLastPathSegment().endsWith(".conf")))
- ) {
- possibleName = data.getLastPathSegment();
- if (possibleName.lastIndexOf('/') != -1)
- possibleName = possibleName.substring(possibleName.lastIndexOf('/') + 1);
-
- }
-
- mPathsegments = data.getPathSegments();
-
- Cursor cursor = getContentResolver().query(data, null, null, null, null);
-
- try {
-
- if (cursor != null && cursor.moveToFirst()) {
- int columnIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
-
- if (columnIndex != -1) {
- String displayName = cursor.getString(columnIndex);
- if (displayName != null)
- possibleName = displayName;
- }
- columnIndex = cursor.getColumnIndex("mime_type");
- if (columnIndex != -1) {
- log("Mime type: " + cursor.getString(columnIndex));
- }
- }
- } finally {
- if (cursor != null)
- cursor.close();
- }
- if (possibleName != null) {
- possibleName = possibleName.replace(".ovpn", "");
- possibleName = possibleName.replace(".conf", "");
- }
-
- startImportTask(data, possibleName);
-
-
- }
-
- private void startImportTask(final Uri data, final String possibleName) {
- mImportTask = new AsyncTask<Void, Void, Integer>() {
- private ProgressBar mProgress;
-
- @Override
- protected void onPreExecute() {
- mProgress = new ProgressBar(ConfigConverter.this);
- addViewToLog(mProgress);
- }
-
- @Override
- protected Integer doInBackground(Void... params) {
- try {
- InputStream is = getContentResolver().openInputStream(data);
-
- doImport(is);
- is.close();
- if (mResult==null)
- return -3;
- } catch (IOException| SecurityException se)
-
- {
- log(R.string.import_content_resolve_error + ":" + se.getLocalizedMessage());
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
- checkMarschmallowFileImportError(data);
- return -2;
- }
-
- return 0;
- }
-
- @Override
- protected void onPostExecute(Integer errorCode) {
- mLogLayout.removeView(mProgress);
- addMissingFileDialogs();
- updateFileSelectDialogs();
-
- if (errorCode == 0) {
- displayWarnings();
- mResult.mName = getUniqueProfileName(possibleName);
- mProfilename.setVisibility(View.VISIBLE);
- mProfilenameLabel.setVisibility(View.VISIBLE);
- mProfilename.setText(mResult.getName());
-
- log(R.string.import_done);
- }
- }
- }.execute();
- }
-
-
- @TargetApi(Build.VERSION_CODES.M)
- private void checkMarschmallowFileImportError(Uri data) {
- // Permission already granted, not the source of the error
- if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
- return;
-
- // We got a file:/// URL and have no permission to read it. Technically an error of the calling app since
- // it makes an assumption about other apps being able to read the url but well ...
- if (data != null && "file".equals(data.getScheme()))
- doRequestSDCardPermission(PERMISSION_REQUEST_READ_URL);
-
- }
-
-
- @Override
- protected void onStart() {
- super.onStart();
- }
-
- private void log(final String logmessage) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- TextView tv = new TextView(ConfigConverter.this);
- mLogEntries.add(logmessage);
- tv.setText(logmessage);
-
- addViewToLog(tv);
- }
- });
- }
-
- private void addViewToLog(View view) {
- mLogLayout.addView(view, mLogLayout.getChildCount() - 1);
- }
-
- private void doImport(InputStream is) {
- ConfigParser cp = new ConfigParser();
- try {
- InputStreamReader isr = new InputStreamReader(is);
-
- cp.parseConfig(isr);
- mResult = cp.convertProfile();
- embedFiles(cp);
- return;
-
- } catch (IOException | ConfigParseError e) {
- log(R.string.error_reading_config_file);
- log(e.getLocalizedMessage());
- }
- mResult = null;
-
- }
-
- private void displayWarnings() {
- if (mResult.mUseCustomConfig) {
- log(R.string.import_warning_custom_options);
- String copt = mResult.mCustomConfigOptions;
- if (copt.startsWith("#")) {
- int until = copt.indexOf('\n');
- copt = copt.substring(until + 1);
- }
-
- log(copt);
- }
-
- if (mResult.mAuthenticationType == VpnProfile.TYPE_KEYSTORE ||
- mResult.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) {
- findViewById(R.id.importpkcs12).setVisibility(View.VISIBLE);
- }
-
- }
-
- private void log(int ressourceId, Object... formatArgs) {
- log(getString(ressourceId, formatArgs));
- }
-
-}
diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt
new file mode 100644
index 00000000..672ffae6
--- /dev/null
+++ b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt
@@ -0,0 +1,797 @@
+/*
+ * 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.activities
+
+import android.Manifest
+import android.annotation.TargetApi
+import android.app.Activity
+import android.app.AlertDialog
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.AsyncTask
+import android.os.Build
+import android.os.Bundle
+import android.os.Environment
+import android.provider.OpenableColumns
+import android.security.KeyChain
+import android.text.TextUtils
+import android.util.Base64
+import android.util.Pair
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.*
+import de.blinkt.openvpn.R
+import de.blinkt.openvpn.VpnProfile
+import de.blinkt.openvpn.core.ConfigParser
+import de.blinkt.openvpn.core.ConfigParser.ConfigParseError
+import de.blinkt.openvpn.core.ProfileManager
+import de.blinkt.openvpn.fragments.Utils
+import de.blinkt.openvpn.views.FileSelectLayout
+import de.blinkt.openvpn.views.FileSelectLayout.FileSelectCallback
+import java.io.*
+import java.net.URLDecoder
+import java.util.*
+
+class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener {
+
+ private var mResult: VpnProfile? = null
+
+ @Transient
+ private var mPathsegments: List<String>? = null
+
+ private var mAliasName: String? = null
+
+
+ private val fileSelectMap = HashMap<Utils.FileType, FileSelectLayout?>()
+ private var mEmbeddedPwFile: String? = null
+ private val mLogEntries = Vector<String>()
+ private var mSourceUri: Uri? = null
+ private lateinit var mProfilename: EditText
+ private var mImportTask: AsyncTask<Void, Void, Int>? = null
+ private lateinit var mLogLayout: LinearLayout
+ private lateinit var mProfilenameLabel: TextView
+
+ override fun onClick(v: View) {
+ if (v.id == R.id.fab_save)
+ userActionSaveProfile()
+ if (v.id == R.id.permssion_hint && Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
+ doRequestSDCardPermission(PERMISSION_REQUEST_EMBED_FILES)
+
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private fun doRequestSDCardPermission(requestCode: Int) {
+ requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), requestCode)
+ }
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
+ // Permission declined, do nothing
+ if (grantResults.size == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED)
+ return
+
+ // Reset file select dialogs
+ findViewById<View>(R.id.files_missing_hint).visibility = View.GONE
+ findViewById<View>(R.id.permssion_hint).visibility = View.GONE
+ val fileroot = findViewById<View>(R.id.config_convert_root) as LinearLayout
+ var i = 0
+ while (i < fileroot.childCount) {
+ if (fileroot.getChildAt(i) is FileSelectLayout)
+ fileroot.removeViewAt(i)
+ else
+ i++
+ }
+
+ if (requestCode == PERMISSION_REQUEST_EMBED_FILES)
+ embedFiles(null)
+ else if (requestCode == PERMISSION_REQUEST_READ_URL) {
+ if (mSourceUri != null)
+ doImportUri(mSourceUri!!)
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == R.id.cancel) {
+ setResult(Activity.RESULT_CANCELED)
+ finish()
+ } else if (item.itemId == R.id.ok) {
+ return userActionSaveProfile()
+ }
+
+ return super.onOptionsItemSelected(item)
+
+ }
+
+ private fun userActionSaveProfile(): Boolean {
+ if (mResult == null) {
+ log(R.string.import_config_error)
+ Toast.makeText(this, R.string.import_config_error, Toast.LENGTH_LONG).show()
+ return true
+ }
+
+ mResult!!.mName = mProfilename.text.toString()
+ val vpl = ProfileManager.getInstance(this)
+ if (vpl.getProfileByName(mResult!!.mName) != null) {
+ mProfilename.error = getString(R.string.duplicate_profile_name)
+ return true
+ }
+
+ val `in` = installPKCS12()
+
+ if (`in` != null)
+ startActivityForResult(`in`, RESULT_INSTALLPKCS12)
+ else
+ saveProfile()
+
+ return true
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ if (mResult != null)
+ outState.putSerializable(VPNPROFILE, mResult)
+ outState.putString("mAliasName", mAliasName)
+
+
+ val logentries = mLogEntries.toTypedArray()
+
+ outState.putStringArray("logentries", logentries)
+
+ val fileselects = IntArray(fileSelectMap.size)
+ var k = 0
+ for (key in fileSelectMap.keys) {
+ fileselects[k] = key.value
+ k++
+ }
+ outState.putIntArray("fileselects", fileselects)
+ outState.putString("pwfile", mEmbeddedPwFile)
+ outState.putParcelable("mSourceUri", mSourceUri)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent) {
+ if (requestCode == RESULT_INSTALLPKCS12 && resultCode == Activity.RESULT_OK) {
+ showCertDialog()
+ }
+
+ if (resultCode == Activity.RESULT_OK && requestCode >= CHOOSE_FILE_OFFSET) {
+ val type = Utils.FileType.getFileTypeByValue(requestCode - CHOOSE_FILE_OFFSET)
+
+
+ val fs = fileSelectMap[type]
+ fs!!.parseResponse(result, this)
+
+ val data = fs.data
+
+ when (type) {
+ Utils.FileType.USERPW_FILE -> mEmbeddedPwFile = data
+ Utils.FileType.PKCS12 -> mResult!!.mPKCS12Filename = data
+ Utils.FileType.TLS_AUTH_FILE -> mResult!!.mTLSAuthFilename = data
+ Utils.FileType.CA_CERTIFICATE -> mResult!!.mCaFilename = data
+ Utils.FileType.CLIENT_CERTIFICATE -> mResult!!.mClientCertFilename = data
+ Utils.FileType.KEYFILE -> mResult!!.mClientKeyFilename = data
+ Utils.FileType.CRL_FILE -> mResult!!.mCrlFilename = data
+ else -> throw RuntimeException("Type is wrong somehow?")
+ }
+ }
+
+ super.onActivityResult(requestCode, resultCode, result)
+ }
+
+ private fun saveProfile() {
+ val result = Intent()
+ val vpl = ProfileManager.getInstance(this)
+
+ if (!TextUtils.isEmpty(mEmbeddedPwFile))
+ ConfigParser.useEmbbedUserAuth(mResult, mEmbeddedPwFile)
+
+ vpl.addProfile(mResult)
+ vpl.saveProfile(this, mResult)
+ vpl.saveProfileList(this)
+ result.putExtra(VpnProfile.EXTRA_PROFILEUUID, mResult!!.uuid.toString())
+ setResult(Activity.RESULT_OK, result)
+ finish()
+ }
+
+ fun showCertDialog() {
+ try {
+
+ KeyChain.choosePrivateKeyAlias(this,
+ { alias ->
+ // Credential alias selected. Remember the alias selection for future use.
+ mResult!!.mAlias = alias
+ saveProfile()
+ },
+ arrayOf("RSA", "EC"), null, // issuer, null for any
+ mResult!!.mServerName, // host name of server requesting the cert, null if unavailable
+ -1, // port of server requesting the cert, -1 if unavailable
+ mAliasName)// List of acceptable key types. null for any
+ // alias to preselect, null if unavailable
+ } catch (anf: ActivityNotFoundException) {
+ val ab = AlertDialog.Builder(this)
+ ab.setTitle(R.string.broken_image_cert_title)
+ ab.setMessage(R.string.broken_image_cert)
+ ab.setPositiveButton(android.R.string.ok, null)
+ ab.show()
+ }
+
+ }
+
+
+ private fun installPKCS12(): Intent? {
+
+ if (!(findViewById<View>(R.id.importpkcs12) as CheckBox).isChecked) {
+ setAuthTypeToEmbeddedPKCS12()
+ return null
+
+ }
+ var pkcs12datastr = mResult!!.mPKCS12Filename
+ if (VpnProfile.isEmbedded(pkcs12datastr)) {
+ val inkeyIntent = KeyChain.createInstallIntent()
+
+ pkcs12datastr = VpnProfile.getEmbeddedContent(pkcs12datastr)
+
+
+ val pkcs12data = Base64.decode(pkcs12datastr, Base64.DEFAULT)
+
+
+ inkeyIntent.putExtra(KeyChain.EXTRA_PKCS12, pkcs12data)
+
+ if (mAliasName == "")
+ mAliasName = null
+
+ if (mAliasName != null) {
+ inkeyIntent.putExtra(KeyChain.EXTRA_NAME, mAliasName)
+ }
+ return inkeyIntent
+
+ }
+ return null
+ }
+
+
+ private fun setAuthTypeToEmbeddedPKCS12() {
+ if (VpnProfile.isEmbedded(mResult!!.mPKCS12Filename)) {
+ if (mResult!!.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE)
+ mResult!!.mAuthenticationType = VpnProfile.TYPE_USERPASS_PKCS12
+
+ if (mResult!!.mAuthenticationType == VpnProfile.TYPE_KEYSTORE)
+ mResult!!.mAuthenticationType = VpnProfile.TYPE_PKCS12
+
+ }
+ }
+
+
+ private fun getUniqueProfileName(possibleName: String?): String {
+
+ var i = 0
+
+ val vpl = ProfileManager.getInstance(this)
+
+ var newname = possibleName
+
+ // Default to
+ if (mResult!!.mName != null && ConfigParser.CONVERTED_PROFILE != mResult!!.mName)
+ newname = mResult!!.mName
+
+ while (newname == null || vpl.getProfileByName(newname) != null) {
+ i++
+ if (i == 1)
+ newname = getString(R.string.converted_profile)
+ else
+ newname = getString(R.string.converted_profile_i, i)
+ }
+
+ return newname
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ val inflater = menuInflater
+ inflater.inflate(R.menu.import_menu, menu)
+ return true
+ }
+
+ private fun embedFile(filename: String?, type: Utils.FileType, onlyFindFileAndNullonNotFound: Boolean): String? {
+ if (filename == null)
+ return null
+
+ // Already embedded, nothing to do
+ if (VpnProfile.isEmbedded(filename))
+ return filename
+
+ val possibleFile = findFile(filename, type)
+ return if (possibleFile == null)
+ if (onlyFindFileAndNullonNotFound)
+ null
+ else
+ filename
+ else if (onlyFindFileAndNullonNotFound)
+ possibleFile.absolutePath
+ else
+ readFileContent(possibleFile, type == Utils.FileType.PKCS12)
+
+ }
+
+
+ private fun getFileDialogInfo(type: Utils.FileType): Pair<Int, String> {
+ var titleRes = 0
+ var value: String? = null
+ when (type) {
+ Utils.FileType.KEYFILE -> {
+ titleRes = R.string.client_key_title
+ if (mResult != null)
+ value = mResult!!.mClientKeyFilename
+ }
+ Utils.FileType.CLIENT_CERTIFICATE -> {
+ titleRes = R.string.client_certificate_title
+ if (mResult != null)
+ value = mResult!!.mClientCertFilename
+ }
+ Utils.FileType.CA_CERTIFICATE -> {
+ titleRes = R.string.ca_title
+ if (mResult != null)
+ value = mResult!!.mCaFilename
+ }
+ Utils.FileType.TLS_AUTH_FILE -> {
+ titleRes = R.string.tls_auth_file
+ if (mResult != null)
+ value = mResult!!.mTLSAuthFilename
+ }
+ Utils.FileType.PKCS12 -> {
+ titleRes = R.string.client_pkcs12_title
+ if (mResult != null)
+ value = mResult!!.mPKCS12Filename
+ }
+
+ Utils.FileType.USERPW_FILE -> {
+ titleRes = R.string.userpw_file
+ value = mEmbeddedPwFile
+ }
+
+ Utils.FileType.CRL_FILE -> {
+ titleRes = R.string.crl_file
+ value = mResult!!.mCrlFilename
+ }
+ Utils.FileType.OVPN_CONFIG -> TODO()
+ }
+
+ return Pair.create(titleRes, value)
+
+ }
+
+ private fun findFile(filename: String?, fileType: Utils.FileType): File? {
+ val foundfile = findFileRaw(filename)
+
+ if (foundfile == null && filename != null && filename != "") {
+ log(R.string.import_could_not_open, filename)
+ }
+ fileSelectMap[fileType] = null
+
+ return foundfile
+ }
+
+ private fun addMissingFileDialogs() {
+ for ((key, value) in fileSelectMap) {
+ if (value == null)
+ addFileSelectDialog(key)
+ }
+ }
+
+ private fun addFileSelectDialog(type: Utils.FileType?) {
+
+ val fileDialogInfo = getFileDialogInfo(type!!)
+
+ val isCert = type == Utils.FileType.CA_CERTIFICATE || type == Utils.FileType.CLIENT_CERTIFICATE
+ val fl = FileSelectLayout(this, getString(fileDialogInfo.first), isCert, false)
+ fileSelectMap[type] = fl
+ fl.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+
+ (findViewById<View>(R.id.config_convert_root) as LinearLayout).addView(fl, 2)
+ findViewById<View>(R.id.files_missing_hint).visibility = View.VISIBLE
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
+ checkPermission()
+
+ fl.setData(fileDialogInfo.second, this)
+ val i = getFileLayoutOffset(type)
+ fl.setCaller(this, i, type)
+
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private fun checkPermission() {
+ if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ findViewById<View>(R.id.permssion_hint).visibility = View.VISIBLE
+ findViewById<View>(R.id.permssion_hint).setOnClickListener(this)
+ }
+ }
+
+ private fun getFileLayoutOffset(type: Utils.FileType): Int {
+ return CHOOSE_FILE_OFFSET + type.value
+ }
+
+
+ private fun findFileRaw(filename: String?): File? {
+ if (filename == null || filename == "")
+ return null
+
+ // Try diffent path relative to /mnt/sdcard
+ val sdcard = Environment.getExternalStorageDirectory()
+ val root = File("/")
+
+ val dirlist = HashSet<File>()
+
+ for (i in mPathsegments!!.indices.reversed()) {
+ var path = ""
+ for (j in 0..i) {
+ path += "/" + mPathsegments!![j]
+ }
+ // Do a little hackish dance for the Android File Importer
+ // /document/primary:ovpn/openvpn-imt.conf
+
+
+ if (path.indexOf(':') != -1 && path.lastIndexOf('/') > path.indexOf(':')) {
+ var possibleDir = path.substring(path.indexOf(':') + 1, path.length)
+ // Unquote chars in the path
+ try {
+ possibleDir = URLDecoder.decode(possibleDir, "UTF-8")
+ } catch (ignored: UnsupportedEncodingException) {
+ }
+
+ possibleDir = possibleDir.substring(0, possibleDir.lastIndexOf('/'))
+
+
+
+
+ dirlist.add(File(sdcard, possibleDir))
+
+ }
+ dirlist.add(File(path))
+
+
+ }
+ dirlist.add(sdcard)
+ dirlist.add(root)
+
+
+ val fileparts = filename.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ for (rootdir in dirlist) {
+ var suffix = ""
+ for (i in fileparts.indices.reversed()) {
+ if (i == fileparts.size - 1)
+ suffix = fileparts[i]
+ else
+ suffix = fileparts[i] + "/" + suffix
+
+ val possibleFile = File(rootdir, suffix)
+ if (possibleFile.canRead())
+ return possibleFile
+
+ }
+ }
+ return null
+ }
+
+ internal fun readFileContent(possibleFile: File, base64encode: Boolean): String? {
+ val filedata: ByteArray
+ try {
+ filedata = readBytesFromFile(possibleFile)
+ } catch (e: IOException) {
+ log(e.localizedMessage)
+ return null
+ }
+
+ val data: String
+ if (base64encode) {
+ data = Base64.encodeToString(filedata, Base64.DEFAULT)
+ } else {
+ data = String(filedata)
+
+ }
+
+ return VpnProfile.DISPLAYNAME_TAG + possibleFile.name + VpnProfile.INLINE_TAG + data
+
+ }
+
+
+ @Throws(IOException::class)
+ private fun readBytesFromFile(file: File): ByteArray {
+ val input = FileInputStream(file)
+
+ val len = file.length()
+ if (len > VpnProfile.MAX_EMBED_FILE_SIZE)
+ throw IOException("File size of file to import too large.")
+
+ // Create the byte array to hold the data
+ val bytes = ByteArray(len.toInt())
+
+ // Read in the bytes
+ var offset = 0
+ var bytesRead:Int
+ do {
+ bytesRead = input.read(bytes, offset, bytes.size - offset)
+ offset += bytesRead
+ } while (offset < bytes.size && bytesRead >= 0)
+
+ input.close()
+ return bytes
+ }
+
+ internal fun embedFiles(cp: ConfigParser?) {
+ // This where I would like to have a c++ style
+ // void embedFile(std::string & option)
+
+ if (mResult!!.mPKCS12Filename != null) {
+ val pkcs12file = findFileRaw(mResult!!.mPKCS12Filename)
+ if (pkcs12file != null) {
+ mAliasName = pkcs12file.name.replace(".p12", "")
+ } else {
+ mAliasName = "Imported PKCS12"
+ }
+ }
+
+
+ mResult!!.mCaFilename = embedFile(mResult!!.mCaFilename, Utils.FileType.CA_CERTIFICATE, false)
+ mResult!!.mClientCertFilename = embedFile(mResult!!.mClientCertFilename, Utils.FileType.CLIENT_CERTIFICATE, false)
+ mResult!!.mClientKeyFilename = embedFile(mResult!!.mClientKeyFilename, Utils.FileType.KEYFILE, false)
+ mResult!!.mTLSAuthFilename = embedFile(mResult!!.mTLSAuthFilename, Utils.FileType.TLS_AUTH_FILE, false)
+ mResult!!.mPKCS12Filename = embedFile(mResult!!.mPKCS12Filename, Utils.FileType.PKCS12, false)
+ mResult!!.mCrlFilename = embedFile(mResult!!.mCrlFilename, Utils.FileType.CRL_FILE, true)
+ if (cp != null) {
+ mEmbeddedPwFile = cp.authUserPassFile
+ mEmbeddedPwFile = embedFile(cp.authUserPassFile, Utils.FileType.USERPW_FILE, false)
+ }
+
+ }
+
+ private fun updateFileSelectDialogs() {
+ for ((key, value) in fileSelectMap) {
+ value?.setData(getFileDialogInfo(key).second, this)
+ }
+ }
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.config_converter)
+
+ val fab_button = findViewById<ImageButton?>(R.id.fab_save)
+ if (fab_button != null) {
+ fab_button.setOnClickListener(this)
+ findViewById<View>(R.id.fab_footerspace).visibility = View.VISIBLE
+ }
+
+ mLogLayout = findViewById<View>(R.id.config_convert_root) as LinearLayout
+
+
+ mProfilename = findViewById<View>(R.id.profilename) as EditText
+ mProfilenameLabel = findViewById<View>(R.id.profilename_label) as TextView
+
+ if (savedInstanceState != null && savedInstanceState.containsKey(VPNPROFILE)) {
+ mResult = savedInstanceState.getSerializable(VPNPROFILE) as VpnProfile?
+ mAliasName = savedInstanceState.getString("mAliasName")
+ mEmbeddedPwFile = savedInstanceState.getString("pwfile")
+ mSourceUri = savedInstanceState.getParcelable("mSourceUri")
+ mProfilename.setText(mResult!!.mName)
+
+ if (savedInstanceState.containsKey("logentries")) {
+
+ for (logItem in savedInstanceState.getStringArray("logentries")!!)
+ log(logItem)
+ }
+ if (savedInstanceState.containsKey("fileselects")) {
+
+ for (k in savedInstanceState.getIntArray("fileselects")!!) {
+ addFileSelectDialog(Utils.FileType.getFileTypeByValue(k))
+ }
+ }
+ return
+ }
+
+
+ val intent = intent
+
+ if (intent != null) {
+ doImportIntent(intent)
+
+ // We parsed the intent, relay on saved instance for restoring
+ setIntent(null)
+ }
+
+
+ }
+
+ private fun doImportIntent(intent: Intent) {
+ val data = intent.data
+ if (data != null) {
+ mSourceUri = data
+ doImportUri(data)
+ }
+ }
+
+ private fun doImportUri(data: Uri) {
+ //log(R.string.import_experimental);
+ log(R.string.importing_config, data.toString())
+ var possibleName: String? = null
+ if (data.scheme != null && data.scheme == "file" || data.lastPathSegment != null && (data.lastPathSegment!!.endsWith(".ovpn") || data.lastPathSegment!!.endsWith(".conf"))) {
+ possibleName = data.lastPathSegment
+ if (possibleName!!.lastIndexOf('/') != -1)
+ possibleName = possibleName.substring(possibleName.lastIndexOf('/') + 1)
+
+ }
+
+ mPathsegments = data.pathSegments
+
+ val cursor = contentResolver.query(data, null, null, null, null)
+
+ try {
+
+ if (cursor != null && cursor.moveToFirst()) {
+ var columnIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
+
+ if (columnIndex != -1) {
+ val displayName = cursor.getString(columnIndex)
+ if (displayName != null)
+ possibleName = displayName
+ }
+ columnIndex = cursor.getColumnIndex("mime_type")
+ if (columnIndex != -1) {
+ log("Mime type: " + cursor.getString(columnIndex))
+ }
+ }
+ } finally {
+ cursor?.close()
+ }
+ if (possibleName != null) {
+ possibleName = possibleName.replace(".ovpn", "")
+ possibleName = possibleName.replace(".conf", "")
+ }
+
+ startImportTask(data, possibleName)
+
+
+ }
+
+ private fun startImportTask(data: Uri, possibleName: String?) {
+ mImportTask = object : AsyncTask<Void, Void, Int>() {
+ private var mProgress: ProgressBar? = null
+
+ override fun onPreExecute() {
+ mProgress = ProgressBar(this@ConfigConverter)
+ addViewToLog(mProgress)
+ }
+
+ override fun doInBackground(vararg params: Void): Int? {
+ try {
+ val `is` = contentResolver.openInputStream(data)
+
+ doImport(`is`)
+ `is`!!.close()
+ if (mResult == null)
+ return -3
+ } catch (se: IOException) {
+ log(R.string.import_content_resolve_error.toString() + ":" + se.localizedMessage)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ checkMarschmallowFileImportError(data)
+ return -2
+ } catch (se: SecurityException) {
+ log(R.string.import_content_resolve_error.toString() + ":" + se.localizedMessage)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ checkMarschmallowFileImportError(data)
+ return -2
+ }
+
+ return 0
+ }
+
+ override fun onPostExecute(errorCode: Int?) {
+ mLogLayout.removeView(mProgress)
+ addMissingFileDialogs()
+ updateFileSelectDialogs()
+
+ if (errorCode == 0) {
+ displayWarnings()
+ mResult!!.mName = getUniqueProfileName(possibleName)
+ mProfilename.visibility = View.VISIBLE
+ mProfilenameLabel.visibility = View.VISIBLE
+ mProfilename!!.setText(mResult!!.name)
+
+ log(R.string.import_done)
+ }
+ }
+ }.execute()
+ }
+
+
+ @TargetApi(Build.VERSION_CODES.M)
+ private fun checkMarschmallowFileImportError(data: Uri?) {
+ // Permission already granted, not the source of the error
+ if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
+ return
+
+ // We got a file:/// URL and have no permission to read it. Technically an error of the calling app since
+ // it makes an assumption about other apps being able to read the url but well ...
+ if (data != null && "file" == data.scheme)
+ doRequestSDCardPermission(PERMISSION_REQUEST_READ_URL)
+
+ }
+
+
+ override fun onStart() {
+ super.onStart()
+ }
+
+ private fun log(logmessage: String?) {
+ runOnUiThread {
+ val tv = TextView(this@ConfigConverter)
+ mLogEntries.add(logmessage)
+ tv.text = logmessage
+
+ addViewToLog(tv)
+ }
+ }
+
+ private fun addViewToLog(view: View?) {
+ mLogLayout.addView(view, mLogLayout.childCount - 1)
+ }
+
+ private fun doImport(`is`: InputStream?) {
+ val cp = ConfigParser()
+ try {
+ val isr = InputStreamReader(`is`!!)
+
+ cp.parseConfig(isr)
+ mResult = cp.convertProfile()
+ embedFiles(cp)
+ return
+
+ } catch (e: IOException) {
+ log(R.string.error_reading_config_file)
+ log(e.localizedMessage)
+ } catch (e: ConfigParseError) {
+ log(R.string.error_reading_config_file)
+ log(e.localizedMessage)
+ }
+
+ mResult = null
+
+ }
+
+ private fun displayWarnings() {
+ if (mResult!!.mUseCustomConfig) {
+ log(R.string.import_warning_custom_options)
+ var copt = mResult!!.mCustomConfigOptions
+ if (copt.startsWith("#")) {
+ val until = copt.indexOf('\n')
+ copt = copt.substring(until + 1)
+ }
+
+ log(copt)
+ }
+
+ if (mResult!!.mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mResult!!.mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) {
+ findViewById<View>(R.id.importpkcs12).visibility = View.VISIBLE
+ }
+
+ }
+
+ private fun log(ressourceId: Int, vararg formatArgs: Any) {
+ log(getString(ressourceId, *formatArgs))
+ }
+
+ companion object {
+
+ val IMPORT_PROFILE = "de.blinkt.openvpn.IMPORT_PROFILE"
+ val IMPORT_PROFILE_DATA = "de.blinkt.openvpn.IMPORT_PROFILE_DATA"
+ private val RESULT_INSTALLPKCS12 = 7
+ private val CHOOSE_FILE_OFFSET = 1000
+ val VPNPROFILE = "vpnProfile"
+ private val PERMISSION_REQUEST_EMBED_FILES = 37231
+ private val PERMISSION_REQUEST_READ_URL = PERMISSION_REQUEST_EMBED_FILES + 1
+ }
+
+}