diff options
-rw-r--r-- | AndroidManifest.xml | 41 | ||||
-rw-r--r-- | res/menu/import_menu.xml | 18 | ||||
-rw-r--r-- | res/values/strings.xml | 13 | ||||
-rw-r--r-- | res/xml/vpn_headers.xml | 1 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/ConfigConverter.java | 195 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/ConfigParser.java | 227 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/FileSelect.java | 28 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/FileSelectLayout.java | 4 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/FileSelectionFragment.java | 10 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/Settings_Authentication.java | 2 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/VPNProfileList.java | 41 | ||||
-rw-r--r-- | src/de/blinkt/openvpn/VpnProfile.java | 19 |
12 files changed, 552 insertions, 47 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 8943334a..50d6c9f1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -39,6 +39,7 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> @@ -52,6 +53,46 @@ </service> <activity + android:name=".ConfigConverter" + android:label="Convert Config File" > + <intent-filter android:label="foo" > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + + <data android:mimeType="application/x-openvpn-profile" /> + </intent-filter> + <intent-filter android:label="foo" > + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + + <data android:mimeType="application/ovpn" /> + </intent-filter> + <intent-filter android:label="foo" > + <action android:name="android.intent.action.VIEW" /> + + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + + <data + android:pathPattern=".*\\.ovpn" + android:scheme="content" /> + </intent-filter> + <intent-filter android:label="foo" > + <action android:name="android.intent.action.VIEW" /> + + <category android:name="android.intent.category.BROWSABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + + <data + android:pathPattern=".*\\.ovpn" + android:scheme="file" /> + </intent-filter> + </activity> + <activity android:name=".LaunchVPN" android:label="@string/vpn_launch_title" > <intent-filter> diff --git a/res/menu/import_menu.xml b/res/menu/import_menu.xml new file mode 100644 index 00000000..27498da3 --- /dev/null +++ b/res/menu/import_menu.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/ok"
+ android:icon="@android:drawable/ic_menu_save"
+ android:showAsAction="ifRoom|withText"
+ android:title="@string/add_profile"
+ android:titleCondensed="@string/clear"/>
+ <item
+ android:id="@+id/cancel"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel"
+ android:showAsAction="ifRoom|withText"
+ android:title="@android:string/cancel"
+ android:titleCondensed="@string/cancel"/>
+
+
+</menu>
\ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 5d605e08..a5a88219 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -195,5 +195,16 @@ <string name="error_importing_file">Error importing File</string> <string name="import_error_message">Could not import File from Filesystem</string> <string name="inline_file_data">[[Inline file data]]</string> - <string name="opentun_no_ipaddr">Refusing to open tun device without IP information</string> + <string name="opentun_no_ipaddr">Refusing to open tun device without IP information</string> + <string name="menu_import">Import Profile from ovpn file</string> + <string name="menu_import_short">Import</string> + <string name="import_content_resolve_error">Could not read Profile to import</string> + <string name="error_reading_config_file">Error reading Config file</string> + <string name="add_profile">add Profile</string> + <string name="trying_to_read">Trying to read file: %1$s</string> + <string name="import_could_not_open">Could not find file %1$s mentioned in the imported Config file</string> + <string name="importing_config">Importing config file from source %1$s</string> + <string name="import_pkcs12_to_keystore">Your config file specified a pkcs12 file. Please import the file by selecting select in the Basic Settings configuration of the converted VPN</string> + <string name="import_warning_custom_options">Your configuration had a few configuration options that could be parsed. These options were added as custom configuration options. The custom configuration is displayed below:</string> + <string name="import_done">Done reading config file.</string> </resources> diff --git a/res/xml/vpn_headers.xml b/res/xml/vpn_headers.xml index 60ddce7c..c299d2a5 100644 --- a/res/xml/vpn_headers.xml +++ b/res/xml/vpn_headers.xml @@ -4,7 +4,6 @@ <header android:tag="BasicSettings" android:fragment="de.blinkt.openvpn.Settings_Basic" - android:summary="Server, port and authentication method. Normally you should only settings specified here." android:title="Basic Settings" android:id="@+id/basicsettingsid"/> <!-- android:icon="@drawable/ic_settings_applications" --> diff --git a/src/de/blinkt/openvpn/ConfigConverter.java b/src/de/blinkt/openvpn/ConfigConverter.java new file mode 100644 index 00000000..1973e0ac --- /dev/null +++ b/src/de/blinkt/openvpn/ConfigConverter.java @@ -0,0 +1,195 @@ +package de.blinkt.openvpn;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.app.Activity;
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+import de.blinkt.openvpn.ConfigParser.ConfigParseError;
+
+public class ConfigConverter extends ListActivity {
+
+ public static final String IMPORT_PROFILE = "de.blinkt.openvpn.IMPORT_PROFILE";
+
+ private VpnProfile mResult;
+ private ArrayAdapter<String> mArrayAdapter;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Toast.makeText(this, "Got called!", Toast.LENGTH_LONG).show();
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if(item.getItemId()==R.id.cancel){
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ } else if(item.getItemId()==R.id.ok) {
+ if(mResult==null) {
+ log("Importing the config had error, cannot save it");
+ }
+ Intent result = new Intent();
+ ProfileManager vpl = ProfileManager.getInstance(this);
+ vpl.addProfile(mResult);
+ result.putExtra(VpnProfile.EXTRA_PROFILEUUID,mResult.getUUID().toString());
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ }
+
+ return super.onOptionsItemSelected(item);
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.import_menu, menu);
+ return true;
+ }
+
+
+ private String embedFile(String filename)
+ {
+ if(filename == null || filename.equals(""))
+ return null;
+ // Already embedded, nothing to do
+ if(filename.startsWith(VpnProfile.INLINE_TAG))
+ return filename;
+
+ // Try diffent path relative to /mnt/sdcard
+ File sdcard = Environment.getExternalStorageDirectory();
+ File root = new File("/");
+ File[] dirlist = {root, sdcard};
+ 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())
+ continue;
+
+ // read the file inline
+ String filedata = VpnProfile.INLINE_TAG;
+ byte[] buf =new byte[2048];
+
+ log(R.string.trying_to_read, possibleFile.getAbsolutePath());
+ try {
+ FileInputStream fis = new FileInputStream(possibleFile);
+ int len = fis.read(buf);
+ while( len > 0){
+ filedata += new String(buf,0,len);
+ len = fis.read(buf);
+ }
+ return filedata;
+ } catch (FileNotFoundException e) {
+ log(e.getLocalizedMessage());
+ } catch (IOException e) {
+ log(e.getLocalizedMessage());
+ }
+
+
+ }
+ }
+ log(R.string.import_could_not_open,filename);
+ return null;
+ }
+
+ void embedFiles() {
+ // This where I would like to have a c++ style
+ // void embedFile(std::string & option)
+
+ mResult.mCaFilename = embedFile(mResult.mCaFilename);
+ mResult.mClientCertFilename = embedFile(mResult.mClientCertFilename);
+ mResult.mClientKeyFilename = embedFile(mResult.mClientKeyFilename);
+ mResult.mTLSAuthFilename = embedFile(mResult.mTLSAuthFilename);
+ }
+
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ mArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
+ getListView().setAdapter(mArrayAdapter);
+ final android.content.Intent intent = getIntent ();
+
+ if (intent != null)
+ {
+ final android.net.Uri data = intent.getData ();
+ if (data != null)
+ {
+ log(R.string.importing_config,data.toString());
+ try {
+ InputStream is = getContentResolver().openInputStream(data);
+ doImport(is);
+ } catch (FileNotFoundException e) {
+ log(R.string.import_content_resolve_error);
+ }
+ }
+ }
+
+ return;
+ }
+
+ private void log(String logmessage) {
+ mArrayAdapter.add(logmessage);
+ }
+
+ private void doImport(InputStream is) {
+ ConfigParser cp = new ConfigParser();
+ try {
+ cp.parseConfig(is);
+ VpnProfile vp = cp.convertProfile();
+ mResult = vp;
+ embedFiles();
+ displayWarnings();
+ log(R.string.import_done);
+ return;
+
+ } catch (IOException e) {
+ log(R.string.error_reading_config_file);
+ log(e.getLocalizedMessage());
+ } catch (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);
+ log(mResult.mCustomConfigOptions);
+ }
+
+ if(mResult.mAuthenticationType==VpnProfile.TYPE_KEYSTORE) {
+ log(R.string.import_pkcs12_to_keystore);
+ }
+
+ }
+
+
+ private void log(int ressourceId, Object... formatArgs) {
+ log(getString(ressourceId,formatArgs));
+ }
+}
diff --git a/src/de/blinkt/openvpn/ConfigParser.java b/src/de/blinkt/openvpn/ConfigParser.java index 97ca6396..8fde12b8 100644 --- a/src/de/blinkt/openvpn/ConfigParser.java +++ b/src/de/blinkt/openvpn/ConfigParser.java @@ -1,9 +1,11 @@ package de.blinkt.openvpn; import java.io.BufferedReader; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.HashMap; +import java.util.Map.Entry; import java.util.Vector; //! Openvpn Config FIle Parser, probably not 100% accurate but close enough @@ -16,12 +18,13 @@ public class ConfigParser { private HashMap<String,Vector<String>> options = new HashMap<String, Vector<String>>(); - private void parseConfig(String filename) throws IOException, ConfigParseError { + public void parseConfig(InputStream inputStream) throws IOException, ConfigParseError { - FileReader fr = new FileReader(filename); + InputStreamReader fr = new InputStreamReader(inputStream); BufferedReader br =new BufferedReader(fr); + @SuppressWarnings("unused") int lineno=0; while (true){ @@ -29,8 +32,6 @@ public class ConfigParser { if(line==null) break; lineno++; - System.out.print("LINE:"); - System.out.println(line); Vector<String> args = parseline(line); if(args.size() ==0) continue; @@ -51,7 +52,7 @@ public class ConfigParser { // CHeck for <foo> if(arg0.startsWith("<") && arg0.endsWith(">")) { String argname = arg0.substring(1, arg0.length()-1); - String inlinefile = ""; + String inlinefile = VpnProfile.INLINE_TAG; String endtag = String.format("</%s>",argname); do { @@ -192,37 +193,217 @@ public class ConfigParser { return parameters; } - void convertProfile() throws ConfigParseError{ - VpnProfile newprofile = new VpnProfile("converted Profile"); + + final String[] unsupportedOptions = { "config", + "connection", + "proto-force", + "remote-random", + "tls-server" + + }; + + // Ignore all scripts + // in most cases these won't work and user who wish to execute scripts will + // figure out themselves + final String[] ignoreOptions = { "tls-client", + "askpass", + "auth-nocache", + "up", + "down", + "route-up", + "ipchange", + "route-up", + "auth-user-pass-verify" + }; + // Missing + // proto tcp-client|udp + + VpnProfile convertProfile() throws ConfigParseError{ + VpnProfile np = new VpnProfile("converted Profile"); // Pull, client, tls-client - + np.clearDefaults(); + if(options.containsKey("client") || options.containsKey("pull")) { - newprofile.mUsePull=true; + np.mUsePull=true; options.remove("pull"); options.remove("client"); } - - if(options.containsKey("secret")){ - newprofile.mAuthenticationType=VpnProfile.TYPE_STATICKEYS; - options.remove("secret"); + + Vector<String> secret = getOption("secret", 1, 2); + if(secret!=null) + { + np.mAuthenticationType=VpnProfile.TYPE_STATICKEYS; + np.mUseTLSAuth=true; + np.mTLSAuthFilename=secret.get(1); + if(secret.size()==3) + np.mTLSAuthDirection=secret.get(2); } - + + Vector<String> tlsauth = getOption("tls-auth", 1, 2); + if(tlsauth!=null) + { + np.mUseTLSAuth=true; + np.mTLSAuthFilename=tlsauth.get(1); + if(tlsauth.size()==3) + np.mTLSAuthDirection=tlsauth.get(2); + } + + Vector<String> direction = getOption("key-direction", 1, 1); + if(direction!=null) + np.mTLSAuthDirection=direction.get(1); + if(options.containsKey("redirect-gateway")) { options.remove("redirect-gateway"); - newprofile.mUseDefaultRoute=true; + np.mUseDefaultRoute=true; } else { - newprofile.mUseDefaultRoute=true; + np.mUseDefaultRoute=true; } - - Vector<String> mode = options.get("mode"); + + Vector<String> dev =getOption("dev",1,1); + Vector<String> devtype =getOption("dev-type",1,1); + + if( (devtype !=null && devtype.get(1).equals("tun")) || + (dev!=null && dev.get(1).startsWith("tun")) || + (devtype ==null && dev == null) ) { + //everything okay + } else { + throw new ConfigParseError("Sorry. Only tun mode is supported. See the FAQ for more detail"); + } + + + + Vector<String> mode =getOption("mode",1,1); if (mode != null){ - options.remove("mode"); - if(mode.size() != 2) - throw new ConfigParseError("--mode has more than one parameter"); if(!mode.get(1).equals("p2p")) - throw new ConfigParseError("Invalid mode for --mode specified"); + throw new ConfigParseError("Invalid mode for --mode specified, need p2p"); + } + + // Parse remote config + Vector<String> remote = getOption("remote",1,3); + if(remote != null){ + switch (remote.size()) { + case 4: + String proto = remote.get(3); + if(proto.equals("udp")) + np.mUseUdp=true; + else if (proto.equals("tcp")) + np.mUseUdp=false; + else + throw new ConfigParseError("remote protocol must be tcp or udp"); + case 3: + np.mServerPort = remote.get(2); + case 2: + np.mServerName = remote.get(1); + } } + Vector<String> proto = getOption("proto, ", 1,1); + if(proto!=null){ + if(proto.get(1).equals("udp")) + np.mUseUdp=true; + else if (proto.get(1).equals("tcp-client")) + np.mUseUdp=false; + else + throw new ConfigParseError("Unsupported option to --proto " + proto.get(1)); + + } + + Vector<String> dhcpoption = getOption("dhcp-options", 1, 3); + if(dhcpoption!=null) { + String type=dhcpoption.get(1); + + } + + if(getOption("remote-random-hostname", 0, 0)!=null) + np.mUseRandomHostname=true; + + if(getOption("float", 0, 0)!=null) + np.mUseFloat=true; + + if(getOption("comp-lzo", 0, 1)!=null) + np.mUseLzo=true; + + Vector<String> cipher = getOption("cipher", 1, 1); + if(cipher!=null) + np.mCipher= cipher.get(1); + + Vector<String> ca = getOption("ca",1,1); + if(ca!=null){ + np.mCaFilename = ca.get(1); + } + + Vector<String> cert = getOption("cert",1,1); + if(cert!=null){ + np.mClientCertFilename = cert.get(1); + np.mAuthenticationType = VpnProfile.TYPE_CERTIFICATES; + } + Vector<String> key= getOption("key",1,1); + if(key!=null) + np.mClientKeyFilename=key.get(1); + + Vector<String> pkcs12 = getOption("pkcs12",1,1); + if(pkcs12!=null) { + np.mPKCS12Filename = pkcs12.get(1); + np.mAuthenticationType = VpnProfile.TYPE_KEYSTORE; + } + + Vector<String> tlsremote = getOption("tls-remote",1,1); + if(tlsremote!=null){ + np.mRemoteCN = tlsremote.get(1); + np.mCheckRemoteCN=true; + } + + Vector<String> verb = getOption("verb",1,1); + if(verb!=null){ + np.mVerb=verb.get(1); + } + + // Check the other options + + for(String option:unsupportedOptions) + if(options.containsKey(option)) + throw new ConfigParseError(String.format("Unsupported Option %s encountered in config file. Aborting",option)); + + for(String option:ignoreOptions) + // removing an item which is not in the map is no error + options.remove(option); + + if(options.size()> 0) { + String custom = "# These Options were found in the config file but not parsed:\n"; + for(Entry<String, Vector<String>> option:options.entrySet()) { + for (String arg : option.getValue()) { + custom+= arg + " "; + } + custom+="\n"; + } + np.mCustomConfigOptions = custom; + np.mUseCustomConfig=true; + + } + + + fixup(np); + + return np; + } + + private void fixup(VpnProfile np) { + if(np.mRemoteCN.equals(np.mServerName)) { + np.mRemoteCN=""; + } + } + + private Vector<String> getOption(String option, int minarg, int maxarg) throws ConfigParseError { + Vector<String> args = options.get(option); + if(args==null) + return null; + if(args.size()< (minarg+1) || args.size() > maxarg+1) { + String err = String.format("Option %s has %d parameters, expected between %d and %d", + option,args.size()-1,minarg,maxarg ); + throw new ConfigParseError(err); + } + options.remove(option); + return args; } } diff --git a/src/de/blinkt/openvpn/FileSelect.java b/src/de/blinkt/openvpn/FileSelect.java index 3cc060c6..12a3ae01 100644 --- a/src/de/blinkt/openvpn/FileSelect.java +++ b/src/de/blinkt/openvpn/FileSelect.java @@ -13,19 +13,19 @@ import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.Fragment; import android.app.FragmentTransaction; -import android.content.Context; import android.content.Intent; import android.os.Bundle; public class FileSelect extends Activity { public static final String RESULT_DATA = "RESULT_PATH"; public static final String START_DATA = "START_DATA"; - public static final String INLINE_TAG = "[[INLINE]]"; + public static final String NO_INLINE_SELECTION = "de.blinkt.openvpn.NO_INLINE_SELECTION"; private FileSelectionFragment mFSFragment; private InlineFileTab mInlineFragment; private String mData; private Tab inlineFileTab; private Tab fileExplorerTab; + private boolean mNoInline; public void onCreate(Bundle savedInstanceState) { @@ -33,6 +33,10 @@ public class FileSelect extends Activity { setContentView(R.layout.file_dialog); mData = getIntent().getStringExtra(START_DATA); + if(mData==null) + mData="/sdcard"; + + mNoInline = getIntent().getBooleanExtra(NO_INLINE_SELECTION, false); ActionBar bar = getActionBar(); bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); @@ -40,13 +44,15 @@ public class FileSelect extends Activity { inlineFileTab = bar.newTab().setText(R.string.inline_file_tab); mFSFragment = new FileSelectionFragment(); - mInlineFragment = new InlineFileTab(); + mFSFragment.setNoInLine(); fileExplorerTab.setTabListener(new MyTabsListener<FileSelectionFragment>(this, mFSFragment)); - inlineFileTab.setTabListener(new MyTabsListener<InlineFileTab>(this, mInlineFragment)); - bar.addTab(fileExplorerTab); - bar.addTab(inlineFileTab); + if(!mNoInline) { + mInlineFragment = new InlineFileTab(); + inlineFileTab.setTabListener(new MyTabsListener<InlineFileTab>(this, mInlineFragment)); + bar.addTab(inlineFileTab); + } } @@ -88,7 +94,7 @@ public class FileSelect extends Activity { Exception fe = null; try { FileInputStream fis = new FileInputStream(ifile); - String data =INLINE_TAG; + String data =VpnProfile.INLINE_TAG; byte buf[] =new byte[16384]; int len=fis.read(buf); @@ -116,21 +122,21 @@ public class FileSelect extends Activity { public void setFile(String path) { Intent intent = new Intent(); - intent.putExtra(RESULT_DATA, mData); + intent.putExtra(RESULT_DATA, path); setResult(Activity.RESULT_OK,intent); finish(); } public String getSelectPath() { - if(mData.startsWith(INLINE_TAG)) + if(mData.startsWith(VpnProfile.INLINE_TAG)) return mData; else return "/mnt/sdcard"; } public CharSequence getInlineData() { - if(mData.startsWith(INLINE_TAG)) - return mData.substring(INLINE_TAG.length()); + if(mData.startsWith(VpnProfile.INLINE_TAG)) + return mData.substring(VpnProfile.INLINE_TAG.length()); else return ""; } diff --git a/src/de/blinkt/openvpn/FileSelectLayout.java b/src/de/blinkt/openvpn/FileSelectLayout.java index bbaf7778..0be099af 100644 --- a/src/de/blinkt/openvpn/FileSelectLayout.java +++ b/src/de/blinkt/openvpn/FileSelectLayout.java @@ -57,7 +57,9 @@ public class FileSelectLayout extends LinearLayout implements OnClickListener { public void setData(String data) { mData = data; - if(mData.startsWith(FileSelect.INLINE_TAG)) + if(data==null) + mDataView.setText(mFragment.getString(R.string.no_data)); + else if(mData.startsWith(VpnProfile.INLINE_TAG)) mDataView.setText(R.string.inline_file_data); else mDataView.setText(data); diff --git a/src/de/blinkt/openvpn/FileSelectionFragment.java b/src/de/blinkt/openvpn/FileSelectionFragment.java index 31390280..41c7a1eb 100644 --- a/src/de/blinkt/openvpn/FileSelectionFragment.java +++ b/src/de/blinkt/openvpn/FileSelectionFragment.java @@ -51,6 +51,7 @@ public class FileSelectionFragment extends ListFragment { private HashMap<String, Integer> lastPositions = new HashMap<String, Integer>(); private String mStartPath; private Button importFile; + private boolean mHideImport=false; @Override @@ -73,6 +74,7 @@ public class FileSelectionFragment extends ListFragment { } }); + importFile = (Button) v.findViewById(R.id.importfile); importFile.setEnabled(false); importFile.setOnClickListener(new OnClickListener() { @@ -83,7 +85,9 @@ public class FileSelectionFragment extends ListFragment { } }); - + if(mHideImport== true) { + importFile.setVisibility(View.GONE); + } return v; @@ -241,4 +245,8 @@ public class FileSelectionFragment extends ListFragment { } } + public void setNoInLine() { + mHideImport=true; + } + } diff --git a/src/de/blinkt/openvpn/Settings_Authentication.java b/src/de/blinkt/openvpn/Settings_Authentication.java index 5849923d..e8740b5d 100644 --- a/src/de/blinkt/openvpn/Settings_Authentication.java +++ b/src/de/blinkt/openvpn/Settings_Authentication.java @@ -131,7 +131,7 @@ public class Settings_Authentication extends PreferenceFragment implements OnPre private void setTlsAuthSummary(String result) { if(result==null) result = getString(R.string.no_certificate); - if(result.startsWith(FileSelect.INLINE_TAG)) + if(result.startsWith(VpnProfile.INLINE_TAG)) mTLSAuthFile.setSummary(R.string.inline_file_data); else mTLSAuthFile.setSummary(result); diff --git a/src/de/blinkt/openvpn/VPNProfileList.java b/src/de/blinkt/openvpn/VPNProfileList.java index a578251c..a14c835e 100644 --- a/src/de/blinkt/openvpn/VPNProfileList.java +++ b/src/de/blinkt/openvpn/VPNProfileList.java @@ -1,11 +1,13 @@ package de.blinkt.openvpn; +import de.blinkt.openvpn.FileSelect.MyTabsListener; import android.app.Activity; import android.app.AlertDialog; import android.app.ListFragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.view.ActionMode; import android.view.Menu; @@ -56,11 +58,8 @@ public class VPNProfileList extends ListFragment { } }); - return v; } - - } @@ -68,6 +67,13 @@ public class VPNProfileList extends ListFragment { private static final int MENU_ADD_PROFILE = Menu.FIRST; private static final int START_VPN_CONFIG = 92; + private static final int SELECT_PROFILE = 43; + private static final int IMPORT_PROFILE = 231; + + private static final int MENU_IMPORT_PROFILE = Menu.FIRST +1; + + + private ArrayAdapter<VpnProfile> mArrayadapter; @@ -104,6 +110,13 @@ public class VPNProfileList extends ListFragment { .setAlphabeticShortcut('a') .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + menu.add(0, MENU_IMPORT_PROFILE, 0, R.string.menu_import) + .setIcon(android.R.drawable.ic_menu_myplaces) + .setAlphabeticShortcut('a') + .setTitleCondensed(getActivity().getString(R.string.menu_import_short)) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM + | MenuItem.SHOW_AS_ACTION_WITH_TEXT ); } @@ -113,6 +126,11 @@ public class VPNProfileList extends ListFragment { if (itemId == MENU_ADD_PROFILE) { onAddProfileClicked(); return true; + } else if (itemId == MENU_IMPORT_PROFILE) { + Intent intent = new Intent(getActivity(),FileSelect.class); + intent.putExtra(FileSelect.NO_INLINE_SELECTION, true); + startActivityForResult(intent, SELECT_PROFILE); + return true; } else { return super.onOptionsItemSelected(item); } @@ -198,13 +216,26 @@ public class VPNProfileList extends ListFragment { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == START_VPN_CONFIG && resultCode == Activity.RESULT_OK) { - String configuredVPN = data.getStringExtra(getActivity().getPackageName() + ".profileUUID"); + if(resultCode != Activity.RESULT_OK) + return; + + if (requestCode == START_VPN_CONFIG) { + String configuredVPN = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID); VpnProfile profile = ProfileManager.get(configuredVPN); getPM().saveProfile(getActivity(), profile); // Name could be modified + } else if(requestCode== SELECT_PROFILE) { + String filedata = data.getStringExtra(FileSelect.RESULT_DATA); + Intent startImport = new Intent(getActivity(),ConfigConverter.class); + startImport.setAction(ConfigConverter.IMPORT_PROFILE); + Uri uri = new Uri.Builder().path(filedata).scheme("file").build(); + startImport.setData(uri); + startActivityForResult(startImport, IMPORT_PROFILE); + } else if(requestCode == IMPORT_PROFILE) { + String profileUUID = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID); + mArrayadapter.add(ProfileManager.get(profileUUID)); } } diff --git a/src/de/blinkt/openvpn/VpnProfile.java b/src/de/blinkt/openvpn/VpnProfile.java index 639619ff..b7297e89 100644 --- a/src/de/blinkt/openvpn/VpnProfile.java +++ b/src/de/blinkt/openvpn/VpnProfile.java @@ -38,6 +38,8 @@ public class VpnProfile implements Serializable{ public static final int TYPE_USERPASS_PKCS12 = 6; public static final int TYPE_USERPASS_KEYSTORE = 7; + // Don't change this, not all parts of the program use this constant + public static final String EXTRA_PROFILEUUID = "de.blinkt.openvpn.profileUUID"; @@ -90,6 +92,17 @@ public class VpnProfile implements Serializable{ public String mCustomConfigOptions=""; public String mVerb="1"; public String mCipher=""; + public static final String INLINE_TAG = "[[INLINE]]"; + + + + public void clearDefaults() { + mServerName="unkown"; + mUsePull=false; + mUseLzo=false; + mUseDefaultRoute=false; + mExpectTLSCert=false; + } public static String openVpnEscape(String unescaped) { @@ -294,11 +307,11 @@ public class VpnProfile implements Serializable{ //! Put inline data inline and other data as normal escaped filename private String insertFileData(String cfgentry, String filedata) { - if(filedata.startsWith(FileSelect.INLINE_TAG)){ - String datawoheader = filedata.substring(FileSelect.INLINE_TAG.length()); + if(filedata.startsWith(VpnProfile.INLINE_TAG)){ + String datawoheader = filedata.substring(VpnProfile.INLINE_TAG.length()); return String.format("<%s>\n%s\n</%s>\n",cfgentry,datawoheader,cfgentry); } else { - return String.format("%s %s",cfgentry,openVpnEscape(filedata)); + return String.format("%s %s\n",cfgentry,openVpnEscape(filedata)); } } |