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));  		}  	} | 
