From 5ee6a9d698e6231f37a6d2a47e05459d107fb049 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Thu, 16 Jan 2014 23:13:41 +0100 Subject: Switch to android file dialog on 4.4 for importing pkcs12, certificates and key files --- src/de/blinkt/openvpn/activities/FileSelect.java | 2 +- .../blinkt/openvpn/fragments/Settings_Basic.java | 29 +-- src/de/blinkt/openvpn/views/FileSelectLayout.java | 277 +++++++++++++++------ 3 files changed, 209 insertions(+), 99 deletions(-) diff --git a/src/de/blinkt/openvpn/activities/FileSelect.java b/src/de/blinkt/openvpn/activities/FileSelect.java index 405e0dbf..50584063 100644 --- a/src/de/blinkt/openvpn/activities/FileSelect.java +++ b/src/de/blinkt/openvpn/activities/FileSelect.java @@ -154,7 +154,7 @@ public class FileSelect extends Activity { } } - private byte[] readBytesFromFile(File file) throws IOException { + static private byte[] readBytesFromFile(File file) throws IOException { InputStream input = new FileInputStream(file); long len= file.length(); diff --git a/src/de/blinkt/openvpn/fragments/Settings_Basic.java b/src/de/blinkt/openvpn/fragments/Settings_Basic.java index d18a2ed1..cf4d22d1 100644 --- a/src/de/blinkt/openvpn/fragments/Settings_Basic.java +++ b/src/de/blinkt/openvpn/fragments/Settings_Basic.java @@ -40,8 +40,6 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On private static final int CHOOSE_FILE_OFFSET = 1000; private static final int UPDATE_ALIAS = 20; - - private TextView mServerAddress; private TextView mServerPort; private FileSelectLayout mClientCert; @@ -66,16 +64,16 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On - private void addFileSelectLayout (FileSelectLayout fsl) { + private void addFileSelectLayout (FileSelectLayout fsl, FileSelectLayout.FileType type) { int i = fileselects.size() + CHOOSE_FILE_OFFSET; fileselects.put(i, fsl); - fsl.setFragment(this,i); + fsl.setFragment(this,i,type); } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - String profileuuid =getArguments().getString(getActivity().getPackageName() + ".profileUUID"); - mProfile=ProfileManager.get(getActivity(),profileuuid); + String profileUuid = getArguments().getString(getActivity().getPackageName() + ".profileUUID"); + mProfile=ProfileManager.get(getActivity(),profileUuid); getActivity().setTitle(getString(R.string.edit_profile_title, mProfile.getName())); } @@ -143,13 +141,10 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On mPassword = (EditText) mView.findViewById(R.id.auth_password); mKeyPassword = (EditText) mView.findViewById(R.id.key_password); - - - addFileSelectLayout(mCaCert); - addFileSelectLayout(mClientCert); - addFileSelectLayout(mClientKey); - addFileSelectLayout(mpkcs12); - mpkcs12.setBase64Encode(); + addFileSelectLayout(mCaCert, FileSelectLayout.FileType.CERTIFICATE); + addFileSelectLayout(mClientCert, FileSelectLayout.FileType.CERTIFICATE); + addFileSelectLayout(mClientKey, FileSelectLayout.FileType.KEYFILE); + addFileSelectLayout(mpkcs12, FileSelectLayout.FileType.PKCS12); mCaCert.setShowClear(); mType.setOnItemSelectedListener(this); @@ -168,8 +163,8 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On @Override public void onStart() { super.onStart(); - String profileuuid =getArguments().getString(getActivity().getPackageName() + ".profileUUID"); - mProfile=ProfileManager.get(getActivity(),profileuuid); + String profileUuid =getArguments().getString(getActivity().getPackageName() + ".profileUUID"); + mProfile=ProfileManager.get(getActivity(),profileUuid); loadPreferences(); } @@ -177,9 +172,9 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On @Override public void onActivityResult(int request, int result, Intent data) { if (result == Activity.RESULT_OK && request >= CHOOSE_FILE_OFFSET) { - String filedata = data.getStringExtra(FileSelect.RESULT_DATA); + FileSelectLayout fsl = fileselects.get(request); - fsl.setData(filedata, getActivity()); + fsl.parseResponse(data, getActivity()); savePreferences(); diff --git a/src/de/blinkt/openvpn/views/FileSelectLayout.java b/src/de/blinkt/openvpn/views/FileSelectLayout.java index 7ee96884..57ccd895 100644 --- a/src/de/blinkt/openvpn/views/FileSelectLayout.java +++ b/src/de/blinkt/openvpn/views/FileSelectLayout.java @@ -1,8 +1,17 @@ package de.blinkt.openvpn.views; +import android.annotation.TargetApi; +import android.app.Activity; +import android.net.Uri; +import android.os.Build; +import android.text.TextUtils; +import android.text.style.CharacterStyle; +import android.util.Base64; +import android.webkit.MimeTypeMap; import de.blinkt.openvpn.R; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.activities.FileSelect; +import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.X509Utils; import android.app.Fragment; import android.content.Context; @@ -15,91 +24,197 @@ import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; +import java.io.*; +import java.util.TreeSet; +import java.util.Vector; + +import static android.os.Build.*; + public class FileSelectLayout extends LinearLayout implements OnClickListener { + public void parseResponse(Intent data, Context c) { + if (VERSION.SDK_INT < VERSION_CODES.KITKAT) { + String fileData = data.getStringExtra(FileSelect.RESULT_DATA); + setData(fileData, c); + } else if (data != null) { + Uri uri = data.getData(); + try { + byte[] fileData = readBytesFromStream(c.getContentResolver().openInputStream(uri)); + String newData = null; + switch (fileType) { + case CERTIFICATE: + case KEYFILE: + newData = new String(fileData, "UTF-8"); + break; + case PKCS12: + newData = Base64.encodeToString(fileData, Base64.DEFAULT); + break; + } + setData(VpnProfile.INLINE_TAG + newData, c); + + } catch (IOException e) { + VpnStatus.logException(e); + } + + + } + } + + static private byte[] readBytesFromStream(InputStream input) throws IOException { + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[16384]; + + while ((nRead = input.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + + buffer.flush(); + input.close(); + return buffer.toByteArray(); + } + + public enum FileType { + PKCS12, + CERTIFICATE, + KEYFILE + } + private final boolean mIsCertificate; private TextView mDataView; - private String mData; - private Fragment mFragment; - private int mTaskId; - private Button mSelectButton; - private boolean mBase64Encode; - private String mTitle; - private boolean mShowClear; - private TextView mDataDetails; - - public FileSelectLayout( Context context, AttributeSet attrset) { - super(context,attrset); - inflate(getContext(), R.layout.file_select, this); - - TypedArray ta = context.obtainStyledAttributes(attrset, R.styleable.FileSelectLayout); - - mTitle = ta.getString(R.styleable.FileSelectLayout_title); - mIsCertificate = ta.getBoolean(R.styleable.FileSelectLayout_certificate,true); - - TextView tview = (TextView) findViewById(R.id.file_title); - tview.setText(mTitle); - - mDataView = (TextView) findViewById(R.id.file_selected_item); - mDataDetails = (TextView) findViewById(R.id.file_selected_description); - mSelectButton = (Button) findViewById(R.id.file_select_button); - mSelectButton.setOnClickListener(this); - - ta.recycle(); - } - - public void setFragment(Fragment fragment, int i) - { - mTaskId = i; - mFragment = fragment; - } - - public void getCertificateFileDialog() { - Intent startFC = new Intent(getContext(),FileSelect.class); - startFC.putExtra(FileSelect.START_DATA, mData); - startFC.putExtra(FileSelect.WINDOW_TITLE,mTitle); - if(mBase64Encode) - startFC.putExtra(FileSelect.DO_BASE64_ENCODE, true); - if(mShowClear) - startFC.putExtra(FileSelect.SHOW_CLEAR_BUTTON, true); - mFragment.startActivityForResult(startFC,mTaskId); - } - - - public String getData() { - return mData; - } - - public void setData(String data, Context c) { - mData = data; - if(data==null) { - mDataView.setText(mFragment.getString(R.string.no_data)); - mDataDetails.setText(""); - }else { - if(mData.startsWith(VpnProfile.INLINE_TAG)) - mDataView.setText(R.string.inline_file_data); - else - mDataView.setText(data); - if(mIsCertificate) - mDataDetails.setText(X509Utils.getCertificateFriendlyName(c,data)); - } - - } - - @Override - public void onClick(View v) { - if(v == mSelectButton) { - getCertificateFileDialog(); - } - } - - public void setBase64Encode() { - mBase64Encode =true; - } - - public void setShowClear() { - mShowClear=true; - } + private String mData; + private Fragment mFragment; + private int mTaskId; + private Button mSelectButton; + private FileType fileType; + private String mTitle; + private boolean mShowClear; + private TextView mDataDetails; + + public FileSelectLayout(Context context, AttributeSet attrset) { + super(context, attrset); + inflate(getContext(), R.layout.file_select, this); + + TypedArray ta = context.obtainStyledAttributes(attrset, R.styleable.FileSelectLayout); + + mTitle = ta.getString(R.styleable.FileSelectLayout_title); + mIsCertificate = ta.getBoolean(R.styleable.FileSelectLayout_certificate, true); + + TextView tview = (TextView) findViewById(R.id.file_title); + tview.setText(mTitle); + + mDataView = (TextView) findViewById(R.id.file_selected_item); + mDataDetails = (TextView) findViewById(R.id.file_selected_description); + mSelectButton = (Button) findViewById(R.id.file_select_button); + mSelectButton.setOnClickListener(this); + + ta.recycle(); + } + + public void setFragment(Fragment fragment, int i, FileType ft) { + mTaskId = i; + mFragment = fragment; + fileType = ft; + } + + public void getCertificateFileDialog() { + Intent startFC = new Intent(getContext(), FileSelect.class); + startFC.putExtra(FileSelect.START_DATA, mData); + startFC.putExtra(FileSelect.WINDOW_TITLE, mTitle); + if (fileType == FileType.PKCS12) + startFC.putExtra(FileSelect.DO_BASE64_ENCODE, true); + if (mShowClear) + startFC.putExtra(FileSelect.SHOW_CLEAR_BUTTON, true); + + mFragment.startActivityForResult(startFC, mTaskId); + } + + + public String getData() { + return mData; + } + + public void setData(String data, Context c) { + mData = data; + if (data == null) { + mDataView.setText(mFragment.getString(R.string.no_data)); + mDataDetails.setText(""); + } else { + if (mData.startsWith(VpnProfile.INLINE_TAG)) + mDataView.setText(R.string.inline_file_data); + else + mDataView.setText(data); + if (mIsCertificate) + mDataDetails.setText(X509Utils.getCertificateFriendlyName(c, data)); + } + + } + + @Override + public void onClick(View v) { + if (v == mSelectButton) { + if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) + startFilePicker(); + else + getCertificateFileDialog(); + } + } + + + @TargetApi(VERSION_CODES.KITKAT) + private void startFilePicker() { + + + Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT); + i.addCategory(Intent.CATEGORY_OPENABLE); + TreeSet supportedMimeTypes = new TreeSet(); + Vector extensions = new Vector(); + + switch (fileType) { + case PKCS12: + i.setType("application/x-pkcs12"); + supportedMimeTypes.add("application/x-pkcs12"); + extensions.add("p12"); + extensions.add("pfx"); + break; + case CERTIFICATE: + i.setType("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("text/plain"); + + extensions.add("pem"); + extensions.add("crt"); + break; + case KEYFILE: + i.setType("application/x-pem-file"); + supportedMimeTypes.add("application/x-pem-file"); + supportedMimeTypes.add("application/pkcs8"); + extensions.add("key"); + break; + } + + MimeTypeMap mtm = MimeTypeMap.getSingleton(); + + for (String ext : new String[]{"ovpn", "conf"}) { + String mimeType = mtm.getMimeTypeFromExtension(ext); + if (mimeType != null) + supportedMimeTypes.add(mimeType); + else + supportedMimeTypes.add("application/octet-stream"); + } + + + i.putExtra(Intent.EXTRA_MIME_TYPES, supportedMimeTypes.toArray(new String[supportedMimeTypes.size()])); + mFragment.startActivityForResult(i, mTaskId); + } + + public void setShowClear() { + mShowClear = true; + } } -- cgit v1.2.3