summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/assets/eip-service-test.json69
-rw-r--r--app/src/androidTest/assets/gateway.json1
-rw-r--r--app/src/androidTest/assets/secrets.json1
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java6
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/FromAssets.java26
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/TestConstants.java26
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java106
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java193
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java50
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/testGatewaysManager.java125
-rw-r--r--app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnCertificateValidator.java75
-rw-r--r--app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java295
-rw-r--r--app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java71
-rw-r--r--app/src/main/AndroidManifest.xml8
-rw-r--r--app/src/main/java/de/blinkt/openvpn/LaunchVPN.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/VpnProfile.java127
-rw-r--r--app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java280
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/Connection.java51
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java12
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java53
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java3
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java11
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java153
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java4
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java3
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java8
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/X509Utils.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Dashboard.java655
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipFragment.java99
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java31
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java142
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java54
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java184
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java94
-rw-r--r--app/src/main/res/drawable-hdpi/ic_delete_grey600_24dp.pngbin0 -> 248 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_delete_grey600_24dp.pngbin0 -> 199 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_delete_grey600_24dp.pngbin0 -> 271 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_delete_grey600_24dp.pngbin0 -> 341 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_delete_grey600_24dp.pngbin0 -> 402 bytes
-rw-r--r--app/src/main/res/drawable/white_rect.xml10
-rw-r--r--app/src/main/res/layout-sw600dp-port/log_fragment.xml8
-rw-r--r--app/src/main/res/layout-sw600dp/log_fragment.xml9
-rw-r--r--app/src/main/res/layout-xlarge/configuration_wizard_activity.xml8
-rw-r--r--app/src/main/res/layout-xlarge/provider_list_item.xml8
-rw-r--r--app/src/main/res/layout/configuration_wizard_activity.xml4
-rw-r--r--app/src/main/res/layout/log_fragment.xml8
-rw-r--r--app/src/main/res/layout/log_silders.xml2
-rw-r--r--app/src/main/res/layout/log_window.xml2
-rw-r--r--app/src/main/res/layout/provider_detail_fragment.xml1
-rw-r--r--app/src/main/res/layout/vpnstatus.xml2
-rw-r--r--app/src/main/res/menu/logmenu.xml2
-rwxr-xr-xapp/src/main/res/values-ca/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-cs/strings-icsopenvpn.xml24
-rwxr-xr-xapp/src/main/res/values-de/strings-icsopenvpn.xml27
-rwxr-xr-xapp/src/main/res/values-es/strings-icsopenvpn.xml39
-rwxr-xr-xapp/src/main/res/values-et/strings-icsopenvpn.xml27
-rwxr-xr-xapp/src/main/res/values-fr/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-hu/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-in/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-it/strings-icsopenvpn.xml17
-rwxr-xr-xapp/src/main/res/values-ja/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-ko/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-nl/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-no/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-pl/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-pt/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-ro/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-ru/strings-icsopenvpn.xml5
-rwxr-xr-xapp/src/main/res/values-sv/strings-icsopenvpn.xml5
-rw-r--r--app/src/main/res/values-sw600dp/dimens.xml5
-rw-r--r--app/src/main/res/values-sw600dp/styles.xml5
-rwxr-xr-xapp/src/main/res/values-tr/strings-icsopenvpn.xml23
-rwxr-xr-xapp/src/main/res/values-uk/strings-icsopenvpn.xml5
-rw-r--r--app/src/main/res/values-v21/colours.xml9
-rw-r--r--app/src/main/res/values-v21/refs.xml4
-rw-r--r--app/src/main/res/values-v21/styles.xml6
-rwxr-xr-xapp/src/main/res/values-zh-rCN/strings-icsopenvpn.xml10
-rwxr-xr-xapp/src/main/res/values-zh-rTW/strings-icsopenvpn.xml5
-rw-r--r--app/src/main/res/values/colours.xml16
-rw-r--r--app/src/main/res/values/dimens.xml10
-rw-r--r--app/src/main/res/values/refs.xml3
-rwxr-xr-xapp/src/main/res/values/strings-icsopenvpn.xml38
-rw-r--r--app/src/main/res/values/strings.xml8
-rw-r--r--app/src/main/res/values/styles.xml14
-rw-r--r--app/src/main/res/values/untranslatable.xml860
-rw-r--r--app/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java275
-rw-r--r--app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java302
-rw-r--r--app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java133
100 files changed, 3630 insertions, 1414 deletions
diff --git a/app/src/androidTest/assets/eip-service-test.json b/app/src/androidTest/assets/eip-service-test.json
new file mode 100644
index 00000000..78b49bae
--- /dev/null
+++ b/app/src/androidTest/assets/eip-service-test.json
@@ -0,0 +1,69 @@
+{
+ "gateways": [
+ {
+ "capabilities": {
+ "adblock": false,
+ "filter_dns": false,
+ "limited": false,
+ "ports": [
+ "443"
+ ],
+ "protocols": [
+ "tcp",
+ "udp"
+ ],
+ "transport": [
+ "openvpn"
+ ],
+ "user_ips": false
+ },
+ "host": "millipede.demo.bitmask.net",
+ "ip_address": "198.252.153.84",
+ "location": "seattle__wa"
+ },
+ {
+ "capabilities": {
+ "adblock": false,
+ "filter_dns": false,
+ "limited": false,
+ "ports": [
+ "443"
+ ],
+ "protocols": [
+ "tcp",
+ "udp"
+ ],
+ "transport": [
+ "openvpn"
+ ],
+ "user_ips": false
+ },
+ "host": "otter.demo.bitmask.net",
+ "ip_address": "46.165.242.169",
+ "location": "frankfurt"
+ }
+ ],
+ "locations": {
+ "frankfurt": {
+ "country_code": "DE",
+ "hemisphere": "N",
+ "name": "Frankfurt",
+ "timezone": "+1"
+ },
+ "seattle__wa": {
+ "country_code": "US",
+ "hemisphere": "N",
+ "name": "Seattle, WA",
+ "timezone": "-7"
+ }
+ },
+ "openvpn_configuration": {
+ "auth": "SHA1",
+ "cipher": "AES-128-CBC",
+ "keepalive": "10 30",
+ "tls-cipher": "DHE-RSA-AES128-SHA",
+ "tun-ipv6": true
+ },
+ "serial": 1,
+ "version": 1
+} \ No newline at end of file
diff --git a/app/src/androidTest/assets/gateway.json b/app/src/androidTest/assets/gateway.json
new file mode 100644
index 00000000..51a19ec9
--- /dev/null
+++ b/app/src/androidTest/assets/gateway.json
@@ -0,0 +1 @@
+{"location":"seattle__wa","ip_address":"198.252.153.84","capabilities":{"limited":false,"ports":["443"],"adblock":false,"transport":["openvpn"],"filter_dns":false,"protocols":["tcp","udp"],"user_ips":false},"host":"millipede.demo.bitmask.net"}
diff --git a/app/src/androidTest/assets/secrets.json b/app/src/androidTest/assets/secrets.json
new file mode 100644
index 00000000..36ba5977
--- /dev/null
+++ b/app/src/androidTest/assets/secrets.json
@@ -0,0 +1 @@
+{"ca_cert":"-----BEGIN CERTIFICATE-----\nMIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRgwFgYDVQQDDA9CaXRt\nYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNVBAsME2h0dHBzOi8v\nYml0bWFzay5uZXQwHhcNMTIxMTA2MDAwMDAwWhcNMjIxMTA2MDAwMDAwWjBKMRgw\nFgYDVQQDDA9CaXRtYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNV\nBAsME2h0dHBzOi8vYml0bWFzay5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\nggIKAoICAQC1eV4YvayaU+maJbWrD4OHo3d7S1BtDlcvkIRS1Fw3iYDjsyDkZxai\ndHp4EUasfNQ+EVtXUvtk6170EmLco6Elg8SJBQ27trE6nielPRPCfX3fQzETRfvB\n7tNvGw4Jn2YKiYoMD79kkjgyZjkJ2r\/bEHUSevmR09BRp86syHZerdNGpXYhcQ84\nCA1+V+603GFIHnrP+uQDdssW93rgDNYu+exT+Wj6STfnUkugyjmPRPjL7wh0tzy+\nznCeLl4xiV3g9sjPnc7r2EQKd5uaTe3j71sDPF92KRk0SSUndREz+B1+Dbe\/RGk4\nMEqGFuOzrtsgEhPIX0hplhb0Tgz\/rtug+yTT7oJjBa3u20AAOQ38\/M99EfdeJvc4\nlPFF1XBBLh6X9UKF72an2NuANiX6XPySnJgZ7nZ09RiYZqVwu\/qt3DfvLfhboq+0\nbQvLUPXrVDr70onv5UDjpmEA\/cLmaIqqrduuTkFZOym65\/PfAPvpGnt7crQj\/Ibl\nDEDYZQmP7AS+6zBjoOzNjUGE5r40zWAR1RSi7zliXTu+yfsjXUIhUAWmYR6J3KxB\nlfsiHBQ+8dn9kC3YrUexWoOqBiqJOAJzZh5Y1tqgzfh+2nmHSB2dsQRs7rDRRlyy\nYMbkpzL9ZsOUO2eTP1mmar6YjCN+rggYjRrX71K2SpBG6b1zZxOG+wIDAQABo2Aw\nXjAdBgNVHQ4EFgQUuYGDLL2sswnYpHHvProt1JU+D48wDgYDVR0PAQH\/BAQDAgIE\nMAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUuYGDLL2sswnYpHHvProt1JU+D48w\nDQYJKoZIhvcNAQENBQADggIBADeG67vaFcbITGpi51264kHPYPEWaXUa5XYbtmBl\ncXYyB6hY5hv\/YNuVGJ1gWsDmdeXEyj0j2icGQjYdHRfwhrbEri+h1EZOm1cSBDuY\nk\/P5+ctHyOXx8IE79DBsZ6IL61UKIaKhqZBfLGYcWu17DVV6+LT+AKtHhOrv3TSj\nRnAcKnCbKqXLhUPXpK0eTjPYS2zQGQGIhIy9sQXVXJJJsGrPgMxna1Xw2JikBOCG\nhtD\/JKwt6xBmNwktH0GI\/LVtVgSp82Clbn9C4eZN9E5YbVYjLkIEDhpByeC71QhX\nEIQ0ZR56bFuJA\/CwValBqV\/G9gscTPQqd+iETp8yrFpAVHOW+YzSFbxjTEkBte1J\naF0vmbqdMAWLk+LEFPQRptZh0B88igtx6tV5oVd+p5IVRM49poLhuPNJGPvMj99l\nmlZ4+AeRUnbOOeAEuvpLJbel4rhwFzmUiGoeTVoPZyMevWcVFq6BMkS+jRR2w0jK\nG6b0v5XDHlcFYPOgUrtsOBFJVwbutLvxdk6q37kIFnWCd8L3kmES5q4wjyFK47Co\nJa8zlx64jmMZPg\/t3wWqkZgXZ14qnbyG5\/lGsj5CwVtfDljrhN0oCWK1FZaUmW3d\n69db12\/g4f6phldhxiWuGC\/W6fCW5kre7nmhshcltqAJJuU47iX+DarBFiIj816e\nyV8e\n-----END CERTIFICATE-----\n","cert":"-----BEGIN CERTIFICATE-----\nMIIEjDCCAnSgAwIBAgIQG6MBp\/cd9DlY+7cdvp3R3jANBgkqhkiG9w0BAQsFADBmMRAwDgYDVQQK\nDAdCaXRtYXNrMRwwGgYDVQQLDBNodHRwczovL2JpdG1hc2submV0MTQwMgYDVQQDDCtCaXRtYXNr\nIFJvb3QgQ0EgKGNsaWVudCBjZXJ0aWZpY2F0ZXMgb25seSEpMB4XDTE0MTIwNTAwMDAwMFoXDTE1\nMDMwNTAwMDAwMFowLTErMCkGA1UEAwwiVU5MSU1JVEVEZDBwZDdkMzE4eTNtOHNkeXllaTFqYmZl\neDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANRNhZ4aCwdL5+OKObOKeI2rDqEwGnIr\nhL9wzo\/FXbwLfdW45Y9Mxwhh6xy2NkA1YUKCB8VNBKNXlBrGr1QriLbu1rItsJ2VVLqGluVV\/gO4\njcaPU+\/Wu0hMFKG28J\/dPvIGeNbjBWk6mxQAA5WIpRK9RTeQ88wVaGIZDDzIdivza2zpcyiPAyii\ndbkyXh7sLsKvbZB6wLrert6Y1ylR3SlkZP0LfdGAMAdkMyuXKOjgcSnUltR8HSBuZcSUlsTVM11n\nrYeGCYyPNNQ3UYatDW33UASgRDBorrmjhhKP7IW\/opdlnPk5ZrP3i0qI32\/boRe0EWZGXJvr4P3K\ndJ30uCECAwEAAaNvMG0wHQYDVR0OBBYEFK8bMVAM4GBB5sHptoIOAaIvlYueMAsGA1UdDwQEAwIH\ngDATBgNVHSUEDDAKBggrBgEFBQcDAjAJBgNVHRMEAjAAMB8GA1UdIwQYMBaAFId+E7bsWFsUWah9\nvZuPvZ7O+aJsMA0GCSqGSIb3DQEBCwUAA4ICAQAQOX81csVhvP422NKkZH7+g3npBpl+sEHedaGR\nxYPOu4HrA4TVF9h44sljRoRJyenGNdBZCXcLKHg889eePTf8Z5K3lTojp6hvwyA6tgxOMHT1kESW\nPfqnRw8mHfHJuE3g+4YNUMwggzwc\/VZATdV\/7M33sarVN9AUOHou9n9BizgCC+UnYlS+F2POumE3\nFbOhKo5uubI02MwBYlN2JVO2TBt1Q20w8wc6cU07Xi5Epp+1mkgFiOShkNtPcJmEyBWJhxDtSDOW\n2doqWYNqH2kq7B5R\/kyyfcpFJqAnBTV7xs+C5rTS1mW7LpxfdCUMbYuLCpyxpO3A\/DhAm8n47tUH\nlBtmo8Avdb8VdFpYiGBpB0o9kTFcsWFb2GkWFBduGfSEB8jUI7QtqhgZqocAKK\/cweSRV8FwyUcn\nR0prRm3QEi9fbXqEddzjSY9y\/lqWYzT7u+IOAQpKroeZ4wzgYperDNOUFuYk1rP7yuvjP2pV5rcN\nyPoBP60TPVWMRM4WJm6nTogAz2qBrFsf\/XwT\/ajzbsjT6HNB7QbRE+wkFkqspoXG5Agp7KQ8lW3L\nSKCDGOQJz7VIE85pD0tg7QEXBEw8oaRZtMjQ0Gvs25mxXAKka4wGasaWfYH6d0E+iKYcWn86V1rH\nK2ZoknT+Nno5jgjFuUR3fZseNizEfx7BteooKQ==\n-----END CERTIFICATE-----","Constants.PRIVATE_KEY":"-----BEGIN RSA PRIVATE KEY-----\nMIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDUTYWeGgsHS+fjijmziniNqw6h\nMBpyK4S\/cM6PxV28C33VuOWPTMcIYesctjZANWFCggfFTQSjV5Qaxq9UK4i27tayLbCdlVS6hpbl\nVf4DuI3Gj1Pv1rtITBShtvCf3T7yBnjW4wVpOpsUAAOViKUSvUU3kPPMFWhiGQw8yHYr82ts6XMo\njwMoonW5Ml4e7C7Cr22QesC63q7emNcpUd0pZGT9C33RgDAHZDMrlyjo4HEp1JbUfB0gbmXElJbE\n1TNdZ62HhgmMjzTUN1GGrQ1t91AEoEQwaK65o4YSj+yFv6KXZZz5OWaz94tKiN9v26EXtBFmRlyb\n6+D9ynSd9LghAgMBAAECggEBANPHLRXkhsHVj1EkzqBx7gXr8CEMmiTvknFh9zvltrZhhDoRQjWr\nchPDkcRHY2Cznvy4N0YyqQDD2ULIlZdSAgPxxothFoBruWSD47yMBmLx08ORsDpcqt\/YvPAATJI8\nIpFNsXcyaXBp\/M57oRemgnxp\/8UJPJmFdWX99H4hvffh\/jdj7POgYiWUaAl37XTYZKZ4nzKU2wpL\nEDLj9RKPz9gG7CYp2zrLC9LaAsrXVrKwPBw6g+XwbClaqFj97db3mrY4lr6mTo89qmus1AU+fBDH\n3Xlpmc8JwB+30TvhRNKrpLx9cEjuEj7K1gm8Y4dWCjPi+lNbtAyUBcgPJFa\/81ECgYEA7pLoBU\/Y\nZYjyHFca8FvDBcBh6haHfqJr9doXWtgjDrbi3o2n5wHqfKhFWOH6vPEQozkOVeX1ze6HOiRmGBpW\nr+r7x8TD25L7I6HJw3M351RWOAfkF0w\/RTVdetcTgduQtfN1u6BDhYSVceXMjyQYx7MhfETWI8Gh\nKSYm8OEDYiUCgYEA489fmbrCcUnXzpTsbswJ5NmSoEXbcX8cLxnQuzE0z9GHhQdrMjOpXR76reTW\n6jcuudarNcwRUYSWWhjCDKHhpx4HhasWPaHgr7jIzcRw8yZSJRSxKr8sl1qh6g7s47JcmfXOMWLt\nyuyE933XrT19Th4ODZHY40Uv35mPjMi9d00CgYEAyRNAQtndBRa7GG\/B4Ls2T+6pl+aNJIo4e+no\nrURlp800wWabEPRocdBRQmyULBLxduBr2LIMzhgwGSz8b2wji\/l9ZA3PFY135bxClVzSzUIjuO3N\nrGUzHl2wAAyuAFDSUshzfkPBJRNt8aVBF5PQ3t93ZYmPAmv8LPZe875yX5ECgYEAsUEcwK\/ZNW7g\ndQPZR4iJNkC4Xu6cBZ6Cnn92swBheEYvLSoNlX0vDZ7aLE3\/jzQqrjzC8NP8sbH5jtbuvgeDXZX3\nAmGRp5j6C6A61ihAPmEVz3ZfN8SSfJ3vl\/\/PAIg6lyz0J+cy4Q7RkwSeuVQ72Hl4M8TEvmmKC3Af\nispy6Y0CgYEAgl1o2lo+ACyk+oVQPaaPqK3d7WOBFp4eR2nXFor\/vsx9igQOlZUgzRDQsR8jo1o9\nefOSBf87igrZGgssys89pWa2dnXnz5PMmzkKr6bw4D9Ez6u6Puc9UZhGw\/8wDYg6fSosdB9utspm\nM698ycef7jBNMDgmhpSvfw5GctoNQ4s=\n-----END RSA PRIVATE KEY-----"}
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java
index f1cbff19..e67dd820 100644
--- a/app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/ConnectionManager.java
@@ -17,12 +17,10 @@ public class ConnectionManager {
method.setAccessible(true);
try {
method.invoke(conman, enabled);
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
+ } catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
- }
+ }
}
}
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/FromAssets.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/FromAssets.java
new file mode 100644
index 00000000..4f771922
--- /dev/null
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/FromAssets.java
@@ -0,0 +1,26 @@
+package se.leap.bitmaskclient.test;
+
+import android.content.Context;
+
+import org.json.JSONException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class FromAssets {
+
+ Context context;
+
+ public FromAssets(Context context) {
+ this.context = context;
+ }
+ public String toString(String filename) throws IOException, JSONException {
+ String result = "";
+ InputStream is = context.getAssets().open(filename);
+ byte[] bytes = new byte[is.available()];
+ if(is.read(bytes) > 0) {
+ result = new String(bytes);
+ }
+ return result;
+ }
+}
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConstants.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConstants.java
new file mode 100644
index 00000000..6b4cdfb1
--- /dev/null
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/TestConstants.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.test;
+
+/**
+ * @author parmegv
+ */
+public class TestConstants {
+ public final static String EIP_DEFINITION_FILE = "eip-service-test.json";
+ public final static String SECRETS_FILE = "secrets.json";
+ public final static String GATEWAY_FILE = "gateway.json";
+}
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java
index 229c3452..755f83a7 100644
--- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testConfigurationWizard.java
@@ -1,14 +1,10 @@
package se.leap.bitmaskclient.test;
-import android.test.ActivityInstrumentationTestCase2;
-import android.widget.ListView;
-import com.robotium.solo.Solo;
-import java.io.IOException;
-import se.leap.bitmaskclient.AboutActivity;
-import se.leap.bitmaskclient.ConfigurationWizard;
-import se.leap.bitmaskclient.ProviderDetailFragment;
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.test.ConnectionManager;
+import android.test.*;
+import android.widget.*;
+import com.robotium.solo.*;
+import java.io.*;
+import se.leap.bitmaskclient.*;
public class testConfigurationWizard extends ActivityInstrumentationTestCase2<ConfigurationWizard> {
@@ -19,10 +15,15 @@ public class testConfigurationWizard extends ActivityInstrumentationTestCase2<Co
super(ConfigurationWizard.class);
}
+ public testConfigurationWizard(Solo solo) {
+ super(ConfigurationWizard.class);
+ this.solo = solo;
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();
- solo = new Solo(getInstrumentation(), getActivity());
+ solo = new Solo(getInstrumentation(), getActivity());
ConnectionManager.setMobileDataEnabled(true, solo.getCurrentActivity().getApplicationContext());
}
@@ -31,35 +32,76 @@ public class testConfigurationWizard extends ActivityInstrumentationTestCase2<Co
solo.finishOpenedActivities();
}
- public void testListProviders() throws IOException {
+ public void testListProviders() {
assertEquals(solo.getCurrentViews(ListView.class).size(), 1);
-
- int number_of_available_providers = solo.getCurrentViews(ListView.class).get(0).getCount();
-
- assertEquals("Number of available providers differ", solo.getCurrentActivity().getAssets().list("urls").length + added_providers, number_of_available_providers);
+
+ assertEquals("Number of available providers differ", predefinedProviders() + added_providers, shownProviders());
}
+
+ private int shownProviders() {
+ return solo.getCurrentViews(ListView.class).get(0).getCount();
+ }
+
+ private int predefinedProviders() {
+ int predefined_providers = 0;
+ try {
+ predefined_providers = solo.getCurrentActivity().getAssets().list("urls").length;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return predefined_providers;
+ }
+
+ return predefined_providers;
+ }
public void testSelectProvider() {
- solo.clickOnText("bitmask");
- assertTrue("Provider details dialog did not appear", solo.waitForFragmentByTag(ProviderDetailFragment.TAG, 60*1000));
+ selectProvider("demo.bitmask.net");
}
-
- public void testAddNewProvider() {
- solo.clickOnActionBarItem(R.id.new_provider);
- solo.enterText(0, "calyx.net");
- solo.clickOnCheckBox(0);
- solo.clickOnText(solo.getString(R.string.save));
- added_providers = added_providers+1;
- assertTrue("Provider details dialog did not appear", solo.waitForFragmentByTag(ProviderDetailFragment.TAG, 60*1000));
- solo.goBack();
+
+ private void selectProvider(String provider) {
+ solo.clickOnText(provider);
+ waitForProviderDetails();
+ }
+
+ private void waitForProviderDetails() {
+ String text = solo.getString(R.string.provider_details_fragment_title);
+ assertTrue("Provider details dialog did not appear", solo.waitForText(text));
+ }
+
+ public void testAddNewProvider() {
+ addProvider("calyx.net");
}
+
+ private void addProvider(String url) {
+ boolean is_new_provider = !solo.searchText(url);
+ if(is_new_provider)
+ added_providers = added_providers+1;
+ solo.clickOnActionBarItem(R.id.new_provider);
+ solo.enterText(0, url);
+ solo.clickOnCheckBox(0);
+ solo.clickOnText(solo.getString(R.string.save));
+ waitForProviderDetails();
+ solo.goBack();
+ }
public void testShowAbout() {
- solo.clickOnMenuItem(solo.getString(R.string.about));
- assertTrue("Provider details dialog did not appear", solo.waitForActivity(AboutActivity.class));
- }
-
- public void testShowSettings() {
- //TODO We still don't have the settings button
+ showAbout();
}
+
+ private void showAbout() {
+ String text = solo.getString(R.string.about);
+ solo.clickOnMenuItem(text);
+ assertTrue("Provider details dialog did not appear", solo.waitForActivity(AboutActivity.class));
+ }
+
+ protected void toDashboard(String provider) {
+ selectProvider(provider);
+ useAnonymously();
+ }
+
+ private void useAnonymously() {
+ String text = solo.getString(R.string.use_anonymously_button);
+ solo.clickOnText(text);
+ solo.waitForText(solo.getString(R.string.title_activity_dashboard));
+ }
}
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java
index 94cb67a3..91b93d42 100644
--- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testDashboardIntegration.java
@@ -1,24 +1,19 @@
package se.leap.bitmaskclient.test;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.provider.Settings;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-import com.robotium.solo.Solo;
-
-import de.blinkt.openvpn.activities.DisconnectVPN;
-import se.leap.bitmaskclient.ConfigurationWizard;
-import se.leap.bitmaskclient.Dashboard;
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.test.ConnectionManager;
+import android.test.*;
+import com.robotium.solo.*;
+
+import java.io.IOException;
+
+import de.blinkt.openvpn.activities.*;
+import se.leap.bitmaskclient.*;
public class testDashboardIntegration extends ActivityInstrumentationTestCase2<Dashboard> {
private Solo solo;
-
+ private Context context;
+
public testDashboardIntegration() {
super(Dashboard.class);
}
@@ -26,8 +21,12 @@ public class testDashboardIntegration extends ActivityInstrumentationTestCase2<D
@Override
protected void setUp() throws Exception {
super.setUp();
+ context = getInstrumentation().getContext();
solo = new Solo(getInstrumentation(), getActivity());
- ConnectionManager.setMobileDataEnabled(true, solo.getCurrentActivity().getApplicationContext());
+ ConnectionManager.setMobileDataEnabled(true, context);
+ solo.unlockScreen();
+ if(solo.searchText(solo.getString(R.string.configuration_wizard_title)))
+ new testConfigurationWizard(solo).toDashboard("demo.bitmask.net");
}
@Override
@@ -41,113 +40,129 @@ public class testDashboardIntegration extends ActivityInstrumentationTestCase2<D
*/
public void testOnOffOpenVpn() {
solo.clickOnView(solo.getView(R.id.eipSwitch));
- testEipTurningOn();
+ turningEipOn();
solo.clickOnView(solo.getView(R.id.eipSwitch));
- testEipTurningOff();
+ turningEipOff();
solo.clickOnView(solo.getView(R.id.eipSwitch));
- testEipTurningOn();
+ turningEipOn();
solo.clickOnView(solo.getView(R.id.eipSwitch));
- testEipTurningOff();
-
- solo.clickOnView(solo.getView(R.id.eipSwitch));
- testEipTurningOn();
-
- solo.clickOnView(solo.getView(R.id.eipSwitch));
- testEipTurningOff();
+ turningEipOff();
- solo.clickOnView(solo.getView(R.id.eipSwitch));
- testEipTurningOn();
+ /*solo.clickOnView(solo.getView(R.id.eipSwitch));
+ turningEipOn();
- testEipIsOnNoNetwork();
+ turnNetworkOff();
+ restartAdbServer(); // This doesn't work
+ */
}
- private void testEipTurningOn() {
- if(!solo.waitForText(getActivity().getString(R.string.state_auth)))
- fail();
- if(!solo.waitForText(getActivity().getString(R.string.eip_state_connected), 1, 30*1000))
- fail();
- solo.sleep(2*1000);
+ private void turningEipOn() {
+ assertAuthenticating();
+ int max_seconds_until_connected = 30;
+ assertConnected(max_seconds_until_connected);
+ solo.sleep(2*1000);
+ }
+
+ private void assertAuthenticating() {
+ String message = solo.getString(R.string.state_auth);
+ assertTrue(solo.waitForText(message));
}
- private void testEipTurningOff() {
+ private void assertConnected(int max_seconds_until_connected) {
+ String message = solo.getString(R.string.eip_state_connected);
+ assertTrue(solo.waitForText(message, 1, max_seconds_until_connected * 1000));
+ }
+
+ private void turningEipOff() {
sayOkToDisconnect();
- if(!solo.waitForText(getActivity().getString(R.string.eip_state_not_connected)))
- fail();
+ assertDisconnected();
solo.sleep(2*1000);
}
private void sayOkToDisconnect() {
- if(!solo.waitForActivity(DisconnectVPN.class))
- fail();
- solo.clickOnText(getActivity().getString(android.R.string.yes));
+ assertTrue(solo.waitForActivity(DisconnectVPN.class));
+ String yes = solo.getString(android.R.string.yes);
+ solo.clickOnText(yes);
+ }
+
+ private void assertDisconnected() {
+ String message = solo.getString(R.string.eip_state_not_connected);
+ assertTrue(solo.waitForText(message));
}
- private void testEipIsOnNoNetwork() {
- ConnectionManager.setMobileDataEnabled(false, solo.getCurrentActivity().getApplicationContext());
+ private void turnNetworkOff() {
+ ConnectionManager.setMobileDataEnabled(false, context);
if(!solo.waitForText(getActivity().getString(R.string.eip_state_not_connected), 1, 15*1000))
fail();
}
+
+ private void restartAdbServer() {
+ runAdbCommand("kill-server");
+ runAdbCommand("start-server");
+ }
public void testLogInAndOut() {
- long miliseconds_to_log_in = 40 * 1000;
- solo.clickOnActionBarItem(R.id.login_button);
- solo.enterText(0, "parmegvtest1");
- solo.enterText(1, " S_Zw3'-");
- solo.clickOnText("Log In");
- solo.waitForDialogToClose();
- solo.waitForDialogToClose(miliseconds_to_log_in);
- if(!solo.waitForText(getActivity().getString(R.string.succesful_authentication_message)))
- fail();
-
- solo.clickOnActionBarItem(R.string.logout_button);
- if(!solo.waitForDialogToClose())
- fail();
+ long milliseconds_to_log_in = 40 * 1000;
+ logIn("parmegvtest1", " S_Zw3'-");
+ solo.waitForDialogToClose(milliseconds_to_log_in);
+ assertSuccessfulLogin();
+
+ logOut();
}
-
- public void testShowAbout() {
- solo.clickOnMenuItem(getActivity().getString(R.string.about));
- solo.waitForText(getActivity().getString(R.string.repository_url_text));
+
+ private void logIn(String username, String password) {
+ solo.clickOnActionBarItem(R.id.login_button);
+ solo.enterText(0, username);
+ solo.enterText(1, password);
+ solo.clickOnText("Log In");
+ solo.waitForDialogToClose();
+ }
+
+ private void assertSuccessfulLogin() {
+ String message = solo.getString(R.string.succesful_authentication_message);
+ assertTrue(solo.waitForText(message));
+ }
+
+ private void logOut() {
+ solo.clickOnActionBarItem(R.string.logout_button);
+ assertTrue(solo.waitForDialogToClose());
+ }
+
+ public void testShowAbout() {
+ showAbout();
solo.goBack();
-
- solo.clickOnMenuItem(getActivity().getString(R.string.about));
- solo.waitForText(getActivity().getString(R.string.repository_url_text));
+ showAbout();
solo.goBack();
}
-
- public void testSwitchProvider() {
- solo.clickOnMenuItem(getActivity().getString(R.string.switch_provider_menu_option));
+
+ private void showAbout() {
+ String menu_item = solo.getString(R.string.about);
+ solo.clickOnMenuItem(menu_item);
+
+ String text_unique_to_about = solo.getString(R.string.repository_url_text);
+ solo.waitForText(text_unique_to_about);
+ }
+
+ public void testSwitchProvider() {
+ solo.clickOnMenuItem(solo.getString(R.string.switch_provider_menu_option));
solo.waitForActivity(ConfigurationWizard.class);
solo.goBack();
}
- public void testUpdateExpiredCertificate() {
- String certificate = "-----BEGIN CERTIFICATE-----" +
- "MIIEnDCCAoSgAwIBAgIRAOBkcbMKR0Jlw+xNalHn7aIwDQYJKoZIhvcNAQELBQAwdTEYMBYGA1UE" +
- "CgwPUmlzZXVwIE5ldHdvcmtzMRswGQYDVQQLDBJodHRwczovL3Jpc2V1cC5uZXQxPDA6BgNVBAMM" +
- "M1Jpc2V1cCBOZXR3b3JrcyBSb290IENBIChjbGllbnQgY2VydGlmaWNhdGVzIG9ubHkhKTAeFw0x" +
- "NDA5MTkwMDAwMDBaFw0xNDExMTkwMDAwMDBaMC0xKzApBgNVBAMMIlVOTElNSVRFRDcwZWhxZG9l" +
- "ZXQ2Z243bmc3eWx3ZWNxeGwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdaKQHSwg2" +
- "Q2Uz9t5mae9BfV9Jkk+WSU6jXixsTbtLAr8gvuNcVuI0lKm2zXVqoS8aRCSsCt12vhjU/WBTSv0t" +
- "vwTaT2HQYFQ1GlVUBKssJEUpaVyQKL6LN9BA5ZODBpbhefRIX8z+02afxmNWdnOQfDtLU6nHSQLL" +
- "IUBSmgu+Y2Q3SdIBojIl9Kj0Zt6uZkhtOXZqkwLBiMr+/ukSidpcmNgbAN0eXSfVouaduzsDPQ6M" +
- "eCJTz2lhUvC0/57h5mlkNLzEjyb/pAVTtnK4zdiH6XAuCxU/AkF0yzhaiQWMG0RQb4vEx/UHjkDU" +
- "+K0GDy/qx1BmBB7C4vHLauqSXOs1AgMBAAGjbzBtMB0GA1UdDgQWBBQioBn7DdhjmtBKgQKpx/aW" +
- "XHYkGjALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCQYDVR0TBAIwADAfBgNVHSME" +
- "GDAWgBQX9BvV5SoBAU1rol02CikJlmWARjANBgkqhkiG9w0BAQsFAAOCAgEAV7q102FQ62IOX84o" +
- "pPvUL3hJkGtZ5chgQwZhfl2fGtEdeqpU27Hx1jLP9o3n1z9XYaZg/d8xYhpY6Mm4rFl6hA4gk81Z" +
- "yg/A3QeUgIjOsA0Xp+RNB5ACaLjCPUtWNk5brfuelDdFHjl1noC2P3vQ9ErhUna6TKVsxxrueimO" +
- "nc3sV7YMGiVfPC7wEmhERuyhQxftIUHUy2kDCY5QgXtru6IZmc3SP4FcM8LUSC49kqmU9if2GTLo" +
- "wQZmz6T7+N5PIJWIOiDh9PyoojRo7ep9szeIZpzgxcsoE/9ed84tg36JLOWi0GOyrdzVExv0rQQt" +
- "q/NpqAe1mX5XQVbY8nwgaJ8eWIWIXIn+5RB7b+fm5ZFeM4eFyWeDk99bvS8jdH6uQP5WusL55+ft" +
- "ADtESsmBvzUEGqxk5GL4lmmeqE+vsR5TesqGjZ+yH67rR+1+Uy2mhbqJBP0E0LHwWCCPYEVfngHj" +
- "aZkDF1UVQdfc9Amc5u5J5YliWrEG80BNeJF7740Gwx69DHEIhElN+BBeeqLLYIZTKmt28/9iWbKL" +
- "vhCrz/29wLYksL1bXmyHzvzyAcDHPpO9sQrKYiP1mGRDmXJmZU3i3cgeqQFZ8+lr55wcYdMGJOcx" +
- "bz+jL0VkHdnoZdzGzelrAhZtgMtsJ/kgWYRgtFmhpYF1Xtj2MYrpBDxgQck=" +
- "-----END CERTIFICATE-----";
+ /*public void testReboot() {
+ runAdbCommand("shell am broadcast -a android.intent.action.BOOT_COMPLETED");
+ }*/
+ private void runAdbCommand(String adb_command) {
+ try {
+ String command = "adb " + adb_command;
+ Runtime.getRuntime().exec(command).waitFor();
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
}
}
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java
index 4e1819d0..d9235085 100644
--- a/app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testEIP.java
@@ -1,21 +1,45 @@
+/**
+ * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
package se.leap.bitmaskclient.test;
import android.content.Context;
import android.content.Intent;
-import android.test.ActivityUnitTestCase;
+import android.content.SharedPreferences;
import android.test.ServiceTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
import se.leap.bitmaskclient.Dashboard;
+import se.leap.bitmaskclient.eip.Constants;
import se.leap.bitmaskclient.eip.EIP;
+/**
+ * @author parmegv
+ */
public class testEIP extends ServiceTestCase<EIP> {
private Context context;
private Intent intent;
- private EIP activity;
+ private SharedPreferences preferences;
public testEIP(Class<EIP> activityClass) {
super(activityClass);
+ context = getSystemContext();
+ intent = new Intent(context, EIP.class);
+ preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE);
}
@Override
@@ -28,5 +52,27 @@ public class testEIP extends ServiceTestCase<EIP> {
super.tearDown();
}
+ @MediumTest
+ private void testCheckCertValidity() {
+ testEmptyCertificate();
+ testExpiredCertificate();
+ // Wait for the service to start
+ // Check result is OK.
+ }
+
+ private void testEmptyCertificate() {
+ preferences.edit().putString(Constants.CERTIFICATE, "").apply();
+ startService(Constants.ACTION_CHECK_CERT_VALIDITY);
+ }
+ private void testExpiredCertificate() {
+ String expired_certificate = "expired certificate";
+ preferences.edit().putString(Constants.CERTIFICATE, expired_certificate).apply();
+ startService(Constants.ACTION_CHECK_CERT_VALIDITY);
+ }
+
+ private void startService(String action) {
+ intent.setAction(action);
+ startService(intent);
+ }
}
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testGatewaysManager.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testGatewaysManager.java
new file mode 100644
index 00000000..c4303251
--- /dev/null
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testGatewaysManager.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.test;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.Test;
+
+import org.json.JSONObject;
+
+import se.leap.bitmaskclient.Dashboard;
+import se.leap.bitmaskclient.eip.Gateway;
+import se.leap.bitmaskclient.eip.GatewaysManager;
+
+/**
+ * @author parmegv
+ */
+public class testGatewaysManager extends InstrumentationTestCase {
+
+ GatewaysManager gateways_manager;
+ Gateway gateway;
+ JSONObject eip_definition;
+
+ FromAssets assets;
+
+ Context context;
+ SharedPreferences preferences;
+
+ @Override
+ protected void setUp() throws Exception {
+ context = getInstrumentation().getContext();
+ assets = new FromAssets(context);
+ mockGatewaysManager();
+ mockRealGateway();
+ super.setUp();
+ }
+
+ @MediumTest
+ public void testFromEipServiceJson() {
+ gateways_manager.fromEipServiceJson(eip_definition);
+ assertEquals(2, gateways_manager.size());
+ gateways_manager.addFromString(gateway.toString());
+ assertEquals(2, gateways_manager.size());
+ }
+
+ @SmallTest
+ public void testAddFromString() {
+ gateways_manager.addFromString("");
+ gateways_manager.addFromString(gateway.toString());
+ }
+
+ @MediumTest
+ public void testRemoveDuplicate() {
+ gateways_manager.addFromString(gateway.toString());
+ assertEquals(1, gateways_manager.size());
+
+ mockArtificialGateway();
+ gateways_manager.addFromString(gateway.toString());
+ assertEquals(1, gateways_manager.size());
+ }
+
+ @MediumTest
+ public void testToString() {
+ assertEquals("[]", gateways_manager.toString());
+
+ gateways_manager.addFromString(gateway.toString());
+ assertEquals("["+gateway.toString()+"]", gateways_manager.toString());
+ }
+
+ @SmallTest
+ public void testIsEmpty() {
+ assertTrue(gateways_manager.isEmpty());
+ gateways_manager.addFromString("");
+ assertTrue(gateways_manager.isEmpty());
+ gateways_manager.addFromString(gateway.toString());
+ assertFalse(gateways_manager.isEmpty());
+ }
+
+ private void mockGatewaysManager() {
+ context = getInstrumentation().getContext();
+ preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE);
+ gateways_manager = new GatewaysManager(context, preferences);
+ }
+
+ private void mockRealGateway() {
+ try {
+ eip_definition = new JSONObject(assets.toString(TestConstants.EIP_DEFINITION_FILE));
+ JSONObject secrets = new JSONObject(assets.toString(TestConstants.SECRETS_FILE));
+ JSONObject gateway = new JSONObject(assets.toString(TestConstants.GATEWAY_FILE));
+ this.gateway = new Gateway(eip_definition, secrets, gateway);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void mockArtificialGateway() {
+ try {
+ eip_definition = new JSONObject(assets.toString(TestConstants.EIP_DEFINITION_FILE));
+ JSONObject secrets = new JSONObject(assets.toString(TestConstants.SECRETS_FILE).replace("6u6", "7u7"));
+ JSONObject gateway = new JSONObject(assets.toString(TestConstants.GATEWAY_FILE));
+ this.gateway = new Gateway(eip_definition, secrets, gateway);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnCertificateValidator.java b/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnCertificateValidator.java
new file mode 100644
index 00000000..cd1d6c3b
--- /dev/null
+++ b/app/src/androidTest/java/se/leap/bitmaskclient/test/testVpnCertificateValidator.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.test;
+
+import android.content.*;
+import android.os.*;
+import android.test.*;
+
+import org.json.*;
+
+import java.io.*;
+import java.util.*;
+
+import se.leap.bitmaskclient.*;
+import se.leap.bitmaskclient.eip.*;
+
+/**
+ * @author parmegv
+ */
+public class testVpnCertificateValidator extends InstrumentationTestCase {
+
+ String certificate_valid_from_jan2015_to_nov2022 = "";
+
+ Context context;
+ FromAssets assets;
+
+ @Override
+ protected void setUp() throws Exception {
+ context = getInstrumentation().getContext();
+ assets = new FromAssets(context);
+ JSONObject secrets = new JSONObject(assets.toString(TestConstants.SECRETS_FILE));
+ certificate_valid_from_jan2015_to_nov2022 = secrets.getString(Provider.CA_CERT);
+ super.setUp();
+ }
+
+ public void testIsValid() {
+ VpnCertificateValidator validator = new VpnCertificateValidator(certificate_valid_from_jan2015_to_nov2022);
+ setTime(2015, 1, 6);
+ assertTrue(validator.isValid());
+ setTime(2020, 1, 6);
+ assertFalse(validator.isValid());
+ }
+
+ private void setTime(int year, int month, int day) {
+ shellCommand("adb shell chmod 666 /dev/alarm");
+ Calendar c = Calendar.getInstance();
+ c.set(year, month, day, 12, 00, 00);
+ SystemClock.setCurrentTimeMillis(c.getTimeInMillis());
+ shellCommand("adb shell chmod 664 /dev/alarm");
+ }
+
+ private int shellCommand(String command) {
+ int result = 0;
+ try {
+ Runtime.getRuntime().exec(command);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+}
diff --git a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java
index ac2e00b8..faf3779a 100644
--- a/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java
+++ b/app/src/debug/java/se/leap/bitmaskclient/ConfigurationWizard.java
@@ -29,8 +29,7 @@ import java.util.*;
import butterknife.*;
import org.jetbrains.annotations.NotNull;
-import org.json.JSONException;
-import org.json.JSONObject;
+import org.json.*;
import javax.inject.Inject;
@@ -59,16 +58,13 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
@Inject ProviderListAdapter adapter;
private ProviderManager provider_manager;
- private Intent mConfigState = new Intent();
+ private Intent mConfigState = new Intent();
private Provider selected_provider;
- final public static String TAG = ConfigurationWizard.class.getSimpleName();
- final public static String TYPE_OF_CERTIFICATE = "type_of_certificate";
- final public static String ANON_CERTIFICATE = "anon_certificate";
- final public static String AUTHED_CERTIFICATE = "authed_certificate";
+ final public static String TAG = ConfigurationWizard.class.getSimpleName();
- final protected static String PROVIDER_SET = "PROVIDER SET";
- final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED";
+ final protected static String PROVIDER_SET = "PROVIDER SET";
+ final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED";
final private static String PROGRESSBAR_TEXT = TAG + "PROGRESSBAR_TEXT";
final private static String PROGRESSBAR_NUMBER = TAG + "PROGRESSBAR_NUMBER";
@@ -84,7 +80,7 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
private int progress = -1;
private void initProviderList() {
- List<Renderer<Provider>> prototypes = new ArrayList<Renderer<Provider>>();
+ List<Renderer<Provider>> prototypes = new ArrayList<>();
prototypes.add(new ProviderRenderer(this));
ProviderRendererBuilder providerRendererBuilder = new ProviderRendererBuilder(prototypes);
adapter = new ProviderListAdapter(getLayoutInflater(), providerRendererBuilder, provider_manager);
@@ -112,13 +108,12 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
setUpInitialUI();
- setUpProviderAPIResultReceiver();
+ initProviderList();
- setUpProviderList();
-
- if(savedInstanceState != null) {
+ if(savedInstanceState != null)
restoreState(savedInstanceState);
- }
+ else
+ setUpProviderAPIResultReceiver();
}
private void restoreState(Bundle savedInstanceState) {
@@ -135,7 +130,6 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
super.onPostResume();
if(!progressbar_text.isEmpty() && !provider_name.isEmpty() && progress != -1) {
progressbar_description.setText(progressbar_text);
- //onItemSelectedUi(getProvider(provider_name));
mProgressBar.setProgress(progress);
progressbar_text = "";
@@ -156,10 +150,6 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
progressbar_description.setVisibility(TextView.INVISIBLE);
}
- private void setUpProviderList() {
- initProviderList();
- }
-
@Override
protected void onDestroy() {
super.onDestroy();
@@ -193,7 +183,7 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
if (preferences.getBoolean(Constants.ALLOWED_ANON, false)){
mConfigState.putExtra(SERVICES_RETRIEVED, true);
- downloadAnonCert();
+ downloadVpnCertificate();
} else {
mProgressBar.incrementProgressBy(1);
hideProgressBar();
@@ -249,8 +239,6 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
adapter.hideAllBut(adapter.indexOf(provider));
}
-
-
@Override
public void onBackPressed() {
if(setting_up_provider) {
@@ -271,15 +259,15 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
}
public void cancelSettingUpProvider() {
- showAllProviders();
+ adapter.showAllProviders();
setting_up_provider = false;
preferences.edit().remove(Provider.KEY).remove(ProviderItem.DANGER_ON).remove(Constants.ALLOWED_ANON).remove(Constants.KEY).commit();
}
private void askDashboardToQuitApp() {
- Intent ask_quit = new Intent();
- ask_quit.putExtra(Dashboard.ACTION_QUIT, Dashboard.ACTION_QUIT);
- setResult(RESULT_CANCELED, ask_quit);
+ Intent ask_quit = new Intent();
+ ask_quit.putExtra(Dashboard.ACTION_QUIT, Dashboard.ACTION_QUIT);
+ setResult(RESULT_CANCELED, ask_quit);
}
private void startProgressBar() {
@@ -296,86 +284,80 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
private int listItemHeight() {
View listItem = adapter.getView(0, null, provider_list_view);
listItem.setLayoutParams(new RelativeLayout.LayoutParams(
- RelativeLayout.LayoutParams.WRAP_CONTENT,
- RelativeLayout.LayoutParams.WRAP_CONTENT));
+ RelativeLayout.LayoutParams.WRAP_CONTENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT));
WindowManager wm = (WindowManager) getApplicationContext()
- .getSystemService(Context.WINDOW_SERVICE);
+ .getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int screenWidth = display.getWidth(); // deprecated
int listViewWidth = screenWidth - 10 - 10;
int widthSpec = View.MeasureSpec.makeMeasureSpec(listViewWidth,
- View.MeasureSpec.AT_MOST);
+ View.MeasureSpec.AT_MOST);
listItem.measure(widthSpec, 0);
return listItem.getMeasuredHeight();
-}
+ }
- /**
- * Asks ProviderAPI to download an anonymous (anon) VPN certificate.
- */
- private void downloadAnonCert() {
- Intent provider_API_command = new Intent(this, ProviderAPI.class);
-
- Bundle parameters = new Bundle();
-
- parameters.putString(TYPE_OF_CERTIFICATE, ANON_CERTIFICATE);
-
- provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
- provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
-
- startService(provider_API_command);
- }
+ /**
+ * Asks ProviderAPI to download an anonymous (anon) VPN certificate.
+ */
+ private void downloadVpnCertificate() {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+ startService(provider_API_command);
+ }
- /**
- * Open the new provider dialog
- */
- public void addAndSelectNewProvider() {
- FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG);
+ /**
+ * Open the new provider dialog
+ */
+ public void addAndSelectNewProvider() {
+ FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG);
new NewProviderDialog().show(fragment_transaction, NewProviderDialog.TAG);
- }
+ }
- /**
- * Open the new provider dialog with data
- */
- public void addAndSelectNewProvider(String main_url, boolean danger_on) {
- FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG);
+ /**
+ * Open the new provider dialog with data
+ */
+ public void addAndSelectNewProvider(String main_url, boolean danger_on) {
+ FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG);
- DialogFragment newFragment = new NewProviderDialog();
- Bundle data = new Bundle();
- data.putString(Provider.MAIN_URL, main_url);
- data.putBoolean(ProviderItem.DANGER_ON, danger_on);
- newFragment.setArguments(data);
- newFragment.show(fragment_transaction, NewProviderDialog.TAG);
- }
+ DialogFragment newFragment = new NewProviderDialog();
+ Bundle data = new Bundle();
+ data.putString(Provider.MAIN_URL, main_url);
+ data.putBoolean(ProviderItem.DANGER_ON, danger_on);
+ newFragment.setArguments(data);
+ newFragment.show(fragment_transaction, NewProviderDialog.TAG);
+ }
- /**
- * Once selected a provider, this fragment offers the user to log in,
- * use it anonymously (if possible)
- * or cancel his/her election pressing the back button.
- * @param reason_to_fail
- */
- public void showDownloadFailedDialog(String reason_to_fail) {
- FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(DownloadFailedDialog.TAG);
+ /**
+ * Once selected a provider, this fragment offers the user to log in,
+ * use it anonymously (if possible)
+ * or cancel his/her election pressing the back button.
+ * @param reason_to_fail
+ */
+ public void showDownloadFailedDialog(String reason_to_fail) {
+ FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(DownloadFailedDialog.TAG);
- DialogFragment newFragment = DownloadFailedDialog.newInstance(reason_to_fail);
- newFragment.show(fragment_transaction, DownloadFailedDialog.TAG);
- }
+ DialogFragment newFragment = DownloadFailedDialog.newInstance(reason_to_fail);
+ newFragment.show(fragment_transaction, DownloadFailedDialog.TAG);
+ }
- /**
- * Once selected a provider, this fragment offers the user to log in,
- * use it anonymously (if possible)
- * or cancel his/her election pressing the back button.
- */
- private void showProviderDetails() {
- if(setting_up_provider) {
- FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(ProviderDetailFragment.TAG);
-
- DialogFragment newFragment = ProviderDetailFragment.newInstance();
- newFragment.show(fragment_transaction, ProviderDetailFragment.TAG);
- }
+ /**
+ * Once selected a provider, this fragment offers the user to log in,
+ * use it anonymously (if possible)
+ * or cancel his/her election pressing the back button.
+ */
+ private void showProviderDetails() {
+ if(setting_up_provider) {
+ FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(ProviderDetailFragment.TAG);
+
+ DialogFragment newFragment = ProviderDetailFragment.newInstance();
+ newFragment.show(fragment_transaction, ProviderDetailFragment.TAG);
}
+ }
public void showAndSelectProvider(String provider_main_url, boolean danger_on) {
try {
@@ -395,87 +377,82 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
onItemSelectedUi(selected_provider);
}
- /**
- * Asks ProviderAPI to download a new provider.json file
-n * @param provider_main_url
- * @param danger_on tells if HTTPS client should bypass certificate errors
- */
- public void setUpProvider(URL provider_main_url, boolean danger_on) {
- Intent provider_API_command = new Intent(this, ProviderAPI.class);
- Bundle parameters = new Bundle();
- parameters.putString(Provider.MAIN_URL, provider_main_url.toString());
- parameters.putBoolean(ProviderItem.DANGER_ON, danger_on);
-
- provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
- provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
-
- startService(provider_API_command);
- setting_up_provider = true;
- }
+ /**
+ * Asks ProviderAPI to download a new provider.json file
+ * @param provider_main_url
+ * @param danger_on tells if HTTPS client should bypass certificate errors
+ */
+ public void setUpProvider(URL provider_main_url, boolean danger_on) {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+ Bundle parameters = new Bundle();
+ parameters.putString(Provider.MAIN_URL, provider_main_url.toString());
+ parameters.putBoolean(ProviderItem.DANGER_ON, danger_on);
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ setting_up_provider = true;
+ }
- public void retrySetUpProvider() {
- cancelSettingUpProvider();
- if(!ProviderAPI.caCertDownloaded()) {
- addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl(), ProviderAPI.lastDangerOn());
- } else {
- Intent provider_API_command = new Intent(this, ProviderAPI.class);
+ public void retrySetUpProvider() {
+ cancelSettingUpProvider();
+ if(!ProviderAPI.caCertDownloaded()) {
+ addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl(), ProviderAPI.lastDangerOn());
+ } else {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
- provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
- startService(provider_API_command);
- }
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.configuration_wizard_activity, menu);
- return true;
+ startService(provider_API_command);
}
+ }
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.configuration_wizard_activity, menu);
+ return true;
+ }
- @Override
- public boolean onOptionsItemSelected(MenuItem item){
- switch (item.getItemId()){
- case R.id.about_leap:
- startActivityForResult(new Intent(this, AboutActivity.class), 0);
- return true;
- case R.id.new_provider:
- addAndSelectNewProvider();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item){
+ switch (item.getItemId()){
+ case R.id.about_leap:
+ startActivityForResult(new Intent(this, AboutActivity.class), 0);
+ return true;
+ case R.id.new_provider:
+ addAndSelectNewProvider();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
}
+ }
- public void showAllProviders() {
- adapter.showAllProviders();
- }
-
- @Override
- public void login() {
- Intent ask_login = new Intent();
- ask_login.putExtra(SessionDialog.TAG, SessionDialog.TAG);
+ @Override
+ public void login() {
+ Intent ask_login = new Intent();
+ ask_login.putExtra(SessionDialog.TAG, SessionDialog.TAG);
ask_login.putExtra(Provider.KEY, selected_provider);
- setResult(RESULT_OK, ask_login);
- setting_up_provider = false;
- finish();
- }
+ setResult(RESULT_OK, ask_login);
+ setting_up_provider = false;
+ finish();
+ }
- @Override
- public void use_anonymously() {
+ @Override
+ public void use_anonymously() {
Intent pass_provider = new Intent();
pass_provider.putExtra(Provider.KEY, selected_provider);
- setResult(RESULT_OK, pass_provider);
- setting_up_provider = false;
- finish();
- }
-
- public class ProviderAPIBroadcastReceiver_Update extends BroadcastReceiver {
+ setResult(RESULT_OK, pass_provider);
+ setting_up_provider = false;
+ finish();
+ }
- @Override
- public void onReceive(Context context, Intent intent) {
- int update = intent.getIntExtra(ProviderAPI.CURRENT_PROGRESS, 0);
- mProgressBar.setProgress(update);
- }
+ public class ProviderAPIBroadcastReceiver_Update extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int update = intent.getIntExtra(ProviderAPI.CURRENT_PROGRESS, 0);
+ mProgressBar.setProgress(update);
}
+ }
}
diff --git a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java
index ce13d322..1dbe11d3 100644
--- a/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java
+++ b/app/src/debug/java/se/leap/bitmaskclient/ProviderAPI.java
@@ -187,7 +187,7 @@ public class ProviderAPI extends IntentService {
if(validUserLoginData(username, password)) {
session_id_bundle = register(username, password);
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
} else {
if(!wellFormedPassword(password)) {
session_id_bundle.putBoolean(RESULT_KEY, false);
@@ -232,18 +232,18 @@ public class ProviderAPI extends IntentService {
Bundle result = new Bundle();
int progress = 0;
- String username = (String) task.get(SessionDialog.USERNAME);
- String password = (String) task.get(SessionDialog.PASSWORD);
+ String username = task.getString(SessionDialog.USERNAME);
+ String password = task.getString(SessionDialog.PASSWORD);
if(validUserLoginData(username, password)) {
result = authenticate(username, password);
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
} else {
if(!wellFormedPassword(password)) {
result.putBoolean(RESULT_KEY, false);
result.putString(SessionDialog.USERNAME, username);
result.putBoolean(SessionDialog.PASSWORD_INVALID_LENGTH, true);
}
- if(username.isEmpty()) {
+ if(!validUsername(username)) {
result.putBoolean(RESULT_KEY, false);
result.putBoolean(SessionDialog.USERNAME_MISSING, true);
}
@@ -317,7 +317,7 @@ public class ProviderAPI extends IntentService {
* and sends it as a broadcast.
* @param progress
*/
- private void broadcast_progress(int progress) {
+ private void broadcastProgress(int progress) {
Intent intentUpdate = new Intent();
intentUpdate.setAction(UPDATE_PROGRESSBAR);
intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
@@ -327,21 +327,25 @@ public class ProviderAPI extends IntentService {
/**
* Validates parameters entered by the user to log in
- * @param entered_username
- * @param entered_password
+ * @param username
+ * @param password
* @return true if both parameters are present and the entered password length is greater or equal to eight (8).
*/
- private boolean validUserLoginData(String entered_username, String entered_password) {
- return !(entered_username.isEmpty()) && wellFormedPassword(entered_password);
+ private boolean validUserLoginData(String username, String password) {
+ return validUsername(username) && wellFormedPassword(password);
}
+ private boolean validUsername(String username) {
+ return username != null && !username.isEmpty();
+ }
+
/**
* Validates a password
- * @param entered_password
+ * @param password
* @return true if the entered password length is greater or equal to eight (8).
*/
- private boolean wellFormedPassword(String entered_password) {
- return entered_password.length() >= 8;
+ private boolean wellFormedPassword(String password) {
+ return password != null && password.length() >= 8;
}
/**
@@ -503,16 +507,16 @@ public class ProviderAPI extends IntentService {
if(!PROVIDER_JSON_DOWNLOADED)
current_download = getAndSetProviderJson(last_provider_main_url, last_danger_on);
if(PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) {
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
PROVIDER_JSON_DOWNLOADED = true;
current_download = downloadCACert(last_danger_on);
if(CA_CERT_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) {
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
CA_CERT_DOWNLOADED = true;
current_download = getAndSetEipServiceJson();
if(current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY)) {
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
EIP_SERVICE_JSON_DOWNLOADED = true;
}
}
@@ -826,21 +830,24 @@ public class ProviderAPI extends IntentService {
return string;
}
- /**
- * Logs out from the api url retrieved from the task.
- * @return true if there were no exceptions
- */
+ /**
+ * Logs out from the api url retrieved from the task.
+ * @return true if there were no exceptions
+ */
private boolean logOut() {
+ String delete_url = provider_api_url + "/logout";
+
+ HttpsURLConnection urlConnection = null;
+ int responseCode = 0;
+ int progress = 0;
try {
- String delete_url = provider_api_url + "/logout";
- int progress = 0;
- HttpsURLConnection urlConnection = (HttpsURLConnection)new URL(delete_url).openConnection();
+ urlConnection = (HttpsURLConnection)new URL(delete_url).openConnection();
urlConnection.setRequestMethod("DELETE");
urlConnection.setSSLSocketFactory(getProviderSSLSocketFactory());
- int responseCode = urlConnection.getResponseCode();
- broadcast_progress(progress++);
+ responseCode = urlConnection.getResponseCode();
+ broadcastProgress(progress++);
LeapSRPSession.setToken("");
Log.d(TAG, Integer.toString(responseCode));
} catch (ClientProtocolException e) {
@@ -853,6 +860,20 @@ public class ProviderAPI extends IntentService {
return false;
} catch (IOException e) {
// TODO Auto-generated catch block
+ try {
+ if(urlConnection != null) {
+ responseCode = urlConnection.getResponseCode();
+ if(responseCode == 401) {
+ broadcastProgress(progress++);
+ LeapSRPSession.setToken("");
+ Log.d(TAG, Integer.toString(responseCode));
+ return true;
+ }
+ }
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+
e.printStackTrace();
return false;
} catch (KeyManagementException e) {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4893ce4c..436a6bb6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,8 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.leap.bitmaskclient"
- android:versionCode="107"
- android:versionName="0.8.1" >
+ android:versionCode="111"
+ android:versionName="0.9.0" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -35,7 +35,7 @@
android:icon="@drawable/icon"
android:logo="@drawable/icon"
android:label="@string/app"
- android:theme="@style/appstyle">
+ android:theme="@style/blinkt">
<service
android:name="se.leap.bitmaskclient.eip.VoidVpnService"
@@ -94,7 +94,7 @@
</activity>
<activity
android:name="se.leap.bitmaskclient.ConfigurationWizard"
- android:label="@string/title_activity_configuration_wizard"
+ android:label="@string/configuration_wizard_title"
android:uiOptions="splitActionBarWhenNarrow" >
</activity>
<activity
diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
index d7f3e110..02abd7a1 100644
--- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn;
diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
index fb2ba90d..4f747d21 100644
--- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn;
@@ -42,6 +42,7 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Locale;
import java.util.UUID;
import java.util.Vector;
@@ -51,13 +52,14 @@ import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
+import de.blinkt.openvpn.core.Connection;
import de.blinkt.openvpn.core.NativeUtils;
import de.blinkt.openvpn.core.OpenVPNService;
import de.blinkt.openvpn.core.VPNLaunchHelper;
import de.blinkt.openvpn.core.VpnStatus;
import de.blinkt.openvpn.core.X509Utils;
-public class VpnProfile implements Serializable {
+public class VpnProfile implements Serializable, Cloneable {
// Note that this class cannot be moved to core where it belongs since
// the profile loading depends on it being here
// The Serializable documentation mentions that class name change are possible
@@ -71,7 +73,7 @@ public class VpnProfile implements Serializable {
private static final long serialVersionUID = 7085688938959334563L;
public static final int MAXLOGLEVEL = 4;
- public static final int CURRENT_PROFILE_VERSION = 2;
+ public static final int CURRENT_PROFILE_VERSION = 5;
public static final int DEFAULT_MSSFIX_SIZE = 1450;
public static String DEFAULT_DNS1 = "8.8.8.8";
public static String DEFAULT_DNS2 = "8.8.4.4";
@@ -106,12 +108,10 @@ public class VpnProfile implements Serializable {
public String mClientKeyFilename;
public String mCaFilename;
public boolean mUseLzo = true;
- public String mServerPort = "1194";
- public boolean mUseUdp = true;
public String mPKCS12Filename;
public String mPKCS12Password;
public boolean mUseTLSAuth = false;
- public String mServerName = "openvpn.blinkt.de";
+
public String mDNS1 = DEFAULT_DNS1;
public String mDNS2 = DEFAULT_DNS2;
public String mIPv4Address;
@@ -152,6 +152,16 @@ public class VpnProfile implements Serializable {
public String mExcludedRoutes;
public String mExcludedRoutesv6;
public int mMssFix =0; // -1 is default,
+ public Connection[] mConnections = new Connection[0];
+ public boolean mRemoteRandom=false;
+ public HashSet<String> mAllowedAppsVpn = new HashSet<String>();
+ public boolean mAllowedAppsVpnAreDisallowed = true;
+
+
+ /* Options no long used in new profiles */
+ public String mServerName = "openvpn.blinkt.de";
+ public String mServerPort = "1194";
+ public boolean mUseUdp = true;
@@ -159,6 +169,9 @@ public class VpnProfile implements Serializable {
mUuid = UUID.randomUUID();
mName = name;
mProfileVersion = CURRENT_PROFILE_VERSION;
+
+ mConnections = new Connection[1];
+ mConnections[0] = new Connection();
}
public static String openVpnEscape(String unescaped) {
@@ -206,7 +219,30 @@ public class VpnProfile implements Serializable {
mAllowLocalLAN = Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT;
}
+ if (mProfileVersion < 4) {
+ moveOptionsToConnection();
+ mAllowedAppsVpnAreDisallowed=true;
+ }
+ if (mAllowedAppsVpn==null)
+ mAllowedAppsVpn = new HashSet<String>();
+ if (mConnections ==null)
+ mConnections = new Connection[0];
+
mProfileVersion= CURRENT_PROFILE_VERSION;
+
+ }
+
+ private void moveOptionsToConnection() {
+ mConnections = new Connection[1];
+ Connection conn = new Connection();
+
+ conn.mServerName = mServerName;
+ conn.mServerPort = mServerPort;
+ conn.mUseUdp = mUseUdp;
+ conn.mCustomConfiguration = "";
+
+ mConnections[0] = conn;
+
}
public String getConfigFile(Context context, boolean configForOvpn3) {
@@ -267,15 +303,27 @@ public class VpnProfile implements Serializable {
// We cannot use anything else than tun
cfg += "dev tun\n";
- // Server Address
- cfg += "remote ";
- cfg += mServerName;
- cfg += " ";
- cfg += mServerPort;
- if (mUseUdp)
- cfg += " udp\n";
- else
- cfg += " tcp-client\n";
+
+ boolean canUsePlainRemotes = true;
+
+ if (mConnections.length==1) {
+ cfg += mConnections[0].getConnectionBlock();
+ } else {
+ for (Connection conn : mConnections) {
+ canUsePlainRemotes = canUsePlainRemotes && conn.isOnlyRemote();
+ }
+
+ if (mRemoteRandom)
+ cfg+="remote-random\n";
+
+ if (canUsePlainRemotes) {
+ for (Connection conn : mConnections) {
+ if (conn.mEnabled) {
+ cfg += conn.getConnectionBlock();
+ }
+ }
+ }
+ }
switch (mAuthenticationType) {
@@ -365,11 +413,6 @@ public class VpnProfile implements Serializable {
}
}
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && !mAllowLocalLAN)
- cfg+="redirect-private block-local\n";
- else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mAllowLocalLAN)
- cfg+="redirect-private unblock-local\n";
-
if (mUseDefaultRoutev6)
cfg += "route-ipv6 ::/0\n";
@@ -405,7 +448,7 @@ public class VpnProfile implements Serializable {
if (mAuthenticationType != TYPE_STATICKEYS) {
if (mCheckRemoteCN) {
if (mRemoteCN == null || mRemoteCN.equals(""))
- cfg += "verify-x509-name " + mServerName + " name\n";
+ cfg += "verify-x509-name " + mConnections[0].mServerName + " name\n";
else
switch (mX509AuthType) {
@@ -470,6 +513,19 @@ public class VpnProfile implements Serializable {
}
+ if (!canUsePlainRemotes) {
+ cfg += "# Connection Options are at the end to allow global options (and global custom options) to influence connection blocks\n";
+ for (Connection conn : mConnections) {
+ if (conn.mEnabled) {
+ cfg += "<connection>\n";
+ cfg += conn.getConnectionBlock();
+ cfg += "</connection>\n";
+ }
+ }
+ }
+
+
+
return cfg;
}
@@ -639,6 +695,27 @@ public class VpnProfile implements Serializable {
}
}
+ @Override
+ protected VpnProfile clone() throws CloneNotSupportedException {
+ VpnProfile copy = (VpnProfile) super.clone();
+ copy.mUuid = UUID.randomUUID();
+ copy.mConnections = mConnections.clone();
+ copy.mAllowedAppsVpn = (HashSet<String>) mAllowedAppsVpn.clone();
+ return copy;
+ }
+
+ public VpnProfile copy(String name) {
+ try {
+ VpnProfile copy = (VpnProfile) clone();
+ copy.mName = name;
+ return copy;
+
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
class NoCertReturnedException extends Exception {
public NoCertReturnedException (String msg) {
@@ -769,6 +846,14 @@ public class VpnProfile implements Serializable {
if (!mUseDefaultRoute && (getCustomRoutes(mCustomRoutes) == null || getCustomRoutes(mExcludedRoutes) ==null))
return R.string.custom_route_format_error;
+ boolean noRemoteEnabled = true;
+ for (Connection c : mConnections)
+ if (c.mEnabled)
+ noRemoteEnabled = false;
+
+ if(noRemoteEnabled)
+ return R.string.remote_no_server_selected;
+
// Everything okay
return R.string.no_error_found;
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
index 4940d5d6..dfd815e4 100644
--- a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.activities;
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java b/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java
index 5e4f9517..45f09c8e 100644
--- a/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java
+++ b/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.activities;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java b/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java
index ac9a8ccb..e525abd5 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
index 0d8230b7..5dc96bbc 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
@@ -1,13 +1,17 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
+import android.text.TextUtils;
+import android.util.Pair;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
+import java.io.StringReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
@@ -28,48 +32,49 @@ public class ConfigParser {
private HashMap<String, Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>();
private HashMap<String, Vector<String>> meta = new HashMap<String, Vector<String>>();
-
- private boolean extraRemotesAsCustom=false;
-
public void parseConfig(Reader reader) throws IOException, ConfigParseError {
- BufferedReader br =new BufferedReader(reader);
+ BufferedReader br = new BufferedReader(reader);
- int lineno=0;
- while (true){
- String line = br.readLine();
- lineno++;
- if(line==null)
- break;
+ int lineno = 0;
+ try {
+ while (true) {
+ String line = br.readLine();
+ lineno++;
+ if (line == null)
+ break;
- if (lineno==1 && (line.startsWith("PK\003\004")
- || (line.startsWith("PK\007\008"))))
+ if (lineno == 1 && (line.startsWith("PK\003\004")
+ || (line.startsWith("PK\007\008"))))
throw new ConfigParseError("Input looks like a ZIP Archive. Import is only possible for OpenVPN config files (.ovpn/.conf)");
- // Check for OpenVPN Access Server Meta information
- if (line.startsWith("# OVPN_ACCESS_SERVER_")) {
- Vector<String> metaarg = parsemeta(line);
- meta.put(metaarg.get(0),metaarg);
- continue;
- }
- Vector<String> args = parseline(line);
+ // Check for OpenVPN Access Server Meta information
+ if (line.startsWith("# OVPN_ACCESS_SERVER_")) {
+ Vector<String> metaarg = parsemeta(line);
+ meta.put(metaarg.get(0), metaarg);
+ continue;
+ }
+ Vector<String> args = parseline(line);
- if(args.size() ==0)
- continue;
+ if (args.size() == 0)
+ continue;
- if(args.get(0).startsWith("--"))
- args.set(0, args.get(0).substring(2));
+ if (args.get(0).startsWith("--"))
+ args.set(0, args.get(0).substring(2));
- checkinlinefile(args,br);
+ checkinlinefile(args, br);
- String optionname = args.get(0);
- if(!options.containsKey(optionname)) {
- options.put(optionname, new Vector<Vector<String>>());
- }
- options.get(optionname).add(args);
- }
+ String optionname = args.get(0);
+ if (!options.containsKey(optionname)) {
+ options.put(optionname, new Vector<Vector<String>>());
+ }
+ options.get(optionname).add(args);
+ }
+ } catch (java.lang.OutOfMemoryError memoryError) {
+ throw new ConfigParseError("File too large to parse: " + memoryError.getLocalizedMessage());
+ }
}
private Vector<String> parsemeta(String line) {
@@ -98,7 +103,7 @@ public class ConfigParser {
break;
else {
inlinefile+=line;
- inlinefile+= "\n";
+ inlinefile+= "\n";
}
} while(true);
@@ -132,7 +137,7 @@ public class ConfigParser {
// adapted openvpn's parse function to java
private Vector<String> parseline(String line) throws ConfigParseError {
- Vector<String> parameters = new Vector<String>();
+ Vector<String> parameters = new Vector<String>();
if (line.length()==0)
return parameters;
@@ -145,12 +150,12 @@ public class ConfigParser {
int pos=0;
String currentarg="";
- do {
+ do {
// Emulate the c parsing ...
char in;
if(pos < line.length())
in = line.charAt(pos);
- else
+ else
in = '\0';
if (!backslash && in == '\\' && state != linestate.readin_single_quote)
@@ -228,10 +233,7 @@ public class ConfigParser {
}
- final String[] unsupportedOptions = { "config",
- "connection",
- "proto-force",
- "remote-random",
+ final String[] unsupportedOptions = { "config",
"tls-server"
};
@@ -299,7 +301,7 @@ public class ConfigParser {
"remote",
"float",
"port",
-// "connect-retry",
+ "connect-retry",
"connect-timeout",
"connect-retry-max",
"link-mtu",
@@ -325,7 +327,7 @@ public class ConfigParser {
// This method is far too long
@SuppressWarnings("ConstantConditions")
- public VpnProfile convertProfile() throws ConfigParseError{
+ public VpnProfile convertProfile() throws ConfigParseError, IOException {
boolean noauthtypeset=true;
VpnProfile np = new VpnProfile(CONVERTED_PROFILE);
// Pull, client, tls-client
@@ -338,7 +340,7 @@ public class ConfigParser {
}
Vector<String> secret = getOption("secret", 1, 2);
- if(secret!=null)
+ if(secret!=null)
{
np.mAuthenticationType=VpnProfile.TYPE_STATICKEYS;
noauthtypeset=false;
@@ -362,7 +364,7 @@ public class ConfigParser {
if (route.size() >= 4)
gateway = route.get(3);
- String net = route.get(1);
+ String net = route.get(1);
try {
CIDRIP cidr = new CIDRIP(net, netmask);
if (gateway.equals("net_gateway"))
@@ -398,7 +400,7 @@ public class ConfigParser {
Vector<Vector<String>> tlsauthoptions = getAllOption("tls-auth", 1, 2);
if(tlsauthoptions!=null) {
for(Vector<String> tlsauth:tlsauthoptions) {
- if(tlsauth!=null)
+ if(tlsauth!=null)
{
if(!tlsauth.get(1).equals("[inline]")) {
np.mTLSAuthFilename=tlsauth.get(1);
@@ -458,36 +460,6 @@ public class ConfigParser {
throw new ConfigParseError("Invalid mode for --mode specified, need p2p");
}
- Vector<String> port = getOption("port", 1,1);
- if(port!=null){
- np.mServerPort = port.get(1);
- }
-
- Vector<String> rport = getOption("rport", 1,1);
- if(rport!=null){
- np.mServerPort = rport.get(1);
- }
-
- Vector<String> proto = getOption("proto", 1,1);
- if(proto!=null){
- np.mUseUdp=isUdpProto(proto.get(1));
- }
-
- // Parse remote config
- Vector<Vector<String>> remotes = getAllOption("remote",1,3);
-
- if(remotes!=null && remotes.size()>=1 ) {
- Vector<String> remote = remotes.get(0);
- switch (remote.size()) {
- case 4:
- np.mUseUdp=isUdpProto(remote.get(3));
- case 3:
- np.mServerPort = remote.get(2);
- case 2:
- np.mServerName = remote.get(1);
- }
- }
-
Vector<Vector<String>> dhcpoptions = getAllOption("dhcp-option", 2, 2);
@@ -581,18 +553,18 @@ public class ConfigParser {
if(verifyx509name!=null){
np.mRemoteCN = verifyx509name.get(1);
np.mCheckRemoteCN=true;
- if(verifyx509name.size()>2) {
+ if(verifyx509name.size()>2) {
if (verifyx509name.get(2).equals("name"))
np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN;
else if (verifyx509name.get(2).equals("name-prefix"))
np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX;
- else
+ else
throw new ConfigParseError("Unknown parameter to x509-verify-name: " + verifyx509name.get(2) );
} else {
np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_DN;
}
- }
+ }
Vector<String> verb = getOption("verb",1,1);
@@ -615,7 +587,7 @@ public class ConfigParser {
if(connectretrymax!=null)
np.mConnectRetryMax =connectretrymax.get(1);
- Vector<Vector<String>> remotetls = getAllOption("remote-cert-tls", 1, 1);
+ Vector<Vector<String>> remotetls = getAllOption("remote-cert-tls", 1, 1);
if(remotetls!=null)
if(remotetls.get(0).get(1).equals("server"))
np.mExpectTLSCert=true;
@@ -632,14 +604,55 @@ public class ConfigParser {
np.mAuthenticationType=VpnProfile.TYPE_USERPASS_KEYSTORE;
}
if(authuser.size()>1) {
- // Set option value to password get to get cance to embed later.
+ // Set option value to password get to embed later.
np.mUsername=null;
- np.mPassword=authuser.get(1);
- useEmbbedUserAuth(np,authuser.get(1));
+ useEmbbedUserAuth(np, authuser.get(1));
}
}
- // Parse OpenVPN Access Server extra
+ Pair<Connection, Connection[]> conns = parseConnectionOptions(null);
+ np.mConnections =conns.second;
+
+ Vector<Vector<String>> connectionBlocks = getAllOption("connection", 1, 1);
+
+ if (np.mConnections.length > 0 && connectionBlocks !=null ) {
+ throw new ConfigParseError("Using a <connection> block and --remote is not allowed.");
+ }
+
+ if (connectionBlocks!=null) {
+ np.mConnections = new Connection[connectionBlocks.size()];
+
+ int connIndex = 0;
+ for (Vector<String> conn : connectionBlocks) {
+ Pair<Connection, Connection[]> connectionBlockConnection =
+ parseConnection(conn.get(1), conns.first);
+
+ if (connectionBlockConnection.second.length != 1)
+ throw new ConfigParseError("A <connection> block must have exactly one remote");
+ np.mConnections[connIndex] = connectionBlockConnection.second[0];
+ connIndex++;
+ }
+ }
+ if(getOption("remote-random", 0, 0) != null)
+ np.mRemoteRandom=true;
+
+ Vector<String> protoforce = getOption("proto-force", 1, 1);
+ if(protoforce!=null) {
+ boolean disableUDP;
+ String protoToDisable = protoforce.get(1);
+ if (protoToDisable.equals("udp"))
+ disableUDP=true;
+ else if (protoToDisable.equals("tcp"))
+ disableUDP=false;
+ else
+ throw new ConfigParseError(String.format("Unknown protocol %s in proto-force", protoToDisable));
+
+ for (Connection conn:np.mConnections)
+ if(conn.mUseUdp==disableUDP)
+ conn.mEnabled=false;
+ }
+
+ // Parse OpenVPN Access Server extra
Vector<String> friendlyname = meta.get("FRIENDLY_NAME");
if(friendlyname !=null && friendlyname.size() > 1)
np.mName=friendlyname.get(1);
@@ -649,20 +662,95 @@ public class ConfigParser {
if(ocusername !=null && ocusername.size() > 1)
np.mUsername=ocusername.get(1);
- // Check the other options
- if(remotes !=null && remotes.size()>1 && extraRemotesAsCustom) {
- // first is already added
- remotes.remove(0);
- np.mCustomConfigOptions += getOptionStrings(remotes);
- np.mUseCustomConfig=true;
-
- }
- checkIgnoreAndInvalidOptions(np);
+ checkIgnoreAndInvalidOptions(np);
fixup(np);
return np;
}
+ private Pair<Connection, Connection[]> parseConnection(String connection, Connection defaultValues) throws IOException, ConfigParseError {
+ // Parse a connection Block as a new configuration file
+
+
+ ConfigParser connectionParser = new ConfigParser();
+ StringReader reader = new StringReader(connection.substring(VpnProfile.INLINE_TAG.length()));
+ connectionParser.parseConfig(reader);
+
+ Pair<Connection, Connection[]> conn = connectionParser.parseConnectionOptions(defaultValues);
+
+ return conn;
+ }
+
+ private Pair<Connection, Connection[]> parseConnectionOptions(Connection connDefault) throws ConfigParseError {
+ Connection conn;
+ if (connDefault!=null)
+ try {
+ conn = connDefault.clone();
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ return null;
+ }
+ else
+ conn = new Connection();
+
+ Vector<String> port = getOption("port", 1,1);
+ if(port!=null){
+ conn.mServerPort = port.get(1);
+ }
+
+ Vector<String> rport = getOption("rport", 1,1);
+ if(rport!=null){
+ conn.mServerPort = rport.get(1);
+ }
+
+ Vector<String> proto = getOption("proto", 1,1);
+ if(proto!=null){
+ conn.mUseUdp=isUdpProto(proto.get(1));
+ }
+
+
+ // Parse remote config
+ Vector<Vector<String>> remotes = getAllOption("remote",1,3);
+
+
+ // Assume that we need custom options if connectionDefault are set
+ if(connDefault!=null) {
+ for (Vector<Vector<String>> option : options.values()) {
+
+ conn.mCustomConfiguration += getOptionStrings(option);
+
+ }
+ if (!TextUtils.isEmpty(conn.mCustomConfiguration))
+ conn.mUseCustomConfig = true;
+ }
+ // Make remotes empty to simplify code
+ if (remotes==null)
+ remotes = new Vector<Vector<String>>();
+
+ Connection[] connections = new Connection[remotes.size()];
+
+
+ int i=0;
+ for (Vector<String> remote: remotes) {
+ try {
+ connections[i] = conn.clone();
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ switch (remote.size()) {
+ case 4:
+ connections[i].mUseUdp=isUdpProto(remote.get(3));
+ case 3:
+ connections[i].mServerPort = remote.get(2);
+ case 2:
+ connections[i].mServerName = remote.get(1);
+ }
+ i++;
+ }
+ return Pair.create(conn, connections);
+
+ }
+
private void checkRedirectParameters(VpnProfile np, Vector<Vector<String>> defgw) {
for (Vector<String> redirect: defgw)
for (int i=1;i<redirect.size();i++){
@@ -673,25 +761,21 @@ public class ConfigParser {
}
}
- public void useExtraRemotesAsCustom(boolean b) {
- this.extraRemotesAsCustom = b;
- }
-
private boolean isUdpProto(String proto) throws ConfigParseError {
boolean isudp;
if(proto.equals("udp") || proto.equals("udp6"))
isudp=true;
else if (proto.equals("tcp-client") ||
- proto.equals("tcp") ||
+ proto.equals("tcp") ||
proto.equals("tcp6") ||
proto.endsWith("tcp6-client"))
isudp =false;
- else
+ else
throw new ConfigParseError("Unsupported option to --proto " + proto);
return isudp;
}
- static public void useEmbbedUserAuth(VpnProfile np,String inlinedata)
+ static public void useEmbbedUserAuth(VpnProfile np, String inlinedata)
{
String data = VpnProfile.getEmbeddedContent(inlinedata);
String[] parts = data.split("\n");
diff --git a/app/src/main/java/de/blinkt/openvpn/core/Connection.java b/app/src/main/java/de/blinkt/openvpn/core/Connection.java
new file mode 100644
index 00000000..b10664ce
--- /dev/null
+++ b/app/src/main/java/de/blinkt/openvpn/core/Connection.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012-2014 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.core;
+
+import android.text.TextUtils;
+
+import java.io.Serializable;
+
+public class Connection implements Serializable, Cloneable {
+ public String mServerName = "openvpn.blinkt.de";
+ public String mServerPort = "1194";
+ public boolean mUseUdp = true;
+ public String mCustomConfiguration="";
+ public boolean mUseCustomConfig=false;
+ public boolean mEnabled=true;
+
+ private static final long serialVersionUID = 92031902903829089L;
+
+
+ public String getConnectionBlock() {
+ String cfg="";
+
+ // Server Address
+ cfg += "remote ";
+ cfg += mServerName;
+ cfg += " ";
+ cfg += mServerPort;
+ if (mUseUdp)
+ cfg += " udp\n";
+ else
+ cfg += " tcp-client\n";
+
+ if (!TextUtils.isEmpty(mCustomConfiguration) && mUseCustomConfig) {
+ cfg += mCustomConfiguration;
+ cfg += "\n";
+ }
+ return cfg;
+ }
+
+ @Override
+ public Connection clone() throws CloneNotSupportedException {
+ return (Connection) super.clone();
+ }
+
+ public boolean isOnlyRemote() {
+ return TextUtils.isEmpty(mCustomConfiguration) || !mUseCustomConfig;
+ }
+}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
index 0d75ae51..4ccf5472 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
@@ -182,18 +182,14 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
screen = connectState.DISCONNECTED;
if (shouldBeConnected()) {
- if (sendusr1) {
- if (lastNetwork == -1) {
- mManagement.resume();
- } else {
- mManagement.reconnect();
- }
+ if (lastNetwork == -1) {
+ mManagement.resume();
} else {
mManagement.networkChange();
+
}
}
-
lastNetwork = newnet;
}
} else if (networkInfo == null) {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
index 83e760ca..56a574dc 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java b/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java
new file mode 100644
index 00000000..440458e4
--- /dev/null
+++ b/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2012-2014 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.core;
+
+import android.annotation.TargetApi;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Build;
+
+/**
+ * Created by arne on 26.11.14.
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class LollipopDeviceStateListener extends ConnectivityManager.NetworkCallback {
+
+ private String mLastConnectedStatus;
+ private String mLastLinkProperties;
+ private String mLastNetworkCapabilities;
+
+ @Override
+ public void onAvailable(Network network) {
+ super.onAvailable(network);
+
+ if (!network.toString().equals(mLastConnectedStatus)) {
+ mLastConnectedStatus = network.toString();
+ VpnStatus.logDebug("Connected to " + mLastConnectedStatus);
+ }
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
+ super.onLinkPropertiesChanged(network, linkProperties);
+
+ if (!linkProperties.toString().equals(mLastLinkProperties)) {
+ mLastLinkProperties = linkProperties.toString();
+ VpnStatus.logDebug(String.format("Linkproperties of %s: %s", network, linkProperties));
+ }
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
+ super.onCapabilitiesChanged(network, networkCapabilities);
+ if (!networkCapabilities.toString().equals(mLastNetworkCapabilities)) {
+ mLastNetworkCapabilities = networkCapabilities.toString();
+ VpnStatus.logDebug(String.format("Network capabilities of %s: %s", network, networkCapabilities));
+ }
+ }
+}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
index 6d7ffdf2..f67b7730 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
@@ -9,6 +9,7 @@ import java.security.InvalidKeyException;
public class NativeUtils {
public static native byte[] rsasign(byte[] input,int pkey) throws InvalidKeyException;
+ public static native String[] getIfconfig() throws IllegalArgumentException;
static native void jniclose(int fdint);
static {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
index 35f46513..26354689 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
@@ -21,6 +21,8 @@ import se.leap.bitmaskclient.BuildConfig;
public class NetworkSpace {
+
+
static class ipAddress implements Comparable<ipAddress> {
private BigInteger netAddress;
public int networkMask;
@@ -198,6 +200,13 @@ public class NetworkSpace {
mIpAddresses.add(new ipAddress(cidrIp, include));
}
+ public void addIPSplit(CIDRIP cidrIp, boolean include) {
+ ipAddress newIP = new ipAddress(cidrIp, include);
+ ipAddress[] splitIps = newIP.split();
+ for (ipAddress split: splitIps)
+ mIpAddresses.add(split);
+ }
+
void addIPv6(Inet6Address address, int mask, boolean included) {
mIpAddresses.add(new ipAddress(address, mask, included));
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
index e90c16d1..1f28c77d 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index d9830955..578d95e7 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
@@ -14,7 +14,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.net.NetworkRequest;
import android.net.VpnService;
import android.os.Binder;
import android.os.Build;
@@ -23,6 +25,7 @@ import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
@@ -81,6 +84,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
private String mLastTunCfg;
private String mRemoteGW;
private final Object mProcessLock = new Object();
+ private LollipopDeviceStateListener mLollipopDeviceStateListener;
// From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
public static String humanReadableByteCount(long bytes, boolean mbit) {
@@ -266,6 +270,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
mDeviceStateReceiver = new DeviceStateReceiver(magnagement);
registerReceiver(mDeviceStateReceiver, filter);
VpnStatus.addByteCountListener(mDeviceStateReceiver);
+
+ /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ addLollipopCMListener(); */
}
synchronized void unregisterDeviceStateReceiver() {
@@ -280,6 +287,10 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
iae.printStackTrace();
}
mDeviceStateReceiver = null;
+
+ /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ removeLollipopCMListener();*/
+
}
public void userPause(boolean shouldBePaused) {
@@ -320,7 +331,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
mProfile = ProfileManager.getLastConnectedProfile(this, false);
/* Got no profile, just stop */
- if (mProfile==null) {
+ if (mProfile == null) {
Log.d("OpenVPN", "Got no last connected profile on null intent. Stopping");
stopSelf(startId);
return START_NOT_STICKY;
@@ -431,7 +442,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
private OpenVPNManagement instantiateOpenVPN3Core() {
try {
Class cl = Class.forName("de.blinkt.openvpn.core.OpenVPNThreadv3");
- return (OpenVPNManagement) cl.getConstructor(OpenVPNService.class,VpnProfile.class).newInstance(this,mProfile);
+ return (OpenVPNManagement) cl.getConstructor(OpenVPNService.class, VpnProfile.class).newInstance(this, mProfile);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
@@ -474,6 +485,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
if (mLocalIPv6 != null)
cfg += mLocalIPv6;
+
cfg += "routes: " + TextUtils.join("|", mRoutes.getNetworks(true)) + TextUtils.join("|", mRoutesv6.getNetworks(true));
cfg += "excl. routes:" + TextUtils.join("|", mRoutes.getNetworks(false)) + TextUtils.join("|", mRoutesv6.getNetworks(false));
cfg += "dns: " + TextUtils.join("|", mDnslist);
@@ -490,6 +502,10 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
VpnStatus.logInfo(R.string.last_openvpn_tun_config);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mProfile.mAllowLocalLAN)
+ {
+ allowAllAFFamilies(builder);
+ }
if (mLocalIP == null && mLocalIPv6 == null) {
VpnStatus.logError(getString(R.string.opentun_no_ipaddr));
@@ -497,6 +513,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
if (mLocalIP != null) {
+ addLocalNetworksToRoutes();
try {
builder.addAddress(mLocalIP.mIp, mLocalIP.len);
} catch (IllegalArgumentException iae) {
@@ -527,7 +544,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
String release = Build.VERSION.RELEASE;
if ((Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3")
- && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
+ && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
&& mMtu < 1280) {
VpnStatus.logInfo(String.format(Locale.US, "Forcing MTU to 1280 instead of %d to workaround Android Bug #70916", mMtu));
builder.setMtu(1280);
@@ -560,8 +577,12 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
VpnStatus.logInfo(R.string.local_ip_info, mLocalIP.mIp, mLocalIP.len, mLocalIPv6, mMtu);
VpnStatus.logInfo(R.string.dns_server_info, TextUtils.join(", ", mDnslist), mDomain);
VpnStatus.logInfo(R.string.routes_info_incl, TextUtils.join(", ", mRoutes.getNetworks(true)), TextUtils.join(", ", mRoutesv6.getNetworks(true)));
- VpnStatus.logInfo(R.string.routes_info_excl, TextUtils.join(", ", mRoutes.getNetworks(false)),TextUtils.join(", ", mRoutesv6.getNetworks(false)));
+ VpnStatus.logInfo(R.string.routes_info_excl, TextUtils.join(", ", mRoutes.getNetworks(false)), TextUtils.join(", ", mRoutesv6.getNetworks(false)));
VpnStatus.logDebug(R.string.routes_debug, TextUtils.join(", ", positiveIPv4Routes), TextUtils.join(", ", positiveIPv6Routes));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ setAllowedVpnPackages(builder);
+ }
+
String session = mProfile.mName;
if (mLocalIP != null && mLocalIPv6 != null)
@@ -601,6 +622,82 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private void allowAllAFFamilies(Builder builder) {
+ builder.allowFamily(OsConstants.AF_INET);
+ builder.allowFamily(OsConstants.AF_INET6);
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ void removeLollipopCMListener() {
+ ConnectivityManager cm = (ConnectivityManager) getBaseContext().getSystemService(CONNECTIVITY_SERVICE);
+ cm.unregisterNetworkCallback(mLollipopDeviceStateListener);
+ mLollipopDeviceStateListener = null;
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ void addLollipopCMListener() {
+ ConnectivityManager cm = (ConnectivityManager) getBaseContext().getSystemService(CONNECTIVITY_SERVICE);
+ NetworkRequest.Builder nrb = new NetworkRequest.Builder();
+
+ mLollipopDeviceStateListener = new LollipopDeviceStateListener();
+ cm.registerNetworkCallback(nrb.build(), mLollipopDeviceStateListener);
+ }
+
+ private void addLocalNetworksToRoutes() {
+
+ // Add local network interfaces
+ String[] localRoutes = NativeUtils.getIfconfig();
+
+ // The format of mLocalRoutes is kind of broken because I don't really like JNI
+ for (int i = 0; i < localRoutes.length; i += 3) {
+ String intf = localRoutes[i];
+ String ipAddr = localRoutes[i + 1];
+ String netMask = localRoutes[i + 2];
+
+ if (intf == null || intf.equals("lo") ||
+ intf.startsWith("tun") || intf.startsWith("rmnet"))
+ continue;
+
+ if (ipAddr==null || netMask == null) {
+ VpnStatus.logError("Local routes are broken?! (Report to author) " + TextUtils.join("|", localRoutes));
+ continue;
+ }
+
+ if (ipAddr.equals(mLocalIP.mIp))
+ continue;
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT && !mProfile.mAllowLocalLAN) {
+ mRoutes.addIPSplit(new CIDRIP(ipAddr, netMask), true);
+
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && mProfile.mAllowLocalLAN)
+ mRoutes.addIP(new CIDRIP(ipAddr, netMask), false);
+ }
+ }
+
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private void setAllowedVpnPackages(Builder builder) {
+ for (String pkg : mProfile.mAllowedAppsVpn) {
+ try {
+ if (mProfile.mAllowedAppsVpnAreDisallowed) {
+ builder.addDisallowedApplication(pkg);
+ } else {
+ builder.addAllowedApplication(pkg);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ mProfile.mAllowedAppsVpn.remove(pkg);
+ VpnStatus.logInfo(R.string.app_no_longer_exists, pkg);
+ }
+ }
+
+ if (mProfile.mAllowedAppsVpnAreDisallowed) {
+ VpnStatus.logDebug(R.string.disallowed_vpn_apps_info, TextUtils.join(", ", mProfile.mAllowedAppsVpn));
+ } else {
+ VpnStatus.logDebug(R.string.allowed_vpn_apps_info, TextUtils.join(", ", mProfile.mAllowedAppsVpn));
+ }
+ }
+
public void addDNS(String dns) {
mDnslist.add(dns);
}
@@ -611,28 +708,30 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
}
- /** Route that is always included, used by the v3 core */
- public void addRoute (CIDRIP route) {
+ /**
+ * Route that is always included, used by the v3 core
+ */
+ public void addRoute(CIDRIP route) {
mRoutes.addIP(route, true);
}
- public void addRoute (String dest, String mask, String gateway, String device) {
+ public void addRoute(String dest, String mask, String gateway, String device) {
CIDRIP route = new CIDRIP(dest, mask);
boolean include = isAndroidTunDevice(device);
- NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32),false);
+ NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32), false);
- if (mLocalIP==null) {
+ if (mLocalIP == null) {
VpnStatus.logError("Local IP address unset but adding route?! This is broken! Please contact author with log");
return;
}
- NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP,true);
+ NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP, true);
if (localNet.containsNet(gatewayIP))
- include=true;
+ include = true;
- if (gateway!= null &&
+ if (gateway != null &&
(gateway.equals("255.255.255.255") || gateway.equals(mRemoteGW)))
- include=true;
+ include = true;
if (route.len == 32 && !mask.equals("255.255.255.255")) {
@@ -664,7 +763,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
private boolean isAndroidTunDevice(String device) {
- return device!=null &&
+ return device != null &&
(device.startsWith("tun") || "(null)".equals(device) || "vpnservice-tun".equals(device));
}
@@ -679,7 +778,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
public void setLocalIP(String local, String netmask, int mtu, String mode) {
mLocalIP = new CIDRIP(local, netmask);
mMtu = mtu;
- mRemoteGW=null;
+ mRemoteGW = null;
long netMaskAsInt = CIDRIP.getInt(netmask);
@@ -687,14 +786,17 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
// get the netmask as IP
int masklen;
- if ("net30".equals(mode))
+ long mask;
+ if ("net30".equals(mode)) {
masklen = 30;
- else
+ mask = 0xfffffffc;
+ } else {
masklen = 31;
+ mask = 0xfffffffe;
+ }
- int mask = ~( 1 << (32 - (mLocalIP.len +1)));
// Netmask is Ip address +/-1, assume net30/p2p with small net
- if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask )) {
+ if ((netMaskAsInt & mask) == (mLocalIP.getInt() & mask)) {
mLocalIP.len = masklen;
} else {
mLocalIP.len = 32;
@@ -702,13 +804,18 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
VpnStatus.logWarning(R.string.ip_not_cidr, local, netmask, mode);
}
}
- if (("p2p".equals(mode) && mLocalIP.len < 32) || ("net30".equals(mode) && mLocalIP.len < 30)) {
+ if (("p2p".equals(mode) && mLocalIP.len < 32) || ("net30".equals(mode) && mLocalIP.len < 30)) {
VpnStatus.logWarning(R.string.ip_looks_like_subnet, local, netmask, mode);
}
+ /* Workaround for Lollipop, it does not route traffic to the VPNs own network mask */
+ if (mLocalIP.len <= 31 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ addRoute(mLocalIP);
+
+
// Configurations are sometimes really broken...
- mRemoteGW=netmask;
+ mRemoteGW = netmask;
}
public void setLocalIPv6(String ipv6addr) {
@@ -810,7 +917,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
} else {
String release = Build.VERSION.RELEASE;
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT && !release.startsWith("4.4.3")
- && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
+ && !release.startsWith("4.4.4") && !release.startsWith("4.4.5") && !release.startsWith("4.4.6"))
// There will be probably no 4.4.4 or 4.4.5 version, so don't waste effort to do parsing here
return "OPEN_AFTER_CLOSE";
else
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
index e36a5b8a..298a6c40 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
index 37094a1b..1c3b3362 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
@@ -157,7 +157,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
}
} catch (IOException e) {
- if (!e.getMessage().equals("socket closed"))
+ if (!e.getMessage().equals("socket closed") && !e.getMessage().equals("Connection reset by peer"))
VpnStatus.logException(e);
}
synchronized (active) {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java b/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java
index bca0a4ab..a788426a 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;/*
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
index 2a26152e..1ebc0a57 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java b/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java
index cf953863..6e2abb13 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
index 208aa359..73ed05bc 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
@@ -76,7 +76,6 @@ public class VPNLaunchHelper {
args.add("--config");
args.add(c.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE);
-
return args.toArray(new String[args.size()]);
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
index 25558f13..ffc8097d 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
@@ -480,7 +480,11 @@ public class VpnStatus {
newLogItem(new LogItem(LogLevel.INFO, message));
}
- public static void logInfo(int resourceId, Object... args) {
+ public static void logDebug(String message) {
+ newLogItem(new LogItem(LogLevel.DEBUG, message));
+ }
+
+ public static void logInfo(int resourceId, Object... args) {
newLogItem(new LogItem(LogLevel.INFO, resourceId, args));
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java b/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
index ff383e0f..0786967b 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.core;
diff --git a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
index 77fc21e6..199caa63 100644
--- a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
+++ b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.fragments;
diff --git a/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java b/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java
index e25c2859..82378b00 100644
--- a/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java
+++ b/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2012-2014 Arne Schwabe
- * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
package de.blinkt.openvpn.views;
diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 52b22695..afe1a638 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -17,37 +17,21 @@
package se.leap.bitmaskclient;
import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.app.FragmentTransaction;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
+import android.app.*;
+import android.content.*;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
+import android.os.*;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.ProgressBar;
-import android.widget.TextView;
+import android.view.*;
+import android.widget.*;
import org.jetbrains.annotations.NotNull;
-import org.json.JSONException;
-import org.json.JSONObject;
+import org.json.*;
+import java.net.*;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import butterknife.ButterKnife;
-import butterknife.InjectView;
-import de.blinkt.openvpn.activities.LogWindow;
-import se.leap.bitmaskclient.eip.Constants;
-import se.leap.bitmaskclient.eip.EIP;
-import se.leap.bitmaskclient.eip.EipStatus;
+import butterknife.*;
+import de.blinkt.openvpn.activities.*;
+import se.leap.bitmaskclient.eip.*;
/**
* The main user facing Activity of Bitmask Android, consisting of status, controls,
@@ -58,16 +42,16 @@ import se.leap.bitmaskclient.eip.EipStatus;
*/
public class Dashboard extends Activity implements SessionDialog.SessionDialogInterface, ProviderAPIResultReceiver.Receiver {
- protected static final int CONFIGURE_LEAP = 0;
- protected static final int SWITCH_PROVIDER = 1;
+ protected static final int CONFIGURE_LEAP = 0;
+ protected static final int SWITCH_PROVIDER = 1;
- final public static String TAG = Dashboard.class.getSimpleName();
- final public static String SHARED_PREFERENCES = "LEAPPreferences";
- final public static String ACTION_QUIT = "quit";
+ public static final String TAG = Dashboard.class.getSimpleName();
+ public static final String SHARED_PREFERENCES = "LEAPPreferences";
+ public static final String ACTION_QUIT = "quit";
public static final String REQUEST_CODE = "request_code";
public static final String PARAMETERS = "dashboard parameters";
public static final String START_ON_BOOT = "dashboard start on boot";
- final public static String ON_BOOT = "dashboard on boot";
+ public static final String ON_BOOT = "dashboard on boot";
public static final String APP_VERSION = "bitmask version";
private static Context app;
@@ -81,6 +65,26 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
private Provider provider;
private static boolean authed_eip;
public ProviderAPIResultReceiver providerAPI_result_receiver;
+ private boolean switching_provider;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ app = this;
+
+ PRNGFixes.apply();
+
+ preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
+ fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
+ handleVersion();
+
+ provider = getSavedProvider(savedInstanceState);
+ if (provider == null || provider.getName().isEmpty())
+ startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
+ else
+ buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false));
+ }
@Override
protected void onSaveInstanceState(@NotNull Bundle outState) {
@@ -89,25 +93,6 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
super.onSaveInstanceState(outState);
}
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- app = this;
-
- PRNGFixes.apply();
-
- preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
- fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
- handleVersion();
-
- provider = getSavedProvider(savedInstanceState);
- if (provider == null || provider.getName().isEmpty())
- startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
- else
- buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false));
- }
-
private Provider getSavedProvider(Bundle savedInstanceState) {
Provider provider = null;
if(savedInstanceState != null)
@@ -123,120 +108,111 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
try {
provider = new Provider(new URL(preferences.getString(Provider.MAIN_URL, "")));
provider.define(new JSONObject(preferences.getString(Provider.KEY, "")));
- } catch (MalformedURLException | JSONException e) {
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
e.printStackTrace();
}
return provider;
}
-
private void handleVersion() {
try {
int versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
int lastDetectedVersion = preferences.getInt(APP_VERSION, 0);
preferences.edit().putInt(APP_VERSION, versionCode).apply();
- Log.d("Dashboard", "detected version code: " + versionCode);
- Log.d("Dashboard", "last detected version code: " + lastDetectedVersion);
switch(versionCode) {
case 91: // 0.6.0 without Bug #5999
case 101: // 0.8.0
- if(!preferences.getString(Constants.KEY, "").isEmpty()) {
- Intent rebuildVpnProfiles = new Intent(getApplicationContext(), EIP.class);
- rebuildVpnProfiles.setAction(Constants.ACTION_UPDATE_EIP_SERVICE);
- startService(rebuildVpnProfiles);
- }
+ if(!preferences.getString(Constants.KEY, "").isEmpty())
+ eip_fragment.updateEipService();
break;
}
} catch (NameNotFoundException e) {
- Log.d(TAG, "Handle version didn't find any " + getPackageName() + " package");
+ Log.d(TAG, "Handle version didn't find any " + getPackageName() + " package");
}
}
-
- @SuppressLint("CommitPrefEdits")
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
- Log.d(TAG, "onActivityResult: requestCode = " + requestCode);
if ( requestCode == CONFIGURE_LEAP || requestCode == SWITCH_PROVIDER) {
- if ( resultCode == RESULT_OK ) {
- preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
- updateEipService();
-
- if (data.hasExtra(Provider.KEY)) {
+ if ( resultCode == RESULT_OK && data.hasExtra(Provider.KEY)) {
provider = data.getParcelableExtra(Provider.KEY);
- preferences.edit().putBoolean(Constants.PROVIDER_CONFIGURED, true).commit();
- preferences.edit().putString(Provider.MAIN_URL, provider.mainUrl().toString()).apply();
- preferences.edit().putString(Provider.KEY, provider.definition().toString()).apply();
- }
- buildDashboard(false);
- invalidateOptionsMenu();
- if (data.hasExtra(SessionDialog.TAG)) {
- logInDialog(Bundle.EMPTY);
- }
- } else if (resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) {
+ providerToPreferences(provider);
+
+ buildDashboard(false);
+ invalidateOptionsMenu();
+ if (data.hasExtra(SessionDialog.TAG)) {
+ sessionDialog(Bundle.EMPTY);
+ }
+
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
+ } else if (resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) {
finish();
- } else
+ } else
configErrorDialog();
} else if(requestCode == EIP.DISCONNECT) {
EipStatus.getInstance().setConnectedOrDisconnected();
}
}
- /**
- * Dialog shown when encountering a configuration error. Such errors require
- * reconfiguring LEAP or aborting the application.
- */
- private void configErrorDialog() {
- AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext());
- alertBuilder.setTitle(getResources().getString(R.string.setup_error_title));
- alertBuilder
- .setMessage(getResources().getString(R.string.setup_error_text))
- .setCancelable(false)
- .setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- startActivityForResult(new Intent(getAppContext(),ConfigurationWizard.class),CONFIGURE_LEAP);
- }
- })
- .setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- preferences.edit().remove(Provider.KEY).remove(Constants.PROVIDER_CONFIGURED).apply();
- finish();
- }
- })
- .show();
- }
+ @SuppressLint("CommitPrefEdits")
+ private void providerToPreferences(Provider provider) {
+ preferences.edit().putBoolean(Constants.PROVIDER_CONFIGURED, true).commit();
+ preferences.edit().putString(Provider.MAIN_URL, provider.mainUrl().toString()).apply();
+ preferences.edit().putString(Provider.KEY, provider.definition().toString()).apply();
+ }
+
+ private void configErrorDialog() {
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getAppContext());
+ alertBuilder.setTitle(getResources().getString(R.string.setup_error_title));
+ alertBuilder
+ .setMessage(getResources().getString(R.string.setup_error_text))
+ .setCancelable(false)
+ .setPositiveButton(getResources().getString(R.string.setup_error_configure_button), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ startActivityForResult(new Intent(getAppContext(),ConfigurationWizard.class),CONFIGURE_LEAP);
+ }
+ })
+ .setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ preferences.edit().remove(Provider.KEY).remove(Constants.PROVIDER_CONFIGURED).apply();
+ finish();
+ }
+ })
+ .show();
+ }
- /**
- * Inflates permanent UI elements of the View and contains logic for what
- * service dependent UI elements to include.
- */
- private void buildDashboard(boolean hide_and_turn_on_eip) {
- setContentView(R.layout.dashboard);
+ /**
+ * Inflates permanent UI elements of the View and contains logic for what
+ * service dependent UI elements to include.
+ */
+ private void buildDashboard(boolean hide_and_turn_on_eip) {
+ setContentView(R.layout.dashboard);
ButterKnife.inject(this);
- provider_name.setText(provider.getDomain());
- if ( provider.hasEIP()){
-
+ provider_name.setText(provider.getDomain());
+ if ( provider.hasEIP()){
fragment_manager.removePreviousFragment(EipFragment.TAG);
eip_fragment = new EipFragment();
- if (hide_and_turn_on_eip) {
- preferences.edit().remove(Dashboard.START_ON_BOOT).apply();
- Bundle arguments = new Bundle();
- arguments.putBoolean(EipFragment.START_ON_BOOT, true);
+ if (hide_and_turn_on_eip) {
+ preferences.edit().remove(Dashboard.START_ON_BOOT).apply();
+ Bundle arguments = new Bundle();
+ arguments.putBoolean(EipFragment.START_ON_BOOT, true);
if(eip_fragment != null) eip_fragment.setArguments(arguments);
- }
+ }
fragment_manager.replace(R.id.servicesCollection, eip_fragment, EipFragment.TAG);
-
- if (hide_and_turn_on_eip) {
- onBackPressed();
- }
- }
+ if (hide_and_turn_on_eip) {
+ onBackPressed();
+ }
}
+ }
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
@@ -267,114 +243,107 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
}
return true;
}
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.client_dashboard, menu);
+ return true;
+ }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.client_dashboard, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item){
- Intent intent;
- switch (item.getItemId()){
- case R.id.about_leap:
- intent = new Intent(this, AboutActivity.class);
- startActivity(intent);
- return true;
- case R.id.log_window:
- Intent startLW = new Intent(getAppContext(), LogWindow.class);
- startLW.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- startActivity(startLW);
- return true;
- case R.id.switch_provider:
- if (provider.hasEIP()){
- if (preferences.getBoolean(Constants.AUTHED_EIP, false)) {
- logOut();
- }
- eip_fragment.askToStopEIP();
- }
- preferences.edit().clear().apply();
- startActivityForResult(new Intent(this,ConfigurationWizard.class), SWITCH_PROVIDER);
- return true;
- case R.id.login_button:
- logInDialog(Bundle.EMPTY);
- return true;
- case R.id.logout_button:
- logOut();
- return true;
- case R.id.signup_button:
- signUpDialog(Bundle.EMPTY);
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
-
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item){
+ Intent intent;
+ switch (item.getItemId()){
+ case R.id.about_leap:
+ intent = new Intent(this, AboutActivity.class);
+ startActivity(intent);
+ return true;
+ case R.id.log_window:
+ Intent startLW = new Intent(getAppContext(), LogWindow.class);
+ startLW.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ startActivity(startLW);
+ return true;
+ case R.id.switch_provider:
+ switching_provider = true;
+ if (preferences.getBoolean(Constants.AUTHED_EIP, false)) logOut();
+ else switchProvider();
+ return true;
+ case R.id.login_button:
+ sessionDialog(Bundle.EMPTY);
+ return true;
+ case R.id.logout_button:
+ logOut();
+ return true;
+ case R.id.signup_button:
+ sessionDialog(Bundle.EMPTY);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
}
-
- protected Intent prepareProviderAPICommand() {
- providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
- providerAPI_result_receiver.setReceiver(this);
-
- Intent command = new Intent(this, ProviderAPI.class);
-
- command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
- return command;
}
-
- /**
- * Shows the log in dialog.
- */
- public void logInDialog(Bundle resultData) {
- FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG);
- DialogFragment newFragment = SessionDialog.newInstance();
- if(resultData != null && !resultData.isEmpty() && fragment_manager.findFragmentByTag(SessionDialog.TAG) == null)
- newFragment.setArguments(resultData);
- newFragment.show(transaction, SessionDialog.TAG);
+ @Override
+ public void signUp(String username, String password) {
+ Bundle parameters = bundleParameters(username, password);
+ providerApiCommand(parameters, R.string.signingup_message, ProviderAPI.SRP_REGISTER);
}
@Override
public void logIn(String username, String password) {
- Intent provider_API_command = prepareProviderAPICommand();
- Bundle parameters = provider_API_command.getExtras().getBundle(ProviderAPI.PARAMETERS);
- if(parameters == null)
- parameters = new Bundle();
-
- parameters.putString(SessionDialog.USERNAME, username);
- parameters.putString(SessionDialog.PASSWORD, password);
+ Bundle parameters = bundleParameters(username, password);
+ providerApiCommand(parameters, R.string.authenticating_message, ProviderAPI.SRP_AUTH);
+ }
+
+ public void logOut() {
+ providerApiCommand(Bundle.EMPTY, R.string.logout_message, ProviderAPI.LOG_OUT);
+ }
+
+ protected void downloadVpnCertificate() {
+ boolean is_authenticated = !LeapSRPSession.getToken().isEmpty();
+ boolean allowed_anon = preferences.getBoolean(Constants.ALLOWED_ANON, false);
+ if(allowed_anon || is_authenticated)
+ providerApiCommand(Bundle.EMPTY, R.string.downloading_certificate_message, ProviderAPI.DOWNLOAD_CERTIFICATE);
+ else
+ sessionDialog(Bundle.EMPTY);
- if(eip_fragment != null) {
- eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
- eip_fragment.status_message.setText(R.string.authenticating_message);
- }
- provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
- provider_API_command.setAction(ProviderAPI.SRP_AUTH);
- startService(provider_API_command);
}
- public void cancelLoginOrSignup() {
- EipStatus.getInstance().setConnectedOrDisconnected();
+ private Bundle bundleParameters(String username, String password) {
+ Bundle parameters = new Bundle();
+ if(!username.isEmpty() && !password.isEmpty()) {
+ parameters.putString(SessionDialog.USERNAME, username);
+ parameters.putString(SessionDialog.PASSWORD, password);
+ }
+ return parameters;
}
-
- /**
- * Asks ProviderAPI to log out.
- */
- public void logOut() {
- Intent provider_API_command = prepareProviderAPICommand();
- if(eip_fragment != null) {
+ protected void providerApiCommand(Bundle parameters, int progressbar_message_resId, String providerApi_action) {
+ if(eip_fragment != null && progressbar_message_resId != 0) {
eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
- eip_fragment.status_message.setText(R.string.logout_message);
+ setStatusMessage(progressbar_message_resId);
}
- provider_API_command.setAction(ProviderAPI.LOG_OUT);
- startService(provider_API_command);
+
+ Intent command = prepareProviderAPICommand(parameters, providerApi_action);
+ startService(command);
+ }
+
+ private Intent prepareProviderAPICommand(Bundle parameters, String action) {
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
+
+ Intent command = new Intent(this, ProviderAPI.class);
+
+ command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+ command.setAction(action);
+ return command;
+ }
+
+ public void cancelLoginOrSignup() {
+ EipStatus.getInstance().setConnectedOrDisconnected();
}
- /**
- * Shows the sign up dialog.
- */
- public void signUpDialog(Bundle resultData) {
+ public void sessionDialog(Bundle resultData) {
FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG);
DialogFragment newFragment = SessionDialog.newInstance();
@@ -384,160 +353,111 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
newFragment.show(transaction, SessionDialog.TAG);
}
- @Override
- public void signUp(String username, String password) {
- Intent provider_API_command = prepareProviderAPICommand();
- Bundle parameters = provider_API_command.getExtras().getBundle(ProviderAPI.PARAMETERS);
- if(parameters == null)
- parameters = new Bundle();
-
- parameters.putString(SessionDialog.USERNAME, username);
- parameters.putString(SessionDialog.PASSWORD, password);
- if(eip_fragment != null) {
- eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
- eip_fragment.status_message.setText(R.string.signingup_message);
- }
- provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
- provider_API_command.setAction(ProviderAPI.SRP_REGISTER);
- startService(provider_API_command);
+ private void switchProvider() {
+ if (provider.hasEIP()) eip_fragment.askToStopEIP();
+
+ preferences.edit().clear().apply();
+ switching_provider = false;
+ startActivityForResult(new Intent(this, ConfigurationWizard.class), SWITCH_PROVIDER);
}
- /**
- * Asks ProviderAPI to download an authenticated OpenVPN certificate.
- */
- private void downloadAuthedUserCertificate() {
- providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
- providerAPI_result_receiver.setReceiver(this);
-
- Intent provider_API_command = new Intent(this, ProviderAPI.class);
-
- Bundle parameters = new Bundle();
- parameters.putString(ConfigurationWizard.TYPE_OF_CERTIFICATE, ConfigurationWizard.AUTHED_CERTIFICATE);
-
- provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
- provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
-
- startService(provider_API_command);
+ @Override
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ Log.d(TAG, "onReceiveResult");
+ if(resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) {
+ String username = resultData.getString(SessionDialog.USERNAME);
+ String password = resultData.getString(SessionDialog.PASSWORD);
+ logIn(username, password);
+ } else if(resultCode == ProviderAPI.FAILED_SIGNUP) {
+ updateViewHidingProgressBar(resultCode);
+ sessionDialog(resultData);
+ } else if(resultCode == ProviderAPI.SUCCESSFUL_LOGIN) {
+ authed_eip = true;
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
+
+ updateViewHidingProgressBar(resultCode);
+ downloadVpnCertificate();
+ } else if(resultCode == ProviderAPI.FAILED_LOGIN) {
+ updateViewHidingProgressBar(resultCode);
+ sessionDialog(resultData);
+ } else if(resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) {
+ authed_eip = false;
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
+
+ updateViewHidingProgressBar(resultCode);
+ if(switching_provider) switchProvider();
+ } else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
+ updateViewHidingProgressBar(resultCode);
+ setResult(RESULT_CANCELED);
+ } else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+ updateViewHidingProgressBar(resultCode);
+ eip_fragment.updateEipService();
+ setResult(RESULT_OK);
+ } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
+ updateViewHidingProgressBar(resultCode);
+ setResult(RESULT_CANCELED);
}
+ else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) {
+ eip_fragment.updateEipService();
+ setResult(RESULT_OK);
+ } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) {
+ setResult(RESULT_CANCELED);
+ }
+ }
- @Override
- public void onReceiveResult(int resultCode, Bundle resultData) {
- Log.d(TAG, "onReceiveResult");
- if(resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) {
- String username = resultData.getString(SessionDialog.USERNAME);
- String password = resultData.getString(SessionDialog.PASSWORD);
- logIn(username, password);
- } else if(resultCode == ProviderAPI.FAILED_SIGNUP) {
- changeStatusMessage(resultCode);
- hideProgressBar();
-
- signUpDialog(resultData);
- } else if(resultCode == ProviderAPI.SUCCESSFUL_LOGIN) {
- changeStatusMessage(resultCode);
- hideProgressBar();
-
- invalidateOptionsMenu();
-
- authed_eip = true;
- preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
-
- downloadAuthedUserCertificate();
- } else if(resultCode == ProviderAPI.FAILED_LOGIN) {
- changeStatusMessage(resultCode);
- hideProgressBar();
-
- logInDialog(resultData);
- } else if(resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) {
- changeStatusMessage(resultCode);
- hideProgressBar();
-
- invalidateOptionsMenu();
-
- authed_eip = false;
- preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
-
- } else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
- changeStatusMessage(resultCode);
- hideProgressBar();
-
- setResult(RESULT_CANCELED);
- } else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE) {
- changeStatusMessage(resultCode);
- hideProgressBar();
-
- setResult(RESULT_OK);
-
- updateEipService();
- } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
- changeStatusMessage(resultCode);
- hideProgressBar();
- setResult(RESULT_CANCELED);
- }
- else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) {
- setResult(RESULT_OK);
-
- updateEipService();
- } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) {
- setResult(RESULT_CANCELED);
+ private void updateViewHidingProgressBar(int resultCode) {
+ changeStatusMessage(resultCode);
+ hideProgressBar();
+ invalidateOptionsMenu();
}
- }
- private void updateEipService() {
- Intent updateEIP = new Intent(getApplicationContext(), EIP.class);
- updateEIP.setAction(Constants.ACTION_UPDATE_EIP_SERVICE);
- ResultReceiver receiver = new ResultReceiver(new Handler()) {
- protected void onReceiveResult(int resultCode, Bundle resultData) {
+ private void changeStatusMessage(final int previous_result_code) {
+ ResultReceiver status_receiver = new ResultReceiver(new Handler()){
+ protected void onReceiveResult(int resultCode, Bundle resultData){
+ super.onReceiveResult(resultCode, resultData);
String request = resultData.getString(Constants.REQUEST_TAG);
- if(request.equalsIgnoreCase(Constants.ACTION_UPDATE_EIP_SERVICE)) {
- if(resultCode == Activity.RESULT_OK) {
- if(authed_eip && eip_fragment != null) eip_fragment.startEipFromScratch();
+ if (request.equalsIgnoreCase(Constants.ACTION_IS_EIP_RUNNING)){
+ if (resultCode == Activity.RESULT_OK){
+ switch(previous_result_code){
+ case ProviderAPI.SUCCESSFUL_LOGIN: setStatusMessage(R.string.succesful_authentication_message); break;
+ case ProviderAPI.FAILED_LOGIN: setStatusMessage(R.string.authentication_failed_message); break;
+ case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: setStatusMessage(R.string.authed_secured_status); break;
+ case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: setStatusMessage(R.string.incorrectly_downloaded_certificate_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGOUT: setStatusMessage(R.string.logged_out_message); break;
+ case ProviderAPI.LOGOUT_FAILED: setStatusMessage(R.string.log_out_failed_message); break;
+
+ }
+ }
+ else if(resultCode == Activity.RESULT_CANCELED){
+ switch(previous_result_code){
+ case ProviderAPI.SUCCESSFUL_LOGIN: setStatusMessage(R.string.succesful_authentication_message); break;
+ case ProviderAPI.FAILED_LOGIN: setStatusMessage(R.string.authentication_failed_message); break;
+ case ProviderAPI.FAILED_SIGNUP: setStatusMessage(R.string.registration_failed_message); break;
+ case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: break;
+ case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: setStatusMessage(R.string.incorrectly_downloaded_certificate_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGOUT: setStatusMessage(R.string.logged_out_message); break;
+ case ProviderAPI.LOGOUT_FAILED: setStatusMessage(R.string.log_out_failed_message); break;
+ }
}
}
+
}
};
- //updateEIP.putExtra(Constants.RECEIVER_TAG, receiver);
- startService(updateEIP);
+ eipIsRunning(status_receiver);
}
- private void changeStatusMessage(final int previous_result_code) {
- // TODO Auto-generated method stub
- ResultReceiver eip_status_receiver = new ResultReceiver(new Handler()){
- protected void onReceiveResult(int resultCode, Bundle resultData){
- super.onReceiveResult(resultCode, resultData);
- String request = resultData.getString(Constants.REQUEST_TAG);
- if (request.equalsIgnoreCase(Constants.ACTION_IS_EIP_RUNNING)){
- if (resultCode == Activity.RESULT_OK){
-
- switch(previous_result_code){
- case ProviderAPI.SUCCESSFUL_LOGIN: eip_fragment.status_message.setText(R.string.succesful_authentication_message); break;
- case ProviderAPI.FAILED_LOGIN: eip_fragment.status_message.setText(R.string.authentication_failed_message); break;
- case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.authed_secured_status); break;
- case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.incorrectly_downloaded_certificate_message); break;
- case ProviderAPI.SUCCESSFUL_LOGOUT: eip_fragment.status_message.setText(R.string.logged_out_message); break;
- case ProviderAPI.LOGOUT_FAILED: eip_fragment.status_message.setText(R.string.log_out_failed_message); break;
-
- }
- }
- else if(resultCode == Activity.RESULT_CANCELED){
-
- switch(previous_result_code){
-
- case ProviderAPI.SUCCESSFUL_LOGIN: eip_fragment.status_message.setText(R.string.succesful_authentication_message); break;
- case ProviderAPI.FAILED_LOGIN: eip_fragment.status_message.setText(R.string.authentication_failed_message); break;
- case ProviderAPI.FAILED_SIGNUP: eip_fragment.status_message.setText(R.string.registration_failed_message); break;
- case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: break;
- case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.incorrectly_downloaded_certificate_message); break;
- case ProviderAPI.SUCCESSFUL_LOGOUT: eip_fragment.status_message.setText(R.string.logged_out_message); break;
- case ProviderAPI.LOGOUT_FAILED: eip_fragment.status_message.setText(R.string.log_out_failed_message); break;
- }
- }
- }
-
- }
- };
- eipIsRunning(eip_status_receiver);
- }
+ private void setStatusMessage(int string_resId) {
+ if(eip_fragment != null && eip_fragment.status_message != null)
+ eip_fragment.status_message.setText(string_resId);
+ }
+
+ private void eipIsRunning(ResultReceiver eip_receiver){
+ // TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
+ Intent intent = new Intent(this, EIP.class);
+ intent.setAction(Constants.ACTION_IS_EIP_RUNNING);
+ intent.putExtra(Constants.RECEIVER_TAG, eip_receiver);
+ startService(intent);
+ }
private void hideProgressBar() {
if(eip_fragment != null) {
@@ -546,28 +466,13 @@ public class Dashboard extends Activity implements SessionDialog.SessionDialogIn
}
}
- /**
- * For retrieving the base application Context in classes that don't extend
- * Android's Activity class
- *
- * @return Application Context as defined by <code>this</code> for Dashboard instance
- */
- public static Context getAppContext() {
- return app;
- }
-
-
+ public static Context getAppContext() {
+ return app;
+ }
+
@Override
public void startActivityForResult(Intent intent, int requestCode) {
intent.putExtra(Dashboard.REQUEST_CODE, requestCode);
super.startActivityForResult(intent, requestCode);
}
-
- private void eipIsRunning(ResultReceiver eip_receiver){
- // TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
- Intent eip_intent = new Intent(this, EIP.class);
- eip_intent.setAction(Constants.ACTION_IS_EIP_RUNNING);
- eip_intent.putExtra(Constants.RECEIVER_TAG, eip_receiver);
- startService(eip_intent);
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
index 420da7a1..588b137b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -37,7 +37,6 @@ public class EipFragment extends Fragment implements Observer {
protected static final String STATUS_MESSAGE = TAG + ".status_message";
public static final String START_ON_BOOT = "start on boot";
- private View view;
@InjectView(R.id.eipSwitch)
Switch eip_switch;
@InjectView(R.id.status_message)
@@ -45,20 +44,18 @@ public class EipFragment extends Fragment implements Observer {
@InjectView(R.id.eipProgress)
ProgressBar progress_bar;
- private static Activity parent_activity;
+ private static Dashboard dashboard;
private static EIPReceiver mEIPReceiver;
private static EipStatus eip_status;
private boolean is_starting_to_connect;
+ private boolean wants_to_connect;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- parent_activity = activity;
- Dashboard dashboard = (Dashboard) parent_activity;
- Intent provider_API_command = dashboard.prepareProviderAPICommand();
- provider_API_command.setAction(ProviderAPI.DOWNLOAD_EIP_SERVICE);
- parent_activity.startService(provider_API_command);
+ dashboard = (Dashboard) activity;
+ dashboard.providerApiCommand(Bundle.EMPTY, 0, ProviderAPI.DOWNLOAD_EIP_SERVICE);
}
@Override
@@ -71,7 +68,7 @@ public class EipFragment extends Fragment implements Observer {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- view = inflater.inflate(R.layout.eip_service_fragment, container, false);
+ View view = inflater.inflate(R.layout.eip_service_fragment, container, false);
ButterKnife.inject(this, view);
if (eip_status.isConnecting())
@@ -110,15 +107,10 @@ public class EipFragment extends Fragment implements Observer {
super.onSaveInstanceState(outState);
}
- protected void saveEipStatus() {
- boolean eip_is_on = false;
- Log.d(TAG, "saveEipStatus");
- if(eip_switch.isChecked()) {
- eip_is_on = true;
- }
-
- if(parent_activity != null)
- Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
+ protected void saveStatus() {
+ boolean is_on = eip_switch.isChecked();
+ Log.d(TAG, "saveStatus: is_on = " + is_on);
+ Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, is_on).commit();
}
@OnCheckedChanged(R.id.eipSwitch)
@@ -128,18 +120,17 @@ public class EipFragment extends Fragment implements Observer {
else
handleSwitchOff();
- saveEipStatus();
+ saveStatus();
}
private void handleSwitchOn() {
if(canStartEIP())
startEipFromScratch();
else if(canLogInToStartEIP()) {
- Log.d(TAG, "Can Log In to start EIP");
- Dashboard dashboard = (Dashboard) parent_activity;
+ wants_to_connect = true;
Bundle bundle = new Bundle();
bundle.putBoolean(IS_PENDING, true);
- dashboard.logInDialog(bundle);
+ dashboard.sessionDialog(bundle);
}
}
@@ -166,16 +157,16 @@ public class EipFragment extends Fragment implements Observer {
}
private void askPendingStartCancellation() {
- AlertDialog.Builder alertBuilder = new AlertDialog.Builder(parent_activity);
- alertBuilder.setTitle(parent_activity.getString(R.string.eip_cancel_connect_title))
- .setMessage(parent_activity.getString(R.string.eip_cancel_connect_text))
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(dashboard);
+ alertBuilder.setTitle(dashboard.getString(R.string.eip_cancel_connect_title))
+ .setMessage(dashboard.getString(R.string.eip_cancel_connect_text))
.setPositiveButton((R.string.yes), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
askToStopEIP();
}
})
- .setNegativeButton(parent_activity.getString(R.string.no), new DialogInterface.OnClickListener() {
+ .setNegativeButton(dashboard.getString(R.string.no), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
eip_switch.setChecked(true);
@@ -185,34 +176,40 @@ public class EipFragment extends Fragment implements Observer {
}
public void startEipFromScratch() {
+ wants_to_connect = false;
is_starting_to_connect = true;
progress_bar.setVisibility(View.VISIBLE);
eip_switch.setVisibility(View.VISIBLE);
- String status = parent_activity.getString(R.string.eip_status_start_pending);
+ String status = dashboard.getString(R.string.eip_status_start_pending);
status_message.setText(status);
if(!eip_switch.isChecked()) {
eip_switch.setChecked(true);
- saveEipStatus();
}
+ saveStatus();
eipCommand(Constants.ACTION_START_EIP);
}
private void stopEIP() {
if(eip_status.isConnecting())
VoidVpnService.stop();
- Intent disconnect_vpn = new Intent(parent_activity, DisconnectVPN.class);
- parent_activity.startActivityForResult(disconnect_vpn, EIP.DISCONNECT);
+ Intent disconnect_vpn = new Intent(dashboard, DisconnectVPN.class);
+ dashboard.startActivityForResult(disconnect_vpn, EIP.DISCONNECT);
eip_status.setDisconnecting();
}
protected void askToStopEIP() {
hideProgressBar();
- String status = parent_activity.getString(R.string.eip_state_not_connected);
+ String status = dashboard.getString(R.string.eip_state_not_connected);
status_message.setText(status);
+
eipCommand(Constants.ACTION_STOP_EIP);
}
+
+ protected void updateEipService() {
+ eipCommand(Constants.ACTION_UPDATE_EIP_SERVICE);
+ }
/**
* Send a command to EIP
@@ -222,10 +219,10 @@ public class EipFragment extends Fragment implements Observer {
*/
private void eipCommand(String action){
// TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
- Intent vpn_intent = new Intent(parent_activity.getApplicationContext(), EIP.class);
+ Intent vpn_intent = new Intent(dashboard.getApplicationContext(), EIP.class);
vpn_intent.setAction(action);
vpn_intent.putExtra(Constants.RECEIVER_TAG, mEIPReceiver);
- parent_activity.startService(vpn_intent);
+ dashboard.startService(vpn_intent);
}
@Override
@@ -233,7 +230,7 @@ public class EipFragment extends Fragment implements Observer {
if(observable instanceof EipStatus) {
eip_status = (EipStatus) observable;
final EipStatus eip_status = (EipStatus) observable;
- parent_activity.runOnUiThread(new Runnable() {
+ dashboard.runOnUiThread(new Runnable() {
@Override
public void run() {
handleNewState(eip_status);
@@ -259,13 +256,13 @@ public class EipFragment extends Fragment implements Observer {
Log.d(TAG, "setConnectedUi? " + eip_status.isConnected());
adjustSwitch();
is_starting_to_connect = false;
- status_message.setText(parent_activity.getString(R.string.eip_state_connected));
+ status_message.setText(dashboard.getString(R.string.eip_state_connected));
}
private void setDisconnectedUI(){
hideProgressBar();
adjustSwitch();
- status_message.setText(parent_activity.getString(R.string.eip_state_not_connected));
+ status_message.setText(dashboard.getString(R.string.eip_state_not_connected));
}
private void adjustSwitch() {
@@ -286,13 +283,18 @@ public class EipFragment extends Fragment implements Observer {
private void setInProgressUI(EipStatus eip_status) {
int localizedResId = eip_status.getLocalizedResId();
String logmessage = eip_status.getLogMessage();
- String prefix = parent_activity.getString(localizedResId);
+ String prefix = dashboard.getString(localizedResId);
status_message.setText(prefix + " " + logmessage);
is_starting_to_connect = false;
adjustSwitch();
}
+ private void updatingCertificateUI() {
+ progress_bar.setVisibility(View.VISIBLE);
+ status_message.setText(getString(R.string.updating_certificate_message));
+ }
+
private void hideProgressBar() {
if(progress_bar != null)
progress_bar.setVisibility(View.GONE);
@@ -338,20 +340,21 @@ public class EipFragment extends Fragment implements Observer {
case Activity.RESULT_OK:
break;
case Activity.RESULT_CANCELED:
- Dashboard dashboard = (Dashboard) parent_activity;
-
- progress_bar.setVisibility(View.VISIBLE);
- status_message.setText(getString(R.string.updating_certificate_message));
- if(LeapSRPSession.getToken().isEmpty() && !Dashboard.preferences.getBoolean(Constants.ALLOWED_ANON, false)) {
- dashboard.logInDialog(Bundle.EMPTY);
- } else {
- Intent provider_API_command = dashboard.prepareProviderAPICommand();
- provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
- parent_activity.startService(provider_API_command);
- }
+ updatingCertificateUI();
+ dashboard.downloadVpnCertificate();
break;
}
- }
+ } else if (request.equals(Constants.ACTION_UPDATE_EIP_SERVICE)) {
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ if(wants_to_connect)
+ startEipFromScratch();
+ break;
+ case Activity.RESULT_CANCELED:
+ handleNewState(eip_status);
+ break;
+ }
+ }
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
index 07ed6c8f..96b87085 100644
--- a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
@@ -3,22 +3,29 @@ package se.leap.bitmaskclient;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.util.Log;
import se.leap.bitmaskclient.eip.Constants;
public class OnBootReceiver extends BroadcastReceiver {
- // Debug: am broadcast -a android.intent.action.BOOT_COMPLETED
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
- if (!context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE).getString(Provider.KEY, "").isEmpty() && context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE).getBoolean(Dashboard.START_ON_BOOT, false)) {
- Intent dashboard_intent = new Intent(context, Dashboard.class);
- dashboard_intent.setAction(Constants.ACTION_START_EIP);
- dashboard_intent.putExtra(Dashboard.ON_BOOT, true);
- dashboard_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(dashboard_intent);
- }
- }
+ SharedPreferences preferences;
+
+ // Debug: am broadcast -a android.intent.action.BOOT_COMPLETED
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE);
+ boolean provider_configured = !preferences.getString(Provider.KEY, "").isEmpty();
+ boolean start_on_boot = preferences.getBoolean(Dashboard.START_ON_BOOT, false);
+ Log.d("OnBootReceiver", "Provider configured " + String.valueOf(provider_configured));
+ Log.d("OnBootReceiver", "Start on boot " + String.valueOf(start_on_boot));
+ if(provider_configured && start_on_boot) {
+ Intent dashboard_intent = new Intent(context, Dashboard.class);
+ dashboard_intent.setAction(Constants.ACTION_START_EIP);
+ dashboard_intent.putExtra(Dashboard.ON_BOOT, true);
+ dashboard_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(dashboard_intent);
}
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
index 3d3070c8..3b72a486 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -16,38 +16,17 @@
*/
package se.leap.bitmaskclient.eip;
-import android.app.Activity;
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.ResultReceiver;
+import android.app.*;
+import android.content.*;
+import android.os.*;
import android.util.Log;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import de.blinkt.openvpn.LaunchVPN;
-import de.blinkt.openvpn.VpnProfile;
-import de.blinkt.openvpn.core.ProfileManager;
-import se.leap.bitmaskclient.Dashboard;
-import se.leap.bitmaskclient.EipFragment;
-
-import static se.leap.bitmaskclient.eip.Constants.ACTION_CHECK_CERT_VALIDITY;
-import static se.leap.bitmaskclient.eip.Constants.ACTION_IS_EIP_RUNNING;
-import static se.leap.bitmaskclient.eip.Constants.ACTION_START_EIP;
-import static se.leap.bitmaskclient.eip.Constants.ACTION_STOP_EIP;
-import static se.leap.bitmaskclient.eip.Constants.ACTION_UPDATE_EIP_SERVICE;
-import static se.leap.bitmaskclient.eip.Constants.CERTIFICATE;
-import static se.leap.bitmaskclient.eip.Constants.KEY;
-import static se.leap.bitmaskclient.eip.Constants.RECEIVER_TAG;
-import static se.leap.bitmaskclient.eip.Constants.REQUEST_TAG;
+import org.json.*;
+
+import de.blinkt.openvpn.*;
+import se.leap.bitmaskclient.*;
+
+import static se.leap.bitmaskclient.eip.Constants.*;
/**
* EIP is the abstract base class for interacting with and managing the Encrypted
@@ -71,25 +50,24 @@ public final class EIP extends IntentService {
private static SharedPreferences preferences;
private static JSONObject eip_definition;
- private static List<Gateway> gateways = new ArrayList<Gateway>();
- private static ProfileManager profile_manager;
+ private static GatewaysManager gateways_manager = new GatewaysManager();
private static Gateway gateway;
- public EIP(){
- super(TAG);
- }
+ public EIP(){
+ super(TAG);
+ }
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- context = getApplicationContext();
- profile_manager = ProfileManager.getInstance(context);
+ context = getApplicationContext();
+ preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
+ eip_definition = eipDefinitionFromPreferences();
+ if(gateways_manager.isEmpty())
+ gatewaysFromPreferences();
+ }
- preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
- refreshEipDefinition();
- }
-
@Override
protected void onHandleIntent(Intent intent) {
String action = intent.getAction();
@@ -101,7 +79,7 @@ public final class EIP extends IntentService {
stopEIP();
else if (action.equals(ACTION_IS_EIP_RUNNING))
isRunning();
- else if (action.equals(ACTION_UPDATE_EIP_SERVICE))
+ else if (action.equals(ACTION_UPDATE_EIP_SERVICE))
updateEIPService();
else if (action.equals(ACTION_CHECK_CERT_VALIDITY))
checkCertValidity();
@@ -113,17 +91,17 @@ public final class EIP extends IntentService {
* It also sets up early routes.
*/
private void startEIP() {
- if(gateways.isEmpty())
+ if(gateways_manager.isEmpty())
updateEIPService();
earlyRoutes();
- GatewaySelector gateway_selector = new GatewaySelector(gateways);
- gateway = gateway_selector.select();
+ gateway = gateways_manager.select();
if(gateway != null && gateway.getProfile() != null) {
mReceiver = EipFragment.getReceiver();
launchActiveGateway();
- }
- tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK);
+ tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK);
+ } else
+ tellToReceiver(ACTION_START_EIP, Activity.RESULT_CANCELED);
}
/**
@@ -147,7 +125,6 @@ public final class EIP extends IntentService {
private void stopEIP() {
EipStatus eip_status = EipStatus.getInstance();
- Log.d(TAG, "stopEip(): eip is connected? " + eip_status.isConnected());
int result_code = Activity.RESULT_CANCELED;
if(eip_status.isConnected() || eip_status.isConnecting())
result_code = Activity.RESULT_OK;
@@ -173,69 +150,46 @@ public final class EIP extends IntentService {
* TODO Implement API call to refresh eip-service.json from the provider
*/
private void updateEIPService() {
- refreshEipDefinition();
- deleteAllVpnProfiles();
- updateGateways();
+ eip_definition = eipDefinitionFromPreferences();
+ if(eip_definition.length() > 0)
+ updateGateways();
tellToReceiver(ACTION_UPDATE_EIP_SERVICE, Activity.RESULT_OK);
}
- private void refreshEipDefinition() {
+ private JSONObject eipDefinitionFromPreferences() {
+ JSONObject result = new JSONObject();
try {
String eip_definition_string = preferences.getString(KEY, "");
if(!eip_definition_string.isEmpty()) {
- eip_definition = new JSONObject(eip_definition_string);
+ result = new JSONObject(eip_definition_string);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
+ return result;
}
-
- private void deleteAllVpnProfiles() {
- Collection<VpnProfile> profiles = profile_manager.getProfiles();
- profiles.removeAll(profiles);
- gateways.clear();
- }
-
- /**
- * Walk the list of gateways defined in eip-service.json and parse them into
- * Gateway objects.
- * TODO Store the Gateways (as Serializable) in SharedPreferences
- */
+
private void updateGateways(){
- try {
- if(eip_definition != null) {
- JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways");
- for (int i = 0; i < gatewaysDefined.length(); i++) {
- JSONObject gw = gatewaysDefined.getJSONObject(i);
- if (isOpenVpnGateway(gw)) {
- addGateway(new Gateway(eip_definition, context, gw));
- }
- }
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
+ gateways_manager.fromEipServiceJson(eip_definition);
+ gatewaysToPreferences();
}
- private boolean isOpenVpnGateway(JSONObject gateway) {
- try {
- String transport = gateway.getJSONObject("capabilities").getJSONArray("transport").toString();
- return transport.contains("openvpn");
- } catch (JSONException e) {
- return false;
- }
+ private void gatewaysFromPreferences() {
+ String gateways_string = preferences.getString(Gateway.TAG, "");
+ gateways_manager = new GatewaysManager(context, preferences);
+ gateways_manager.addFromString(gateways_string);
+ preferences.edit().remove(Gateway.TAG).apply();
}
- private void addGateway(Gateway gateway) {
- profile_manager.addProfile(gateway.getProfile());
- gateways.add(gateway);
+ private void gatewaysToPreferences() {
+ String gateways_string = gateways_manager.toString();
+ preferences.edit().putString(Gateway.TAG, gateways_string).commit();
}
private void checkCertValidity() {
- VpnCertificateValidator validator = new VpnCertificateValidator();
- int resultCode = validator.isValid(preferences.getString(CERTIFICATE, "")) ?
+ VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(CERTIFICATE, ""));
+ int resultCode = validator.isValid() ?
Activity.RESULT_OK :
Activity.RESULT_CANCELED;
tellToReceiver(ACTION_CHECK_CERT_VALIDITY, resultCode);
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
index 3ee9443c..0d8a2f7b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -17,21 +17,19 @@
package se.leap.bitmaskclient.eip;
import android.app.Activity;
-import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
+import com.google.gson.Gson;
+
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.StringReader;
-import java.util.Collection;
-import java.util.Iterator;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ConfigParser;
-import de.blinkt.openvpn.core.ProfileManager;
import se.leap.bitmaskclient.Dashboard;
/**
@@ -44,48 +42,30 @@ import se.leap.bitmaskclient.Dashboard;
*/
public class Gateway {
- private String TAG = Gateway.class.getSimpleName();
-
+ public final static String TAG = Gateway.class.getSimpleName();
+
+ private JSONObject general_configuration;
+ private JSONObject secrets;
+ private JSONObject gateway;
+
private String mName;
private int timezone;
- private JSONObject general_configuration;
- private Context context;
private VpnProfile mVpnProfile;
- private JSONObject mGateway;
-
/**
* Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
* and create a VpnProfile belonging to it.
- *
- * @param gateway The JSON OpenVPN gateway definition to parse
*/
- protected Gateway(JSONObject eip_definition, Context context, JSONObject gateway){
+ public Gateway(JSONObject eip_definition, JSONObject secrets, JSONObject gateway){
+
+ this.gateway = gateway;
+ this.secrets = secrets;
- mGateway = gateway;
-
- this.context = context;
general_configuration = getGeneralConfiguration(eip_definition);
timezone = getTimezone(eip_definition);
mName = locationAsName(eip_definition);
- // Currently deletes VpnProfile for host, if there already is one, and builds new
- ProfileManager vpl = ProfileManager.getInstance(context);
- Collection<VpnProfile> profiles = vpl.getProfiles();
- for (Iterator<VpnProfile> it = profiles.iterator(); it.hasNext(); ){
- VpnProfile p = it.next();
-
- if ( p.mName.equalsIgnoreCase( mName ) ) {
- it.remove();
- vpl.removeProfile(context, p);
- }
- }
-
mVpnProfile = createVPNProfile();
mVpnProfile.mName = mName;
-
- vpl.addProfile(mVpnProfile);
- vpl.saveProfile(context, mVpnProfile);
- vpl.saveProfileList(context);
}
private JSONObject getGeneralConfiguration(JSONObject eip_definition) {
@@ -110,7 +90,7 @@ public class Gateway {
try {
JSONObject locations = eip_definition.getJSONObject("locations");
- return locations.getJSONObject(mGateway.getString("location"));
+ return locations.getJSONObject(gateway.getString("location"));
} catch (JSONException e) {
return new JSONObject();
}
@@ -123,8 +103,7 @@ public class Gateway {
try {
ConfigParser cp = new ConfigParser();
- SharedPreferences preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE);
- VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(preferences, general_configuration, mGateway);
+ VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(general_configuration, secrets, gateway);
String configuration = vpn_configuration_generator.generate();
cp.parseConfig(new StringReader(configuration));
@@ -153,4 +132,9 @@ public class Gateway {
public int getTimezone() {
return timezone;
}
+
+ @Override
+ public String toString() {
+ return new Gson().toJson(this, Gateway.class);
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
new file mode 100644
index 00000000..b1aa5a2f
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.content.*;
+import android.util.Log;
+
+import com.google.gson.*;
+import com.google.gson.reflect.*;
+
+import org.json.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import de.blinkt.openvpn.*;
+import de.blinkt.openvpn.core.*;
+import se.leap.bitmaskclient.*;
+
+/**
+ * @author parmegv
+ */
+public class GatewaysManager {
+
+ private Context context;
+ private SharedPreferences preferences;
+ private List<Gateway> gateways = new ArrayList<>();
+ private ProfileManager profile_manager;
+ private Type list_type = new TypeToken<ArrayList<Gateway>>() {}.getType();
+
+ public GatewaysManager() {}
+
+ public GatewaysManager(Context context, SharedPreferences preferences) {
+ this.context = context;
+ this.preferences = preferences;
+ profile_manager = ProfileManager.getInstance(context);
+ }
+ public Gateway select() {
+ GatewaySelector gateway_selector = new GatewaySelector(gateways);
+ return gateway_selector.select();
+ }
+
+ public boolean isEmpty() {
+ return gateways.isEmpty();
+ }
+
+ public int size() {
+ return gateways.size();
+ }
+
+ public void addFromString(String gateways) {
+ List<Gateway> gateways_list = new ArrayList<Gateway>();
+ try {
+ gateways_list = new Gson().fromJson(gateways, list_type);
+ } catch(JsonSyntaxException e) {
+ gateways_list.add(new Gson().fromJson(gateways, Gateway.class));
+ }
+
+ if(gateways_list != null) {
+ for (Gateway gateway : gateways_list)
+ removeDuplicatedGateway(gateway);
+ this.gateways.addAll(gateways_list);
+ } else
+ Log.d("GatewaysManager", "No gateways added");
+ }
+
+ @Override
+ public String toString() {
+ return new Gson().toJson(gateways, list_type);
+ }
+
+ public void fromEipServiceJson(JSONObject eip_definition) {
+ try {
+ JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways");
+ for (int i = 0; i < gatewaysDefined.length(); i++) {
+ JSONObject gw = gatewaysDefined.getJSONObject(i);
+ if (isOpenVpnGateway(gw)) {
+ JSONObject secrets = secretsConfiguration();
+ Gateway aux = new Gateway(eip_definition, secrets, gw);
+ if(!containsProfileWithSecrets(aux.getProfile())) {
+ addGateway(aux);
+ }
+ }
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private boolean isOpenVpnGateway(JSONObject gateway) {
+ try {
+ String transport = gateway.getJSONObject("capabilities").getJSONArray("transport").toString();
+ return transport.contains("openvpn");
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ private JSONObject secretsConfiguration() {
+ JSONObject result = new JSONObject();
+ try {
+ result.put(Provider.CA_CERT, preferences.getString(Provider.CA_CERT, ""));
+ result.put(Constants.PRIVATE_KEY, preferences.getString(Constants.PRIVATE_KEY, ""));
+ result.put(Constants.CERTIFICATE, preferences.getString(Constants.CERTIFICATE, ""));
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ private boolean containsProfileWithSecrets(VpnProfile profile) {
+ boolean result = false;
+
+ Collection<VpnProfile> profiles = profile_manager.getProfiles();
+ for(VpnProfile aux : profiles) {
+ result = result || sameConnections(profile.mConnections, aux.mConnections)
+ && profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename)
+ && profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename);
+ }
+ return result;
+ }
+
+ private void addGateway(Gateway gateway) {
+ removeDuplicatedGateway(gateway);
+
+ gateways.add(gateway);
+
+ VpnProfile profile = gateway.getProfile();
+ profile_manager.addProfile(profile);
+ //profile_manager.saveProfile(context, profile);
+ //profile_manager.saveProfileList(context);
+ }
+
+ private void removeDuplicatedGateway(Gateway gateway) {
+ Iterator<Gateway> it = gateways.iterator();
+ List<Gateway> gateways_to_remove = new ArrayList<>();
+ while(it.hasNext()) {
+ Gateway aux = it.next();
+ if(sameConnections(aux.getProfile().mConnections, gateway.getProfile().mConnections)) {
+ gateways_to_remove.add(aux);
+ }
+ }
+ gateways.removeAll(gateways_to_remove);
+ removeDuplicatedProfiles(gateway.getProfile());
+ }
+
+ private void removeDuplicatedProfiles(VpnProfile original) {
+ Collection<VpnProfile> profiles = profile_manager.getProfiles();
+ List<VpnProfile> remove_list = new ArrayList<>();
+ for(VpnProfile aux : profiles) {
+ if (sameConnections(original.mConnections, aux.mConnections))
+ remove_list.add(aux);
+ }
+ for (VpnProfile profile : remove_list)
+ profile_manager.removeProfile(context, profile);
+ }
+
+ private boolean sameConnections(Connection[] c1, Connection[] c2) {
+ int same_connections = 0;
+ for(Connection c1_aux : c1) {
+ for(Connection c2_aux : c2)
+ if(c2_aux.mServerName.equals(c1_aux.mServerName)) {
+ same_connections++;
+ break;
+ }
+ }
+ return c1.length == c2.length && c1.length == same_connections;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
index 6487f6c1..0bbe9db4 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
@@ -28,7 +28,13 @@ import se.leap.bitmaskclient.ConfigHelper;
public class VpnCertificateValidator {
public final static String TAG = VpnCertificateValidator.class.getSimpleName();
- public boolean isValid(String certificate) {
+ private String certificate;
+
+ public VpnCertificateValidator(String certificate) {
+ this.certificate = certificate;
+ }
+
+ public boolean isValid() {
if(!certificate.isEmpty()) {
X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate);
return isValid(certificate_x509);
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
index 0c8e9a04..6f260f55 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -31,15 +31,15 @@ public class VpnConfigGenerator {
private JSONObject general_configuration;
private JSONObject gateway;
-
- private static SharedPreferences preferences;
+ private JSONObject secrets;
+
public final static String TAG = VpnConfigGenerator.class.getSimpleName();
private final String new_line = System.getProperty("line.separator"); // Platform new line
- public VpnConfigGenerator(SharedPreferences preferences, JSONObject general_configuration, JSONObject gateway) {
+ public VpnConfigGenerator(JSONObject general_configuration, JSONObject secrets, JSONObject gateway) {
this.general_configuration = general_configuration;
this.gateway = gateway;
- VpnConfigGenerator.preferences = preferences;
+ this.secrets = secrets;
}
public String generate() {
@@ -79,59 +79,67 @@ public class VpnConfigGenerator {
private String gatewayConfiguration() {
String remotes = "";
- String remote = "ip_address";
- String remote_openvpn_keyword = "remote";
- String ports = "ports";
- String protos = "protocols";
- String capabilities = "capabilities";
+ String ip_address_keyword = "ip_address";
+ String remote_keyword = "remote";
+ String ports_keyword = "ports";
+ String protocol_keyword = "protocols";
+ String capabilities_keyword = "capabilities";
String udp = "udp";
try {
- JSONArray protocolsJSON = gateway.getJSONObject(capabilities).getJSONArray(protos);
- for ( int i=0; i<protocolsJSON.length(); i++ ) {
- String remote_line = remote_openvpn_keyword;
- remote_line += " " + gateway.getString(remote);
- remote_line += " " + gateway.getJSONObject(capabilities).getJSONArray(ports).optString(0);
- remote_line += " " + protocolsJSON.optString(i);
- if(remote_line.endsWith(udp))
- remotes = remotes.replaceFirst(remote_openvpn_keyword, remote_line + new_line + remote_openvpn_keyword);
- else
- remotes += remote_line;
- remotes += new_line;
+ String ip_address = gateway.getString(ip_address_keyword);
+ JSONObject capabilities = gateway.getJSONObject(capabilities_keyword);
+ JSONArray ports = capabilities.getJSONArray(ports_keyword);
+ for (int i=0; i<ports.length(); i++) {
+ String port_specific_remotes = "";
+ int port = ports.getInt(i);
+ JSONArray protocols = capabilities.getJSONArray(protocol_keyword);
+ for ( int j=0; j<protocols.length(); j++ ) {
+ String protocol = protocols.optString(j);
+ String new_remote = remote_keyword + " " + ip_address + " " + port + " " + protocol + new_line;
+
+ port_specific_remotes = protocol.equalsIgnoreCase(udp) ?
+ port_specific_remotes.replaceFirst(remote_keyword, new_remote + new_line + remote_keyword) :
+ new_remote;
+ }
+ remotes += port_specific_remotes;
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- Log.d(TAG, "remotes = " + remotes);
return remotes;
}
private String secretsConfiguration() {
-
- String ca =
- "<ca>"
- + new_line
- + preferences.getString(Provider.CA_CERT, "")
- + new_line
- + "</ca>";
-
- String key =
- "<key>"
- + new_line
- + preferences.getString(Constants.PRIVATE_KEY, "")
- + new_line
- + "</key>";
-
- String openvpn_cert =
- "<cert>"
- + new_line
- + preferences.getString(Constants.CERTIFICATE, "")
- + new_line
- + "</cert>";
+ try {
+ String ca =
+ "<ca>"
+ + new_line
+ + secrets.getString(Provider.CA_CERT)
+ + new_line
+ + "</ca>";
+
+ String key =
+ "<key>"
+ + new_line
+ + secrets.getString(Constants.PRIVATE_KEY)
+ + new_line
+ + "</key>";
+
+ String openvpn_cert =
+ "<cert>"
+ + new_line
+ + secrets.getString(Constants.CERTIFICATE)
+ + new_line
+ + "</cert>";
- return ca + new_line + key + new_line + openvpn_cert;
+ return ca + new_line + key + new_line + openvpn_cert;
+ } catch(JSONException e) {
+ e.printStackTrace();
+ return "";
+ }
}
private String androidCustomizations() {
diff --git a/app/src/main/res/drawable-hdpi/ic_delete_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_delete_grey600_24dp.png
new file mode 100644
index 00000000..b72a9f3c
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_delete_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_delete_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_delete_grey600_24dp.png
new file mode 100644
index 00000000..e757fdb0
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_delete_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_delete_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_delete_grey600_24dp.png
new file mode 100644
index 00000000..c6bb43e8
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_delete_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_delete_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_delete_grey600_24dp.png
new file mode 100644
index 00000000..4886ab1e
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_delete_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_delete_grey600_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_delete_grey600_24dp.png
new file mode 100644
index 00000000..e4e21812
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_delete_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable/white_rect.xml b/app/src/main/res/drawable/white_rect.xml
new file mode 100644
index 00000000..5ead4b9b
--- /dev/null
+++ b/app/src/main/res/drawable/white_rect.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2014 Arne Schwabe
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/background_light" />
+
+</shape> \ No newline at end of file
diff --git a/app/src/main/res/layout-sw600dp-port/log_fragment.xml b/app/src/main/res/layout-sw600dp-port/log_fragment.xml
index 2f5c774d..1fb9fa54 100644
--- a/app/src/main/res/layout-sw600dp-port/log_fragment.xml
+++ b/app/src/main/res/layout-sw600dp-port/log_fragment.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -12,6 +12,8 @@
tools:context=".LogWindow">
<LinearLayout
+ android:background="@drawable/white_rect"
+ android:elevation="1dp"
android:layout_height="wrap_content"
android:layout_width="match_parent">
@@ -33,10 +35,6 @@
android:layout_weight="1"/>
</LinearLayout>
- <Space
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:minHeight="5dp"/>
<ListView
android:id="@android:id/list"
diff --git a/app/src/main/res/layout-sw600dp/log_fragment.xml b/app/src/main/res/layout-sw600dp/log_fragment.xml
index b8997982..0bd3f991 100644
--- a/app/src/main/res/layout-sw600dp/log_fragment.xml
+++ b/app/src/main/res/layout-sw600dp/log_fragment.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -14,6 +14,8 @@
<LinearLayout
+ android:background="@drawable/white_rect"
+ android:elevation="1dp"
android:minWidth="300dp"
android:orientation="vertical"
android:layout_width="wrap_content"
@@ -24,11 +26,6 @@
<include layout="@layout/vpnstatus"/>
</LinearLayout>
- <Space
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:minWidth="5dp"/>
-
<ListView
android:id="@android:id/list"
android:transcriptMode="normal"
diff --git a/app/src/main/res/layout-xlarge/configuration_wizard_activity.xml b/app/src/main/res/layout-xlarge/configuration_wizard_activity.xml
index bb169e00..50bb5d0b 100644
--- a/app/src/main/res/layout-xlarge/configuration_wizard_activity.xml
+++ b/app/src/main/res/layout-xlarge/configuration_wizard_activity.xml
@@ -5,6 +5,12 @@
android:layout_height="match_parent"
tools:context=".ConfigurationWizard" >
+ <ListView
+ android:id="@+id/provider_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false" />
+
<ProgressBar
android:id="@+id/progressbar_configuration_wizard"
style="?android:attr/progressBarStyleHorizontal"
@@ -24,4 +30,4 @@
android:layout_centerHorizontal="true"
android:textColor="@android:color/holo_blue_bright" />
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/app/src/main/res/layout-xlarge/provider_list_item.xml b/app/src/main/res/layout-xlarge/provider_list_item.xml
index ec5db117..728a61be 100644
--- a/app/src/main/res/layout-xlarge/provider_list_item.xml
+++ b/app/src/main/res/layout-xlarge/provider_list_item.xml
@@ -24,7 +24,7 @@
android:mode="twoLine"
>
- <TextView android:id="@android:id/text1"
+ <TextView android:id="@+id/provider_domain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="?android:attr/listPreferredItemPaddingLeft"
@@ -33,11 +33,11 @@
android:textAppearance="?android:attr/textAppearanceListItem"
/>
- <TextView android:id="@android:id/text2"
+ <TextView android:id="@+id/provider_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_below="@android:id/text1"
- android:layout_alignLeft="@android:id/text1"
+ android:layout_below="@id/provider_domain"
+ android:layout_alignLeft="@id/provider_domain"
android:textSize = "24sp"
/>
diff --git a/app/src/main/res/layout/configuration_wizard_activity.xml b/app/src/main/res/layout/configuration_wizard_activity.xml
index a5bca1e9..71cd5314 100644
--- a/app/src/main/res/layout/configuration_wizard_activity.xml
+++ b/app/src/main/res/layout/configuration_wizard_activity.xml
@@ -5,7 +5,7 @@
android:layout_height="match_parent"
tools:context=".ConfigurationWizard" >
- <ListView
+ <ListView
android:id="@+id/provider_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -28,4 +28,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_centerHorizontal="true"
android:textColor="@android:color/holo_blue_bright" />
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/app/src/main/res/layout/log_fragment.xml b/app/src/main/res/layout/log_fragment.xml
index 2cc4759e..4fec942e 100644
--- a/app/src/main/res/layout/log_fragment.xml
+++ b/app/src/main/res/layout/log_fragment.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -11,19 +11,21 @@
android:orientation="vertical">
<LinearLayout
+ android:background="@drawable/white_rect"
+ android:elevation="1dp"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<include layout="@layout/log_silders"/>
- </LinearLayout>
- <TextView
+ <TextView
android:text="@string/speed_waiting"
android:singleLine="true"
android:id="@+id/speed"
tools:ignore="InconsistentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
+ </LinearLayout>
<ListView
android:id="@android:id/list"
diff --git a/app/src/main/res/layout/log_silders.xml b/app/src/main/res/layout/log_silders.xml
index 3fcbd85a..152407f9 100644
--- a/app/src/main/res/layout/log_silders.xml
+++ b/app/src/main/res/layout/log_silders.xml
@@ -3,7 +3,7 @@
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<LinearLayout
diff --git a/app/src/main/res/layout/log_window.xml b/app/src/main/res/layout/log_window.xml
index d7576ca3..fcc7aa7a 100644
--- a/app/src/main/res/layout/log_window.xml
+++ b/app/src/main/res/layout/log_window.xml
@@ -1,6 +1,6 @@
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/app/src/main/res/layout/provider_detail_fragment.xml b/app/src/main/res/layout/provider_detail_fragment.xml
index eb90fad9..3b35bae7 100644
--- a/app/src/main/res/layout/provider_detail_fragment.xml
+++ b/app/src/main/res/layout/provider_detail_fragment.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/provider_detail_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
diff --git a/app/src/main/res/layout/vpnstatus.xml b/app/src/main/res/layout/vpnstatus.xml
index 2fd65b4c..2d77bbab 100644
--- a/app/src/main/res/layout/vpnstatus.xml
+++ b/app/src/main/res/layout/vpnstatus.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<merge xmlns:tools="http://schemas.android.com/tools"
diff --git a/app/src/main/res/menu/logmenu.xml b/app/src/main/res/menu/logmenu.xml
index 52ba4b7d..2df53141 100644
--- a/app/src/main/res/menu/logmenu.xml
+++ b/app/src/main/res/menu/logmenu.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
diff --git a/app/src/main/res/values-ca/strings-icsopenvpn.xml b/app/src/main/res/values-ca/strings-icsopenvpn.xml
index b18766ca..ce7b8919 100755
--- a/app/src/main/res/values-ca/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-ca/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-cs/strings-icsopenvpn.xml b/app/src/main/res/values-cs/strings-icsopenvpn.xml
index 6ae5a3f0..87a6bed1 100755
--- a/app/src/main/res/values-cs/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-cs/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
@@ -328,4 +329,23 @@
<string name="mssfix_value_dialog">Oznámit TCP sezením běžícím skrze tunel, že mají limitovat velikost odesílaných paketů tak, aby poté, co je OpenVPN zabalí, byla výsledná velikost UDP paketu, které OpenVPN posílá menší než tento počet bytů. (výchozí je 1450)</string>
<string name="mssfix_checkbox">Přepsat hodnotu MSS pro TCP obsah</string>
<string name="mssfix_dialogtitle">Nastavit MSS pro TCP obsah</string>
+ <string name="client_behaviour">Chování klienta</string>
+ <string name="clear_external_apps">Zrušit povolené externí aplikace</string>
+ <string name="loading">Načítání&#8230;</string>
+ <string name="allowed_vpn_apps_info">Povolené VPN aplikace: %1$s</string>
+ <string name="disallowed_vpn_apps_info">Zakázané VPN aplikace: %1$s</string>
+ <string name="app_no_longer_exists">Balíček %s již není nainstalován, odstraňuji ho ze seznamu povolených/zakázaných aplikací</string>
+ <string name="vpn_disallow_radio">VPN je používaná pro všechny aplikace, kromě</string>
+ <string name="vpn_allow_radio">VPN je používaná je pro vybrané aplikace</string>
+ <string name="query_delete_remote">Odstranit položku vzdáleného serveru?</string>
+ <string name="keep">Zachovat</string>
+ <string name="delete">Smazat</string>
+ <string name="add_remote">Přidat nové vzdálené místo</string>
+ <string name="remote_random">Použít položky k připojení v náhodném pořadí</string>
+ <string name="remote_no_server_selected">Je potřeba definovat a povolit alespoň jeden vzdálený server.</string>
+ <string name="server_list">Seznam serverů</string>
+ <string name="vpn_allowed_apps">Povolené aplikace</string>
+ <string name="advanced_settings">Pokročilé nastavení</string>
+ <string name="payload_options">Možnosti dat</string>
+ <string name="tls_settings">Nastevení TLS</string>
</resources>
diff --git a/app/src/main/res/values-de/strings-icsopenvpn.xml b/app/src/main/res/values-de/strings-icsopenvpn.xml
index cebb9646..ba667502 100755
--- a/app/src/main/res/values-de/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-de/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
@@ -328,4 +329,26 @@
<string name="mssfix_value_dialog">Ändere TCP-Verbindungen, die über den Tunnel laufen, so dass die resultierende UDP-Paketgröße nach der Enkapsulierung durch OpenVPN auf diesen Wert beschränkt bleibt. (Standardwert ist 1450)</string>
<string name="mssfix_checkbox">Überschreiben des MSS-Wert von TCP-Nutzlast</string>
<string name="mssfix_dialogtitle">Setze MSS von TCP-Nutzlast</string>
+ <string name="client_behaviour">Client-Verhalten</string>
+ <string name="clear_external_apps">Widerrufe Berechtigungen OpenVPN zu steuern</string>
+ <string name="loading">Wird geladen&#8230;</string>
+ <string name="allowed_vpn_apps_info">Apps erlaubt für das VPN: %1$s</string>
+ <string name="disallowed_vpn_apps_info">Apps, die nicht das VPN nutzen: %1$s</string>
+ <string name="app_no_longer_exists">Anwendung mit Paketnamen \'%s\' ist nicht mehr installiert, wird von der Liste der erlaubten/nicht erlaubten VPN Anwendungen gelöscht.</string>
+ <string name="vpn_disallow_radio">VPN für alle Anwendung ausgenommen den ausgewählten</string>
+ <string name="vpn_allow_radio">VPN nur für die ausgewählten Anwendungen</string>
+ <string name="query_delete_remote">Servereintrag entfernen?</string>
+ <string name="keep">Behalten</string>
+ <string name="delete">Entfernen</string>
+ <string name="add_remote">Neuen Server hinzufügen</string>
+ <string name="remote_random">Beim Verbinden Servereinträge in zufälliger Reihenfolge verwenden</string>
+ <string name="remote_no_server_selected">Sie müssen mindestens einen Server definieren und aktivieren.</string>
+ <string name="server_list">Serverliste</string>
+ <string name="vpn_allowed_apps">Erlaubte Anwendungen</string>
+ <string name="advanced_settings">Erweiterte Einstellungen</string>
+ <string name="payload_options">Nutzlast-Optionen</string>
+ <string name="tls_settings">TLS-Einstellungen</string>
+ <string name="no_remote_defined">Keine Server definiert</string>
+ <string name="duplicate_vpn">VPN Profil duplizieren</string>
+ <string name="duplicate_profile_title">VPN Profil duplizieren: %s</string>
</resources>
diff --git a/app/src/main/res/values-es/strings-icsopenvpn.xml b/app/src/main/res/values-es/strings-icsopenvpn.xml
index 92995c6f..4cbb152d 100755
--- a/app/src/main/res/values-es/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-es/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
@@ -147,7 +148,7 @@
<string name="import_warning_custom_options">Su configuración tiene algunas opciones de configuración que no están establecidas en la interfaz de usuario . Estas opciones fueron agregadas como opciones de configuración personalizadas. A continuación se muestra la configuración personalizada:</string>
<string name="import_done">Se termino de leer el archivo de configuración.</string>
<string name="nobind_summary">No enlazar con el puerto y la dirección local</string>
- <string name="no_bind">Ningún enlace local</string>
+ <string name="no_bind">No enlazar localmente</string>
<string name="import_configuration_file">Importar archivo de configuracion</string>
<string name="faq_security_title">Consideraciones de seguridad</string>
<string name="faq_security">"Como OpenVPN es sensible a la seguridad, son razonables algunas notas acerca de seguridad. Todos los datos en la tarjeta SD son inherentemente inseguros. Cualquier aplicación puede leerla (por ejemplo, esta aplicación no requiere ningún permiso especial sobre la tarjeta SD). Los datos de esta aplicación sólo pueden ser leidos por la misma aplicación. Al utilizar la opción importar para el certificado de la CA/certificado/llave, en la ventana de diálogo para selección de archivos, los datos se almacenan en el perfil de la VPN. Los perfiles de VPN sólo son accesibles por esta aplicación. (No olvide después borrar las copias de la tarjeta SD). Aunque sólo sea accesible por esta aplicación, los datos aún están sin encriptar. Al acceder el dispositivo portátil como root u otro medio, es posible recuperar estos datos. Las contraseñas guardadas son almacenadas también en texto plano. Para archivos pkcs12 es muy recomendable que los importe al repositorio de llaves de Android."</string>
@@ -173,7 +174,8 @@
<string name="generated_config_summary">Muestra el archivo de configuración OpenVPN generado</string>
<string name="edit_profile_title">Editando \"%s\"</string>
<string name="building_configration">Construyendo configuracion&#8230;</string>
- <string name="netchange_summary">Turning this option on will force a reconnect if the network state is changed (e.g. WiFi to/from mobile)</string>
+ <string name="netchange_summary">Activando esta opción forzara una reconexión si el estado de la red es cambiado (Ej. De/hacia WIFi
+hacia/de Móvil)</string>
<string name="netchange">Reconectar en cambio de red</string>
<string name="netstatus">Estado de la red: %s</string>
<string name="extracahint">El certificado de la CA usualmente es recuperado del almacén de claves de Android. Especifique un certificado diferente si obtiene errores de verificación de certificado.</string>
@@ -185,7 +187,7 @@
<string name="error_rsa_sign">Error al firmar con la llave del almacén de llaves de Android %1$s: %2$s</string>
<string name="faq_system_dialogs">El aviso de conectividad VPN que esta aplicación puede interceptar todo el trafico esta impuesta por el sistema para evitar abusos de la API VPNService.\nLa notificación de conectividad (El símbolo de llave) también esta impuesta por el sistema Android para notificar una conexión VPN en curso. En algunas imágenes, esta notificación también emite un sonido.\nAndroid ha introducido estos diálogos de sistema para su seguridad e se ha asegurado que no pueden ser evitados. (En algunas imágenes, esto incluye la notificación sonora)</string>
<string name="faq_system_dialogs_title">Advertencia de conexión y sonido de notificación</string>
- <string name="translationby">Traducción al español por José Luis Bandala Perez&lt;luis.449bp@gmail.com&gt;</string>
+ <string name="translationby">Traducción al español por José Luis Bandala Pérez&lt;luis.449bp@gmail.com&gt;</string>
<string name="ipdns">IP y DNS</string>
<string name="basic">Básico</string>
<string name="routing">Enrutamiento</string>
@@ -274,7 +276,7 @@
<string name="rdn">RDN (nombre comun)</string>
<string name="rdn_prefix">Prefijo RDN</string>
<string name="tls_remote_deprecated">tls-remote (OBSOLETO)</string>
- <string name="help_translate">Tu puedes ayudar traduciendo visitando http://crowdin.net/project/ics-openvpn/invite</string>
+ <string name="help_translate">Tu puedes ayudar a traducir visitando http://crowdin.net/project/ics-openvpn/invite</string>
<string name="prompt">%1$s intentos de controlar %2$s</string>
<string name="remote_warning">Al proceder, le estás proporcionando permiso a la aplicación para controlar completamente OpenVPN for Android e interceptar todo el tráfico de la red.<b>NO aceptar a menos que confíes en la aplicación.</b> De otro modo, corres el riesgo de que tus datos se vean comprometidos por software malicioso.\"</string>
<string name="remote_trust">Confío en esta aplicación.</string>
@@ -317,7 +319,7 @@
<string name="faq_system_dialog_xposed">Si ha rooteado su dispositivo Android, puede instalar el &lt;a href=\"http://xposed.info/\"&gt;framework Xposed&lt;/a&gt; y añadir el &lt;a href=\"http://repo.xposed.info/module/de.blinkt.vpndialogxposed\"&gt;módulo de confirmación de Diálogo VPN&lt;/a&gt; bajo su propio riesgo\"</string>
<string name="full_licenses">Licencias completas</string>
<string name="blocklocal_summary">Las redes conectadas directamente a los interfaces locales no serán enrutadas a través de la VPN. Al desmarcar esta opción, todo el tráfico previsto para las redes locales será redirigido a la VPN.</string>
- <string name="blocklocal_title">Evitar la VPN para las redes locales</string>
+ <string name="blocklocal_title">Saltarse la VPN en redes locales</string>
<string name="userpw_file">Archivo de Usuario/Contraseña</string>
<string name="imported_from_file">[Importado de:%s]</string>
<string name="files_missing_hint">Algunos archivos no se pudo encontrar. Por favor, seleccione los archivos que desea importar el perfil:</string>
@@ -325,6 +327,29 @@
<string name="import_log">Importar registros:</string>
<string name="ip_looks_like_subnet">Topología de VPN \"%3$s\" especificado pero ifconfig %1$s %2$s se parece más a una dirección IP con una máscara de red. Asumiendo una topología de \"subred\".</string>
<string name="mssfix_invalid_value">El valor de mssfix debe ser un número entero entre 0 y 9000</string>
+ <string name="mssfix_value_dialog">Anunciar a las sesiones TCP ejecutandose sobre el túnel que deben limitar su tamaño de paquetes enviados de tal manera que después de que OpenVPN los hay encapsulado, el tamaño del paquete UDP resultante enviado a su par no exceda este numero de bytes. (Por defecto es 1450)</string>
<string name="mssfix_checkbox">Reemplazar el valor MSS de la carga TCP</string>
<string name="mssfix_dialogtitle">Establecer MSS de la carga TCP</string>
+ <string name="client_behaviour">Comportamiento del cliente</string>
+ <string name="clear_external_apps">Borrar aplicaciones externas permitidas</string>
+ <string name="loading">Cargando&#8230;</string>
+ <string name="allowed_vpn_apps_info">Aplicaciones VPN permitidas: %1$s</string>
+ <string name="disallowed_vpn_apps_info">Aplicaciones VPN no permitidas: %1$s</string>
+ <string name="app_no_longer_exists">El paquete %s ya no está instalado, sacándolo de la lista de apps permitidas/no permitidas</string>
+ <string name="vpn_disallow_radio">La VPN es usada por todas las aplicaciones, excepto por las seleccionadas</string>
+ <string name="vpn_allow_radio">La VPN es utilizada únicamente por las aplicaciones seleccionadas</string>
+ <string name="query_delete_remote">¿Eliminar entrada del servidor remoto?</string>
+ <string name="keep">Mantener</string>
+ <string name="delete">Eliminar</string>
+ <string name="add_remote">Añadir nuevo acceso remoto</string>
+ <string name="remote_random">Utiliza las entradas de conexión en orden aleatorio en la conexión</string>
+ <string name="remote_no_server_selected">Es necesario definir y habilitar al menos un servidor remoto.</string>
+ <string name="server_list">Lista de servidores</string>
+ <string name="vpn_allowed_apps">Aplicaciones permitidas</string>
+ <string name="advanced_settings">Opciones Avanzadas</string>
+ <string name="payload_options">Opciones de carga útil</string>
+ <string name="tls_settings">Configuración TLS</string>
+ <string name="no_remote_defined">Servidor remoto no definido</string>
+ <string name="duplicate_vpn">Duplicar perfil VPN</string>
+ <string name="duplicate_profile_title">Duplicando Perfil: %s</string>
</resources>
diff --git a/app/src/main/res/values-et/strings-icsopenvpn.xml b/app/src/main/res/values-et/strings-icsopenvpn.xml
index 7761726e..c5174284 100755
--- a/app/src/main/res/values-et/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-et/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
@@ -328,4 +329,26 @@
<string name="mssfix_value_dialog">Informeeri tunneldatud TCP sessioone et nad piiraksid saadetavate pakettide suuruse nii, et peale OpenVPN kapseldatud paketi partnerile saatmist ei oleks saadud UDP pakett suurem kui ette antud baitide arv. (vaikeväärtus on 1450)</string>
<string name="mssfix_checkbox">Ignoreeri TCP lasti MSS väärtust</string>
<string name="mssfix_dialogtitle">Sea TCP lasti MSS väärtus</string>
+ <string name="client_behaviour">Kliendi toimimine</string>
+ <string name="clear_external_apps">Nulli lubatud välised programmid</string>
+ <string name="loading">Laadimine&#8230;</string>
+ <string name="allowed_vpn_apps_info">Lubatud VPN programmid: %1$s</string>
+ <string name="disallowed_vpn_apps_info">Keelatud VPN programmid: %1$s</string>
+ <string name="app_no_longer_exists">Programm %s on seadmest eemaldatud, see kustutatakse ka lubatud/keelatud programmide nimistust</string>
+ <string name="vpn_disallow_radio">VPN on kõigi, välja arvatud märgitud, programmide puhul kasutuses</string>
+ <string name="vpn_allow_radio">VPN on kasutuses ainult märgitud programmide puhul</string>
+ <string name="query_delete_remote">Kas eemaldada kaugserveri kirje?</string>
+ <string name="keep">Säilita</string>
+ <string name="delete">Eemalda</string>
+ <string name="add_remote">Uue kaugserveri lisamine</string>
+ <string name="remote_random">Ühendumisel kasuta ühenduskirjeid juhuslikus järjekorras</string>
+ <string name="remote_no_server_selected">Peate määrama vähemalt ühe kaugserveri.</string>
+ <string name="server_list">Serverite Nimistu</string>
+ <string name="vpn_allowed_apps">Lubatud Programmid</string>
+ <string name="advanced_settings">Täpsemad seaded</string>
+ <string name="payload_options">Nimikoormuse valikud</string>
+ <string name="tls_settings">TLS Seaded</string>
+ <string name="no_remote_defined">Kaugserverid määramata</string>
+ <string name="duplicate_vpn">Dubleeritud VPN profiil</string>
+ <string name="duplicate_profile_title">Duplitseeritakse profiili: %s</string>
</resources>
diff --git a/app/src/main/res/values-fr/strings-icsopenvpn.xml b/app/src/main/res/values-fr/strings-icsopenvpn.xml
index 15bc7aa4..657646c8 100755
--- a/app/src/main/res/values-fr/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-fr/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-hu/strings-icsopenvpn.xml b/app/src/main/res/values-hu/strings-icsopenvpn.xml
index 504ab893..2efdcf5f 100755
--- a/app/src/main/res/values-hu/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-hu/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-in/strings-icsopenvpn.xml b/app/src/main/res/values-in/strings-icsopenvpn.xml
index c111cbb5..3dac7c71 100755
--- a/app/src/main/res/values-in/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-in/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-it/strings-icsopenvpn.xml b/app/src/main/res/values-it/strings-icsopenvpn.xml
index 29d48904..36198678 100755
--- a/app/src/main/res/values-it/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-it/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
@@ -325,4 +326,16 @@ Effettuata la lettura del file di configurazione</string>
<string name="files_missing_hint">Alcuni file non possono essere trovati. Si prega di selezionare i file da importare nel profilo:</string>
<string name="openvpn_is_no_free_vpn">Per utilizzare questa applicazione è necessario un provider VPN/gateway VPN che supportino OpenVPN (spesso forniti dal datore di lavoro). Vai a http://community.openvpn.net/ per ulteriori informazioni su OpenVPN e come configurare il proprio server OpenVPN.</string>
<string name="import_log">Registro importazione:</string>
+ <string name="mssfix_checkbox">Sovrascrivi il valore del MSS nel payload TCP</string>
+ <string name="mssfix_dialogtitle">Setta il valore del MSS nel payload TCP</string>
+ <string name="client_behaviour">Comportamento Client</string>
+ <string name="loading">Caricando&#8230;</string>
+ <string name="keep">Mantieni</string>
+ <string name="delete">Elimina</string>
+ <string name="server_list">Lista Server</string>
+ <string name="advanced_settings">Impostazioni Avanzate</string>
+ <string name="payload_options">Opzioni Payload</string>
+ <string name="tls_settings">Impostazioni TLS</string>
+ <string name="duplicate_vpn">Profilo VPN duplicato</string>
+ <string name="duplicate_profile_title">Duplicazione del profilo: %s</string>
</resources>
diff --git a/app/src/main/res/values-ja/strings-icsopenvpn.xml b/app/src/main/res/values-ja/strings-icsopenvpn.xml
index 79474f2d..792e6200 100755
--- a/app/src/main/res/values-ja/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-ja/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-ko/strings-icsopenvpn.xml b/app/src/main/res/values-ko/strings-icsopenvpn.xml
index b05e4f51..9266a36c 100755
--- a/app/src/main/res/values-ko/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-ko/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-nl/strings-icsopenvpn.xml b/app/src/main/res/values-nl/strings-icsopenvpn.xml
index 2a000195..b486706e 100755
--- a/app/src/main/res/values-nl/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-nl/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-no/strings-icsopenvpn.xml b/app/src/main/res/values-no/strings-icsopenvpn.xml
index 66391eb5..501b18df 100755
--- a/app/src/main/res/values-no/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-no/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-pl/strings-icsopenvpn.xml b/app/src/main/res/values-pl/strings-icsopenvpn.xml
index 495eb00e..052b0135 100755
--- a/app/src/main/res/values-pl/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-pl/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-pt/strings-icsopenvpn.xml b/app/src/main/res/values-pt/strings-icsopenvpn.xml
index d0058c68..ec7d2534 100755
--- a/app/src/main/res/values-pt/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-pt/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-ro/strings-icsopenvpn.xml b/app/src/main/res/values-ro/strings-icsopenvpn.xml
index ef4e3a75..3821f964 100755
--- a/app/src/main/res/values-ro/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-ro/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-ru/strings-icsopenvpn.xml b/app/src/main/res/values-ru/strings-icsopenvpn.xml
index e2bc930c..5db415df 100755
--- a/app/src/main/res/values-ru/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-ru/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-sv/strings-icsopenvpn.xml b/app/src/main/res/values-sv/strings-icsopenvpn.xml
index 9b974522..151742a5 100755
--- a/app/src/main/res/values-sv/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-sv/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml
index b83bb856..94a120d1 100644
--- a/app/src/main/res/values-sw600dp/dimens.xml
+++ b/app/src/main/res/values-sw600dp/dimens.xml
@@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2014 Arne Schwabe
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ -->
+
<resources>
<bool name="logSildersAlwaysVisible">true</bool>
diff --git a/app/src/main/res/values-sw600dp/styles.xml b/app/src/main/res/values-sw600dp/styles.xml
index de0bb55d..c320388d 100644
--- a/app/src/main/res/values-sw600dp/styles.xml
+++ b/app/src/main/res/values-sw600dp/styles.xml
@@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2014 Arne Schwabe
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ -->
+
<resources>
<dimen name="stdpadding">16dp</dimen>
diff --git a/app/src/main/res/values-tr/strings-icsopenvpn.xml b/app/src/main/res/values-tr/strings-icsopenvpn.xml
index 90ad068d..ea81d3ed 100755
--- a/app/src/main/res/values-tr/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-tr/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
@@ -11,7 +12,7 @@
<string name="port">Sunucu Portu:</string>
<string name="location">Konum</string>
<string name="cant_read_folder">Dizin Okunamıyor</string>
- <string name="select">Seçin</string>
+ <string name="select">Seç</string>
<string name="cancel">İptal</string>
<string name="no_data">Veri Yok</string>
<string name="useLZO">LZO sıkıştırma</string>
@@ -316,4 +317,20 @@
<string name="userpw_file">Kullanıcı adı / Şifre dosyası</string>
<string name="imported_from_file">[Buradan içeri aktar: %s]</string>
<string name="import_log">Kaydı içe aktar:</string>
+ <string name="client_behaviour">İstemci davranışı</string>
+ <string name="loading">Yükleniyor&#8230;</string>
+ <string name="allowed_vpn_apps_info">Izin verilen VPN uygulamaları: %1$s</string>
+ <string name="disallowed_vpn_apps_info">İzin verilmeyen VPN uygulamaları: %1$s</string>
+ <string name="app_no_longer_exists">%s adlı paket artık yüklü değil, izin listesinden çıkarılıyor</string>
+ <string name="vpn_disallow_radio">Seçilen uygulamar dışındaki tüm uygulamalar için VPN kullanılır</string>
+ <string name="vpn_allow_radio">Sadece seçilen uygulamar için VPN kullanılır</string>
+ <string name="query_delete_remote">Uzak sunucu girişi kaldırılsın mı?</string>
+ <string name="keep">Sakla</string>
+ <string name="delete">Sil</string>
+ <string name="add_remote">Yenı uzak sunucu ekle</string>
+ <string name="remote_no_server_selected">En az bir uzak sunucu tanımlamalı ve etkinleştirmelisiniz.</string>
+ <string name="server_list">Sunucu Listesi</string>
+ <string name="vpn_allowed_apps">İzin verilen uygulamalar</string>
+ <string name="advanced_settings">Gelişmiş Ayarlar</string>
+ <string name="tls_settings">TLS Ayarları</string>
</resources>
diff --git a/app/src/main/res/values-uk/strings-icsopenvpn.xml b/app/src/main/res/values-uk/strings-icsopenvpn.xml
index 92637b74..dbbc65a0 100755
--- a/app/src/main/res/values-uk/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-uk/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values-v21/colours.xml b/app/src/main/res/values-v21/colours.xml
new file mode 100644
index 00000000..1fedf7b9
--- /dev/null
+++ b/app/src/main/res/values-v21/colours.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2014 Arne Schwabe
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ -->
+
+<resources>
+ <color name="switchbar">#5C6BC0</color> <!-- 400-->
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-v21/refs.xml b/app/src/main/res/values-v21/refs.xml
index 0d5d271a..d29d04ed 100644
--- a/app/src/main/res/values-v21/refs.xml
+++ b/app/src/main/res/values-v21/refs.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<resources>
@@ -9,5 +9,7 @@
<drawable name="ic_menu_share">@drawable/ic_share_white_24dp </drawable>
<drawable name="ic_menu_view">@drawable/ic_filter_list_white_24dp</drawable>
<drawable name="ic_menu_delete">@drawable/ic_delete_white_24dp</drawable>
+ <drawable name="ic_menu_delete_grey">@drawable/ic_delete_grey600_24dp</drawable>
+
<drawable name="ic_menu_edit">@drawable/ic_edit_white_24dp</drawable>
</resources>
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
index 892b6cb0..4379dd6d 100644
--- a/app/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -1,13 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<resources>
+ <style name="blinkt.baseTheme" parent="android:Theme.Material.Light.DarkActionBar" />
+
<!-- http://www.google.de/design/spec/style/color.html#color-color-palette -->
- <style name="appstyle" parent="android:Theme.Material.Light.DarkActionBar">
+ <style name="blinkt" parent="blinkt.common">
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorAccent">@color/accent</item>
diff --git a/app/src/main/res/values-zh-rCN/strings-icsopenvpn.xml b/app/src/main/res/values-zh-rCN/strings-icsopenvpn.xml
index 93e0cbb1..4a3da0f4 100755
--- a/app/src/main/res/values-zh-rCN/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-zh-rCN/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
@@ -88,7 +89,9 @@
<string name="default_route_summary">重定向所有流量到VPN</string>
<string name="use_default_title">使用默认路由</string>
<string name="custom_route_message">输入自定义路由。输入 CIDR 格式地址。</string>
+ <string name="custom_route_message_excluded">路线不应该被路由通过VPN。使用相同的语法包括路线。</string>
<string name="custom_routes_title">自定义路由</string>
+ <string name="custom_routes_title_excluded">排查网络</string>
<string name="log_verbosity_level">日志详细级别</string>
<string name="float_summary">允许来自任何 IP 的认证数据包</string>
<string name="float_title">允许浮服务器</string>
@@ -102,7 +105,9 @@
<string name="last_openvpn_tun_config">正在打开 tun 设备:</string>
<string name="local_ip_info">本地 IPv4: %1$s/%2$d IPv6: %3$s MTU: %4$d</string>
<string name="dns_server_info">DNS 服务器: %1$s, 域名: %2$s</string>
+ <string name="routes_info_incl">路线: %1$s %2$s</string>
<string name="routes_info_excl">排除的路由: %1$s %2$s</string>
+ <string name="routes_debug">VpnService安装路线 : %1$s %2$s</string>
<string name="ip_not_cidr">已获得接口信息 %1$s 以及 %2$s,将第二个地址作为远程地址。使用 /32 作为本地掩码。OpenVPN 给出的模式是 \"%3$s\"。</string>
<string name="route_not_cidr">无法将 %1$s 和 %2$s 作为 CIDR 形式的路由,将使用 /32 的子网掩码。</string>
<string name="route_not_netip">纠正路由 %1$s/%2$s 为 %3$s/%2$s</string>
@@ -217,6 +222,7 @@
<string name="vpn_import_hint">使用 &lt;img src=\"ic_menu_archive\"/&gt; 图标导入存储卡中已有的配置文件(.ovpn 或 .conf)。</string>
<string name="faq_hint">请确保阅读常见问题解答。这是一个快速向导。</string>
<string name="faq_routing_title">路由/接口配置</string>
+ <string name="faq_routing">的路由和接口配置不通过传统的ifconfig /路由的命令而将用VPNService API来完成。这导致了比在其它操作系统不同的路由配置。 \ n此VPN隧道的配置包括IP地址,并应被指定到该接口的网络。特别是,没有同行伙伴地址或网关地址是必要的或需要。特殊的路由到达VPN服务器不需要任何(例如,当使用重定向网关添加)。导入配置时,应用程序会因此忽略这些设置。该应用程序可确保与连接到服务器没有通过VPN隧道路由的VPNService API。\ n此VPNService API不允许指定的网络,不应该通过VPN路由。作为一种变通方法的应用程序会检测网络,不应该被路由到隧道(如路由XXXX YYYY net_gateway),并计算一组路由排除这种路线效仿其他平台的行为。日志窗口显示VPNService的在建立连接的配置\ nBehind的场景:安卓4.4+确实使用策略路由。使用route / ifconfig命令不会显示已安装的路径。而使用IP规则,iptables的-t轧-L</string>
<string name="persisttun_summary">当 OpenVPN 重连时,也一直使用 VPN 连接。</string>
<string name="persistent_tun_title">保持 tun 通道</string>
<string name="openvpn_log">OpenVPN 日志</string>
diff --git a/app/src/main/res/values-zh-rTW/strings-icsopenvpn.xml b/app/src/main/res/values-zh-rTW/strings-icsopenvpn.xml
index bd155362..36360cf1 100755
--- a/app/src/main/res/values-zh-rTW/strings-icsopenvpn.xml
+++ b/app/src/main/res/values-zh-rTW/strings-icsopenvpn.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--Generated by crowdin.com-->
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
+
+<!--Generated by crowdin.com-->
<!-- Generated by crowdin.net -->
<resources>
diff --git a/app/src/main/res/values/colours.xml b/app/src/main/res/values/colours.xml
index 89fb41dd..f27167f3 100644
--- a/app/src/main/res/values/colours.xml
+++ b/app/src/main/res/values/colours.xml
@@ -1,13 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<resources>
<!-- Indigo -->
<!-- OpenVPN colours #203155, #C66D0D -->
- <color name="primary">#3F51B5</color>
- <color name="primary_dark">#303F9F</color>
- <color name="accent">#FFA726</color>
+ <color name="primary">#3F51B5</color> <!--500-->
+ <color name="primary_dark">#303F9F</color> <!--700-->
+ <color name="accent">#FFA726</color> <!-- Orange 400 -->
+ <color name="gelb">#ffff00</color>
+ <color name="rot">#ff0000</color>
+
+ <color name="switchbar">@android:color/darker_gray</color>
+
+
+ <color name="background_tab_pressed">#1AFFFFFF</color>
+
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 9a53fe4c..727f5a78 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<resources>
@@ -9,4 +9,12 @@
<dimen name="stdpadding">8dp</dimen>
<bool name="logSildersAlwaysVisible">false</bool>
+ <dimen name="diameter">48dp</dimen>
+ <dimen name="elevation_low">1dp</dimen>
+ <dimen name="elevation_high">4dp</dimen>
+ <dimen name="add_button_margin">16dp</dimen>
+ <dimen name="add_button_margin_topfab">96dp</dimen>
+ <dimen name="round_button_diameter">56dp</dimen>
+ <dimen name="switchbar_pad">16dp</dimen>
+ <dimen name="vpn_setting_padding">16dp</dimen>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/refs.xml b/app/src/main/res/values/refs.xml
index 5e7f5e14..4d97e380 100644
--- a/app/src/main/res/values/refs.xml
+++ b/app/src/main/res/values/refs.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<resources>
@@ -11,5 +11,4 @@
<drawable name="ic_menu_view">@android:drawable/ic_menu_view</drawable>
<drawable name="ic_menu_delete">@android:drawable/ic_menu_delete</drawable>
<drawable name="ic_menu_edit">@android:drawable/ic_menu_edit</drawable>
-
</resources>
diff --git a/app/src/main/res/values/strings-icsopenvpn.xml b/app/src/main/res/values/strings-icsopenvpn.xml
index 15bf8142..307d3a42 100755
--- a/app/src/main/res/values/strings-icsopenvpn.xml
+++ b/app/src/main/res/values/strings-icsopenvpn.xml
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
- --> <!-- Generated by crowdin.net -->
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ -->
<resources>
-
<string name="address">Server Address:</string>
<string name="port">Server Port:</string>
<string name="location">Location</string>
@@ -174,14 +173,14 @@
<string name="netchange_summary">Turning this option on will force a reconnect if the network state is changed (e.g. WiFi to/from mobile)</string>
<string name="netchange">Reconnect on network change</string>
<string name="netstatus">Network Status: %s</string>
- <string name="extracahint">The CA cert is usually returned from the Android Keystore. Specify a separate certificate if you get certificate verification errors.</string>
+ <string name="extracahint">The CA cert is usually returned from the Android keystore. Specify a separate certificate if you get certificate verification errors.</string>
<string name="select_file">Select</string>
- <string name="keychain_nocacert">No CA Certificate returned while reading from Android keystore. Auhtentication will probably fail.</string>
+ <string name="keychain_nocacert">No CA Certificate returned while reading from Android keystore. Authentication will probably fail.</string>
<string name="show_log_summary">Shows the log window on connect. The log window can always be accessed from the notification status.</string>
<string name="show_log_window">Show log window</string>
<string name="mobile_info">Running on %1$s (%2$s) %3$s, Android API %4$d</string>
<string name="error_rsa_sign">Error signing with Android keystore key %1$s: %2$s</string>
- <string name="faq_system_dialogs">The VPN connection warning telling you that this app can intercept all traffic is imposed by the system to prevent abuse of the VPNService API.\nThe VPN connection notification (The key symbol) is also imposed by the Android system to signal an ongoing VPN connection. On some images this notification plays a sound.\nAndroid introduced these system dialogs for your own safety and made sure that they cannot be circumenvented. (On some images this unfortunely includes a notifciation sound)</string>
+ <string name="faq_system_dialogs">The VPN connection warning telling you that this app can intercept all traffic is imposed by the system to prevent abuse of the VPNService API.\nThe VPN connection notification (The key symbol) is also imposed by the Android system to signal an ongoing VPN connection. On some images this notification plays a sound.\nAndroid introduced these system dialogs for your own safety and made sure that they cannot be circumvented. (On some images this unfortunately includes a notification sound)</string>
<string name="faq_system_dialogs_title">Connection warning and notification sound</string>
<string name="translationby">English translation by Arne Schwabe&lt;arne@rfc2549.org&gt;</string>
<string name="ipdns">IP and DNS</string>
@@ -314,7 +313,7 @@
<string name="unhandled_exception_context">%3$s: %1$s\n\n%2$s</string>
<string name="faq_system_dialog_xposed">If you have rooted your Android device you can install the &lt;a href=\"http://xposed.info/\"&gt;Xposed framework&lt;/a&gt; and a the &lt;a href=\"http://repo.xposed.info/module/de.blinkt.vpndialogxposed\"&gt;VPN Dialog confirm module&lt;/a&gt; at your own risk"</string>
<string name="full_licenses">Full licenses</string>
- <string name="blocklocal_summary">Networks directly connected to the local interfaces will not be routed over the VPN. Unchecking this option will redirect all traffic indented for local networks to VPN.</string>
+ <string name="blocklocal_summary">Networks directly connected to the local interfaces will not be routed over the VPN. Deselecting this option will redirect all traffic indented for local networks to the VPN.</string>
<string name="blocklocal_title">Bypass VPN for local networks</string>
<string name="userpw_file">Username/Password file</string>
<string name="imported_from_file">[Imported from: %s]</string>
@@ -322,11 +321,34 @@
<string name="openvpn_is_no_free_vpn">To use this app you need a VPN provider/VPN gateway supporting OpenVPN (often provided by your employer). Check out http://community.openvpn.net/ for more information on OpenVPN and how to setup your own OpenVPN server.</string>
<string name="import_log">Import log:</string>
<string name="ip_looks_like_subnet">Vpn topology \"%3$s\" specified but ifconfig %1$s %2$s looks more like an IP address with a network mask. Assuming \"subnet\" topology.</string>
- <string name="mssfix_invalid_value">mssfix value has to be a integer between 0 and 9000</string>
+ <string name="mssfix_invalid_value">The MSS override value has to be a integer between 0 and 9000</string>
<string name="mssfix_value_dialog">Announce to TCP sessions running over the tunnel that they should limit their send packet sizes such that after OpenVPN has encapsulated them, the resulting UDP packet size that OpenVPN sends to its peer will not exceed this number of bytes. (default is 1450)</string>
<string name="mssfix_checkbox">Override MSS value of TCP payload</string>
<string name="mssfix_dialogtitle">Set MSS of TCP payload</string>
<string name="client_behaviour">Client behaviour</string>
<string name="clear_external_apps">Clear allowed external apps</string>
+ <string name="loading">Loading…</string>
+ <string name="allowed_vpn_apps_info">Allowed VPN apps: %1$s</string>
+ <string name="disallowed_vpn_apps_info">Disallowed VPN apps: %1$s</string>
+ <string name="app_no_longer_exists">Package %s is no longer installed, removing it from app allow/disallow list</string>
+ <string name="vpn_disallow_radio">VPN is used for all apps but exclude selected</string>
+ <string name="vpn_allow_radio">VPN is used for only for selected apps</string>
+ <string name="query_delete_remote">Remove remote server entry?</string>
+ <string name="keep">Keep</string>
+ <string name="delete">Delete</string>
+ <string name="add_remote">Add new remote</string>
+ <string name="remote_random">Use connection entries in random order on connect</string>
+ <string name="remote_no_server_selected">You need to define and enable at least one remote server.</string>
+ <string name="server_list">Server List</string>
+ <string name="vpn_allowed_apps">Allowed Apps</string>
+ <string name="advanced_settings">Advanced Settings</string>
+ <string name="payload_options">Payload options</string>
+ <string name="tls_settings">TLS Settings</string>
+ <string name="no_remote_defined">No remote defined</string>
+ <string name="duplicate_vpn">Duplicate VPN profile</string>
+ <string name="duplicate_profile_title">Duplicating profile: %s</string>
+ <string name="show_log">Show log</string>
+ <string name="faq_android_clients">Multiple OpenVPN clients for Android exist. The most common ones are OpenVPN for Android (this client), OpenVPN Connect and OpenVPN Settings.&lt;p&gt;The clients can be grouped into two groups: OpenVPN for Android and OpenVPN Connect use the official VPNService API (Android 4.0+) and require no root and OpenVPN Settings which uses root.&lt;p&gt;OpenVPN for Android is an open source client and developed by Arne Schwabe. It is targeted at more advanced users and offers many settings and the ability to import profiles from files and to configure/change profiles inside the app. The client is based on the community version of OpenVPN. It is based on the OpenVPN 2.x source code. This client can be seen as the semi officially client of the community. &lt;p&gt;OpenVPN Connect is non open source client that is developed by OpenVPN Technologies, Inc. The client is indented to be general use client and moree targeted at the average user and allows the import of OpenVPN profiles. This client is based on the OpenVPN C++ reimplementation of the OpenVPN protocol (This was required to allow OpenVPN Technologies, Inc to publish an iOS OpenVPN app). This client is the official client of the OpenVPN technologies &lt;p&gt; OpenVPN Settings is the oldest of the clients and also a UI for the open source OpenVPN. In contrast to OpenVPN for Android it requires root and does not use the VPNService API. It does not depend on Android 4.0+</string>
+ <string name="faq_androids_clients_title">Differences between the OpenVPN Android clients</string>
</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 68e71886..1608f487 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -15,12 +15,11 @@
<string name="title_activity_dashboard">Bitmask</string>
<string name="provider_label">Provider:</string>
<string name="provider_label_none">No provider configured</string>
- <string name="eip_settings_button_description">Access EIP connection settings</string>
<string name="status_unknown">Status unknown.</string>
<string name="anonymous_secured_status">Connection secure using an anonymous certificate.</string>
<string name="authed_secured_status">Connection secure using your own certificate.</string>
<string name="eip_service_label">Encrypted Internet</string>
- <string name="title_activity_configuration_wizard">Select a service provider</string>
+ <string name="configuration_wizard_title">Select a service provider</string>
<string name="new_provider_button">Add new Provider</string>
<string name="introduce_new_provider">Add a new service provider</string>
<string name="save">Save</string>
@@ -55,7 +54,8 @@
<string name="service_is_down_error">Service is down.</string>
<string name="configuring_provider">Configuring provider</string>
<string name="incorrectly_downloaded_certificate_message">Your anon cert was not downloaded</string>
- <string name="updating_certificate_message">Updating EIP certificate</string>
+ <string name="downloading_certificate_message">Downloading VPN certificate</string>
+ <string name="updating_certificate_message">Updating VPN certificate</string>
<string name="authenticating_message">Logging in</string>
<string name="signingup_message">Signing up</string>
<string name="logout_message">Logging out from this session.</string>
@@ -63,7 +63,7 @@
<string name="log_out_failed_message">Didn\'t log out.</string>
<string name="succesful_authentication_message">Authentication succeeded.</string>
<string name="authentication_failed_message">Authentication failed.</string>
- <string name="registration_failed_message">Registration failed..</string>
+ <string name="registration_failed_message">Registration failed.</string>
<string name="successful_authed_cert_downloaded_message">Your own cert has been correctly downloaded.</string>
<string name="authed_cert_download_failed_message">Your own cert has incorrectly been downloaded.</string>
<string name="eip_status_start_pending">Initiating connection</string>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index a60e29b8..94970c88 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,14 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2012-2014 Arne Schwabe
- ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
-->
<resources>
- <style name="appstyle" parent="android:Theme.DeviceDefault.Light">
+ <style name="blinkt.baseTheme" parent="android:Theme.DeviceDefault.Light" />
+ <style name="blinkt.common" parent="blinkt.baseTheme" >
+ <!-- Shared between Holo and Material -->
+ <item name="android:preferenceStyle">@style/BlinktPreferencePanel</item>
</style>
+ <style name="blinkt" parent="blinkt.common">
+ </style>
+
+ <!-- No margins or background by default. Not different for x-large screens -->
+ <style name="BlinktPreferencePanel">
+ <item name="android:background">@color/gelb</item>
+ </style>
<style name="item">
<item name="android:layout_width">match_parent</item>
diff --git a/app/src/main/res/values/untranslatable.xml b/app/src/main/res/values/untranslatable.xml
index 82147ab5..3aa47129 100644
--- a/app/src/main/res/values/untranslatable.xml
+++ b/app/src/main/res/values/untranslatable.xml
@@ -3170,4 +3170,864 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
</resources> \ No newline at end of file
diff --git a/app/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java b/app/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java
new file mode 100644
index 00000000..e595106c
--- /dev/null
+++ b/app/src/ovpn3/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java
@@ -0,0 +1,275 @@
+package de.blinkt.openvpn.core;
+
+import net.openvpn.ovpn3.ClientAPI_Config;
+import net.openvpn.ovpn3.ClientAPI_EvalConfig;
+import net.openvpn.ovpn3.ClientAPI_Event;
+import net.openvpn.ovpn3.ClientAPI_ExternalPKICertRequest;
+import net.openvpn.ovpn3.ClientAPI_ExternalPKISignRequest;
+import net.openvpn.ovpn3.ClientAPI_LogInfo;
+import net.openvpn.ovpn3.ClientAPI_OpenVPNClient;
+import net.openvpn.ovpn3.ClientAPI_ProvideCreds;
+import net.openvpn.ovpn3.ClientAPI_Status;
+import net.openvpn.ovpn3.ClientAPI_TransportStats;
+
+import java.lang.Override;
+
+import de.blinkt.openvpn.VpnProfile;
+
+import android.content.Context;
+
+public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable, OpenVPNManagement {
+
+ static {
+ /*System.loadLibrary("crypto");
+ System.loadLibrary("ssl");*/
+ System.loadLibrary("polarssl-dynamic");
+ System.loadLibrary("ovpn3");
+ }
+
+ private VpnProfile mVp;
+ private OpenVPNService mService;
+
+ class StatusPoller implements Runnable
+ {
+ private long mSleeptime;
+
+ boolean mStopped=false;
+
+ public StatusPoller(long sleeptime) {
+ mSleeptime=sleeptime;
+ }
+
+ public void run() {
+ while(!mStopped) {
+ try {
+ Thread.sleep(mSleeptime);
+ } catch (InterruptedException e) {
+ }
+ ClientAPI_TransportStats t = transport_stats();
+ long in = t.getBytesIn();
+ long out = t.getBytesOut();
+ VpnStatus.updateByteCount(in, out);
+ }
+ }
+
+ public void stop() {
+ mStopped=true;
+ }
+ }
+
+ @Override
+ public void run() {
+ String configstr = mVp.getConfigFile((Context)mService,true);
+ if(!setConfig(configstr))
+ return;
+ setUserPW();
+ VpnStatus.logInfo(copyright());
+
+ StatusPoller statuspoller = new StatusPoller(5000);
+ new Thread(statuspoller,"Status Poller").start();
+
+ ClientAPI_Status status = connect();
+ if(status.getError()) {
+ VpnStatus.logError(String.format("connect() error: %s: %s",status.getStatus(),status.getMessage()));
+ } else {
+ VpnStatus.logInfo("OpenVPN3 thread finished");
+ }
+ statuspoller.stop();
+ }
+
+ @Override
+ public boolean tun_builder_set_remote_address(String address, boolean ipv6) {
+ mService.setMtu(1500);
+ return true;
+ }
+
+ @Override
+ public boolean tun_builder_set_mtu(int mtu) {
+ mService.setMtu(mtu);
+ return true;
+ }
+ @Override
+ public boolean tun_builder_add_dns_server(String address, boolean ipv6) {
+ mService.addDNS(address);
+ return true;
+ }
+
+ @Override
+ public boolean tun_builder_add_route(String address, int prefix_length,
+ boolean ipv6) {
+ if (address.equals("remote_host"))
+ return false;
+
+ if(ipv6)
+ mService.addRoutev6(address + "/" + prefix_length,"tun");
+ else
+ mService.addRoute(new CIDRIP(address, prefix_length));
+ return true;
+ }
+
+ @Override
+ public boolean tun_builder_add_search_domain(String domain) {
+ mService.setDomain(domain);
+ return true;
+ }
+
+ @Override
+ public int tun_builder_establish() {
+ return mService.openTun().detachFd();
+ }
+
+ @Override
+ public boolean tun_builder_set_session_name(String name) {
+ VpnStatus.logInfo("We should call this session" + name);
+ return true;
+ }
+
+
+
+ @Override
+ public boolean tun_builder_add_address(String address, int prefix_length,
+ boolean ipv6) {
+ if(!ipv6)
+ mService.setLocalIP(new CIDRIP(address, prefix_length));
+ else
+ mService.setLocalIPv6(address+ "/" + prefix_length);
+ return true;
+ }
+
+ @Override
+ public boolean tun_builder_new() {
+
+ return true;
+ }
+
+ @Override
+ public boolean tun_builder_reroute_gw(String server_address,
+ boolean server_address_ipv6, boolean ipv4, boolean ipv6, long flags) {
+ // ignore
+ return true;
+ }
+
+ @Override
+ public boolean tun_builder_exclude_route(String address, int prefix_length,
+ boolean ipv6) {
+ //ignore
+ return true;
+ }
+
+
+ private boolean setConfig(String vpnconfig) {
+
+ ClientAPI_Config config = new ClientAPI_Config();
+ if(mVp.getPasswordPrivateKey()!=null)
+ config.setPrivateKeyPassword(mVp.getPasswordPrivateKey());
+
+ config.setContent(vpnconfig);
+ config.setTunPersist(mVp.mPersistTun);
+ config.setGuiVersion(mVp.getVersionEnvString(mService));
+ config.setExternalPkiAlias("extpki");
+
+ ClientAPI_EvalConfig ec = eval_config(config);
+ if(ec.getExternalPki()) {
+ VpnStatus.logError("OpenVPN seem to think as external PKI");
+ }
+ if (ec.getError()) {
+ VpnStatus.logError("OpenVPN config file parse error: " + ec.getMessage());
+ return false;
+ } else {
+ config.setContent(vpnconfig);
+ return true;
+ }
+ }
+
+ @Override
+ public void external_pki_cert_request(ClientAPI_ExternalPKICertRequest certreq) {
+ VpnStatus.logError("EXT PKI CERT");
+ String[] ks = mVp.getKeyStoreCertificates((Context) mService);
+ if(ks==null) {
+ certreq.setError(true);
+ certreq.setErrorText("Error in pki cert request");
+ return;
+ }
+
+ String supcerts = ks[0];
+ /* FIXME: How to differentiate between chain and ca certs in OpenVPN 3? */
+ if (ks[1]!=null)
+ supcerts += "\n" + ks[1];
+ certreq.setSupportingChain(supcerts);
+ certreq.setCert(ks[2]);
+ certreq.setError(false);
+ }
+
+ @Override
+ public void external_pki_sign_request(ClientAPI_ExternalPKISignRequest signreq) {
+ signreq.setSig(mVp.getSignedData(signreq.getData()));
+ }
+
+ void setUserPW() {
+ if(mVp.isUserPWAuth()) {
+ ClientAPI_ProvideCreds creds = new ClientAPI_ProvideCreds();
+ creds.setCachePassword(true);
+ creds.setPassword(mVp.getPasswordAuth());
+ creds.setUsername(mVp.mUsername);
+ provide_creds(creds);
+ }
+ }
+
+ @Override
+ public boolean socket_protect(int socket) {
+ boolean b= mService.protect(socket);
+ return b;
+
+ }
+
+ public OpenVPNThreadv3(OpenVPNService openVpnService, VpnProfile vp) {
+ init_process();
+ mVp =vp;
+ mService =openVpnService;
+ }
+
+ @Override
+ public void pause(pauseReason pauseReason)
+ {
+ pause();
+ }
+
+ @Override
+ public void log(ClientAPI_LogInfo arg0) {
+ String logmsg =arg0.getText();
+ while (logmsg.endsWith("\n"))
+ logmsg = logmsg.substring(0, logmsg.length()-1);
+
+ VpnStatus.logInfo(logmsg);
+ }
+
+ @Override
+ public void event(ClientAPI_Event event) {
+ VpnStatus.updateStateString(event.getName(), event.getInfo());
+ if(event.getError())
+ VpnStatus.logError(String.format("EVENT(Error): %s: %s",event.getName(),event.getInfo()));
+ }
+
+
+ // When a connection is close to timeout, the core will call this
+ // method. If it returns false, the core will disconnect with a
+ // CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
+ // state.
+
+ @Override
+ public boolean pause_on_connection_timeout() {
+ VpnStatus.logInfo("pause on connection timeout?! ");
+ return true;
+ }
+
+ public boolean stopVPN() {
+ stop();
+ return true;
+ }
+
+ @Override
+ public void reconnect() {
+ reconnect(1);
+ }
+
+}
diff --git a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java
index 3d9bb7b1..abab106d 100644
--- a/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java
+++ b/app/src/release/java/se/leap/bitmaskclient/ConfigurationWizard.java
@@ -28,7 +28,7 @@ import java.net.*;
import java.util.*;
import butterknife.*;
-
+import org.jetbrains.annotations.NotNull;
import org.json.*;
import javax.inject.Inject;
@@ -58,16 +58,13 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
@Inject ProviderListAdapter adapter;
private ProviderManager provider_manager;
- private Intent mConfigState = new Intent();
+ private Intent mConfigState = new Intent();
private Provider selected_provider;
final public static String TAG = ConfigurationWizard.class.getSimpleName();
- final public static String TYPE_OF_CERTIFICATE = "type_of_certificate";
- final public static String ANON_CERTIFICATE = "anon_certificate";
- final public static String AUTHED_CERTIFICATE = "authed_certificate";
- final protected static String PROVIDER_SET = "PROVIDER SET";
- final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED";
+ final protected static String PROVIDER_SET = "PROVIDER SET";
+ final protected static String SERVICES_RETRIEVED = "SERVICES RETRIEVED";
final protected static String ASSETS_URL_FOLDER = "urls";
final private static String PROGRESSBAR_TEXT = TAG + "PROGRESSBAR_TEXT";
@@ -82,9 +79,17 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
private String progressbar_text = "";
private String provider_name = "";
private int progress = -1;
+
+ private void initProviderList() {
+ List<Renderer<Provider>> prototypes = new ArrayList<Renderer<Provider>>();
+ prototypes.add(new ProviderRenderer(this));
+ ProviderRendererBuilder providerRendererBuilder = new ProviderRendererBuilder(prototypes);
+ adapter = new ProviderListAdapter(getLayoutInflater(), providerRendererBuilder, provider_manager);
+ provider_list_view.setAdapter(adapter);
+ }
@Override
- protected void onSaveInstanceState(Bundle outState) {
+ protected void onSaveInstanceState(@NotNull Bundle outState) {
if(mProgressBar != null)
outState.putInt(PROGRESSBAR_NUMBER, mProgressBar.getProgress());
if(progressbar_description != null)
@@ -102,15 +107,14 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
provider_manager = ProviderManager.getInstance(getAssets(), getExternalFilesDir(null));
- setUpProviderList();
-
setUpInitialUI();
- setUpProviderAPIResultReceiver();
-
- if ( savedInstanceState != null ) {
+ initProviderList();
+
+ if (savedInstanceState != null)
restoreState(savedInstanceState);
- }
+ else
+ setUpProviderAPIResultReceiver();
}
private void restoreState(Bundle savedInstanceState) {
@@ -127,7 +131,6 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
super.onPostResume();
if(!progressbar_text.isEmpty() && !provider_name.isEmpty() && progress != -1) {
progressbar_description.setText(progressbar_text);
- //onItemSelectedUi(getProvider(provider_name));
mProgressBar.setProgress(progress);
progressbar_text = "";
@@ -139,7 +142,6 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
private void setUpInitialUI() {
setContentView(R.layout.configuration_wizard_activity);
ButterKnife.inject(this);
- provider_list_view.setAdapter(adapter);
hideProgressBar();
}
@@ -150,14 +152,11 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
progressbar_description.setVisibility(TextView.INVISIBLE);
}
- private void setUpProviderList() {
- initProviderList();
- }
-
@Override
protected void onDestroy() {
super.onDestroy();
- unregisterReceiver(providerAPI_broadcast_receiver_update);
+ if(providerAPI_broadcast_receiver_update != null)
+ unregisterReceiver(providerAPI_broadcast_receiver_update);
}
private void setUpProviderAPIResultReceiver() {
@@ -169,13 +168,6 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
update_intent_filter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(providerAPI_broadcast_receiver_update, update_intent_filter);
}
-
- private void initProviderList() {
- List<Renderer<Provider>> prototypes = new ArrayList<Renderer<Provider>>();
- prototypes.add(new ProviderRenderer(this));
- ProviderRendererBuilder providerRendererBuilder = new ProviderRendererBuilder(prototypes);
- adapter = new ProviderListAdapter(getLayoutInflater(), providerRendererBuilder, provider_manager);
- }
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
@@ -192,7 +184,7 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
if (preferences.getBoolean(Constants.ALLOWED_ANON, false)){
mConfigState.putExtra(SERVICES_RETRIEVED, true);
- downloadAnonCert();
+ downloadVpnCertificate();
} else {
mProgressBar.incrementProgressBy(1);
hideProgressBar();
@@ -260,24 +252,20 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
mProgressBar.setVisibility(ProgressBar.GONE);
mProgressBar.setProgress(0);
progressbar_description.setVisibility(TextView.GONE);
-
- preferences.edit().remove(Provider.KEY).apply();
- setting_up_provider = false;
- showAllProviders();
+
+ cancelSettingUpProvider();
}
- private void usualBackButton() {
- if(preferences.getString(Provider.KEY, "").isEmpty()) {
- askDashboardToQuitApp();
- } else {
- setResult(RESULT_OK);
- }
- super.onBackPressed();
+ public void cancelSettingUpProvider() {
+ adapter.showAllProviders();
+ setting_up_provider = false;
+ preferences.edit().remove(Provider.KEY).remove(Constants.ALLOWED_ANON).remove(Constants.KEY).apply();
}
+
private void askDashboardToQuitApp() {
- Intent ask_quit = new Intent();
- ask_quit.putExtra(Dashboard.ACTION_QUIT, Dashboard.ACTION_QUIT);
- setResult(RESULT_CANCELED, ask_quit);
+ Intent ask_quit = new Intent();
+ ask_quit.putExtra(Dashboard.ACTION_QUIT, Dashboard.ACTION_QUIT);
+ setResult(RESULT_CANCELED, ask_quit);
}
private void startProgressBar() {
@@ -309,71 +297,66 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
return listItem.getMeasuredHeight();
}
- /**
- * Asks ProviderAPI to download an anonymous (anon) VPN certificate.
- */
- private void downloadAnonCert() {
- Intent provider_API_command = new Intent(this, ProviderAPI.class);
-
- Bundle parameters = new Bundle();
-
- parameters.putString(TYPE_OF_CERTIFICATE, ANON_CERTIFICATE);
+ /**
+ * Asks ProviderAPI to download an anonymous (anon) VPN certificate.
+ */
+ private void downloadVpnCertificate() {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
- provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
- provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+ provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
- startService(provider_API_command);
- }
+ startService(provider_API_command);
+ }
- /**
- * Open the new provider dialog
- */
- public void addAndSelectNewProvider() {
- FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG);
+ /**
+ * Open the new provider dialog
+ */
+ public void addAndSelectNewProvider() {
+ FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG);
new NewProviderDialog().show(fragment_transaction, NewProviderDialog.TAG);
- }
+ }
- /**
- * Open the new provider dialog with data
- */
- public void addAndSelectNewProvider(String main_url) {
- FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG);
+ /**
+ * Open the new provider dialog with data
+ */
+ public void addAndSelectNewProvider(String main_url) {
+ FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(NewProviderDialog.TAG);
- DialogFragment newFragment = new NewProviderDialog();
- Bundle data = new Bundle();
- data.putString(Provider.MAIN_URL, main_url);
- newFragment.setArguments(data);
- newFragment.show(fragment_transaction, NewProviderDialog.TAG);
- }
+ DialogFragment newFragment = new NewProviderDialog();
+ Bundle data = new Bundle();
+ data.putString(Provider.MAIN_URL, main_url);
+ newFragment.setArguments(data);
+ newFragment.show(fragment_transaction, NewProviderDialog.TAG);
+ }
- /**
- * Once selected a provider, this fragment offers the user to log in,
- * use it anonymously (if possible)
- * or cancel his/her election pressing the back button.
- * @param reason_to_fail
- */
- public void showDownloadFailedDialog(String reason_to_fail) {
- FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(DownloadFailedDialog.TAG);
+ /**
+ * Once selected a provider, this fragment offers the user to log in,
+ * use it anonymously (if possible)
+ * or cancel his/her election pressing the back button.
+ * @param reason_to_fail
+ */
+ public void showDownloadFailedDialog(String reason_to_fail) {
+ FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(DownloadFailedDialog.TAG);
- DialogFragment newFragment = DownloadFailedDialog.newInstance(reason_to_fail);
- newFragment.show(fragment_transaction, DownloadFailedDialog.TAG);
- }
+ DialogFragment newFragment = DownloadFailedDialog.newInstance(reason_to_fail);
+ newFragment.show(fragment_transaction, DownloadFailedDialog.TAG);
+ }
- /**
- * Once selected a provider, this fragment offers the user to log in,
- * use it anonymously (if possible)
- * or cancel his/her election pressing the back button.
- * @param view
- */
- private void showProviderDetails() {
- if(setting_up_provider) {
- FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(ProviderDetailFragment.TAG);
-
- DialogFragment newFragment = ProviderDetailFragment.newInstance();
- newFragment.show(fragment_transaction, ProviderDetailFragment.TAG);
- }
+ /**
+ * Once selected a provider, this fragment offers the user to log in,
+ * use it anonymously (if possible)
+ * or cancel his/her election pressing the back button.
+ * @param view
+ */
+ private void showProviderDetails() {
+ if(setting_up_provider) {
+ FragmentTransaction fragment_transaction = fragment_manager.removePreviousFragment(ProviderDetailFragment.TAG);
+
+ DialogFragment newFragment = ProviderDetailFragment.newInstance();
+ newFragment.show(fragment_transaction, ProviderDetailFragment.TAG);
}
+ }
public void showAndSelectProvider(String provider_main_url) {
try {
@@ -392,77 +375,71 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
onItemSelectedLogic(selected_provider);
}
- /**
- * Asks ProviderAPI to download a new provider.json file
- * @param provider_name
- * @param provider_main_url
- */
- public void setUpProvider(URL provider_main_url) {
- Intent provider_API_command = new Intent(this, ProviderAPI.class);
- Bundle parameters = new Bundle();
- parameters.putString(Provider.MAIN_URL, provider_main_url.toString());
-
- provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
- provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
-
- startService(provider_API_command);
- setting_up_provider = true;
- }
+ /**
+ * Asks ProviderAPI to download a new provider.json file
+ * @param provider_name
+ * @param provider_main_url
+ */
+ public void setUpProvider(URL provider_main_url) {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+ Bundle parameters = new Bundle();
+ parameters.putString(Provider.MAIN_URL, provider_main_url.toString());
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ setting_up_provider = true;
+ }
- public void retrySetUpProvider() {
- cancelSettingUpProvider();
- if(!ProviderAPI.caCertDownloaded()) {
- addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl());
- } else {
- Intent provider_API_command = new Intent(this, ProviderAPI.class);
+ public void retrySetUpProvider() {
+ cancelSettingUpProvider();
+ if(!ProviderAPI.caCertDownloaded()) {
+ addAndSelectNewProvider(ProviderAPI.lastProviderMainUrl());
+ } else {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
- provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, providerAPI_result_receiver);
- startService(provider_API_command);
- }
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.configuration_wizard_activity, menu);
- return true;
+ startService(provider_API_command);
}
+ }
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.configuration_wizard_activity, menu);
+ return true;
+ }
- @Override
- public boolean onOptionsItemSelected(MenuItem item){
- switch (item.getItemId()){
- case R.id.about_leap:
- startActivityForResult(new Intent(this, AboutActivity.class), 0);
- return true;
- case R.id.new_provider:
- addAndSelectNewProvider();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item){
+ switch (item.getItemId()){
+ case R.id.about_leap:
+ startActivityForResult(new Intent(this, AboutActivity.class), 0);
+ return true;
+ case R.id.new_provider:
+ addAndSelectNewProvider();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
}
+ }
public void showAllProviders() {
adapter.showAllProviders();
}
-
- public void cancelSettingUpProvider() {
- adapter.showAllProviders();
+
+ @Override
+ public void login() {
+ Intent ask_login = new Intent();
+ ask_login.putExtra(Provider.KEY, selected_provider);
+ ask_login.putExtra(SessionDialog.TAG, SessionDialog.TAG);
+ setResult(RESULT_OK, ask_login);
setting_up_provider = false;
- preferences.edit().remove(Provider.KEY).remove(Constants.ALLOWED_ANON).remove(Constants.KEY).apply();
+ finish();
}
- @Override
- public void login() {
- Intent ask_login = new Intent();
- ask_login.putExtra(Provider.KEY, selected_provider);
- ask_login.putExtra(SessionDialog.TAG, SessionDialog.TAG);
- setResult(RESULT_OK, ask_login);
- setting_up_provider = false;
- finish();
- }
-
@Override
public void use_anonymously() {
Intent pass_provider = new Intent();
@@ -472,12 +449,11 @@ implements NewProviderDialogInterface, ProviderDetailFragmentInterface, Download
finish();
}
- public class ProviderAPIBroadcastReceiver_Update extends BroadcastReceiver {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- int update = intent.getIntExtra(ProviderAPI.CURRENT_PROGRESS, 0);
- mProgressBar.setProgress(update);
- }
+ public class ProviderAPIBroadcastReceiver_Update extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int update = intent.getIntExtra(ProviderAPI.CURRENT_PROGRESS, 0);
+ mProgressBar.setProgress(update);
}
+ }
}
diff --git a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java
index 8b8cabdf..334efaa9 100644
--- a/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java
+++ b/app/src/release/java/se/leap/bitmaskclient/ProviderAPI.java
@@ -182,7 +182,7 @@ public class ProviderAPI extends IntentService {
if(validUserLoginData(username, password)) {
session_id_bundle = register(username, password);
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
} else {
if(!wellFormedPassword(password)) {
session_id_bundle.putBoolean(RESULT_KEY, false);
@@ -232,14 +232,14 @@ public class ProviderAPI extends IntentService {
String password = (String) task.get(SessionDialog.PASSWORD);
if(validUserLoginData(username, password)) {
result = authenticate(username, password);
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
} else {
if(!wellFormedPassword(password)) {
result.putBoolean(RESULT_KEY, false);
result.putString(SessionDialog.USERNAME, username);
result.putBoolean(SessionDialog.PASSWORD_INVALID_LENGTH, true);
}
- if(username.isEmpty()) {
+ if(!validUsername(username)) {
result.putBoolean(RESULT_KEY, false);
result.putBoolean(SessionDialog.USERNAME_MISSING, true);
}
@@ -312,7 +312,7 @@ public class ProviderAPI extends IntentService {
* and sends it as a broadcast.
* @param progress
*/
- private void broadcast_progress(int progress) {
+ private void broadcastProgress(int progress) {
Intent intentUpdate = new Intent();
intentUpdate.setAction(UPDATE_PROGRESSBAR);
intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
@@ -320,23 +320,27 @@ public class ProviderAPI extends IntentService {
sendBroadcast(intentUpdate);
}
- /**
- * Validates parameters entered by the user to log in
- * @param entered_username
- * @param entered_password
- * @return true if both parameters are present and the entered password length is greater or equal to eight (8).
- */
- private boolean validUserLoginData(String entered_username, String entered_password) {
- return !(entered_username.isEmpty()) && wellFormedPassword(entered_password);
- }
+ /**
+ * Validates parameters entered by the user to log in
+ * @param username
+ * @param password
+ * @return true if both parameters are present and the entered password length is greater or equal to eight (8).
+ */
+ private boolean validUserLoginData(String username, String password) {
+ return validUsername(username) && wellFormedPassword(password);
+ }
+
+ private boolean validUsername(String username) {
+ return username != null && !username.isEmpty();
+ }
/**
* Validates a password
- * @param entered_password
+ * @param password
* @return true if the entered password length is greater or equal to eight (8).
*/
- private boolean wellFormedPassword(String entered_password) {
- return entered_password.length() >= 8;
+ private boolean wellFormedPassword(String password) {
+ return password != null && password.length() >= 8;
}
/**
@@ -493,17 +497,17 @@ public class ProviderAPI extends IntentService {
if(!PROVIDER_JSON_DOWNLOADED)
current_download = getAndSetProviderJson(last_provider_main_url);
if(PROVIDER_JSON_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) {
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
PROVIDER_JSON_DOWNLOADED = true;
if(!CA_CERT_DOWNLOADED)
current_download = downloadCACert();
if(CA_CERT_DOWNLOADED || (current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY))) {
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
CA_CERT_DOWNLOADED = true;
current_download = getAndSetEipServiceJson();
if(current_download.containsKey(RESULT_KEY) && current_download.getBoolean(RESULT_KEY)) {
- broadcast_progress(progress++);
+ broadcastProgress(progress++);
EIP_SERVICE_JSON_DOWNLOADED = true;
}
}
@@ -815,51 +819,64 @@ public class ProviderAPI extends IntentService {
}
return string;
}
-
- /**
- * Logs out from the api url retrieved from the task.
- * @return true if there were no exceptions
- */
- private boolean logOut() {
- try {
- String delete_url = provider_api_url + "/logout";
- int progress = 0;
- HttpsURLConnection urlConnection = (HttpsURLConnection)new URL(delete_url).openConnection();
- urlConnection.setRequestMethod("DELETE");
- urlConnection.setSSLSocketFactory(getProviderSSLSocketFactory());
+ private boolean logOut() {
+ String delete_url = provider_api_url + "/logout";
- int responseCode = urlConnection.getResponseCode();
- broadcast_progress(progress++);
+ HttpsURLConnection urlConnection = null;
+ int responseCode = 0;
+ int progress = 0;
+ try {
+
+ urlConnection = (HttpsURLConnection)new URL(delete_url).openConnection();
+ urlConnection.setRequestMethod("DELETE");
+ urlConnection.setSSLSocketFactory(getProviderSSLSocketFactory());
+
+ responseCode = urlConnection.getResponseCode();
+ broadcastProgress(progress++);
+ LeapSRPSession.setToken("");
+ Log.d(TAG, Integer.toString(responseCode));
+ } catch (ClientProtocolException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ } catch (IndexOutOfBoundsException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ try {
+ if(urlConnection != null) {
+ responseCode = urlConnection.getResponseCode();
+ if(responseCode == 401) {
+ broadcastProgress(progress++);
LeapSRPSession.setToken("");
Log.d(TAG, Integer.toString(responseCode));
- } catch (ClientProtocolException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return false;
- } catch (IndexOutOfBoundsException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return false;
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return false;
- } catch (KeyManagementException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (KeyStoreException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (NoSuchAlgorithmException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (CertificateException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ return true;
+ }
}
- return true;
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+
+ e.printStackTrace();
+ return false;
+ } catch (KeyManagementException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (CertificateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
}
+ return true;
+ }
/**
* Downloads a new OpenVPN certificate, attaching authenticated cookie for authenticated certificate.