summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Leonard <meanderingcode@aetherislands.net>2013-07-26 15:30:24 -0600
committerSean Leonard <meanderingcode@aetherislands.net>2013-07-26 15:30:24 -0600
commitf49967fb3f24bf0a22d09b5823bd174a45e758f7 (patch)
tree7fefe9ec3aeff998c5dd99861428717a2c5f3a41
parent8dbb4517b07c42c3b37b0764d63973b5b1ed5ee6 (diff)
parentb69c9a87f60e649221c8f83d58bbd57b35a64aca (diff)
Merge branch 'release-0.2.0'0.2.0
-rw-r--r--.classpath7
-rw-r--r--.gitignore8
-rw-r--r--.project6
-rw-r--r--AndroidManifest.xml41
-rw-r--r--CHANGELOG3
-rw-r--r--LICENSE.txt674
l---------README1
-rw-r--r--README.txt74
-rw-r--r--assets/urls/bitmask.url7
-rw-r--r--build.xml92
-rwxr-xr-xcompile.sh3
-rwxr-xr-xdebug.sh33
-rwxr-xr-xfetchtranslations.sh35
-rwxr-xr-xgenFAQ.py96
-rw-r--r--hosts-for-android-emulator10
-rw-r--r--jni/jbcrypto.cpp4
-rw-r--r--jni/jniglue.c2
-rw-r--r--lint.xml3
-rwxr-xr-xprepareTestProviderAPI.sh15
-rw-r--r--proguard-project.txt20
-rw-r--r--project.properties2
-rw-r--r--res/drawable-hdpi/ic_stat_vpn.pngbin682 -> 1272 bytes
-rw-r--r--res/drawable-hdpi/icon.pngbin2624 -> 4254 bytes
-rw-r--r--res/drawable-ldpi/ic_stat_vpn.pngbin0 -> 632 bytes
-rw-r--r--res/drawable-ldpi/icon.pngbin0 -> 1517 bytes
-rw-r--r--res/drawable-mdpi/ic_stat_vpn.pngbin468 -> 845 bytes
-rw-r--r--res/drawable-mdpi/icon.pngbin3442 -> 2320 bytes
-rw-r--r--res/drawable-xhdpi/ic_stat_vpn.pngbin781 -> 1731 bytes
-rw-r--r--res/drawable-xhdpi/icon.pngbin2994 -> 6320 bytes
-rw-r--r--res/layout/about.xml26
-rw-r--r--res/layout/basic_settings.xml15
-rw-r--r--res/layout/client_dashboard.xml70
-rw-r--r--res/layout/configuration_wizard_activity.xml8
-rw-r--r--res/layout/eip_service_fragment.xml72
-rw-r--r--res/layout/file_select.xml16
-rw-r--r--res/layout/keystore_selector.xml16
-rw-r--r--res/layout/log_in_dialog.xml39
-rw-r--r--res/layout/new_provider_dialog.xml24
-rw-r--r--res/layout/provider_detail_fragment.xml40
-rw-r--r--res/layout/provider_list_fragment.xml22
-rw-r--r--res/layout/vpn_list_item.xml16
-rw-r--r--res/menu/client_dashboard.xml14
-rw-r--r--res/menu/configuration_wizard_activity.xml8
-rw-r--r--res/raw/leapkeystore.bksbin0 -> 2873 bytes
-rw-r--r--res/raw/leapkeystore_bc147.bksbin0 -> 2871 bytes
-rwxr-xr-xres/values-ca/strings.xml6
-rwxr-xr-xres/values-cs/strings.xml10
-rwxr-xr-xres/values-de/strings.xml10
-rwxr-xr-xres/values-es/strings.xml10
-rwxr-xr-xres/values-et/strings.xml10
-rwxr-xr-xres/values-fr/strings.xml10
-rwxr-xr-xres/values-id/strings.xml8
-rwxr-xr-xres/values-it/strings.xml10
-rwxr-xr-xres/values-ja/strings.xml10
-rwxr-xr-xres/values-ko/strings.xml10
-rwxr-xr-xres/values-nl/strings.xml4
-rwxr-xr-xres/values-no/strings.xml8
-rwxr-xr-xres/values-ro/strings.xml8
-rwxr-xr-xres/values-ru/strings.xml10
-rwxr-xr-xres/values-uk/strings.xml10
-rwxr-xr-xres/values-zh-rCN/strings.xml10
-rwxr-xr-xres/values-zh-rTW/strings.xml10
-rwxr-xr-xres/values/strings.xml75
-rw-r--r--res/values/styles.xml17
-rw-r--r--res/values/untranslatable.xml3
-rwxr-xr-xres/values/values-nl/arrays.xml27
-rwxr-xr-xres/values/values-nl/strings.xml108
-rwxr-xr-xrun.sh38
-rw-r--r--src/org/jboss/security/srp/SRPParameters.java150
-rw-r--r--src/se/leap/leapclient/AboutFragment.java (renamed from src/se/leap/openvpn/AboutFragment.java)14
-rw-r--r--src/se/leap/leapclient/ConfigHelper.java382
-rw-r--r--src/se/leap/leapclient/ConfigurationWizard.java411
-rw-r--r--src/se/leap/leapclient/Dashboard.java350
-rw-r--r--src/se/leap/leapclient/EIP.java511
-rw-r--r--src/se/leap/leapclient/EipServiceFragment.java264
-rw-r--r--src/se/leap/leapclient/LeapHttpClient.java87
-rw-r--r--src/se/leap/leapclient/LeapSRPSession.java322
-rw-r--r--src/se/leap/leapclient/LogInDialog.java106
-rw-r--r--src/se/leap/leapclient/NewProviderDialog.java108
-rw-r--r--src/se/leap/leapclient/Provider.java194
-rw-r--r--src/se/leap/leapclient/ProviderAPI.java733
-rw-r--r--src/se/leap/leapclient/ProviderAPIResultReceiver.java56
-rw-r--r--src/se/leap/leapclient/ProviderDetailFragment.java108
-rw-r--r--src/se/leap/leapclient/ProviderListContent.java141
-rw-r--r--src/se/leap/leapclient/ProviderListFragment.java191
-rw-r--r--src/se/leap/openvpn/ConfigParser.java6
-rw-r--r--src/se/leap/openvpn/LICENSE.txt24
-rw-r--r--src/se/leap/openvpn/LaunchVPN.java24
-rw-r--r--src/se/leap/openvpn/OpenVPN.java6
-rw-r--r--src/se/leap/openvpn/OpenVpnService.java68
-rw-r--r--src/se/leap/openvpn/VpnProfile.java21
-rw-r--r--todo.txt35
-rw-r--r--vim-plugin/basic-adt.vim47
93 files changed, 5837 insertions, 466 deletions
diff --git a/.classpath b/.classpath
index 3f9691c5..dfadf670 100644
--- a/.classpath
+++ b/.classpath
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
diff --git a/.gitignore b/.gitignore
index 0ceda544..9aa5a039 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
obj
bin
-libs
gen
+libs
openvpn/.git
openvpn/autom4te.cache
openvpn/aclocal.m4
@@ -63,3 +63,9 @@ zh-CN.zip
zh-TW.zip
google-breakpad/
id.zip
+/hosts-for-android-emulator.bk
+/build-native.sh.bk
+/build-native.sh~
+
+build.gradle
+local.properties
diff --git a/.project b/.project
index 1a0e1cda..6c5495ad 100644
--- a/.project
+++ b/.project
@@ -25,9 +25,15 @@
<arguments>
</arguments>
</buildCommand>
+ <buildCommand>
+ <name>org.jboss.tools.ws.jaxrs.metamodelBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.jboss.tools.ws.jaxrs.nature</nature>
</natures>
</projectDescription>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b4df9219..9833032c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -17,20 +17,18 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.leap.leapclient"
- android:versionCode="57"
- android:versionName="0.5.29" >
+ android:versionCode="60"
+ android:versionName="0.2.0" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <!-- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- <uses-permission android:name="com.android.vending.BILLING" /> -->
<uses-sdk
- android:minSdkVersion="15"
- android:targetSdkVersion="15" />
+ android:minSdkVersion="14"
+ android:targetSdkVersion="17" />
<permission
android:name="se.leap.openvpn.REMOTE_API"
@@ -53,12 +51,6 @@
<activity android:name="se.leap.openvpn.SendDumpActivity" />
<activity android:name="se.leap.openvpn.FileSelect" />
<activity android:name="se.leap.openvpn.MainActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.BROWSABLE" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
</activity>
<service
@@ -75,7 +67,8 @@
<action android:name="se.leap.openvpn.api.IOpenVPNAPIService" />
</intent-filter>
</service>
-
+ <service android:name="se.leap.leapclient.ProviderAPI" android:enabled="true"/>
+
<activity
android:name="se.leap.openvpn.api.GrantPermissionsActivity"
android:permission="se.leap.openvpn.REMOTE_API" >
@@ -146,6 +139,28 @@
android:authorities="se.leap.openvpn.FileProvider"
android:exported="true"
android:grantUriPermissions="true" />
+
+ <activity
+ android:name="se.leap.leapclient.Dashboard"
+ android:label="@string/title_activity_dashboard" >
+ <intent-filter android:label="@string/app_name">
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name="se.leap.leapclient.ConfigurationWizard"
+ android:label="@string/title_activity_configuration_wizard" >
+ </activity>
+
+ <service android:name=".EIP">
+ <intent-filter>
+ <action android:name="se.leap.leapclient.UPDATE_EIP_SERVICE"/>
+ <action android:name="se.leap.leapclient.START_EIP"/>
+ <action android:name="se.leap.leapclient.STOP_EIP"/>
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 00000000..6949a1ef
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,3 @@
+0.2.0
+
+ Initial release.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 00000000..20d40b6b
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>. \ No newline at end of file
diff --git a/README b/README
new file mode 120000
index 00000000..c3ca0746
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+README.txt \ No newline at end of file
diff --git a/README.txt b/README.txt
index 17b9f50a..7d57a6e3 100644
--- a/README.txt
+++ b/README.txt
@@ -1,43 +1,65 @@
-This is my first Android project, so some things may be done in a completely stupid way.
+Compiling
+=========
-See the file todo.txt for ideas/not yet implemented features (and the bug tracker).
+Preconditions
+----------------
-Build instraction:
+1. Android SDK installed (follow instructions from http://developer.android.com/sdk/index.html)
+2. API version 16 or version installed.
+2. Ant 1.6 or greater
-Checkout google breakcode:
+Instructions to compile
+-----------------------
-svn co http://google-breakpad.googlecode.com/svn/trunk/ google-breakpad
+1. cd $PROJECT_LOCATION/leap_android
+2. ./compile.sh
-- Install sdk
-- Install ndk
+Postconditions
+--------------
-Do ./build-native.sh in the root directory of the project.
+1. $PROJECT_LOCATION/leap_android/bin/LEAP Android-debug.apk exists
-Use eclipse with android plugins to build the project.
+Running on the emulator
+=========================
-Optional: Copy minivpn from lib/ to assets (if you want your own compiled version)
+Preconditions
+-----------------
+1. Android SDK is installed, and its tools are in the PATH.
+2. leap_android has been compiled.
+3. An avd exists in ~/.android/avd/ (if you do not have one, follow instructions from http://developer.android.com/tools/devices/managing-avds-cmdline.html)
+Instructions to run on the emulator
+-----------------------------------
+1. cd $PROJECT_LOCATION/leap_android
+1. Run script: ./run.sh @AVD-NAME . (avd names are the names of the files in ~/.android/avd with extension .avd).
-Starting a VPN by name from an external app:
+Postconditions
+--------------
-public class StartOpenVPNActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- final String EXTRA_NAME = "se.leap.openvpn.shortcutProfileName";
+1. LEAP Android is running.
- Intent shortcutIntent = new Intent(Intent.ACTION_MAIN);
- shortcutIntent.setClassName("se.leap.openvpn", "se.leap.openvpn.LaunchVPN");
- shortcutIntent.putExtra(EXTRA_NAME,"upb ssl");
- startActivity(shortcutIntent);
- }
-}
+Debugging from console
+======================
-or from the shell:
+Preconditions
+-----------------
-am start -a android.intent.action.MAIN -n se.leap.openvpn/.LaunchVPN -e se.leap.openvpn.shortcutProfileName Home
+1. Android SDK is installed, and its tools are in the PATH.
+2. leap_android has been compiled.
+3. An avd exists in ~/.android/avd/ (if you do not have one, follow instructions from http://developer.android.com/tools/devices/managing-avds-cmdline.html).
+4. jdb is installed (this program is part of OpenJDK 7)
+Instructions to debug from the console
+-----------------------------------
+
+1. cd $PROJECT_LOCATION/leap_android
+2. Run script: ./debug.sh @AVD-NAME . (avd names are the names of the files in ~/.android/avd with extension .avd).
+
+Postconditions
+--------------
+
+1. LEAP Android is running.
+2. LEAP Android does not show the message "Application LEAP for Android (process se.leap.leapclient) is waiting for the debugger to attach".
+3. You are in a jdb debuggin session.
diff --git a/assets/urls/bitmask.url b/assets/urls/bitmask.url
new file mode 100644
index 00000000..a17a5ff8
--- /dev/null
+++ b/assets/urls/bitmask.url
@@ -0,0 +1,7 @@
+{
+ "name" : "bitmask",
+ "assets_json_provider" : "providers/bitmask.net_provider.json",
+ "provider_json_url" : "https://bitmask.net/provider.json",
+ "cert" : "https://bitmask.net/ca.crt",
+ "json_eip_service" : "https://api.bitmask.net:4430/1/config/eip-service.json"
+} \ No newline at end of file
diff --git a/build.xml b/build.xml
new file mode 100644
index 00000000..538f2eee
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="LEAP Android" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var.
+ This must be done before we load project.properties since
+ the proguard config can use sdk.dir -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/compile.sh b/compile.sh
new file mode 100755
index 00000000..8d946dcc
--- /dev/null
+++ b/compile.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+android update project --path . --name "LEAP Android" --target android-17
+ant debug
diff --git a/debug.sh b/debug.sh
new file mode 100755
index 00000000..18679368
--- /dev/null
+++ b/debug.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+if [ -z "$2" ]
+then
+ echo Usage: debug.sh \"avd name\" \"project folder\"
+ exit 0;
+fi
+avd_name=$1
+PROJECT_FOLDER=$2
+localport=`expr $RANDOM % 65536`
+
+wait_until_booted() {
+ OUT=`adb shell getprop init.svc.bootanim`
+ RES="stopped"
+
+ while [[ ${OUT:0:7} != 'stopped' ]]; do
+ OUT=`adb shell getprop init.svc.bootanim`
+# echo 'Waiting for emulator to fully boot...'
+ sleep 5
+ done
+
+ echo "Emulator booted!"
+}
+
+emulator -wipe-data @$avd_name & # If you want to test the app from scratch
+wait_until_booted
+adb install -r $PROJECT_FOLDER/bin/LEAP\ Android-debug.apk # Install the new version of the application
+adb shell am start -D se.leap.leapclient/.Dashboard # Run app
+sleep 1
+pid=`adb shell ps | grep leap | awk '{print $2}'` # Identify the process id (pid) of the current leapclient process instance
+echo forwarding tcp:$localport to jwdp:$pid
+adb forward tcp:$localport jdwp:$pid
+jdb -sourcepath $PROJECT_FOLDER/src/ -attach localhost:$localport
diff --git a/fetchtranslations.sh b/fetchtranslations.sh
deleted file mode 100755
index 203a8bcc..00000000
--- a/fetchtranslations.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#! /bin/sh
-
-
-if [ "$ICSCROWDAPIKEY" != "" ]
-then
- echo "Generating new translation archives"
- fetch -q -1 -o - http://api.crowdin.net/api/project/ics-openvpn/export?key=$ICSCROWDAPIKEY
-fi
-
-echo "Fetch translation archive"
-fetch -q http://crowdin.net/download/project/ics-openvpn.zip
-
-langtoinclude="ca cs de es et fr id it ja ko no nl ro ru uk"
-
-for lang in $langtoinclude
-do
- tar xfv ics-openvpn.zip /res/values-$lang/
-done
-
-# Chinese language require zh-CN and zh-TW
-
-for lang in zh-CN zh-TW id
-do
- if [ $lang = "zh-CN" ] ; then
- rlang="zh-rCN"
- elif [ $lang = "zh-TW" ] ; then
- rlang="zh-rTW"
- elif [ $lang = "id" ] ; then
- rlang="id"
- fi
-
- echo "Fetch archive for $lang"
- fetch http://crowdin.net/download/project/ics-openvpn/$lang.zip
- tar -xv -C res/values-$rlang/ --strip-components 3 -f $lang.zip
-done \ No newline at end of file
diff --git a/genFAQ.py b/genFAQ.py
deleted file mode 100755
index 1815f7a2..00000000
--- a/genFAQ.py
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/usr/bin/env python
-# Quick and dirty script to generate googlecode wiki pages
-
-import codecs
-import xml.dom.minidom as dom
-import os.path
-
-faqpath = "/Users/arne/oss/ics-openvpn.wiki"
-
-header="""
-<wiki:comment>
-This page is autogenerated. Do not edit
-</wiki:comment>
-
-= Frequently aksed questions =
-"""
-
-def getString(strid,lang):
- if strid in strres[lang]:
- return strres[lang][strid]
- else:
- return strres["default"][strid]
-
-def genPage(faqdom,lang):
- out =""
-
- out+="#summary %s\n" % getString("faq_summary",lang)
- out+= header
-
- for xmld in faqdom.firstChild.childNodes:
- for xmle in xmld.childNodes:
- if xmle.nodeName == "TextView":
- style = xmle.getAttribute("style")
-
- textstyle = None
- if style == "@style/faqhead":
- textstyle = "== %s ==\n"
- elif style == "@style/faqitem":
- textstyle = "%s\n"
-
- atext = xmle.getAttribute("android:text")
- aid = xmle.getAttribute("android:id")
- if atext:
- atextid = atext.replace("@string/","")
- else:
- atextid = aid.replace("@+id/","")
-
- out += textstyle % getString(atextid,lang)
-
- return out
-
-
-strres={}
-
-def loadstrres(filename,lang):
- xmlstr = dom.parse(filename)
- strres[lang]={}
- for xmld in xmlstr.childNodes:
- for xmle in xmld.childNodes:
- if xmle.nodeName == "string":
- strname= xmle.getAttribute("name")
- strdata = xmle.firstChild.data
- strres[lang][strname]=strdata
-
-
-def main():
-
- loadstrres("res/values/strings.xml","default")
-
- faqdom = dom.parse("res/layout/faq.xml")
- faq= genPage(faqdom,"default")
-
- open(faqpath + "/FAQ.wiki","w").write(faq)
-
- for directory in os.listdir("res"):
- if directory.startswith("values-"):
- lang = directory.split("-",1)[1]
- loadstrres("res/values-%s/strings.xml" % lang,lang)
-
- langdir= "%s/%s" %(faqpath,lang)
- if lang=="zh-rCN":
- langdir= "%s/%s" %(faqpath,"zh-Hans")
- elif lang=="zh-rTW":
- langdir= "%s/%s" %(faqpath,"zh-Hant")
-
-
- if not os.path.exists(langdir):
- os.mkdir(langdir)
-
- print lang
- faq= genPage(faqdom,lang)
- open("%s/FAQ.wiki" % langdir,"w").write(faq.encode("utf-8"))
-
-
-if __name__=="__main__":
- main()
diff --git a/hosts-for-android-emulator b/hosts-for-android-emulator
new file mode 100644
index 00000000..ab0cf906
--- /dev/null
+++ b/hosts-for-android-emulator
@@ -0,0 +1,10 @@
+127.0.0.1 localhost
+10.0.2.2 api.lvh.me
+10.0.2.2 lvh.me
+
+# The following lines are desirable for IPv6 capable hosts
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
diff --git a/jni/jbcrypto.cpp b/jni/jbcrypto.cpp
index 8bc6fb8d..1c3e3ca9 100644
--- a/jni/jbcrypto.cpp
+++ b/jni/jbcrypto.cpp
@@ -17,7 +17,7 @@
extern "C" {
-jbyteArray Java_de_blinkt_openvpn_OpenVpnManagementThread_rsasign(JNIEnv* env, jclass, jbyteArray from, jint pkeyRef);
+jbyteArray Java_se_leap_openvpn_OpenVpnManagementThread_rsasign(JNIEnv* env, jclass, jbyteArray from, jint pkeyRef);
}
int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
@@ -41,7 +41,7 @@ int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
}
-jbyteArray Java_de_blinkt_openvpn_OpenVpnManagementThread_rsasign(JNIEnv* env, jclass, jbyteArray from, jint pkeyRef) {
+jbyteArray Java_se_leap_openvpn_OpenVpnManagementThread_rsasign(JNIEnv* env, jclass, jbyteArray from, jint pkeyRef) {
// EVP_MD_CTX* ctx = reinterpret_cast<EVP_MD_CTX*>(ctxRef);
EVP_PKEY* pkey = reinterpret_cast<EVP_PKEY*>(pkeyRef);
diff --git a/jni/jniglue.c b/jni/jniglue.c
index 82b54d16..143cd10b 100644
--- a/jni/jniglue.c
+++ b/jni/jniglue.c
@@ -16,7 +16,7 @@ void android_openvpn_log(int level,const char* prefix,const char* prefix_sep,con
__android_log_print(ANDROID_LOG_DEBUG,"openvpn","%s%s%s",prefix,prefix_sep,m1);
}
-void Java_de_blinkt_openvpn_OpenVpnManagementThread_jniclose(JNIEnv *env,jclass jo, jint fd) {
+void Java_se_leap_openvpn_OpenVpnManagementThread_jniclose(JNIEnv *env,jclass jo, jint fd) {
int ret = close(fd);
}
diff --git a/lint.xml b/lint.xml
new file mode 100644
index 00000000..ee0eead5
--- /dev/null
+++ b/lint.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+</lint> \ No newline at end of file
diff --git a/prepareTestProviderAPI.sh b/prepareTestProviderAPI.sh
new file mode 100755
index 00000000..0eca85a9
--- /dev/null
+++ b/prepareTestProviderAPI.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+if [ -z "$1" ]
+then
+ echo Usage: prepareTestProviderAPI.sh \"project folder\"
+ exit 0;
+fi
+
+PROJECT_FOLDER=$1
+hosts_file="hosts-for-tests"
+
+adb shell mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system
+adb push $PROJECT_FOLDER/$hosts_file /system/etc/hosts
+
+echo "Pushed $PROJECT_FOLDER/$hosts_file"
diff --git a/proguard-project.txt b/proguard-project.txt
new file mode 100644
index 00000000..f2fe1559
--- /dev/null
+++ b/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/project.properties b/project.properties
index 895c9ce2..c4f09d2b 100644
--- a/project.properties
+++ b/project.properties
@@ -8,4 +8,4 @@
# project structure.
# Project target.
-target=android-16
+target=android-17
diff --git a/res/drawable-hdpi/ic_stat_vpn.png b/res/drawable-hdpi/ic_stat_vpn.png
index 788cbd32..98d60516 100644
--- a/res/drawable-hdpi/ic_stat_vpn.png
+++ b/res/drawable-hdpi/ic_stat_vpn.png
Binary files differ
diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png
index b7a83e9a..a07db1ef 100644
--- a/res/drawable-hdpi/icon.png
+++ b/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/res/drawable-ldpi/ic_stat_vpn.png b/res/drawable-ldpi/ic_stat_vpn.png
new file mode 100644
index 00000000..4f0ba1df
--- /dev/null
+++ b/res/drawable-ldpi/ic_stat_vpn.png
Binary files differ
diff --git a/res/drawable-ldpi/icon.png b/res/drawable-ldpi/icon.png
new file mode 100644
index 00000000..6f25cd07
--- /dev/null
+++ b/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_stat_vpn.png b/res/drawable-mdpi/ic_stat_vpn.png
index e1dbeb33..e8c99c1c 100644
--- a/res/drawable-mdpi/ic_stat_vpn.png
+++ b/res/drawable-mdpi/ic_stat_vpn.png
Binary files differ
diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png
index 9748722f..4b3456ff 100644
--- a/res/drawable-mdpi/icon.png
+++ b/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_stat_vpn.png b/res/drawable-xhdpi/ic_stat_vpn.png
index 605d9936..51af141a 100644
--- a/res/drawable-xhdpi/ic_stat_vpn.png
+++ b/res/drawable-xhdpi/ic_stat_vpn.png
Binary files differ
diff --git a/res/drawable-xhdpi/icon.png b/res/drawable-xhdpi/icon.png
index 622315b6..dbb53eef 100644
--- a/res/drawable-xhdpi/icon.png
+++ b/res/drawable-xhdpi/icon.png
Binary files differ
diff --git a/res/layout/about.xml b/res/layout/about.xml
index 5820d291..74835d1e 100644
--- a/res/layout/about.xml
+++ b/res/layout/about.xml
@@ -21,7 +21,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
- android:text="@string/copyright_blinktgui" />
+ android:text="@string/copyright_leapgui" />
<Space
android:layout_width="match_parent"
@@ -33,19 +33,6 @@
android:autoLink="all"
android:text="@string/copyright_guicode" />
- <Space
- android:layout_width="match_parent"
- android:layout_height="10sp" />
-
- <TextView
- android:id="@+id/donatestring"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="10sp" />
-
<TextView
android:id="@+id/translation"
android:layout_width="match_parent"
@@ -87,17 +74,6 @@
android:layout_width="match_parent"
android:layout_height="20sp" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/file_dialog"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/copyright_file_dialog" />
-
<Space
android:layout_width="match_parent"
android:layout_height="20sp" />
diff --git a/res/layout/basic_settings.xml b/res/layout/basic_settings.xml
index d56f6ad5..b71ae664 100644
--- a/res/layout/basic_settings.xml
+++ b/res/layout/basic_settings.xml
@@ -1,19 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:leap="http://schemas.android.com/apk/res/se.leap.leapclient"
android:layout_width="wrap_content"
diff --git a/res/layout/client_dashboard.xml b/res/layout/client_dashboard.xml
new file mode 100644
index 00000000..9f05cfbc
--- /dev/null
+++ b/res/layout/client_dashboard.xml
@@ -0,0 +1,70 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/dashboardLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".Dashboard" >
+
+ <LinearLayout
+ android:id="@+id/providerLine"
+ android:layout_width="match_parent"
+ android:layout_height="40dp"
+ android:background="?android:attr/selectableItemBackground" >
+
+ <LinearLayout
+ android:id="@+id/providerLabelWrapper"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingLeft="10dp" >
+
+ <TextView
+ android:id="@+id/providerLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:text="@string/provider_label"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="24sp" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/providerNameWrapper"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingLeft="15dp" >
+
+ <TextView
+ android:id="@+id/providerName"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:text="@string/provider_label_none"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <View
+ android:layout_width="wrap_content"
+ android:layout_height="2dp"
+ android:layout_marginBottom="15dp"
+ android:background="@android:drawable/divider_horizontal_bright" />
+
+ <LinearLayout
+ android:id="@+id/servicesCollection"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.11"
+ android:orientation="vertical" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/configuration_wizard_activity.xml b/res/layout/configuration_wizard_activity.xml
new file mode 100644
index 00000000..264ccf98
--- /dev/null
+++ b/res/layout/configuration_wizard_activity.xml
@@ -0,0 +1,8 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/configuration_wizard_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".ConfigurationWizard" >
+
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/eip_service_fragment.xml b/res/layout/eip_service_fragment.xml
new file mode 100644
index 00000000..e67f5651
--- /dev/null
+++ b/res/layout/eip_service_fragment.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/eipServiceFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp" >
+
+ <TextView
+ android:id="@+id/eipLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="10dp"
+ android:clickable="true"
+ android:text="@string/eip_service_label"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <Switch
+ android:id="@+id/eipSwitch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginRight="10dp" />
+
+ <ProgressBar
+ android:id="@+id/eipProgress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/Widget.Holo.ProgressBar.Horizontal"
+ android:indeterminate="true"
+ android:visibility="gone"
+ android:layout_below="@id/eipLabel"
+ android:layout_marginLeft="15dp"
+ android:layout_marginRight="15dp" />
+
+ <RelativeLayout
+ android:id="@+id/eipDetail"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@+id/eipLabel"
+ android:paddingBottom="10dp"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:paddingTop="10dp"
+ android:visibility="gone" >
+
+ <ImageView
+ android:id="@+id/eipSettings"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_margin="10dp"
+ android:contentDescription="@string/eip_settings_button_description"
+ android:src="@drawable/ic_sysbar_quicksettings" />
+
+ <TextView
+ android:id="@+id/eipStatus"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:clickable="true"
+ android:text="@string/eip_status" />
+
+ </RelativeLayout>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/file_select.xml b/res/layout/file_select.xml
index 0dd1abba..7cc84c40 100644
--- a/res/layout/file_select.xml
+++ b/res/layout/file_select.xml
@@ -1,20 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<!-- A layout to select a certificate, akin to a file selector on web pages. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
diff --git a/res/layout/keystore_selector.xml b/res/layout/keystore_selector.xml
index 211e977b..39b5d11b 100644
--- a/res/layout/keystore_selector.xml
+++ b/res/layout/keystore_selector.xml
@@ -1,20 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<!-- A layout to select a certificate, akin to a file selector on web pages. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
diff --git a/res/layout/log_in_dialog.xml b/res/layout/log_in_dialog.xml
new file mode 100644
index 00000000..4c9fdbad
--- /dev/null
+++ b/res/layout/log_in_dialog.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ tools:context=".LogInDialog" >
+
+ <TextView
+ android:id="@+id/user_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/username_entered"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginTop="16dp"
+ android:ems="10"
+ android:hint="@string/username_ask"
+ android:inputType="textUri" >
+
+ <requestFocus />
+ </EditText>
+
+ <EditText
+ android:id="@+id/password_entered"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="@string/password_ask"
+ android:inputType="textPassword" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/new_provider_dialog.xml b/res/layout/new_provider_dialog.xml
new file mode 100644
index 00000000..19b8f442
--- /dev/null
+++ b/res/layout/new_provider_dialog.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <EditText
+ android:id="@+id/new_provider_url"
+ android:inputType="textUri"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginBottom="4dp"
+ android:hint="@string/new_provider_uri" />
+
+ <CheckBox
+ android:id="@+id/danger_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/danger_checkbox" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/provider_detail_fragment.xml b/res/layout/provider_detail_fragment.xml
new file mode 100644
index 00000000..eb90fad9
--- /dev/null
+++ b/res/layout/provider_detail_fragment.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/provider_detail_domain"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginTop="16dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/provider_detail_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginBottom="4dp"
+ android:textStyle="italic"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/provider_detail_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginBottom="4dp"
+ android:textStyle="normal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/provider_list_fragment.xml b/res/layout/provider_list_fragment.xml
new file mode 100644
index 00000000..0db0734b
--- /dev/null
+++ b/res/layout/provider_list_fragment.xml
@@ -0,0 +1,22 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp">
+
+ <ListView
+ android:id="@id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_alignParentTop="true"
+ android:drawSelectorOnTop="false" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="addAndSelectNewProvider"
+ android:text="@string/new_provider_button" />
+
+</LinearLayout>
diff --git a/res/layout/vpn_list_item.xml b/res/layout/vpn_list_item.xml
index 16d7d5bd..40cfff4a 100644
--- a/res/layout/vpn_list_item.xml
+++ b/res/layout/vpn_list_item.xml
@@ -1,20 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/menu/client_dashboard.xml b/res/menu/client_dashboard.xml
new file mode 100644
index 00000000..55ad91b4
--- /dev/null
+++ b/res/menu/client_dashboard.xml
@@ -0,0 +1,14 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"
+ android:title="@string/menu_settings"/>
+ <item android:id="@+id/about_leap" android:title="@string/about" android:orderInCategory="110" />
+ <item android:id="@+id/legacy_interface" android:title="ICS OpenVPN Interface" android:orderInCategory="500" />
+ <item android:id="@+id/switch_provider" android:title="@string/switch_provider_menu_option" android:orderInCategory="501"/>
+ <item android:id="@+id/login_button" android:title="@string/login_button" android:visible="false"></item>
+ <item android:id="@+id/logout_button" android:title="@string/logout_button" android:visible="false"></item>
+
+</menu> \ No newline at end of file
diff --git a/res/menu/configuration_wizard_activity.xml b/res/menu/configuration_wizard_activity.xml
new file mode 100644
index 00000000..9e441a18
--- /dev/null
+++ b/res/menu/configuration_wizard_activity.xml
@@ -0,0 +1,8 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/about_leap"
+ android:orderInCategory="110"
+ android:title="@string/about"/>
+
+</menu> \ No newline at end of file
diff --git a/res/raw/leapkeystore.bks b/res/raw/leapkeystore.bks
new file mode 100644
index 00000000..f4d61fbf
--- /dev/null
+++ b/res/raw/leapkeystore.bks
Binary files differ
diff --git a/res/raw/leapkeystore_bc147.bks b/res/raw/leapkeystore_bc147.bks
new file mode 100644
index 00000000..a6eaf30d
--- /dev/null
+++ b/res/raw/leapkeystore_bc147.bks
Binary files differ
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index b9f2f07c..5131250a 100755
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN per Android</string>
+ <string name="app">LEAP per Android</string>
<string name="address">Adreá del servidor:</string>
<string name="port">Port del servidor:</string>
<string name="location">Lloc</string>
@@ -15,7 +15,7 @@
<string name="client_pkcs12_title">Fitxer PKCS12</string>
<string name="ca_title">Certificat CA</string>
<string name="about">Quan a</string>
- <string name="about_summary">Quan a OpenVPN per Android</string>
+ <string name="about_summary">Quan a LEAP per Android</string>
<string name="vpn_list_summary">Llista de VPNs configurades</string>
<string name="vpn_list_title">Perfils VPN</string>
<string name="vpn_type">Tipus</string>
@@ -64,7 +64,7 @@
<string name="routes_info6">Rutes IPv6: %s</string>
<string name="send_logfile">Envia el fitxer de registre</string>
<string name="send">Envia</string>
- <string name="ics_openvpn_log_file">Fitxer de registre de ICS OpenVPN</string>
+ <string name="ics_openvpn_log_file">Fitxer de registre de LEAP Android</string>
<string name="copied_entry">S\'ha copiat l\'entrada al porta-retalls</string>
<string name="tap_mode">Mode Tap</string>
<string name="faq_tap_mode">No es pot utiltizar el mode tap amb la api no rootejada. L\'aplicació no suporta tap</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 5a1039bc..6d2c6a5a 100755
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN pro Android</string>
+ <string name="app">LEAP pro Android</string>
<string name="address">Adresa serveru:</string>
<string name="port">Port serveru:</string>
<string name="location">Lokace</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">PKCS12 soubor</string>
<string name="ca_title">CA certifikát</string>
<string name="no_certificate">Je třeba vybrat certifikát</string>
- <string name="copyright_guicode">Zdrojové kódy a seznam problémů je na http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">Zdrojové kódy a seznam problémů je na https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">Tento program používá následující komponenty; viz zdrojový kód pro detaily o licenci</string>
<string name="about">O programu</string>
- <string name="about_summary">O programu OpenVPN pro Android</string>
+ <string name="about_summary">O programu LEAP pro Android</string>
<string name="vpn_list_summary">Seznam všech nakonfigurovaných VPN</string>
<string name="vpn_list_title">VÅ¡echny VPN</string>
<string name="vpn_type">Typ</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Odeslat soubor s logem</string>
<string name="send">Odeslat</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN logovací soubor</string>
+ <string name="ics_openvpn_log_file">LEAP Android logovací soubor</string>
<string name="copied_entry">Záznam z logu zkopírován do schránky</string>
<string name="tap_mode">Tap mód</string>
<string name="faq_tap_mode">Tap mód není možný bez rootovského VPN API, proto tato aplikace nemá podporu pro tap</string>
@@ -202,7 +202,7 @@
<string name="using_proxy">Používám proxy %1$s %2$d</string>
<string name="use_system_proxy">Použít systémovou proxy</string>
<string name="use_system_proxy_summary">K připojení použít systémové nastavení pro HTTP/HTTPS.</string>
- <string name="donatewithpaypal">K přispění můžeš využít &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPN se opÄ›tovnÄ› pÅ™ipojí k VPN, pokud byla aktivní pÅ™ed vypnutím/restartem systému. PÅ™eÄti si oddíl o varování pÅ™ed pÅ™ipojením než použijeÅ¡ tuto možnost.</string>
<string name="onbootrestart">Znovu připoj po restartu</string>
<string name="ignore">Ignorovat</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 4f8d8ca8..f079ba53 100755
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN für Android</string>
+ <string name="app">LEAP für Android</string>
<string name="address">Server:</string>
<string name="port">Server Port:</string>
<string name="location">Ort</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">PKCS12 Datei</string>
<string name="ca_title">CA Zertifikat</string>
<string name="no_certificate">Kein Zertifikat ausgewählt</string>
- <string name="copyright_guicode">Quellcode und Issue Tracker sind verfügbar unter http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">Quellcode und Issue Tracker sind verfügbar unter https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">Dieses Programm nutzt die folgenden Komponenten. Die kompletten Lizenzdetails sind im Quelltext verfügbar.</string>
<string name="about">Ãœber</string>
- <string name="about_summary">Über OpenVPN für Android</string>
+ <string name="about_summary">Über LEAP für Android</string>
<string name="vpn_list_summary">Alle konfigurierten VPN Profile</string>
<string name="vpn_list_title">VPN Liste</string>
<string name="vpn_type">Typ</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Sende Logdatei</string>
<string name="send">Sende</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN log Datei</string>
+ <string name="ics_openvpn_log_file">LEAP Android log Datei</string>
<string name="copied_entry">Log Eintrag in die Zwischenablage kopiert</string>
<string name="tap_mode">Tap Mode</string>
<string name="faq_tap_mode">Die VPN API von Android, die ohne rooten des Telefons funktioniert, unterstützt nur den tun Modus. Das Unterstützen des Tap Modus ist daher nicht möglich.</string>
@@ -202,7 +202,7 @@
<string name="using_proxy">Benutzt Proxy %1$s %2$d</string>
<string name="use_system_proxy">Benutze System Proxys</string>
<string name="use_system_proxy_summary">Benutze die System weiten Einstellungen für HTTP/HTTPS Proxys beim Verbinden.</string>
- <string name="donatewithpaypal">&lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;Spenden mit PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPN wird bei einem Neustart des Telefon das beim herunterfahren/neu starten aktive VPN wieder verbinden. Bitte lesen Sie die FAQ \"Warnung beim Verbinden\" FAQ bevor Sie diese Option verwenden.</string>
<string name="onbootrestart">Nach Neustart verbinden</string>
<string name="ignore">Ignorieren</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 521985db..e1979c05 100755
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN para Android</string>
+ <string name="app">LEAP para Android</string>
<string name="address">Dirección del servidor:</string>
<string name="port">Puerto del servidor:</string>
<string name="location">Ubicación</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">Archivo PKCS12</string>
<string name="ca_title">Certificado de la CA</string>
<string name="no_certificate">Debe seleccionar un certificado</string>
- <string name="copyright_guicode">Codigo fuente y sistema de reporte de errores disponibles en http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">Codigo fuente y sistema de reporte de errores disponibles en https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">El programa utiliza los siguientes componentes. Vea los códigos fuentes para obtener más información sobre las licencias</string>
<string name="about">Acerca de</string>
- <string name="about_summary">Acerca de OpenVPN para Android</string>
+ <string name="about_summary">Acerca de LEAP para Android</string>
<string name="vpn_list_summary">Lista de todas las VPN configuradas</string>
<string name="vpn_list_title">Perfiles VPN</string>
<string name="vpn_type">Tipo</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Enviar el archivo de registro</string>
<string name="send">Enviar</string>
- <string name="ics_openvpn_log_file">Archivo de registro de OpenVPN de ICS</string>
+ <string name="ics_openvpn_log_file">Archivo de registro de LEAP de Android</string>
<string name="copied_entry">Entrada de registro copiada al Portapapeles</string>
<string name="tap_mode">Modo Tap</string>
<string name="faq_tap_mode">El Modo tap no es posible sin la API VPN de root. Por lo tanto la aplicacion no puede dar soporte a tap</string>
@@ -199,7 +199,7 @@
<string name="using_proxy">Usando proxy %1$s %2$d</string>
<string name="use_system_proxy">Usar el proxy del sistema</string>
<string name="use_system_proxy_summary">Utilice la configuración del sistema para los proxies HTTP/HTTPS a conectar.</string>
- <string name="donatewithpaypal">Usted puede &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;donar con PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPN volvera a conectar a una VPN si estaba activa en el apagado/reinicio del sistema. Por favor lea la P+F de advertencia de conexión antes de usar esta opción.</string>
<string name="onbootrestart">Vuelva a conectar al reiniciar</string>
<string name="ignore">Ignorar</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 9a4c6717..4e13e539 100755
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN Androidile</string>
+ <string name="app">LEAP Androidile</string>
<string name="address">Serveri aadress:</string>
<string name="port">Serveri port:</string>
<string name="location">Asukoht</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">PKCS12 fail</string>
<string name="ca_title">CA sertifikaat</string>
<string name="no_certificate">Peate valima sertifikaadi</string>
- <string name="copyright_guicode">Lähtetekst ja probleemihaldur asuvad veebilehel http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">Lähtetekst ja probleemihaldur asuvad veebilehel https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">Programmis kasutatakse järgnevaid komponente. Detailse litsenseerimisinfo leiate lähtekoodist</string>
<string name="about">Lähemalt</string>
- <string name="about_summary">Täpsemalt programmist OpenVPN Androidile</string>
+ <string name="about_summary">Täpsemalt programmist LEAP Androidile</string>
<string name="vpn_list_summary">Kõigi seadistatud VPN kanalite nimekiri</string>
<string name="vpn_list_title">VPN profiilid</string>
<string name="vpn_type">Tüüp</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Saada logifail</string>
<string name="send">Saada</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN logifail</string>
+ <string name="ics_openvpn_log_file">LEAP Androidile logifail</string>
<string name="copied_entry">Logikirje kopeeriti lõikepuhvrisse</string>
<string name="tap_mode">Tap liides</string>
<string name="faq_tap_mode">VPNService API ei toeta tap liidest. Seega ei ole ruutimata seadmel selle programmiga tap liidese kasutamine võimalik</string>
@@ -202,7 +202,7 @@
<string name="using_proxy">Kasutusel proxy %1$s %2$d</string>
<string name="use_system_proxy">Kasuta süsteemset proxy\'t</string>
<string name="use_system_proxy_summary">Kasuta ühendumisel süsteemse HTTP/HTTPS proxy konfiguratsiooni.</string>
- <string name="donatewithpaypal">Sul on võimalus &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;annetada PayPal vahendusel&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">Kui VPN oli süsteemi uuestilaadimisel/sulgemisel aktiivne siis taastatakse seadme käivitamisel OpenVPN ühendus. Palun lugege enne selle valiku kasutamist läbi ühendumise hoiatuse KKK.</string>
<string name="onbootrestart">Uuestilaadimisel ühendu uuesti</string>
<string name="ignore">Ignoreeri</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 9da62050..f031c5fd 100755
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">"OpenVPN pour Android"</string>
+ <string name="app">"LEAP pour Android"</string>
<string name="address">"Adresse du serveur:"</string>
<string name="port">"Port:"</string>
<string name="location">"Emplacement"</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">"Fichier PKCS12"</string>
<string name="ca_title">"Certificat CA"</string>
<string name="no_certificate">"Vous devez sélectionner un certificat"</string>
- <string name="copyright_guicode">"Le code source et le tracker de bugs est disponible ici: http://code.google.com/p/ics-openvpn/ "</string>
+ <string name="copyright_guicode">"Le code source et le tracker de bugs est disponible ici: https://github.com/leapcode/leap_android/ "</string>
<string name="copyright_others">"Le programme utilise les composants suivants. Voir le code source pour plus de détails sur les licences."</string>
<string name="about">"À propos"</string>
- <string name="about_summary">"À propos d\'OpenVPN pour Android"</string>
+ <string name="about_summary">"À propos d\'LEAP pour Android"</string>
<string name="vpn_list_summary">"Liste de tous les VPN configurés"</string>
<string name="vpn_list_title">"Vos VPNs"</string>
<string name="vpn_type">"Type"</string>
@@ -109,7 +109,7 @@
<string name="version_info">"%1$s %2$s"</string>
<string name="send_logfile">"Envoyer le fichier de log"</string>
<string name="send">"Envoyer"</string>
- <string name="ics_openvpn_log_file">"Fichier de log OpenVPN ICS"</string>
+ <string name="ics_openvpn_log_file">"Fichier de log LEAP Android"</string>
<string name="copied_entry">"Entrée du log copiée"</string>
<string name="tap_mode">"Mode TAP"</string>
<string name="faq_tap_mode">"Le mode TAP est indisponible avec l\'API non root VPN. Par conséquent, cette application ne peut pas supporter TAP"</string>
@@ -202,7 +202,7 @@ Sur certaines images, cette notification joue un son.\nAndroid à introduit ces
<string name="using_proxy">"Utilisation du proxy %1$s %2$d"</string>
<string name="use_system_proxy">"Utiliser le proxy système"</string>
<string name="use_system_proxy_summary">"Utiliser la configuration générale du système pour que les proxy HTTP / HTTPS se connectent."</string>
- <string name="donatewithpaypal">"Vous pouvez faire un &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;cmd=_s-xclick\"&gt;don avec PayPal&lt;/ a&gt; "</string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">"Reconnecter OpenVPN automatiquement si une connexion était active lors de l\'extinction/redémarrage de l\'appareil. Veuillez lire l\'avertissement de connexion dans la FAQ avant d\'utiliser cette option."</string>
<string name="onbootrestart">"Connexion automatique au redémarrage"</string>
<string name="ignore">"Ignorer"</string>
diff --git a/res/values-id/strings.xml b/res/values-id/strings.xml
index 857cde22..fbf2231e 100755
--- a/res/values-id/strings.xml
+++ b/res/values-id/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN untuk Android</string>
+ <string name="app">LEAP untuk Android</string>
<string name="address">Alamat Server:</string>
<string name="port">Port server:</string>
<string name="location">Lokasi</string>
@@ -19,7 +19,7 @@
<string name="copyright_guicode">Kode program dan perekam masalah tersedia di</string>
<string name="copyright_others">Aplikasi memakai komponen berikut; lihat kode program untuk lebih jelas mengenai lisensi</string>
<string name="about">Tentang...</string>
- <string name="about_summary">Tentang OpenVPN untuk Android</string>
+ <string name="about_summary">Tentang LEAP untuk Android</string>
<string name="vpn_list_summary">Daftar konfigurasi VPN</string>
<string name="vpn_list_title">Profil VPN</string>
<string name="vpn_type">Tipe</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Kirim berkas catatan</string>
<string name="send">Kirim</string>
- <string name="ics_openvpn_log_file">Berkas catatan ICS OpenVPN</string>
+ <string name="ics_openvpn_log_file">Berkas catatan LEAP Android</string>
<string name="copied_entry">Salin catatan masuk ke clipboard</string>
<string name="tap_mode">Mode TAP</string>
<string name="faq_tap_mode">Mode TAP tidak diijinkan tanpa VPN API non admin/root. Karena itu aplikasi ini tidak dapat memberikan dukungan mode TAP</string>
@@ -202,7 +202,7 @@
<string name="using_proxy">Menggunakan proxy %1$ s %2$ d</string>
<string name="use_system_proxy">Gunakan sistem proxy</string>
<string name="use_system_proxy_summary">Gunakan konfigurasi lebih luas untuk menyambung system melalui proxy HTTP/HTTPS</string>
- <string name="donatewithpaypal">Anda dapat melakukan donasi &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;dengan PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPN akan menyambung kembali VPN jika VPN aktif pada saat sistem reboot/shutdown. Silakan baca FAQ tentang peringatan sambungan sebelum menggunakan pilihan ini.</string>
<string name="onbootrestart">Koneksi ulang saat perangkat dihidupkan kembali</string>
<string name="ignore">Abaikan</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index a8f39816..84072b1e 100755
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN per Android</string>
+ <string name="app">LEAP per Android</string>
<string name="address">Indirizzo server:</string>
<string name="port">Porta del server:</string>
<string name="location">Posizione</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">File PKCS12</string>
<string name="ca_title">Certificato CA</string>
<string name="no_certificate">Devi selezionare un certificato</string>
- <string name="copyright_guicode">Il codice sorgente e il bug tracker sono disponibili all\'indirizzo http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">Il codice sorgente e il bug tracker sono disponibili all\'indirizzo https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">Questo programma usa i seguenti componenti; guarda il codice sorgente per i dettagli completi sulle licenze</string>
<string name="about">Informazioni</string>
- <string name="about_summary">Informazioni su OpenVPN per Android</string>
+ <string name="about_summary">Informazioni su LEAP per Android</string>
<string name="vpn_list_summary">Elenco connessioni VPN configurate</string>
<string name="vpn_list_title">Profili VPN</string>
<string name="vpn_type">Tipo</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Invia il file di log</string>
<string name="send">Invia</string>
- <string name="ics_openvpn_log_file">File log di OpenVPN ICS</string>
+ <string name="ics_openvpn_log_file">File log di LEAP Android</string>
<string name="copied_entry">Voce di registro copiata negli appunti</string>
<string name="tap_mode">Modalità TAP</string>
<string name="faq_tap_mode">La modalità TAP non è disponibile con le VPN API non root</string>
@@ -205,7 +205,7 @@ nel bug tracker).&lt;/p&gt;&lt;p&gt;Si è visto che ai firmware ufficiali della
<string name="using_proxy">Si sta utilizzando il proxy %1$s %2$d</string>
<string name="use_system_proxy">Utilizza il proxy di sistema</string>
<string name="use_system_proxy_summary">Utilizza la configurazione generale del sistema relativa ai proxy HTTP/HTTPS per connettersi.</string>
- <string name="donatewithpaypal">Puoi &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;donare tramite PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPN is riconnetterà alla VPN se era in funzione durante un riavvio od un spegnimento dell\'apparecchio. Leggi con attenzione le FAQ con gli avvertimenti sulla connessione prima di scegliere questa opzione.</string>
<string name="onbootrestart">Riconnetti al riavvio</string>
<string name="ignore">Ignora</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index aec401af..170b6c25 100755
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN for Android</string>
+ <string name="app">LEAP Android</string>
<string name="address">サーãƒã‚¢ãƒ‰ãƒ¬ã‚¹</string>
<string name="port">ãƒãƒ¼ãƒˆç•ªå·</string>
<string name="location">場所</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">PKCS12ファイル</string>
<string name="ca_title">CA 証明書</string>
<string name="no_certificate">証明書をé¸æŠžã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚</string>
- <string name="copyright_guicode">ソースコードã¨å•é¡Œç®¡ç†ã¯ä»¥ä¸‹ã§ï¼š http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">ソースコードã¨å•é¡Œç®¡ç†ã¯ä»¥ä¸‹ã§ï¼š https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">プログラムã¯ã€æ¬¡ã®ã‚³ãƒ³ãƒãƒ¼ãƒãƒ³ãƒˆã‚’使用ã—ã¾ã™ã€‚完全ãªè©³ç´°ã«ã¤ã„ã¦ã¯ã‚½ãƒ¼ã‚¹ä¸Šã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã‚’å‚ç…§ã—ã¦ãã ã•ã„。</string>
<string name="about">ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…å ±</string>
- <string name="about_summary">OpenVPN for Androidã«ã¤ã„ã¦</string>
+ <string name="about_summary">LEAP Androidã«ã¤ã„ã¦</string>
<string name="vpn_list_summary">設定ã•ã‚ŒãŸã™ã¹ã¦ã®VPN</string>
<string name="vpn_list_title">VPNプロファイル</string>
<string name="vpn_type">種別</string>
@@ -111,7 +111,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">ログ ファイルをé€ä¿¡ã—ã¾ã™ã€‚</string>
<string name="send">é€ä¿¡</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN ログ ファイル</string>
+ <string name="ics_openvpn_log_file">LEAP Android ログ ファイル</string>
<string name="copied_entry">クリップ ボードã«ã‚³ãƒ”ーã•ã‚ŒãŸãƒ­ã‚° エントリ</string>
<string name="tap_mode">TAPモード</string>
<string name="faq_tap_mode">TAPモードã¯éžroot化環境ã§ã¯å‹•ä½œã—ã¾ã›ã‚“。よã£ã¦ã“ã®ã‚¢ãƒ—リケーションã§ã¯TAPをサãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“。</string>
@@ -222,7 +222,7 @@ Androidã¯ã‚ãªãŸè‡ªèº«ã®å®‰å…¨æ€§ã®ãŸã‚ã«ã€ã“れらを迂回ã§ããªã
<string name="using_proxy">プロキシを使用ã—ã¾ã™ %1$s %2$d</string>
<string name="use_system_proxy">システムã®ãƒ—ロキシ設定を使用</string>
<string name="use_system_proxy_summary">システム全体ã®æ§‹æˆã® HTTP/HTTPS プロキシ接続を使用ã—ã¾ã™ã€‚</string>
- <string name="donatewithpaypal">以下ã®URLより寄付ã„ãŸã ã‘ã¾ã™ã€‚ &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;PayPalã§å¯„付&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPNã¯ã‚·ã‚¹ãƒ†ãƒ ã®å†èµ·å‹•ã‚„シャットダウン時ã«å†æŽ¥ç¶šã™ã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã™ã€‚ã“ã®ã‚ªãƒ—ションを使用ã™ã‚‹å‰ã«FAQã‚’ã”一読ãã ã•ã„。</string>
<string name="onbootrestart">システム起動後ã«å†æŽ¥ç¶š</string>
<string name="ignore">無視</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index dd83c09d..fbf069d3 100755
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">안드로ì´ë“œìš© OpenVPN</string>
+ <string name="app">안드로ì´ë“œìš© LEAP</string>
<string name="address">서버 주소:</string>
<string name="port">서버 í¬íŠ¸:</string>
<string name="location">위치</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">PKCS12 파ì¼</string>
<string name="ca_title">CA ì¸ì¦ì„œ</string>
<string name="no_certificate">ì¸ì¦ì„œë¥¼ ì„ íƒ í•´ì•¼ 합니다</string>
- <string name="copyright_guicode">소스 코드와 문제 추ì ê¸°ëŠ” http://code.google.com/p/ics-openvpn/ì—ì„œ 사용할 수 있습니다</string>
+ <string name="copyright_guicode">소스 코드와 문제 추ì ê¸°ëŠ” https://github.com/leapcode/leap_android/ì—ì„œ 사용할 수 있습니다</string>
<string name="copyright_others">í”„ë¡œê·¸ëž¨ì€ ë‹¤ìŒ êµ¬ì„± 요소를 사용합니다. ë¼ì´ì„ ìŠ¤ì— 대 í•œ ìžì„¸í•œ ë‚´ìš©ì€ ì†ŒìŠ¤ë¥¼ 참조 하십시오</string>
<string name="about">소개</string>
- <string name="about_summary">안드로ì´ë“œìš© OpenVPN 소개</string>
+ <string name="about_summary">안드로ì´ë“œìš© LEAP 소개</string>
<string name="vpn_list_summary">ì„¤ì •ëœ VPNì˜ ëª©ë¡</string>
<string name="vpn_list_title">VPN 프로파ì¼</string>
<string name="vpn_type">유형</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">로그 íŒŒì¼ ë³´ë‚´ê¸°</string>
<string name="send">보내기</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN 로그 파ì¼</string>
+ <string name="ics_openvpn_log_file">LEAP 로그 파ì¼</string>
<string name="copied_entry">í´ë¦½ë³´ë“œë¡œ 로그 복사</string>
<string name="tap_mode">Tap 모드</string>
<string name="faq_tap_mode">Tap 모드는 루트가 ì•„ë‹Œ VPN APIì—서는 불가능합니다. ë”°ë¼ì„œ ë³¸ì•±ì€ tap지ì›ì„ 제공할 수 없습니다</string>
@@ -202,7 +202,7 @@
<string name="using_proxy">프ë¡ì‹œ %1$s %2$d ì„ ì‚¬ìš©</string>
<string name="use_system_proxy">시스템 프ë¡ì‹œë¥¼ 사용</string>
<string name="use_system_proxy_summary">ì—°ê²°ì‹œ ì „ 시스템 ì„¤ì •ì— ìžˆëŠ” HTTP/HTTPS 프ë¡ì‹œë¥¼ 사용합니다.</string>
- <string name="donatewithpaypal">ë‹¹ì‹ ì€ &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;PayPalì„ ì‚¬ìš©í•˜ì—¬ 기부할 수 있습니다.&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPNì€ ì‹œìŠ¤í…œ 재부팅/ì¢…ë£Œì— í™œì„±í™” ë˜ì—ˆìœ¼ë©´ VPNì„ ë‹¤ì‹œ 연결합니다. ì´ ì˜µì…˜ì„ ì‚¬ìš© 하기 ì „ì— ì—°ê²° 경고 FAQ를 ì½ì–´ 보시기 ë°”ëžë‹ˆë‹¤.</string>
<string name="onbootrestart">재부팅시 다시 연결</string>
<string name="ignore">무시</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 5403ae06..459c80ec 100755
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN voor Android</string>
+ <string name="app">LEAP voor Android</string>
<string name="address">Server Adres:</string>
<string name="port">Server Poort:</string>
<string name="location">Locatie</string>
@@ -15,7 +15,7 @@
<string name="client_pkcs12_title">PKCS12 Bestand</string>
<string name="ca_title">CA Certificaat</string>
<string name="about">Over</string>
- <string name="about_summary">Over OpenVPN voor Android</string>
+ <string name="about_summary">Over LEAP voor Android</string>
<string name="vpn_list_summary">Lijst van alle geconfigureerde VPN verbindingen</string>
<string name="vpn_list_title">VPN Profielen</string>
<string name="vpn_type">Type</string>
diff --git a/res/values-no/strings.xml b/res/values-no/strings.xml
index 1ee26c0d..99b97277 100755
--- a/res/values-no/strings.xml
+++ b/res/values-no/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN for Android</string>
+ <string name="app">LEAP Android</string>
<string name="address">Server adresse:</string>
<string name="port">Server port:</string>
<string name="location">Plassering</string>
@@ -15,7 +15,7 @@
<string name="client_pkcs12_title">PKCS12 fil</string>
<string name="ca_title">CA-sertifikat</string>
<string name="about">Om</string>
- <string name="about_summary">Om OpenVPN for Android</string>
+ <string name="about_summary">Om LEAP Android</string>
<string name="vpn_list_summary">Liste over alle konfigurerte VPN-tilkoblinger</string>
<string name="vpn_list_title">VPN-profiler</string>
<string name="vpn_type">Type</string>
@@ -63,7 +63,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Send loggfilen</string>
<string name="send">Send</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN loggfil</string>
+ <string name="ics_openvpn_log_file">LEAP Android loggfil</string>
<string name="tap_mode">Tap modus</string>
<string name="faq">FAQ</string>
<string name="faq_summary">Vanlige spørsmål og noen råd</string>
@@ -124,7 +124,7 @@
<string name="using_proxy">Bruker proxy %1$s %2$d</string>
<string name="use_system_proxy">Bruk systemet proxy</string>
<string name="use_system_proxy_summary">Bruk global systemkonfigurasjon for HTTP/HTTPS proxy for å koble til.</string>
- <string name="donatewithpaypal">Du kan &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;donere med PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestart">Koble til på nytt ved restart</string>
<string name="ignore">Ignorer</string>
<string name="restart">Start på nytt</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 842d9352..43d6c17f 100755
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN pentru Android</string>
+ <string name="app">LEAP pentru Android</string>
<string name="address">Adresa server:</string>
<string name="port">Port server:</string>
<string name="location">Locaţie</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">FiÅŸier PKCS12</string>
<string name="ca_title">Certificat CA</string>
<string name="no_certificate">Trebuie să selectați un certificat</string>
- <string name="copyright_guicode">Cod sursă şi tracker probleme disponibile la http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">Cod sursă şi tracker probleme disponibile la https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">Acest program utilizează următoarele componente; a se vedea codul sursă pentru mai multe detalii despre licente</string>
<string name="about">Despre</string>
- <string name="about_summary">Despre OpenVPN pentru Android</string>
+ <string name="about_summary">Despre LEAP pentru Android</string>
<string name="vpn_list_summary">Lista tuturor VPN-urilor configurate</string>
<string name="vpn_list_title">Profile VPN</string>
<string name="vpn_type">Tip</string>
@@ -107,7 +107,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Trimite fiÅŸier jurnal</string>
<string name="send">Trimite</string>
- <string name="ics_openvpn_log_file">FiÅŸier jurnal OpenVPN ICS</string>
+ <string name="ics_openvpn_log_file">FiÅŸier jurnal LEAP Android</string>
<string name="tap_mode">Mod Tap</string>
<string name="faq">FAQ</string>
<string name="copying_log_entries">Copiere linii jurnal</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index a4a9feed..ee1fed82 100755
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN Ð´Ð»Ñ Android</string>
+ <string name="app">LEAP Ð´Ð»Ñ Android</string>
<string name="address">ÐÐ´Ñ€ÐµÑ Ñервера:</string>
<string name="port">Порт Ñервера:</string>
<string name="location">РаÑположение</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">PKCS12 файл</string>
<string name="ca_title">CA Ñертификат</string>
<string name="no_certificate">Вам необходимо выбрать Ñертификат</string>
- <string name="copyright_guicode">ИÑходники и Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ верÑиÑÑ… находÑÑ‚ÑÑ Ð¿Ð¾ адреÑу http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">ИÑходники и Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ верÑиÑÑ… находÑÑ‚ÑÑ Ð¿Ð¾ адреÑу https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">Ð”Ð°Ð½Ð½Ð°Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð° иÑпользует Ñледующие компоненты; Ñмотрите иÑходный код Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ð¹ информации о лицензии</string>
<string name="about">О программе</string>
- <string name="about_summary">ОпиÑание OpenVPN Ð´Ð»Ñ Android</string>
+ <string name="about_summary">ОпиÑание LEAP Ð´Ð»Ñ Android</string>
<string name="vpn_list_summary">СпиÑок вÑех туннелей VPN</string>
<string name="vpn_list_title">Профили VPN</string>
<string name="vpn_type">Тип</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Отправить файл журнала</string>
<string name="send">Отправить</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN файла лога</string>
+ <string name="ics_openvpn_log_file">LEAP Android файла лога</string>
<string name="copied_entry">Скопировать лог в буфер обмена</string>
<string name="tap_mode">Режим TAP</string>
<string name="faq_tap_mode">Режим TAP невозможен на уÑтройÑтвах без root-а. ПоÑтому Ñто приложение не поддерживает TAP</string>
@@ -202,7 +202,7 @@
<string name="using_proxy">ИÑпользуетÑÑ Ð¿Ñ€Ð¾ÐºÑи-Ñервер %1$s %2$d</string>
<string name="use_system_proxy">ИÑпользовать прокÑи-Ñервер ÑиÑтемы</string>
<string name="use_system_proxy_summary">ИÑпользовать ÑиÑтемную конфигурацию прокÑи HTTP/HTTPS Ð´Ð»Ñ ÑоединениÑ.</string>
- <string name="donatewithpaypal">Ð’Ñ‹ можете &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;пожертвовать Ñ PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPN будет переподключатьÑÑ, еÑли он был активен в момент выключениÑ/перезагрузки. ПожалуйÑта, прочтите FAQ перед тем, как иÑпользовать Ñту наÑтройку.</string>
<string name="onbootrestart">Переподключение поÑле перезагрузки</string>
<string name="ignore">Игнорировать</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 54f48fa4..20cd0d6a 100755
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN Ð´Ð»Ñ Android</string>
+ <string name="app">LEAP Ð´Ð»Ñ Android</string>
<string name="address">ÐдреÑа Ñервера:</string>
<string name="port">Порт Ñервера:</string>
<string name="location">РозташуваннÑ</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">Файл PKCS12</string>
<string name="ca_title">Сертифікат CA</string>
<string name="no_certificate">Потрібно вибрати Ñертифікат</string>
- <string name="copyright_guicode">Початковий код Ñ– відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ доÑтупні по http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">Початковий код Ñ– відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ доÑтупні по https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">Ð¦Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð° викориÑтовує такі компоненти; переглÑньте вихідний код Ð´Ð»Ñ Ð¿Ð¾Ð²Ð½Ð¾Ñ— інформації про ліцензії</string>
<string name="about">Про</string>
- <string name="about_summary">Про OpenVPN Ð´Ð»Ñ Android</string>
+ <string name="about_summary">Про LEAP Ð´Ð»Ñ Android</string>
<string name="vpn_list_summary">СпиÑок вÑÑ–Ñ… налаштованих VPN</string>
<string name="vpn_list_title">VPN профілі</string>
<string name="vpn_type">Тип</string>
@@ -110,7 +110,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">ÐадіÑлати файл журналу</string>
<string name="send">ÐадіÑлати</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN файл журналу</string>
+ <string name="ics_openvpn_log_file">LEAP Android файл журналу</string>
<string name="copied_entry">Скопійовано Ð·Ð°Ð¿Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ñƒ до буферу обміну</string>
<string name="tap_mode">TAP режим</string>
<string name="faq_tap_mode">TAP режим не можливий в VPN API без рут. З цієї причини цей додаток не може надати підтримку TAP</string>
@@ -202,7 +202,7 @@
<string name="using_proxy">ВикориÑтовуєтьÑÑ Ð¿Ñ€Ð¾ÐºÑÑ– %1$s %2$d</string>
<string name="use_system_proxy">ВикориÑтовувати ÑиÑтемний прокÑÑ–</string>
<string name="use_system_proxy_summary">ВикориÑтовувати ÑиÑтемну конфігурацію HTTP/HTTPS прокÑÑ– Ð´Ð»Ñ Ð·\'єднаннÑ.</string>
- <string name="donatewithpaypal">Ви можете &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;пожертвувати через PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPN буде перепідключатиÑÑ, Ñкщо він був активний в момент вимиканнÑ/перезавантаженнÑ. Будь лаÑка, прочитайте ЧаП перед тим, Ñк викориÑтовувати цей параметр.</string>
<string name="onbootrestart">Перепідключати при перезавантаженні</string>
<string name="ignore">Ігнорувати</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 74a672ad..0d6f4d2c 100755
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN Android版</string>
+ <string name="app">LEAP Android版</string>
<string name="address">æœåŠ¡å™¨åœ°å€ï¼š</string>
<string name="port">æœåŠ¡å™¨ç«¯å£ï¼š</string>
<string name="location">地点</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">PKCS12 文件</string>
<string name="ca_title">CA è¯ä¹¦</string>
<string name="no_certificate">您必须选择一个è¯ä¹¦</string>
- <string name="copyright_guicode">请å‰å¾€ http://code.google.com/p/ics-openvpn/ æºç æˆ–æ供问题å馈</string>
+ <string name="copyright_guicode">请å‰å¾€ https://github.com/leapcode/leap_android/ æºç æˆ–æ供问题å馈</string>
<string name="copyright_others">本程åºä½¿ç”¨ä»¥ä¸‹ç»„件,请在 Licenses 查看æºç èŽ·å–更详细内容。</string>
<string name="about">关于</string>
- <string name="about_summary">关于 OpenVPN for Android</string>
+ <string name="about_summary">关于 LEAP Android</string>
<string name="vpn_list_summary">已完æˆé…置的 VPN 列表</string>
<string name="vpn_list_title">VPN é…置文件</string>
<string name="vpn_type">类型</string>
@@ -98,7 +98,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">å‘é€æ—¥å¿—文件</string>
<string name="send">å‘é€</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN 日志文件</string>
+ <string name="ics_openvpn_log_file">LEAP Android 日志文件</string>
<string name="copied_entry">日志æ¡ç›®å·²å¤åˆ¶å‰ªè´´æ¿</string>
<string name="tap_mode">Tap 模å¼</string>
<string name="faq">常è§é—®é¢˜</string>
@@ -168,7 +168,7 @@
<string name="getproxy_error">获å–代ç†è®¾ç½®æ—¶å‡ºé”™ï¼š%s</string>
<string name="using_proxy">ä½¿ç”¨ä»£ç† %1$s %2$d</string>
<string name="use_system_proxy">使用系统代ç†</string>
- <string name="donatewithpaypal">您å¯ä»¥ &lt; href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS &amp; amp; cmd = _s xclick\"&gt; 通过 PayPal æ助&lt;/a&gt;</string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestart">é‡å¯æ—¶é‡æ–°è¿žæŽ¥</string>
<string name="ignore">忽略</string>
<string name="restart">é‡å¯</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 23e9c002..76404d12 100755
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<resources>
- <string name="app">OpenVPN for Android</string>
+ <string name="app">LEAP Android</string>
<string name="address">伺æœå™¨åœ°å€:</string>
<string name="port">伺æœå™¨ç«¯å£:</string>
<string name="location">ä½ç½®</string>
@@ -16,10 +16,10 @@
<string name="client_pkcs12_title">PKCS12 檔案</string>
<string name="ca_title">CA證書</string>
<string name="no_certificate">您必須é¸æ“‡ä¸€å€‹æ†‘è­‰</string>
- <string name="copyright_guicode">å–得原始碼與個案追蹤,å¯ä¸Š http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">å–得原始碼與個案追蹤,å¯ä¸Š https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">本程åºä½¿ç”¨äº†ä»¥ä¸‹å…ƒä»¶ï¼Œå…¶ä½œè€…和授權資訊如下</string>
<string name="about">關於</string>
- <string name="about_summary">關於 OpenVPN for Android</string>
+ <string name="about_summary">關於 LEAP Android</string>
<string name="vpn_list_summary">列出所有已設置的VPN</string>
<string name="vpn_list_title">VPN設定檔</string>
<string name="vpn_type">é¡žåž‹</string>
@@ -99,7 +99,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">分享記錄檔</string>
<string name="send">分享</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN 記錄檔</string>
+ <string name="ics_openvpn_log_file">LEAP Android 記錄檔</string>
<string name="copied_entry">已將記錄複製到剪貼簿</string>
<string name="tap_mode">Tap模å¼</string>
<string name="faq_tap_mode">Android內置的VPN API並ä¸æ”¯æ´Tap介é¢ï¼Œæ•…此本程åºä¸¦ä¸æ”¯æ´Tap模å¼ã€‚</string>
@@ -167,7 +167,7 @@
<string name="using_proxy">使用代ç†ä¼ºæœå™¨ %1$s %2$d</string>
<string name="use_system_proxy">使用系統代ç†</string>
<string name="use_system_proxy_summary">使用系統é…置的 HTTP/HTTPS 代ç†ä¼ºæœå™¨é€²è¡Œé€£æŽ¥ã€‚</string>
- <string name="donatewithpaypal">ä½ å¯ä»¥é€éŽ &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;PayPal&lt;/a&gt; æä¾›æ助</string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">如果在é‡æ–°é–‹æ©Ÿæˆ–關機å‰æ­£é€£æŽ¥VPN,開機時自動é‡æ–°é€£æŽ¥ã€‚在使用這個é¸é …之å‰è«‹å…ˆé–±è®€é€£ç·šè­¦å‘ŠFAQ。</string>
<string name="onbootrestart">開機時é‡æ–°é€£æŽ¥</string>
<string name="ignore">忽略</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a101d024..532d2379 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2,7 +2,8 @@
<!-- Generated by crowdin.net -->
<resources>
- <string name="app">OpenVPN for Android</string>
+ <string name="app">LEAP for Android</string>
+ <string name="app_name">LEAP</string>
<string name="address">Server Address:</string>
<string name="port">Server Port:</string>
<string name="location">Location</string>
@@ -17,10 +18,11 @@
<string name="client_pkcs12_title">PKCS12 File</string>
<string name="ca_title">CA Certificate</string>
<string name="no_certificate">You must select a certificate</string>
- <string name="copyright_guicode">Source code and issue tracker available at http://code.google.com/p/ics-openvpn/</string>
+ <string name="copyright_guicode">Source code and issue tracker available at https://github.com/leapcode/leap_android/</string>
<string name="copyright_others">This program uses the following components; see the source code for full details on the licenses</string>
<string name="about">About</string>
- <string name="about_summary">About OpenVPN for Android</string>
+ <string name="switch_provider_menu_option">Switch provider</string>
+ <string name="about_summary">About LEAP for Android</string>
<string name="vpn_list_summary">List of all configured VPNs</string>
<string name="vpn_list_title">VPN Profiles</string>
<string name="vpn_type">Type</string>
@@ -111,7 +113,7 @@
<string name="version_info">%1$s %2$s</string>
<string name="send_logfile">Send log file</string>
<string name="send">Send</string>
- <string name="ics_openvpn_log_file">ICS OpenVPN log file</string>
+ <string name="ics_openvpn_log_file">LEAP OpenVPN log file</string>
<string name="copied_entry">Copied log entry to clip board</string>
<string name="tap_mode">Tap Mode</string>
<string name="faq_tap_mode">Tap Mode is not possible with the non root VPN API. Therefore this application cannot provide tap support</string>
@@ -203,7 +205,7 @@
<string name="using_proxy">Using proxy %1$s %2$d</string>
<string name="use_system_proxy">Use system proxy</string>
<string name="use_system_proxy_summary">Use the system wide configuration for HTTP/HTTPS proxies to connect.</string>
- <string name="donatewithpaypal">You can &lt;a href=\"https://www.paypal.com/cgi-bin/webscr?hosted_button_id=R2M6ZP9AF25LS&amp;amp;cmd=_s-xclick\"&gt;donate with PayPal&lt;/a&gt; </string>
+ <string name="donatewithpaypal"></string>
<string name="onbootrestartsummary">OpenVPN will reconnect a VPN if it was active on system reboot/shutdown. Please read the Connection warning FAQ before using this option.</string>
<string name="onbootrestart">Reconnect on reboot</string>
<string name="ignore">Ignore</string>
@@ -253,5 +255,68 @@
<string name="state_tcp_connect">Connecting (TCP)</string>
<string name="state_auth_failed">Authentication failed</string>
<string name="state_nonetwork">Waiting for usable network</string>
+ <string name="menu_settings">Settings</string>
+ <string name="title_activity_dashboard">LEAP Dashboard</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="eip_status">Status unknown</string>
+ <string name="eip_service_label">Encrypted Internet</string>
+ <string name="title_activity_configuration_wizard">Configure LEAP</string>
+ <string name="new_provider_button">Select new Provider</string>
+ <string name="introduce_new_provider">Introduce new provider</string>
+ <string name="save">Save</string>
+ <string name="new_provider_uri">New provider\'s main URL</string>
+ <string name="valid_url_entered">It seems your URL is well formed</string>
+ <string name="not_valid_url_entered">It seems your URL is not well formed</string>
+ <string name="provider_details_fragment_title">Provider details</string>
+ <string name="domain">Domain</string>
+ <string name="name">Name</string>
+ <string name="description">Description</string>
+ <string name="use_anonymously_button">Use anonymously</string>
+ <string name="username_ask">Introduce your username</string>
+ <string name="password_ask">Enter your password</string>
+ <string name="user_message">User message</string>
+ <string name="error_bad_user_password_user_message">Not valid username and/or password.</string>
+ <string name="error_not_valid_password_user_message">Your password is not well-formed: it should have at least 8 characters.</string>
+ <string name="error_client_http_user_message">Try again: Client HTTP error</string>
+ <string name="error_io_exception_user_message">Try again: I/O error</string>
+ <string name="error_json_exception_user_message">Try again: Bad response from the server</string>
+ <string name="error_no_such_algorithm_exception_user_message">Update the app</string>
+ <string name="login_button">Log In</string>
+ <string name="logout_button">Log Out</string>
+ <string name="danger_checkbox">Trust completely</string>
+ <string name="setup_error_title">Configuration Error</string>
+ <string name="setup_error_configure_button">Configure</string>
+ <string name="setup_error_close_button">Exit</string>
+ <string name="setup_error_text">There was an error configuring LEAP with your chosen provider.\n\nYou may choose to reconfigure, or exit and configure a provider upon next launch.</string>
+ <string name="config_wait_title">Configuring LEAP provider</string>
+ <string name="config_connecting_provider">Downloading provider configuration</string>
+ <string name="config_downloading_services">Downloading service definitions</string>
+ <string name="config_downloading_certificates">Downloading authentication certificates</string>
+ <string name="config_error_parsing">Error parsing provider\'s responses!</string>
+ <string name="success">Success!</string>
+ <string name="incorrectly_downloaded_json_files_message">You have not entered a LEAP provider URL or it is unavailable</string>
+ <string name="correctly_downloaded_json_files_message">Your anon cert has been correctly downloaded</string>
+ <string name="incorrectly_downloaded_certificate_message">Your anon cert was not downloaded</string>
+ <string name="server_is_down_message">Server is down.</string>
+ <string name="malformed_url">It doesn\'t seem to be a LEAP provider.</string>
+ <string name="certificate_error">This is not a trusted LEAP provider.</string>
+ <string name="authenticating_title">Authenticating</string>
+ <string name="authenticating_message">Authenticating with entered login and password.</string>
+ <string name="logout_title">Log out</string>
+ <string name="logout_message">Logging out from this session.</string>
+ <string name="not_valid_password_message">Your password is not well-formed: it should have at least 8 characters.</string>
+ <string name="succesful_authentication_message">Authentication succeeded.</string>
+ <string name="authentication_failed_message">Authentication failed.</string>
+ <string name="successful_log_out_message">Logged out.</string>
+ <string name="log_out_failed_message">Didn\'t logged out.</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>
+ <string name="eip_cancel_connect_title">Cancel connection?</string>
+ <string name="eip_cancel_connect_text">There is a connection attempt in progress. Do you wish to cancel it?</string>
+ <string name="eip_cancel_connect_cancel">Yes</string>
+ <string name="eip_cancel_connect_false">No</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d549b596..1c20cbcd 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -1,21 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2011 The Android Open Source Project
- Copyright (C) 2012 Arne Schwabe
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<resources>
<style name="item">
diff --git a/res/values/untranslatable.xml b/res/values/untranslatable.xml
index 79271141..fe620aa8 100644
--- a/res/values/untranslatable.xml
+++ b/res/values/untranslatable.xml
@@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="copyright_blinktgui" translatable="false">Copyright 2012\nLEAP &lt;info@leap.se></string>
+ <string name="copyright_leapgui" translatable="false">Copyright 2012\nLEAP Encryption Access Project &lt;info@leap.se></string>
<string name="opevpn_copyright" translatable="false">Copyright © 2002–2010 OpenVPN Technologies, Inc. &lt;sales@openvpn.net>\n
"OpenVPN" is a trademark of OpenVPN Technologies, Inc.\n</string>
<string name="defaultserver" translatable="false">openvpn.uni-paderborn.de</string>
<string name="defaultport" translatable="false">1194</string>
- <string name="copyright_file_dialog" translatable="false">File Dialog based on work by Alexander Ponomarev</string>
<string name="lzo_copyright" translatable="false">Copyright © 1996 – 2011 Markus Franz Xaver Johannes Oberhumer</string>
<string name="copyright_openssl" translatable="false"> This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit\n
Copyright © 1998-2008 The OpenSSL Project. All rights reserved.\n\n
diff --git a/res/values/values-nl/arrays.xml b/res/values/values-nl/arrays.xml
new file mode 100755
index 00000000..9be2a9d4
--- /dev/null
+++ b/res/values/values-nl/arrays.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.net-->
+<resources>
+ <string-array name="vpn_types">
+ <item>Certificaten</item>
+ <item>PKCS12 Bestand</item>
+ <item>Android Certificaat</item>
+ <item>Gebruikersnaam/Wachtwoord</item>
+ <item>Statische Sleutels</item>
+ <item>Gebruiker/WW + Certificaten</item>
+ <item>Gebruiker/WW + PKCS12 </item>
+ <item>Gebruiker/WW + Android</item>
+ </string-array>
+ <string-array name="tls_directions_entries">
+ <item>0</item>
+ <item>1</item>
+ <item>Niet-gespecificeerd</item>
+ </string-array>
+ <string-array name="verb_entries">
+ <item>0 - Geen logboek</item>
+ <item>1 - Standaard logboek</item>
+ <item>2 - Uitgebreid logboek</item>
+ <item>3</item>
+ <item>4</item>
+ <item>5 - Debug logboek</item>
+ </string-array>
+</resources>
diff --git a/res/values/values-nl/strings.xml b/res/values/values-nl/strings.xml
new file mode 100755
index 00000000..459c80ec
--- /dev/null
+++ b/res/values/values-nl/strings.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.net-->
+<resources>
+ <string name="app">LEAP voor Android</string>
+ <string name="address">Server Adres:</string>
+ <string name="port">Server Poort:</string>
+ <string name="location">Locatie</string>
+ <string name="select">Selecteer</string>
+ <string name="cancel">Annuleer</string>
+ <string name="no_data">Geen Gegevens</string>
+ <string name="useLZO">LZO Compressie</string>
+ <string name="client_no_certificate">Geen Certificaat</string>
+ <string name="client_certificate_title">Client Certificaat</string>
+ <string name="client_key_title">Client Certificaat Sleutel</string>
+ <string name="client_pkcs12_title">PKCS12 Bestand</string>
+ <string name="ca_title">CA Certificaat</string>
+ <string name="about">Over</string>
+ <string name="about_summary">Over LEAP voor Android</string>
+ <string name="vpn_list_summary">Lijst van alle geconfigureerde VPN verbindingen</string>
+ <string name="vpn_list_title">VPN Profielen</string>
+ <string name="vpn_type">Type</string>
+ <string name="pkcs12pwquery">PKCS12 Wachtwoord</string>
+ <string name="file_select">Selecteer…</string>
+ <string name="useTLSAuth">Gebruik TLS autentificatie</string>
+ <string name="tls_direction">TLS Richting</string>
+ <string name="ipv6_dialog_tile">Voer een IPv6 Adres/Netmask in met het CIDR Formaat (v.b. 2000:dd::23/64)</string>
+ <string name="ipv4_dialog_title">Voer een IPv4 Adres/Netmask in met het CIDR Formaat (v.b. 1.2.3.4/24)</string>
+ <string name="ipv4_address">IPv4 Adres</string>
+ <string name="ipv6_address">IPv4 Adres</string>
+ <string name="auth_username">Gebruikersnaam</string>
+ <string name="auth_pwquery">wachtwoord</string>
+ <string name="configure_the_vpn">VPN configureren</string>
+ <string name="menu_add_profile">Profiel toevoegen</string>
+ <string name="add_profile_name_prompt">Voer een naam in voor het nieuwe Profiel</string>
+ <string name="profilename">Profiel name</string>
+ <string name="no_error_found">Geen fout.</string>
+ <string name="config_error_found">Fout in de configuratie</string>
+ <string name="vpn_shortcut">Open VPN shortcut</string>
+ <string name="vpn_launch_title">Met VPN verbinden</string>
+ <string name="shortcut_profile_notfound">Het profiel zoals aangegeven in de snelkoppeling kon niet gevonden worden.</string>
+ <string name="random_host_prefix">Willekeurig Host Voorvoegsel</string>
+ <string name="random_host_summary">Voegt 6 willekeurige tekens toe voor de hostname</string>
+ <string name="custom_config_title">Eigen configuratie opties</string>
+ <string name="custom_config_summary">Geef je eigen configuratieopties aan. Wees voorzichtig!</string>
+ <string name="route_rejected">Route geweigert door Android</string>
+ <string name="cancel_connection">Verbinding verbreken</string>
+ <string name="clear_log">logboek wissen</string>
+ <string name="title_cancel">Annuleer bevestiging</string>
+ <string name="cancel_connection_query">Sluit de verbonden VPN af/annuleer de verbindingspoging?</string>
+ <string name="remove_vpn">VPN wissen</string>
+ <string name="check_remote_tlscert">Checkt of de server een TLS server certificaat gebruikt.</string>
+ <string name="remote_tlscn_check_title">Controleer Certificaat Hostname</string>
+ <string name="enter_tlscn_title">Externe Hostname(CN)</string>
+ <string name="tls_auth_file">TLS Auth Bestand</string>
+ <string name="pull_on_summary">Vraag IP adres, routes en timing opties van de server.</string>
+ <string name="use_pull">Pull Instellingen</string>
+ <string name="dns">DNS</string>
+ <string name="override_dns">DNS Instellingen van Server Overschrijven</string>
+ <string name="dns_override_summary">Gebruik eigen DNS Servers</string>
+ <string name="searchdomain">Zoekd domein</string>
+ <string name="dns1_summary">Primaire DNS server</string>
+ <string name="dns_server">DNS Server</string>
+ <string name="secondary_dns_message">Secundaire DNS server. Deze wordt gebruikt voor het geval dat de primaire DNS server niet bereikbaar is</string>
+ <string name="backup_dns">Backup DNS server</string>
+ <string name="ignored_pushed_routes">Negeer ontvangen routes</string>
+ <string name="ignore_routes_summary">Negeer routes ontvangen van de server.</string>
+ <string name="default_route_summary">Leid al het Verkeer over de VPN</string>
+ <string name="use_default_title">Gebruik standaard Route</string>
+ <string name="custom_routes_title">Eigen routes</string>
+ <string name="float_summary">Geverifieerde pakketen zijn vanuit elk IP toegestaan</string>
+ <string name="float_title">Zwevende server toestaan</string>
+ <string name="custom_options_title">Aangepaste Opties</string>
+ <string name="edit_vpn">VPN Instellingen Bewerken</string>
+ <string name="error">"Fout:"</string>
+ <string name="clear">Leeg maken</string>
+ <string name="info">info</string>
+ <string name="show_connection_details">Details van de verbinding weergeven</string>
+ <string name="last_openvpn_tun_config">Laatste interfaceconfiguratie van OpenVPN:</string>
+ <string name="local_ip_info">Lokaal IPv4: %1$s/%2$d IPv6: %3$s MTU: %4$d</string>
+ <string name="dns_server_info">DNS Server: %s</string>
+ <string name="dns_domain_info">DNS Domein: %s</string>
+ <string name="routes_info">Routes: %s</string>
+ <string name="routes_info6">Routes IPv6: %s</string>
+ <string name="version_info">%1$s %2$s</string>
+ <string name="send_logfile">Logboek verzenden</string>
+ <string name="send">Verzenden</string>
+ <string name="tap_mode">Tap mode</string>
+ <string name="faq_tap_mode">De VPN API van Android werkt zonder rooten van de telefoon en ondersteunt alleen de tun modus. Daarom is de tap modus niet mogelijk met deze app.</string>
+ <string name="import_configuration_file">configuratie bestand importeren</string>
+ <string name="faq_security_title">Beveiligingsoverwegingen</string>
+ <string name="import_vpn">Importeren</string>
+ <string name="broken_image_cert_title">Fout bij het weergeven van de certificaat selectie</string>
+ <string name="ipv4">IPv4</string>
+ <string name="ipv6">IPv6</string>
+ <string name="speed_waiting">Wachten op status bericht…</string>
+ <string name="converted_profile">Geïmporteerd profiel</string>
+ <string name="converted_profile_i">Geïmporteerd profiel %d</string>
+ <string name="broken_images">Niet Werkende Afbeeldingen</string>
+ <string name="error_empty_username">De gebruikersnaam moet niet leeg zijn.</string>
+ <string name="pkcs12_file_encryption_key">PKCS12 Bestand Encryptie Sleutel</string>
+ <string name="private_key_password">Privé Sleutel Wachtwoord</string>
+ <string name="password">Wachtwoord</string>
+ <string name="file_icon">bestands pictogram</string>
+ <string name="tls_authentication">TLS Verificatie</string>
+ <string name="generated_config">Gegenereerde Configuratie</string>
+ <string name="generalsettings">Algemene Instellingen</string>
+ <string name="ipdns">IP en DNS</string>
+</resources>
diff --git a/run.sh b/run.sh
new file mode 100755
index 00000000..b5a08612
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+if [ -z "$2" ]
+then
+ echo Usage: run.sh \"avd name\" \"project folder\"
+ exit 0;
+fi
+avd_name=$1
+PROJECT_FOLDER=$2
+localport=`expr $RANDOM % 65536`
+
+wait_until_booted() {
+ OUT=`adb shell getprop init.svc.bootanim`
+ RES="stopped"
+
+ while [[ ${OUT:0:7} != 'stopped' ]]; do
+ OUT=`adb shell getprop init.svc.bootanim`
+# echo 'Waiting for emulator to fully boot...'
+ sleep 5
+ done
+
+ echo "Emulator booted!"
+}
+
+echo "Press \"y\" key and enter if you want to wipe emulator's data"
+read wipe_data_or_not
+if [ $wipe_data_or_not == "y" ]
+then
+ echo "Wiping data"
+ emulator -wipe-data @$avd_name & # If you want to test the app from scratch
+else
+ echo "Not wiping data"
+ emulator @$avd_name & # If you want to test the app from scratch
+fi
+
+wait_until_booted
+adb install -r $PROJECT_FOLDER/bin/LEAP\ Android-debug.apk # Install the new version of the application
+adb shell am start se.leap.leapclient/.Dashboard # Run app
diff --git a/src/org/jboss/security/srp/SRPParameters.java b/src/org/jboss/security/srp/SRPParameters.java
new file mode 100644
index 00000000..4b188cb3
--- /dev/null
+++ b/src/org/jboss/security/srp/SRPParameters.java
@@ -0,0 +1,150 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.security.srp;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.spongycastle.util.encoders.Base64;
+
+/** The RFC2945 algorithm session parameters that the client and server
+agree to use. In addition to the base RFC2945 parameters, one can choose an
+alternate hash algorithm for the private session key.
+
+@author Scott.Stark@jboss.org
+@version $Revision: 57210 $
+*/
+public class SRPParameters implements Cloneable, Serializable
+{
+ /** The serial version ID.
+ * @since 1.2.4.1
+ */
+ private static final long serialVersionUID = 6438772808805276693L;
+
+ /** The algorithm safe-prime modulus */
+ public final byte[] N;
+ /** The algorithm primitive generator */
+ public final byte[] g;
+ /** The random password salt originally used to verify the password */
+ public final byte[] s;
+ /** The algorithm to hash the session key to produce K. To be consistent
+ with the RFC2945 description this must be SHA_Interleave as implemented
+ by the JBossSX security provider. For compatibility with earlier JBossSX
+ SRP releases the algorithm must be SHA_ReverseInterleave. This name is
+ passed to java.security.MessageDigest.getInstance(). */
+ public final String hashAlgorithm;
+ /** The algorithm to use for any encryption of data.
+ */
+ public final String cipherAlgorithm;
+ /** The cipher intialization vector bytes
+ */
+ public byte[] cipherIV;
+
+ /** Creates a new instance of SRPParameters */
+ public SRPParameters(byte[] N, byte[] g, byte[] s)
+ {
+ this(N, g, s, "SHA_Interleave", null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm)
+ {
+ this(N, g, s, hashAlgorithm, null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm,
+ String cipherAlgorithm)
+ {
+ this(N, g, s, hashAlgorithm, cipherAlgorithm, null);
+ }
+ public SRPParameters(byte[] N, byte[] g, byte[] s, String hashAlgorithm,
+ String cipherAlgorithm, byte[] cipherIV)
+ {
+ this.N = N;
+ this.g = g;
+ this.s = s;
+ if( hashAlgorithm == null )
+ hashAlgorithm = "SHA_Interleave";
+ this.hashAlgorithm = hashAlgorithm;
+ this.cipherAlgorithm = cipherAlgorithm;
+ this.cipherIV = cipherIV;
+ }
+
+ public Object clone()
+ {
+ Object clone = null;
+ try
+ {
+ clone = super.clone();
+ }
+ catch(CloneNotSupportedException e)
+ {
+ }
+ return clone;
+ }
+
+ public int hashCode()
+ {
+ int hashCode = hashAlgorithm.hashCode();
+ for(int i = 0; i < N.length; i ++)
+ hashCode += N[i];
+ for(int i = 0; i < g.length; i ++)
+ hashCode += g[i];
+ for(int i = 0; i < s.length; i ++)
+ hashCode += s[i];
+ return hashCode;
+ }
+
+ public boolean equals(Object obj)
+ {
+ boolean equals = false;
+ if( obj instanceof SRPParameters )
+ {
+ SRPParameters p = (SRPParameters) obj;
+ equals = hashAlgorithm.equals(p.hashAlgorithm);
+ if( equals == true )
+ equals = Arrays.equals(N, p.N);
+ if( equals == true )
+ equals = Arrays.equals(g, p.g);
+ if( equals == true )
+ equals = Arrays.equals(s, p.s);
+ }
+ return equals;
+ }
+
+ public String toString()
+ {
+ StringBuffer tmp = new StringBuffer(super.toString());
+ tmp.append('{');
+ tmp.append("N: ");
+ tmp.append(Base64.encode(N));
+ tmp.append("|g: ");
+ tmp.append(Base64.encode(g));
+ tmp.append("|s: ");
+ tmp.append(Base64.encode(s));
+ tmp.append("|hashAlgorithm: ");
+ tmp.append(hashAlgorithm);
+ tmp.append("|cipherAlgorithm: ");
+ tmp.append(cipherAlgorithm);
+ tmp.append("|cipherIV: ");
+ tmp.append(cipherIV);
+ tmp.append('}');
+ return tmp.toString();
+ }
+}
diff --git a/src/se/leap/openvpn/AboutFragment.java b/src/se/leap/leapclient/AboutFragment.java
index 3563528b..a3fbbf93 100644
--- a/src/se/leap/openvpn/AboutFragment.java
+++ b/src/se/leap/leapclient/AboutFragment.java
@@ -1,4 +1,4 @@
-package se.leap.openvpn;
+package se.leap.leapclient;
import android.app.Fragment;
import android.content.pm.PackageInfo;
@@ -15,6 +15,11 @@ import se.leap.leapclient.R;
public class AboutFragment extends Fragment {
+ public static Fragment newInstance() {
+ AboutFragment provider_detail_fragment = new AboutFragment();
+ return provider_detail_fragment;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -40,13 +45,6 @@ public class AboutFragment extends Fragment {
ver.setText(getString(R.string.version_info,name,version));
- TextView paypal = (TextView) v.findViewById(R.id.donatestring);
-
- String donatetext = getActivity().getString(R.string.donatewithpaypal);
- Spanned htmltext = Html.fromHtml(donatetext);
- paypal.setText(htmltext);
- paypal.setMovementMethod(LinkMovementMethod.getInstance());
-
TextView translation = (TextView) v.findViewById(R.id.translation);
// Don't print a text for myself
diff --git a/src/se/leap/leapclient/ConfigHelper.java b/src/se/leap/leapclient/ConfigHelper.java
new file mode 100644
index 00000000..62ebf8f1
--- /dev/null
+++ b/src/se/leap/leapclient/ConfigHelper.java
@@ -0,0 +1,382 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.util.Base64;
+
+/**
+ * Stores constants, and implements auxiliary methods used across all LEAP Android classes.
+ *
+ * @author parmegv
+ * @author MeanderingCode
+ *
+ */
+public class ConfigHelper {
+
+ public static SharedPreferences shared_preferences;
+ private static KeyStore keystore_trusted;
+
+ final public static String
+ ABOUT_FRAGMENT = "aboutFragment",
+ DOWNLOAD_JSON_FILES_BUNDLE_EXTRA = "downloadJSONFiles",
+ UPDATE_PROVIDER_DOTJSON = "updateProviderDotJSON",
+ DOWNLOAD_NEW_PROVIDER_DOTJSON = "downloadNewProviderDotJSON",
+ LOG_IN_DIALOG = "logInDialog",
+ NEW_PROVIDER_DIALOG = "logInDialog",
+ SRP_REGISTER = "srpRegister",
+ SRP_AUTH = "srpAuth",
+ M1_KEY = "M1",
+ M2_KEY = "M2",
+ LOG_IN = "logIn",
+ LOG_OUT = "logOut",
+ DOWNLOAD_CERTIFICATE = "downloadUserAuthedCertificate",
+ API_VERSION_KEY = "api_version",
+ API_RETURN_SERIAL_KEY = "serial",
+ RESULT_KEY = "result",
+ RECEIVER_KEY = "receiver",
+ PROVIDER_KEY = "provider",
+ SERVICE_KEY = "service",
+ ALLOWED_ANON = "allow_anonymous",
+ MAIN_CERT_KEY = "main_cert",
+ CERT_KEY = "cert",
+ KEY_KEY = "key",
+ EIP_SERVICE_KEY = "eip",
+ EIP_PARSED_SERIAL = "eip_parsed_serial",
+ TYPE_OF_CERTIFICATE = "type_of_certificate",
+ ANON_CERTIFICATE = "anon_certificate",
+ AUTHED_CERTIFICATE = "authed_certificate",
+ SALT_KEY = "salt",
+ SESSION_ID_COOKIE_KEY = "session_id_cookie_key",
+ SESSION_ID_KEY = "session_id",
+ PREFERENCES_KEY = "LEAPPreferences",
+ USER_DIRECTORY = "leap_android",
+ PROVIDER_NAME = "provider_name",
+ PROVIDER_ID = "provider_id",
+ PROVIDER_MAIN_URL = "provider_main_url",
+ PROVIDER_JSON_URL = "provider_json_url",
+ CUSTOM = "custom",
+ DANGER_ON = "danger_on",
+ API_URL_KEY = "api_uri",
+ USERNAME_KEY = "username",
+ PASSWORD_KEY = "password",
+ ALLOW_REGISTRATION_KEY = "allow_registration",
+ EIP_SERVICE_API_PATH = "config/eip-service.json",
+ ERRORS_KEY = "errors",
+ RECEIVER_TAG = "receiverTag",
+ REQUEST_TAG = "requestTag",
+ PROVIDER_DETAILS_DIALOG = "providerDetailsFragment",
+ DOMAIN = "domain",
+ NAME = "name",
+ DESCRIPTION = "description",
+ QUIT = "quit"
+ ;
+
+ final public static String NG_1024 =
+ "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
+ final public static BigInteger G = new BigInteger("2");
+
+ final public static int
+ CUSTOM_PROVIDER_ADDED = 0,
+ CORRECTLY_DOWNLOADED_JSON_FILES = 1,
+ INCORRECTLY_DOWNLOADED_JSON_FILES = 2,
+ SRP_AUTHENTICATION_SUCCESSFUL = 3,
+ SRP_AUTHENTICATION_FAILED = 4,
+ SRP_REGISTRATION_SUCCESSFUL = 5,
+ SRP_REGISTRATION_FAILED = 6,
+ LOGOUT_SUCCESSFUL = 7,
+ LOGOUT_FAILED = 8,
+ CORRECTLY_DOWNLOADED_CERTIFICATE = 9,
+ INCORRECTLY_DOWNLOADED_CERTIFICATE = 10,
+ CORRECTLY_UPDATED_PROVIDER_DOT_JSON = 11,
+ INCORRECTLY_UPDATED_PROVIDER_DOT_JSON = 12,
+ CORRECTLY_DOWNLOADED_ANON_CERTIFICATE = 13,
+ INCORRECTLY_DOWNLOADED_ANON_CERTIFICATE = 14
+ ;
+
+
+ private static boolean checkSharedPrefs() {
+ try {
+ shared_preferences = Dashboard.getAppContext().getSharedPreferences(PREFERENCES_KEY,Context.MODE_PRIVATE);
+ } catch (Exception e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Saves a JSON object into class scope Shared Preferences
+ * @param shared_preferences_key
+ * @param content
+ */
+ public static void saveSharedPref(String shared_preferences_key, JSONObject content) {
+
+ SharedPreferences.Editor shared_preferences_editor = shared_preferences
+ .edit();
+ shared_preferences_editor.putString(shared_preferences_key,
+ content.toString());
+ shared_preferences_editor.commit();
+ }
+
+ /**
+ * Saves a String object into class scope Shared Preferences
+ * @param shared_preferences_key
+ * @param content
+ */
+ public static void saveSharedPref(String shared_preferences_key, String content) {
+
+ SharedPreferences.Editor shared_preferences_editor = shared_preferences
+ .edit();
+ shared_preferences_editor.putString(shared_preferences_key,
+ content);
+ shared_preferences_editor.commit();
+ }
+
+ /**
+ * Saves a boolean object into class scope Shared Preferences
+ * @param shared_preferences_key
+ * @param content
+ */
+ public static void saveSharedPref(String shared_preferences_key, boolean content) {
+
+ SharedPreferences.Editor shared_preferences_editor = shared_preferences
+ .edit();
+ shared_preferences_editor.putBoolean(shared_preferences_key, content);
+ shared_preferences_editor.commit();
+ }
+
+ /**
+ * Saves an int into class scope Shared Preferences
+ *
+ * @param shared_preferences_key
+ * @param value
+ */
+ protected static void saveSharedPref(String shared_preferences_key, int value) {
+ SharedPreferences.Editor shared_preferences_editor = shared_preferences.edit();
+ shared_preferences_editor.putInt(shared_preferences_key, value).commit();
+ }
+
+ /**
+ * Gets String object from class scope Shared Preferences
+ * @param shared_preferences_key
+ * @return the string correspondent to the key parameter
+ */
+ public static String getStringFromSharedPref(String shared_preferences_key) {
+ String content = null;
+ content = shared_preferences.getString(shared_preferences_key, "");
+ return content;
+ }
+
+ /**
+ * Gets JSON object from class scope Shared Preferences
+ * @param shared_preferences_key
+ * @return the JSON object correspondent to the key parameter
+ */
+ public static JSONObject getJsonFromSharedPref(String shared_preferences_key) throws JSONException {
+ JSONObject content = null;
+ if ( checkSharedPrefs() ) {
+ String json_string = shared_preferences.getString(shared_preferences_key, "");
+ content = new JSONObject(json_string);
+ }
+
+ return content;
+ }
+
+ /*
+ * This method defaults to false.
+ * If you use this method, be sure to fail-closed on false!
+ * TODO This is obviously less than ideal...solve it!
+ */
+ public static boolean getBoolFromSharedPref(String shared_preferences_key) {
+ boolean value = false;
+ if ( checkSharedPrefs() ) {
+ value = shared_preferences.getBoolean(shared_preferences_key, false);
+ }
+ return value;
+ }
+
+ /**
+ * Get an int from SharedPreferences
+ *
+ * @param shared_preferences_key Key to retrieve
+ * @return The value for the key or 0
+ */
+ protected static int getIntFromSharedPref(String shared_preferences_key) {
+ return shared_preferences.getInt(shared_preferences_key, 0);
+ }
+
+ /*
+ * This method defaults to false.
+ * If you use this method, be sure to fail-closed on false!
+ * TODO This is obviously less than ideal...solve it!
+ */
+ public static boolean removeFromSharedPref(String shared_preferences_key) {
+ SharedPreferences.Editor shared_preferences_editor = shared_preferences
+ .edit();
+ shared_preferences_editor.remove(shared_preferences_key);
+ return shared_preferences_editor.commit();
+ }
+
+ /**
+ * Opens a FileInputStream from the user directory of the external storage directory.
+ * @param filename
+ * @return a file input stream
+ */
+ public static FileInputStream openFileInputStream(String filename) {
+ FileInputStream input_stream = null;
+ File root = Environment.getExternalStorageDirectory();
+ File leap_dir = new File(root.getAbsolutePath() + File.separator + USER_DIRECTORY);
+ try {
+ input_stream = new FileInputStream(leap_dir + File.separator + filename);
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return input_stream;
+ }
+
+ /**
+ * Treat the input as the MSB representation of a number,
+ * and lop off leading zero elements. For efficiency, the
+ * input is simply returned if no leading zeroes are found.
+ *
+ * @param in array to be trimmed
+ */
+ public static byte[] trim(byte[] in) {
+ if(in.length == 0 || in[0] != 0)
+ return in;
+
+ int len = in.length;
+ int i = 1;
+ while(in[i] == 0 && i < len)
+ ++i;
+ byte[] ret = new byte[len - i];
+ System.arraycopy(in, i, ret, 0, len - i);
+ return ret;
+ }
+
+ /**
+ * Sets class scope Shared Preferences
+ * @param shared_preferences
+ */
+ public static void setSharedPreferences(
+ SharedPreferences shared_preferences) {
+ ConfigHelper.shared_preferences = shared_preferences;
+ }
+
+ public static X509Certificate parseX509CertificateFromString(String certificate_string) {
+ java.security.cert.Certificate certificate = null;
+ CertificateFactory cf;
+ try {
+ cf = CertificateFactory.getInstance("X.509");
+
+ certificate_string = certificate_string.replaceFirst("-----BEGIN CERTIFICATE-----", "").replaceFirst("-----END CERTIFICATE-----", "").trim();
+ byte[] cert_bytes = Base64.decode(certificate_string, Base64.DEFAULT);
+ InputStream caInput = new ByteArrayInputStream(cert_bytes);
+ try {
+ certificate = cf.generateCertificate(caInput);
+ System.out.println("ca=" + ((X509Certificate) certificate).getSubjectDN());
+ } finally {
+ caInput.close();
+ }
+ } catch (CertificateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ return null;
+ }
+
+ return (X509Certificate) certificate;
+ }
+
+ /**
+ * Adds a new X509 certificate given its input stream and its provider name
+ * @param provider used to store the certificate in the keystore
+ * @param inputStream from which X509 certificate must be generated.
+ */
+ public static void addTrustedCertificate(String provider, InputStream inputStream) {
+ CertificateFactory cf;
+ try {
+ cf = CertificateFactory.getInstance("X.509");
+ X509Certificate cert =
+ (X509Certificate)cf.generateCertificate(inputStream);
+ keystore_trusted.setCertificateEntry(provider, cert);
+ } catch (CertificateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Adds a new X509 certificate given in its string from and using its provider name
+ * @param provider used to store the certificate in the keystore
+ * @param certificate
+ */
+ public static void addTrustedCertificate(String provider, String certificate) {
+
+ try {
+ X509Certificate cert = ConfigHelper.parseX509CertificateFromString(certificate);
+ if(keystore_trusted == null) {
+ keystore_trusted = KeyStore.getInstance("BKS");
+ keystore_trusted.load(null);
+ }
+ keystore_trusted.setCertificateEntry(provider, cert);
+ } 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();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @return class wide keystore
+ */
+ public static KeyStore getKeystore() {
+ return keystore_trusted;
+ }
+}
diff --git a/src/se/leap/leapclient/ConfigurationWizard.java b/src/se/leap/leapclient/ConfigurationWizard.java
new file mode 100644
index 00000000..934e2ea0
--- /dev/null
+++ b/src/se/leap/leapclient/ConfigurationWizard.java
@@ -0,0 +1,411 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import se.leap.leapclient.ProviderAPIResultReceiver.Receiver;
+import se.leap.leapclient.ProviderListContent.ProviderItem;
+import se.leap.leapclient.R;
+import se.leap.openvpn.MainActivity;
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.content.res.AssetManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Activity that builds and shows the list of known available providers.
+ *
+ * It also allows the user to enter custom providers with a button.
+ *
+ * @author parmegv
+ *
+ */
+public class ConfigurationWizard extends Activity
+implements ProviderListFragment.Callbacks, NewProviderDialog.NewProviderDialogInterface, ProviderDetailFragment.ProviderDetailFragmentInterface, Receiver {
+
+ private ProviderItem mSelectedProvider;
+ private ProgressDialog mProgressDialog;
+ private Intent mConfigState = new Intent();
+
+ protected static final String PROVIDER_SET = "PROVIDER SET";
+ protected static final String SERVICES_RETRIEVED = "SERVICES RETRIEVED";
+
+ public ProviderAPIResultReceiver providerAPI_result_receiver;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.configuration_wizard_activity);
+
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
+
+ ConfigHelper.setSharedPreferences(getSharedPreferences(ConfigHelper.PREFERENCES_KEY, MODE_PRIVATE));
+
+ loadPreseededProviders();
+
+ // Only create our fragments if we're not restoring a saved instance
+ if ( savedInstanceState == null ){
+ // TODO Some welcome screen?
+ // We will need better flow control when we have more Fragments (e.g. user auth)
+ ProviderListFragment providerList = new ProviderListFragment();
+
+ FragmentManager fragmentManager = getFragmentManager();
+ fragmentManager.beginTransaction()
+ .add(R.id.configuration_wizard_layout, providerList, "providerlist")
+ .commit();
+ }
+
+ // TODO: If exposing deep links into your app, handle intents here.
+ }
+
+ @Override
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ if(resultCode == ConfigHelper.CORRECTLY_UPDATED_PROVIDER_DOT_JSON) {
+ JSONObject provider_json;
+ try {
+ provider_json = new JSONObject(resultData.getString(ConfigHelper.PROVIDER_KEY));
+ boolean danger_on = resultData.getBoolean(ConfigHelper.DANGER_ON);
+ ConfigHelper.saveSharedPref(ConfigHelper.PROVIDER_KEY, provider_json);
+ ConfigHelper.saveSharedPref(ConfigHelper.DANGER_ON, danger_on);
+ ConfigHelper.saveSharedPref(ConfigHelper.ALLOWED_ANON, provider_json.getJSONObject(ConfigHelper.SERVICE_KEY).getBoolean(ConfigHelper.ALLOWED_ANON));
+ mConfigState.setAction(PROVIDER_SET);
+
+ if(mProgressDialog != null) mProgressDialog.dismiss();
+ mProgressDialog = ProgressDialog.show(this, getResources().getString(R.string.config_wait_title), getResources().getString(R.string.config_connecting_provider), true);
+ mProgressDialog.setMessage(getResources().getString(R.string.config_downloading_services));
+ if(resultData.containsKey(ConfigHelper.PROVIDER_ID))
+ mSelectedProvider = getProvider(resultData.getString(ConfigHelper.PROVIDER_ID));
+
+ ProviderListFragment providerList = new ProviderListFragment();
+
+ FragmentManager fragmentManager = getFragmentManager();
+ fragmentManager.beginTransaction()
+ .replace(R.id.configuration_wizard_layout, providerList, "providerlist")
+ .commit();
+ downloadJSONFiles(mSelectedProvider);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+
+ mProgressDialog.dismiss();
+ //Toast.makeText(this, getResources().getString(R.string.config_error_parsing), Toast.LENGTH_LONG);
+ setResult(RESULT_CANCELED, mConfigState);
+ }
+ }
+ else if(resultCode == ConfigHelper.INCORRECTLY_UPDATED_PROVIDER_DOT_JSON) {
+ mProgressDialog.dismiss();
+ setResult(RESULT_CANCELED, mConfigState);
+ }
+ else if(resultCode == ConfigHelper.CORRECTLY_DOWNLOADED_JSON_FILES) {
+ if (ConfigHelper.getBoolFromSharedPref(ConfigHelper.ALLOWED_ANON)){
+ mProgressDialog.setMessage(getResources().getString(R.string.config_downloading_certificates));
+ mConfigState.putExtra(SERVICES_RETRIEVED, true);
+ downloadAnonCert();
+ } else {
+ mProgressDialog.dismiss();
+ //Toast.makeText(getApplicationContext(), R.string.success, Toast.LENGTH_LONG).show();
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+ else if(resultCode == ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES) {
+ //Toast.makeText(getApplicationContext(), R.string.incorrectly_downloaded_json_files_message, Toast.LENGTH_LONG).show();
+ setResult(RESULT_CANCELED, mConfigState);
+ }
+ else if(resultCode == ConfigHelper.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+ mProgressDialog.dismiss();
+ setResult(RESULT_OK);
+ showProviderDetails(getCurrentFocus());
+ } else if(resultCode == ConfigHelper.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
+ mProgressDialog.dismiss();
+ //Toast.makeText(getApplicationContext(), R.string.incorrectly_downloaded_certificate_message, Toast.LENGTH_LONG).show();
+ setResult(RESULT_CANCELED, mConfigState);
+ }
+ }
+
+ /**
+ * Callback method from {@link ProviderListFragment.Callbacks}
+ * indicating that the item with the given ID was selected.
+ */
+ @Override
+ public void onItemSelected(String id) {
+ //TODO Code 2 pane view
+ ProviderItem selected_provider = getProvider(id);
+ mProgressDialog = ProgressDialog.show(this, getResources().getString(R.string.config_wait_title), getResources().getString(R.string.config_connecting_provider), true);
+ mSelectedProvider = selected_provider;
+ saveProviderJson(mSelectedProvider);
+ }
+
+ @Override
+ public void onBackPressed() {
+ try {
+ if(ConfigHelper.getJsonFromSharedPref(ConfigHelper.PROVIDER_KEY) == null || ConfigHelper.getJsonFromSharedPref(ConfigHelper.PROVIDER_KEY).length() == 0) {
+ askDashboardToQuitApp();
+ } else {
+ setResult(RESULT_OK);
+ }
+ } catch (JSONException e) {
+ askDashboardToQuitApp();
+ }
+ super.onBackPressed();
+ }
+
+ private void askDashboardToQuitApp() {
+ Intent ask_quit = new Intent();
+ ask_quit.putExtra(ConfigHelper.QUIT, ConfigHelper.QUIT);
+ setResult(RESULT_CANCELED, ask_quit);
+ }
+
+ private ProviderItem getProvider(String id) {
+ Iterator<ProviderItem> providers_iterator = ProviderListContent.ITEMS.iterator();
+ while(providers_iterator.hasNext()) {
+ ProviderItem provider = providers_iterator.next();
+ if(provider.id.equalsIgnoreCase(id)) {
+ return provider;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Loads providers data from url file contained in the project
+ * @return true if the file was read correctly
+ */
+ private boolean loadPreseededProviders() {
+ boolean loaded_preseeded_providers = false;
+ AssetManager asset_manager = getAssets();
+ String[] urls_filepaths = null;
+ try {
+ String url_files_folder = "urls";
+ //TODO Put that folder in a better place (also inside the "for")
+ urls_filepaths = asset_manager.list(url_files_folder);
+ String provider_name = "";
+ for(String url_filepath : urls_filepaths)
+ {
+ boolean custom = false;
+ provider_name = url_filepath.subSequence(0, url_filepath.indexOf(".")).toString();
+ if(ProviderListContent.ITEMS.isEmpty()) //TODO I have to implement a way of checking if a provider new or is already present in that ITEMS list
+ ProviderListContent.addItem(new ProviderItem(provider_name, asset_manager.open(url_files_folder + "/" + url_filepath), custom, true)); // By default, it trusts the provider
+ loaded_preseeded_providers = true;
+ }
+ } catch (IOException e) {
+ loaded_preseeded_providers = false;
+ }
+
+ return loaded_preseeded_providers;
+ }
+
+ /**
+ * Saves provider.json file associated with provider.
+ *
+ * If the provider is custom, the file has already been downloaded so we load it from memory.
+ * If not, the file is updated using the provider's URL.
+ * @param provider
+ */
+ private void saveProviderJson(ProviderItem provider) {
+ JSONObject provider_json = new JSONObject();
+ try {
+ if(!provider.custom) {
+ updateProviderDotJson(provider.name, provider.provider_json_url, provider.danger_on);
+ } else {
+ // FIXME!! We should we be updating our seeded providers list at ConfigurationWizard onStart() ?
+ // I think yes, but if so, where does this list live? leap.se, as it's the non-profit project for the software?
+ // If not, we should just be getting names/urls, and fetching the provider.json like in custom entries
+ provider_json = provider.provider_json;
+ ConfigHelper.saveSharedPref(ConfigHelper.PROVIDER_KEY, provider_json);
+ ConfigHelper.saveSharedPref(ConfigHelper.ALLOWED_ANON, provider_json.getJSONObject(ConfigHelper.SERVICE_KEY).getBoolean(ConfigHelper.ALLOWED_ANON));
+ ConfigHelper.saveSharedPref(ConfigHelper.DANGER_ON, provider.danger_on);
+
+ mProgressDialog.setMessage(getResources().getString(R.string.config_downloading_services));
+ downloadJSONFiles(mSelectedProvider);
+ }
+ } catch (JSONException e) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+
+ /**
+ * Asks ProviderAPI to download provider site's certificate and eip-service.json
+ *
+ * URLs are fetched from the provider parameter
+ * @param provider from which certificate and eip-service.json files are going to be downloaded
+ */
+ private void downloadJSONFiles(ProviderItem provider) {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+
+ method_and_parameters.putString(ConfigHelper.PROVIDER_KEY, provider.name);
+ method_and_parameters.putString(ConfigHelper.MAIN_CERT_KEY, provider.cert_json_url);
+ method_and_parameters.putString(ConfigHelper.EIP_SERVICE_KEY, provider.eip_service_json_url);
+ method_and_parameters.putBoolean(ConfigHelper.DANGER_ON, provider.danger_on);
+
+ provider_API_command.putExtra(ConfigHelper.DOWNLOAD_JSON_FILES_BUNDLE_EXTRA, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ }
+
+ /**
+ * Asks ProviderAPI to download an anonymous (anon) VPN certificate.
+ */
+ private void downloadAnonCert() {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+
+ method_and_parameters.putString(ConfigHelper.TYPE_OF_CERTIFICATE, ConfigHelper.ANON_CERTIFICATE);
+
+ provider_API_command.putExtra(ConfigHelper.DOWNLOAD_CERTIFICATE, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ }
+
+ /**
+ * Open the new provider dialog
+ * @param view from which the dialog is showed
+ */
+ public void addAndSelectNewProvider(View view) {
+ FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
+ Fragment previous_new_provider_dialog = getFragmentManager().findFragmentByTag(ConfigHelper.NEW_PROVIDER_DIALOG);
+ if (previous_new_provider_dialog != null) {
+ fragment_transaction.remove(previous_new_provider_dialog);
+ }
+ fragment_transaction.addToBackStack(null);
+
+ DialogFragment newFragment = NewProviderDialog.newInstance();
+ newFragment.show(fragment_transaction, ConfigHelper.NEW_PROVIDER_DIALOG);
+ }
+
+ /**
+ * 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
+ */
+ public void showProviderDetails(View view) {
+ FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
+ Fragment previous_provider_details_dialog = getFragmentManager().findFragmentByTag(ConfigHelper.PROVIDER_DETAILS_DIALOG);
+ if (previous_provider_details_dialog != null) {
+ fragment_transaction.remove(previous_provider_details_dialog);
+ }
+ fragment_transaction.addToBackStack(null);
+
+ DialogFragment newFragment = ProviderDetailFragment.newInstance();
+ newFragment.show(fragment_transaction, ConfigHelper.PROVIDER_DETAILS_DIALOG);
+ }
+
+ @Override
+ public void saveAndSelectProvider(String provider_main_url, boolean danger_on) {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+ method_and_parameters.putString(ConfigHelper.PROVIDER_MAIN_URL, provider_main_url);
+ method_and_parameters.putBoolean(ConfigHelper.DANGER_ON, danger_on);
+
+ provider_API_command.putExtra(ConfigHelper.DOWNLOAD_NEW_PROVIDER_DOTJSON, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ }
+
+ /**
+ * Asks ProviderAPI to download a new provider.json file
+ * @param provider_name
+ * @param provider_json_url
+ * @param danger_on tells if HTTPS client should bypass certificate errors
+ */
+ public void updateProviderDotJson(String provider_name, String provider_json_url, boolean danger_on) {
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+ method_and_parameters.putString(ConfigHelper.PROVIDER_NAME, provider_name);
+ method_and_parameters.putString(ConfigHelper.PROVIDER_JSON_URL, provider_json_url);
+ method_and_parameters.putBoolean(ConfigHelper.DANGER_ON, danger_on);
+
+ provider_API_command.putExtra(ConfigHelper.UPDATE_PROVIDER_DOTJSON, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.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;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item){
+ switch (item.getItemId()){
+ case R.id.about_leap:
+ showAboutFragment(getCurrentFocus());
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /**
+ * 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
+ */
+ public void showAboutFragment(View view) {
+ FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
+ Fragment previous_about_fragment = getFragmentManager().findFragmentByTag(ConfigHelper.ABOUT_FRAGMENT);
+ if (previous_about_fragment == null) {
+ fragment_transaction.addToBackStack(null);
+
+ Fragment newFragment = AboutFragment.newInstance();
+ fragment_transaction.replace(R.id.configuration_wizard_layout, newFragment, ConfigHelper.ABOUT_FRAGMENT).commit();
+ }
+ }
+
+ @Override
+ public void login() {
+ Intent ask_login = new Intent();
+ ask_login.putExtra(ConfigHelper.LOG_IN, ConfigHelper.LOG_IN);
+ setResult(RESULT_OK, ask_login);
+ finish();
+ }
+
+ @Override
+ public void use_anonymously() {
+ setResult(RESULT_OK);
+ finish();
+ }
+}
diff --git a/src/se/leap/leapclient/Dashboard.java b/src/se/leap/leapclient/Dashboard.java
new file mode 100644
index 00000000..063cd3cd
--- /dev/null
+++ b/src/se/leap/leapclient/Dashboard.java
@@ -0,0 +1,350 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import se.leap.leapclient.ProviderAPIResultReceiver.Receiver;
+import se.leap.openvpn.MainActivity;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * The main user facing Activity of LEAP Android, consisting of status, controls,
+ * and access to preferences.
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ */
+public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface,Receiver {
+
+ protected static final int CONFIGURE_LEAP = 0;
+
+ private static final String TAG_EIP_FRAGMENT = "EIP_DASHBOARD_FRAGMENT";
+
+ private ProgressDialog mProgressDialog;
+
+ private static Context app;
+ private static SharedPreferences preferences;
+ private static Provider provider;
+
+ private TextView providerNameTV;
+
+ private boolean authed = false;
+
+ public ProviderAPIResultReceiver providerAPI_result_receiver;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ app = this;
+
+ ConfigHelper.setSharedPreferences(getSharedPreferences(ConfigHelper.PREFERENCES_KEY, MODE_PRIVATE));
+ preferences = ConfigHelper.shared_preferences;
+
+ if (ConfigHelper.getStringFromSharedPref(ConfigHelper.PROVIDER_KEY).isEmpty())
+ startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
+ else
+ buildDashboard();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ if ( requestCode == CONFIGURE_LEAP ) {
+ if ( resultCode == RESULT_OK ){
+ startService( new Intent(EIP.ACTION_UPDATE_EIP_SERVICE) );
+ buildDashboard();
+ if(data != null && data.hasExtra(ConfigHelper.LOG_IN)) {
+ View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
+ logInDialog(view, "");
+ }
+ } else if(resultCode == RESULT_CANCELED && data.hasExtra(ConfigHelper.QUIT)) {
+ finish();
+ } else
+ configErrorDialog();
+ }
+ }
+
+ /**
+ * 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) {
+ SharedPreferences.Editor prefsEdit = getSharedPreferences(ConfigHelper.PREFERENCES_KEY, MODE_PRIVATE).edit();
+ prefsEdit.remove(ConfigHelper.PROVIDER_KEY).commit();
+ finish();
+ }
+ })
+ .show();
+ }
+
+ /**
+ * Inflates permanent UI elements of the View and contains logic for what
+ * service dependent UI elements to include.
+ */
+ private void buildDashboard() {
+ provider = Provider.getInstance();
+ provider.init( this );
+
+ setContentView(R.layout.client_dashboard);
+
+ providerNameTV = (TextView) findViewById(R.id.providerName);
+ providerNameTV.setText(provider.getDomain());
+ providerNameTV.setTextSize(28);
+
+ FragmentManager fragMan = getFragmentManager();
+ if ( provider.hasEIP() && fragMan.findFragmentByTag(TAG_EIP_FRAGMENT) == null){
+ EipServiceFragment eipFragment = new EipServiceFragment();
+ fragMan.beginTransaction().add(R.id.servicesCollection, eipFragment, TAG_EIP_FRAGMENT).commit();
+ }
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ JSONObject provider_json;
+ try {
+ provider_json = ConfigHelper.getJsonFromSharedPref(ConfigHelper.PROVIDER_KEY);
+ JSONObject service_description = provider_json.getJSONObject(ConfigHelper.SERVICE_KEY);
+ if(service_description.getBoolean(ConfigHelper.ALLOW_REGISTRATION_KEY)) {
+ if(authed) {
+ menu.findItem(R.id.login_button).setVisible(false);
+ menu.findItem(R.id.logout_button).setVisible(true);
+ } else {
+ menu.findItem(R.id.login_button).setVisible(true);
+ menu.findItem(R.id.logout_button).setVisible(false);
+ }
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ 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:
+ Fragment aboutFragment = new AboutFragment();
+ FragmentTransaction trans = getFragmentManager().beginTransaction();
+ trans.replace(R.id.dashboardLayout, aboutFragment);
+ trans.addToBackStack(null);
+ trans.commit();
+ return true;
+ case R.id.legacy_interface:
+ intent = new Intent(this,MainActivity.class);
+ startActivity(intent);
+ return true;
+ case R.id.switch_provider:
+ startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
+ return true;
+ case R.id.login_button:
+ View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
+ logInDialog(view, "");
+ return true;
+ case R.id.logout_button:
+ logOut();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+
+ }
+
+ @Override
+ public void authenticate(String username, String password) {
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
+
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+ method_and_parameters.putString(ConfigHelper.USERNAME_KEY, username);
+ method_and_parameters.putString(ConfigHelper.PASSWORD_KEY, password);
+
+ JSONObject provider_json;
+ try {
+ provider_json = new JSONObject(preferences.getString(ConfigHelper.PROVIDER_KEY, ""));
+ method_and_parameters.putString(ConfigHelper.API_URL_KEY, provider_json.getString(ConfigHelper.API_URL_KEY) + "/" + provider_json.getString(ConfigHelper.API_VERSION_KEY));
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ provider_API_command.putExtra(ConfigHelper.SRP_AUTH, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ if(mProgressDialog != null) mProgressDialog.dismiss();
+ mProgressDialog = ProgressDialog.show(this, getResources().getString(R.string.authenticating_title), getResources().getString(R.string.authenticating_message), true);
+ startService(provider_API_command);
+ }
+
+ /**
+ * Asks ProviderAPI to log out.
+ */
+ public void logOut() {
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+
+ JSONObject provider_json;
+ try {
+ provider_json = new JSONObject(preferences.getString(ConfigHelper.PROVIDER_KEY, ""));
+ method_and_parameters.putString(ConfigHelper.API_URL_KEY, provider_json.getString(ConfigHelper.API_URL_KEY) + "/" + provider_json.getString(ConfigHelper.API_VERSION_KEY));
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ provider_API_command.putExtra(ConfigHelper.LOG_OUT, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ if(mProgressDialog != null) mProgressDialog.dismiss();
+ mProgressDialog = ProgressDialog.show(this, getResources().getString(R.string.logout_title), getResources().getString(R.string.logout_message), true);
+ startService(provider_API_command);
+ }
+
+ /**
+ * Shows the log in dialog.
+ * @param view from which the dialog is created.
+ */
+ public void logInDialog(View view, String user_message) {
+ FragmentTransaction fragment_transaction = getFragmentManager().beginTransaction();
+ Fragment previous_log_in_dialog = getFragmentManager().findFragmentByTag(ConfigHelper.LOG_IN_DIALOG);
+ if (previous_log_in_dialog != null) {
+ fragment_transaction.remove(previous_log_in_dialog);
+ }
+ fragment_transaction.addToBackStack(null);
+
+ DialogFragment newFragment = LogInDialog.newInstance();
+ if(user_message != null && !user_message.isEmpty()) {
+ Bundle user_message_bundle = new Bundle();
+ user_message_bundle.putString(getResources().getString(R.string.user_message), user_message);
+ newFragment.setArguments(user_message_bundle);
+ }
+ newFragment.show(fragment_transaction, ConfigHelper.LOG_IN_DIALOG);
+ }
+
+ /**
+ * Asks ProviderAPI to download an authenticated OpenVPN certificate.
+ * @param session_id cookie for the server to allow us to download the certificate.
+ */
+ private void downloadAuthedUserCertificate(Cookie session_id) {
+ providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
+ providerAPI_result_receiver.setReceiver(this);
+
+ Intent provider_API_command = new Intent(this, ProviderAPI.class);
+
+ Bundle method_and_parameters = new Bundle();
+ method_and_parameters.putString(ConfigHelper.TYPE_OF_CERTIFICATE, ConfigHelper.AUTHED_CERTIFICATE);
+ method_and_parameters.putString(ConfigHelper.SESSION_ID_COOKIE_KEY, session_id.getName());
+ method_and_parameters.putString(ConfigHelper.SESSION_ID_KEY, session_id.getValue());
+
+ provider_API_command.putExtra(ConfigHelper.DOWNLOAD_CERTIFICATE, method_and_parameters);
+ provider_API_command.putExtra(ConfigHelper.RECEIVER_KEY, providerAPI_result_receiver);
+
+ startService(provider_API_command);
+ }
+
+ @Override
+ public void onReceiveResult(int resultCode, Bundle resultData) {
+ if(resultCode == ConfigHelper.SRP_AUTHENTICATION_SUCCESSFUL){
+ String session_id_cookie_key = resultData.getString(ConfigHelper.SESSION_ID_COOKIE_KEY);
+ String session_id_string = resultData.getString(ConfigHelper.SESSION_ID_KEY);
+ setResult(RESULT_OK);
+ authed = true;
+ invalidateOptionsMenu();
+
+ Cookie session_id = new BasicClientCookie(session_id_cookie_key, session_id_string);
+ downloadAuthedUserCertificate(session_id);
+ } else if(resultCode == ConfigHelper.SRP_AUTHENTICATION_FAILED) {
+ mProgressDialog.dismiss();
+ logInDialog(getCurrentFocus(), resultData.getString(getResources().getString(R.string.user_message)));
+ } else if(resultCode == ConfigHelper.LOGOUT_SUCCESSFUL) {
+ authed = false;
+ invalidateOptionsMenu();
+ setResult(RESULT_OK);
+ mProgressDialog.dismiss();
+ } else if(resultCode == ConfigHelper.LOGOUT_FAILED) {
+ setResult(RESULT_CANCELED);
+ mProgressDialog.dismiss();
+ Toast.makeText(getApplicationContext(), R.string.log_out_failed_message, Toast.LENGTH_LONG).show();
+ } else if(resultCode == ConfigHelper.CORRECTLY_DOWNLOADED_CERTIFICATE) {
+ setResult(RESULT_OK);
+ mProgressDialog.dismiss();
+ Toast.makeText(getApplicationContext(), R.string.successful_authed_cert_downloaded_message, Toast.LENGTH_LONG).show();
+ } else if(resultCode == ConfigHelper.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
+ setResult(RESULT_CANCELED);
+ mProgressDialog.dismiss();
+ Toast.makeText(getApplicationContext(), R.string.authed_cert_download_failed_message, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+}
diff --git a/src/se/leap/leapclient/EIP.java b/src/se/leap/leapclient/EIP.java
new file mode 100644
index 00000000..e0685c15
--- /dev/null
+++ b/src/se/leap/leapclient/EIP.java
@@ -0,0 +1,511 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import se.leap.openvpn.ConfigParser;
+import se.leap.openvpn.ConfigParser.ConfigParseError;
+import se.leap.openvpn.LaunchVPN;
+import se.leap.openvpn.OpenVpnManagementThread;
+import se.leap.openvpn.OpenVpnService;
+import se.leap.openvpn.OpenVpnService.LocalBinder;
+import se.leap.openvpn.ProfileManager;
+import se.leap.openvpn.VpnProfile;
+
+import android.app.Activity;
+import android.app.IntentService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * EIP is the abstract base class for interacting with and managing the Encrypted
+ * Internet Proxy connection. Connections are started, stopped, and queried through
+ * this IntentService.
+ * Contains logic for parsing eip-service.json from the provider, configuring and selecting
+ * gateways, and controlling {@link .openvpn.OpenVpnService} connections.
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ */
+public final class EIP extends IntentService {
+
+ public final static String ACTION_START_EIP = "se.leap.leapclient.START_EIP";
+ public final static String ACTION_STOP_EIP = "se.leap.leapclient.STOP_EIP";
+ public final static String ACTION_UPDATE_EIP_SERVICE = "se.leap.leapclient.UPDATE_EIP_SERVICE";
+ public final static String ACTION_IS_EIP_RUNNING = "se.leap.leapclient.IS_RUNNING";
+ public final static String EIP_NOTIFICATION = "EIP_NOTIFICATION";
+
+ private static Context context;
+ private static ResultReceiver mReceiver;
+ private static OpenVpnService mVpnService;
+ private static boolean mBound = false;
+ // Used to store actions to "resume" onServiceConnection
+ private static String mPending = null;
+
+ private static int parsedEipSerial;
+ private static JSONObject eipDefinition = null;
+
+ private static OVPNGateway activeGateway = null;
+
+ public EIP(){
+ super("LEAPEIP");
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ context = getApplicationContext();
+
+ try {
+ eipDefinition = ConfigHelper.getJsonFromSharedPref(ConfigHelper.EIP_SERVICE_KEY);
+ parsedEipSerial = ConfigHelper.getIntFromSharedPref(ConfigHelper.EIP_PARSED_SERIAL);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ this.retreiveVpnService();
+ }
+
+ @Override
+ public void onDestroy() {
+ unbindService(mVpnServiceConn);
+ mBound = false;
+
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent.getAction();
+ mReceiver = intent.getParcelableExtra(ConfigHelper.RECEIVER_TAG);
+
+ if ( action == ACTION_IS_EIP_RUNNING )
+ this.isRunning();
+ if ( action == ACTION_UPDATE_EIP_SERVICE )
+ this.updateEIPService();
+ else if ( action == ACTION_START_EIP )
+ this.startEIP();
+ else if ( action == ACTION_STOP_EIP )
+ this.stopEIP();
+ }
+
+ /**
+ * Sends an Intent to bind OpenVpnService.
+ * Used when OpenVpnService isn't bound but might be running.
+ */
+ private void retreiveVpnService() {
+ Intent bindIntent = new Intent(this,OpenVpnService.class);
+ bindIntent.setAction(OpenVpnService.RETRIEVE_SERVICE);
+ bindService(bindIntent, mVpnServiceConn, 0);
+ }
+
+ private static ServiceConnection mVpnServiceConn = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ LocalBinder binder = (LocalBinder) service;
+ mVpnService = binder.getService();
+ mBound = true;
+
+ if (mReceiver != null && mPending != null) {
+
+ boolean running = mVpnService.isRunning();
+ int resultCode = Activity.RESULT_CANCELED;
+
+ if (mPending.equals(ACTION_IS_EIP_RUNNING))
+ resultCode = (running) ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
+ if (mPending.equals(ACTION_START_EIP))
+ resultCode = (running) ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
+ else if (mPending.equals(ACTION_STOP_EIP))
+ resultCode = (running) ? Activity.RESULT_CANCELED
+ : Activity.RESULT_OK;
+ Bundle resultData = new Bundle();
+ resultData.putString(ConfigHelper.REQUEST_TAG, EIP_NOTIFICATION);
+ mReceiver.send(resultCode, resultData);
+
+ mPending = null;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBound = false;
+
+ if (mReceiver != null){
+ Bundle resultData = new Bundle();
+ resultData.putString(ConfigHelper.REQUEST_TAG, EIP_NOTIFICATION);
+ mReceiver.send(Activity.RESULT_CANCELED, resultData);
+ }
+ }
+
+ };
+
+ /**
+ * Attempts to determine if OpenVpnService has an established VPN connection
+ * through the bound ServiceConnection. If there is no bound service, this
+ * method will attempt to bind a running OpenVpnService and send
+ * <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
+ * request.
+ * Note: If the request to bind OpenVpnService is successful, the ResultReceiver
+ * will be notified in {@link onServiceConnected()}
+ */
+ private void isRunning() {
+ Bundle resultData = new Bundle();
+ resultData.putString(ConfigHelper.REQUEST_TAG, ACTION_IS_EIP_RUNNING);
+ int resultCode = Activity.RESULT_CANCELED;
+ if (mBound) {
+ resultCode = (mVpnService.isRunning()) ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
+ } else {
+ mPending = ACTION_IS_EIP_RUNNING;
+ this.retreiveVpnService();
+ }
+
+ if (mReceiver != null){
+ mReceiver.send(resultCode, resultData);
+ }
+ }
+
+ /**
+ * Initiates an EIP connection by selecting a gateway and preparing and sending an
+ * Intent to {@link se.leap.openvpn.LaunchVPN}
+ */
+ private void startEIP() {
+ if (activeGateway==null)
+ activeGateway = selectGateway();
+
+ Intent intent = new Intent(this,LaunchVPN.class);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LaunchVPN.EXTRA_KEY, activeGateway.mVpnProfile.getUUID().toString() );
+ intent.putExtra(LaunchVPN.EXTRA_NAME, activeGateway.mVpnProfile.getName() );
+ intent.putExtra(ConfigHelper.RECEIVER_TAG, mReceiver);
+ startActivity(intent);
+ mPending = ACTION_START_EIP;
+ }
+
+ /**
+ * Disconnects the EIP connection gracefully through the bound service or forcefully
+ * if there is no bound service. Sends a message to the requesting ResultReceiver.
+ */
+ private void stopEIP() {
+ if (mBound)
+ mVpnService.onRevoke();
+ else
+ OpenVpnManagementThread.stopOpenVPN();
+
+ if (mReceiver != null){
+ Bundle resultData = new Bundle();
+ resultData.putString(ConfigHelper.REQUEST_TAG, ACTION_STOP_EIP);
+ mReceiver.send(Activity.RESULT_OK, resultData);
+ }
+ }
+
+ /**
+ * Loads eip-service.json from SharedPreferences and calls {@link updateGateways()}
+ * to parse gateway definitions.
+ * TODO Implement API call to refresh eip-service.json from the provider
+ */
+ private void updateEIPService() {
+ try {
+ eipDefinition = ConfigHelper.getJsonFromSharedPref(ConfigHelper.EIP_SERVICE_KEY);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ if (eipDefinition.optInt("serial") > parsedEipSerial)
+ updateGateways();
+ }
+
+ /**
+ * Choose a gateway to connect to based on timezone from system locale data
+ *
+ * @return The gateway to connect to
+ */
+ private OVPNGateway selectGateway() {
+ // TODO Implement gateway selection logic based on TZ or preferences
+ // TODO Implement search through gateways loaded from SharedPreferences
+ // TODO Remove String arg constructor in favor of findGatewayByName(String)
+ return new OVPNGateway("first");
+ }
+
+ /**
+ * Walk the list of gateways defined in eip-service.json and parse them into
+ * OVPNGateway objects.
+ * TODO Store the OVPNGateways (as Serializable) in SharedPreferences
+ */
+ private void updateGateways(){
+ JSONArray gatewaysDefined = null;
+
+ try {
+ gatewaysDefined = eipDefinition.getJSONArray("gateways");
+ } catch (JSONException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ for ( int i=0 ; i < gatewaysDefined.length(); i++ ){
+
+ JSONObject gw = null;
+
+ try {
+ gw = gatewaysDefined.getJSONObject(i);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ try {
+ if ( gw.getJSONObject("capabilities").getJSONArray("transport").toString().contains("openvpn") ){
+ new OVPNGateway(gw);
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ ConfigHelper.saveSharedPref(ConfigHelper.EIP_PARSED_SERIAL, eipDefinition.optInt(ConfigHelper.API_RETURN_SERIAL_KEY));
+ }
+
+ /**
+ * OVPNGateway provides objects defining gateways and their options and metadata.
+ * Each instance contains a VpnProfile for OpenVPN specific data and member
+ * variables describing capabilities and location
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ */
+ private class OVPNGateway {
+
+ private String TAG = "OVPNGateway";
+
+ private String mName;
+ private VpnProfile mVpnProfile;
+ private JSONObject mGateway;
+ private HashMap<String,Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>();
+
+
+ /**
+ * Attempts to retrieve a VpnProfile by name and build an OVPNGateway around it.
+ * FIXME This needs to become a findGatewayByName() method
+ *
+ * @param name The hostname of the gateway to inflate
+ */
+ private OVPNGateway(String name){
+ mName = name;
+
+ this.loadVpnProfile();
+ }
+
+ private void loadVpnProfile() {
+ ProfileManager vpl = ProfileManager.getInstance(context);
+
+ try {
+ if ( mName == "first" ) {
+ mName = vpl.getProfiles().iterator().next().mName;
+ }
+
+ mVpnProfile = vpl.getProfileByName(mName);
+
+ } catch (NoSuchElementException e) {
+ updateEIPService();
+ this.loadVpnProfile(); // FIXME catch infinite loops
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 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 OVPNGateway(JSONObject gateway){
+
+ mGateway = gateway;
+
+ // 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();
+ try {
+ if ( p.mName.contains( gateway.getString("host") ) )
+ it.remove();
+ vpl.removeProfile(context, p);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ this.parseOptions();
+ this.createVPNProfile();
+
+ setUniqueProfileName(vpl);
+ vpl.addProfile(mVpnProfile);
+ vpl.saveProfile(context, mVpnProfile);
+ vpl.saveProfileList(context);
+ }
+
+ /**
+ * Attempts to create a unique profile name from the hostname of the gateway
+ *
+ * @param profileManager
+ */
+ private void setUniqueProfileName(ProfileManager profileManager) {
+ int i=0;
+
+ String newname;
+ try {
+ newname = mGateway.getString("host");
+ while(profileManager.getProfileByName(newname)!=null) {
+ i++;
+ if(i==1)
+ newname = getString(R.string.converted_profile);
+ else
+ newname = getString(R.string.converted_profile_i,i);
+ }
+
+ mVpnProfile.mName=newname;
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ Log.v(TAG,"Couldn't read gateway name for profile creation!");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * FIXME This method is really the outline of the refactoring needed in se.leap.openvpn.ConfigParser
+ */
+ private void parseOptions(){
+
+ // FIXME move these to a common API (& version) definition place, like ProviderAPI or ConfigHelper
+ String common_options = "openvpn_configuration";
+ String remote = "ip_address";
+ String ports = "ports";
+ String protos = "protocols";
+ String capabilities = "capabilities";
+
+ Vector<String> arg = new Vector<String>();
+ Vector<Vector<String>> args = new Vector<Vector<String>>();
+
+ try {
+ JSONObject def = (JSONObject) eipDefinition.get(common_options);
+ Iterator keys = def.keys();
+ Vector<Vector<String>> value = new Vector<Vector<String>>();
+ while ( keys.hasNext() ){
+ String key = keys.next().toString();
+
+ arg.add(key);
+ for ( String word : def.getString(key).split(" ") )
+ arg.add(word);
+ value.add( (Vector<String>) arg.clone() );
+ options.put(key, (Vector<Vector<String>>) value.clone());
+
+ value.clear();
+ arg.clear();
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ try {
+ arg.add("remote");
+ arg.add(mGateway.getString(remote));
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ args.add((Vector<String>) arg.clone());
+ options.put("remote", (Vector<Vector<String>>) args.clone() );
+ arg.clear();
+ args.clear();
+
+ JSONArray protocolsJSON = null;
+ arg.add("proto");
+ try {
+ protocolsJSON = mGateway.getJSONObject(capabilities).getJSONArray(protos);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ Vector<String> protocols = new Vector<String>();
+ for ( int i=0; i<protocolsJSON.length(); i++ )
+ protocols.add(protocolsJSON.optString(i));
+ if ( protocols.contains("udp"))
+ arg.add("udp");
+ else if ( protocols.contains("tcp"))
+ arg.add("tcp");
+ args.add((Vector<String>) arg.clone());
+ options.put("proto", (Vector<Vector<String>>) args.clone());
+ arg.clear();
+ args.clear();
+
+
+ String port = null;
+ arg.add("port");
+ try {
+ port = mGateway.getJSONObject(capabilities).getJSONArray(ports).optString(0);
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ arg.add(port);
+ args.add((Vector<String>) arg.clone());
+ options.put("port", (Vector<Vector<String>>) args.clone());
+ args.clear();
+ arg.clear();
+ }
+
+ /**
+ * Create and attach the VpnProfile to our gateway object
+ */
+ protected void createVPNProfile(){
+ try {
+ ConfigParser cp = new ConfigParser();
+ cp.setDefinition(options);
+ VpnProfile vp = cp.convertProfile();
+ mVpnProfile = vp;
+ Log.v(TAG,"Created VPNProfile");
+ } catch (ConfigParseError e) {
+ // FIXME We didn't get a VpnProfile! Error handling! and log level
+ Log.v(TAG,"Error createing VPNProfile");
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/src/se/leap/leapclient/EipServiceFragment.java b/src/se/leap/leapclient/EipServiceFragment.java
new file mode 100644
index 00000000..c18f83da
--- /dev/null
+++ b/src/se/leap/leapclient/EipServiceFragment.java
@@ -0,0 +1,264 @@
+package se.leap.leapclient;
+
+import se.leap.openvpn.LogWindow;
+import se.leap.openvpn.OpenVPN;
+import se.leap.openvpn.OpenVPN.StateListener;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.CompoundButton;
+import android.widget.RelativeLayout;
+import android.widget.Switch;
+import android.widget.TextView;
+
+public class EipServiceFragment extends Fragment implements StateListener, OnClickListener, OnCheckedChangeListener {
+
+ private static final String IS_EIP_PENDING = "is_eip_pending";
+
+ private View eipFragment;
+ private Switch eipSwitch;
+ private View eipDetail;
+ private TextView eipStatus;
+
+ private boolean eipAutoSwitched = true;
+
+ private boolean mEipStartPending = false;
+
+ private EIPReceiver mEIPReceiver;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ eipFragment = inflater.inflate(R.layout.eip_service_fragment, container, false);
+
+ eipDetail = ((RelativeLayout) eipFragment.findViewById(R.id.eipDetail));
+ eipDetail.setVisibility(View.VISIBLE);
+
+ View eipSettings = eipFragment.findViewById(R.id.eipSettings);
+ eipSettings.setVisibility(View.GONE); // FIXME too!
+
+ if (mEipStartPending)
+ eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
+
+ eipStatus = (TextView) eipFragment.findViewById(R.id.eipStatus);
+ eipStatus.setOnClickListener(this);
+
+ eipSwitch = (Switch) eipFragment.findViewById(R.id.eipSwitch);
+ eipSwitch.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ eipAutoSwitched = false;
+ return false;
+ }
+ });
+ eipSwitch.setOnCheckedChangeListener(this);
+
+ return eipFragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mEIPReceiver = new EIPReceiver(new Handler());
+
+ if (savedInstanceState != null)
+ mEipStartPending = savedInstanceState.getBoolean(IS_EIP_PENDING);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ OpenVPN.addStateListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ OpenVPN.removeStateListener(this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(IS_EIP_PENDING, mEipStartPending);
+ }
+
+ @Override
+ public void onClick(View buttonView) {
+ if (buttonView.equals(eipStatus))
+ showEIPLog();
+ }
+
+ /**
+ * Launches the se.leap.openvpn.LogWindow Activity showing detailed OpenVPN log
+ */
+ public void showEIPLog(){
+ Intent intent = new Intent(getActivity().getBaseContext(),LogWindow.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ startActivity(intent);
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.equals(eipSwitch) && !eipAutoSwitched){
+ if (isChecked){
+ mEipStartPending = true;
+ eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
+ ((TextView) eipFragment.findViewById(R.id.eipStatus)).setText(R.string.eip_status_start_pending);
+ eipCommand(EIP.ACTION_START_EIP);
+ } else {
+ if (mEipStartPending){
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());
+ alertBuilder.setTitle(getResources().getString(R.string.eip_cancel_connect_title));
+ alertBuilder
+ .setMessage(getResources().getString(R.string.eip_cancel_connect_text))
+ .setPositiveButton(getResources().getString(R.string.eip_cancel_connect_cancel), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ eipCommand(EIP.ACTION_STOP_EIP);
+ }
+ })
+ .setNegativeButton(getResources().getString(R.string.eip_cancel_connect_false), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ eipAutoSwitched = true;
+ eipSwitch.setChecked(true);
+ eipAutoSwitched = false;
+ }
+ })
+ .show();
+ } else {
+ eipCommand(EIP.ACTION_STOP_EIP);
+ }
+ }
+ }
+ eipAutoSwitched = true;
+ }
+
+ /**
+ * Send a command to EIP
+ *
+ * @param action A valid String constant from EIP class representing an Intent
+ * filter for the EIP class
+ */
+ private void eipCommand(String action){
+ // TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
+ Intent vpnIntent = new Intent(action);
+ vpnIntent.putExtra(ConfigHelper.RECEIVER_TAG, mEIPReceiver);
+ getActivity().startService(vpnIntent);
+ }
+
+ @Override
+ public void updateState(final String state, final String logmessage, final int localizedResId) {
+ // Note: "states" are not organized anywhere...collected state strings:
+ // NOPROCESS,NONETWORK,BYTECOUNT,AUTH_FAILED + some parsing thing ( WAIT(?),AUTH,GET_CONFIG,ASSIGN_IP,CONNECTED,SIGINT )
+ getActivity().runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ if (eipStatus != null) {
+ boolean switchState = true;
+ String statusMessage = "";
+ String prefix = getString(localizedResId);
+ if (state.equals("CONNECTED")){
+ statusMessage = "Connection Secure";
+ getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
+ mEipStartPending = false;
+ } else if (state.equals("BYTECOUNT")) {
+ statusMessage = logmessage;
+ } else if ( (state.equals("NOPROCESS") && !mEipStartPending ) || state.equals("EXITING")) {
+ statusMessage = "Not running! Connection not secure!";
+ getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
+ mEipStartPending = false;
+ switchState = false;
+ } else {
+ statusMessage = prefix + logmessage;
+ }
+
+ eipAutoSwitched = true;
+ eipSwitch.setChecked(switchState);
+ eipAutoSwitched = false;
+ eipStatus.setText(statusMessage);
+ }
+ }
+ });
+ }
+
+ /**
+ * Inner class for handling messages related to EIP status and control requests
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ */
+ protected class EIPReceiver extends ResultReceiver {
+
+ protected EIPReceiver(Handler handler){
+ super(handler);
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ super.onReceiveResult(resultCode, resultData);
+
+ String request = resultData.getString(ConfigHelper.REQUEST_TAG);
+ boolean checked = false;
+
+ if (request == EIP.ACTION_IS_EIP_RUNNING) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ checked = true;
+ break;
+ case Activity.RESULT_CANCELED:
+ checked = false;
+ break;
+ }
+ } else if (request == EIP.ACTION_START_EIP) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ checked = true;
+ break;
+ case Activity.RESULT_CANCELED:
+ checked = false;
+ eipFragment.findViewById(R.id.eipProgress).setVisibility(View.GONE);
+ break;
+ }
+ } else if (request == EIP.ACTION_STOP_EIP) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ checked = false;
+ break;
+ case Activity.RESULT_CANCELED:
+ checked = true;
+ break;
+ }
+ } else if (request == EIP.EIP_NOTIFICATION) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ checked = true;
+ break;
+ case Activity.RESULT_CANCELED:
+ checked = false;
+ break;
+ }
+ }
+
+ eipAutoSwitched = true;
+ eipSwitch.setChecked(checked);
+ eipAutoSwitched = false;
+ }
+ }
+}
diff --git a/src/se/leap/leapclient/LeapHttpClient.java b/src/se/leap/leapclient/LeapHttpClient.java
new file mode 100644
index 00000000..686d3cc0
--- /dev/null
+++ b/src/se/leap/leapclient/LeapHttpClient.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import java.security.KeyStore;
+
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.SingleClientConnManager;
+import android.content.Context;
+
+/**
+ * Implements an HTTP client, enabling LEAP Android app to manage its own runtime keystore or bypass default Android security measures.
+ *
+ * @author rafa
+ *
+ */
+public class LeapHttpClient extends DefaultHttpClient {
+ final Context context;
+
+ private static LeapHttpClient client;
+
+ /**
+ * If the class scope client is null, it creates one and imports, if existing, the main certificate from Shared Preferences.
+ * @param context
+ * @return the new client.
+ */
+ public static LeapHttpClient getInstance(Context context) {
+ if(client == null) {
+ client = new LeapHttpClient(context);
+ String cert_string = ConfigHelper.getStringFromSharedPref(ConfigHelper.MAIN_CERT_KEY);
+ if(cert_string != null) {
+ ConfigHelper.addTrustedCertificate("recovered_certificate", cert_string);
+ }
+ }
+ return client;
+ }
+
+ @Override
+ protected ClientConnectionManager createClientConnectionManager() {
+ SchemeRegistry registry = new SchemeRegistry();
+ registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
+ registry.register(new Scheme("https", newSslSocketFactory(), 443));
+
+ return new SingleClientConnManager(getParams(), registry);
+ }
+
+ /**
+ * Uses keystore from ConfigHelper for the SSLSocketFactory.
+ *
+ * Sets hostname verifier to allow all hostname verifier.
+ * @return
+ */
+ private SSLSocketFactory newSslSocketFactory() {
+ try {
+ KeyStore trusted = ConfigHelper.getKeystore();
+ SSLSocketFactory sf = new SSLSocketFactory(trusted);
+ sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+
+ return sf;
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ public LeapHttpClient(Context context) {
+ this.context = context;
+ }
+}
diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java
new file mode 100644
index 00000000..9451c1be
--- /dev/null
+++ b/src/se/leap/leapclient/LeapSRPSession.java
@@ -0,0 +1,322 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import org.jboss.security.srp.SRPParameters;
+
+/**
+ * Implements all SRP algorithm logic.
+ *
+ * It's derived from JBoss implementation, with adjustments to make it work with LEAP platform.
+ *
+ * @author parmegv
+ *
+ */
+public class LeapSRPSession {
+
+ private SRPParameters params;
+ private String username;
+ private String password;
+ private BigInteger N;
+ private byte[] N_bytes;
+ private BigInteger g;
+ private BigInteger x;
+ private BigInteger v;
+ private BigInteger a;
+ private BigInteger A;
+ private byte[] K;
+ private SecureRandom pseudoRng;
+ /** The M1 = H(H(N) xor H(g) | H(U) | s | A | B | K) hash */
+ private MessageDigest clientHash;
+ /** The M2 = H(A | M | K) hash */
+ private MessageDigest serverHash;
+
+ private static int A_LEN;
+
+ /** Creates a new SRP server session object from the username, password
+ verifier,
+ @param username, the user ID
+ @param password, the user clear text password
+ @param params, the SRP parameters for the session
+ */
+ public LeapSRPSession(String username, String password, SRPParameters params)
+ {
+ this(username, password, params, null);
+ }
+
+ /** Creates a new SRP server session object from the username, password
+ verifier,
+ @param username, the user ID
+ @param password, the user clear text password
+ @param params, the SRP parameters for the session
+ @param abytes, the random exponent used in the A public key
+ */
+ public LeapSRPSession(String username, String password, SRPParameters params,
+ byte[] abytes) {
+ this.params = params;
+ this.g = new BigInteger(1, params.g);
+ N_bytes = ConfigHelper.trim(params.N);
+ this.N = new BigInteger(1, N_bytes);
+ this.username = username;
+ this.password = password;
+
+ try {
+ pseudoRng = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if( abytes != null ) {
+ A_LEN = 8*abytes.length;
+ /* TODO Why did they put this condition?
+ if( 8*abytes.length != A_LEN )
+ throw new IllegalArgumentException("The abytes param must be "
+ +(A_LEN/8)+" in length, abytes.length="+abytes.length);
+ */
+ this.a = new BigInteger(abytes);
+ }
+ else
+ A_LEN = 64;
+
+ serverHash = newDigest();
+ clientHash = newDigest();
+ }
+
+ /**
+ * Calculates the parameter x of the SRP-6a algorithm.
+ * @param username
+ * @param password
+ * @param salt the salt of the user
+ * @return x
+ */
+ public byte[] calculatePasswordHash(String username, String password, byte[] salt)
+ {
+ //password = password.replaceAll("\\\\", "\\\\\\\\");
+ // Calculate x = H(s | H(U | ':' | password))
+ MessageDigest x_digest = newDigest();
+ // Try to convert the username to a byte[] using ISO-8859-1
+ byte[] user = null;
+ byte[] password_bytes = null;
+ byte[] colon = {};
+ String encoding = "ISO-8859-1";
+ try {
+ user = ConfigHelper.trim(username.getBytes(encoding));
+ colon = ConfigHelper.trim(":".getBytes(encoding));
+ password_bytes = ConfigHelper.trim(password.getBytes(encoding));
+ }
+ catch(UnsupportedEncodingException e) {
+ // Use the default platform encoding
+ user = ConfigHelper.trim(username.getBytes());
+ colon = ConfigHelper.trim(":".getBytes());
+ password_bytes = ConfigHelper.trim(password.getBytes());
+ }
+
+ // Build the hash
+ x_digest.update(user);
+ x_digest.update(colon);
+ x_digest.update(password_bytes);
+ byte[] h = x_digest.digest();
+
+ x_digest.reset();
+ x_digest.update(salt);
+ x_digest.update(h);
+ byte[] x_digest_bytes = x_digest.digest();
+
+ return x_digest_bytes;
+ }
+
+ /**
+ * Calculates the parameter V of the SRP-6a algorithm.
+ * @param k_string constant k predefined by the SRP server implementation.
+ * @return the value of V
+ */
+ private BigInteger calculateV(String k_string) {
+ BigInteger k = new BigInteger(k_string, 16);
+ BigInteger v = k.multiply(g.modPow(x, N)); // g^x % N
+ return v;
+ }
+
+ /**
+ * Calculates the trimmed xor from two BigInteger numbers
+ * @param b1 the positive source to build first BigInteger
+ * @param b2 the positive source to build second BigInteger
+ * @param length
+ * @return
+ */
+ public byte[] xor(byte[] b1, byte[] b2)
+ {
+ //TODO Check if length matters in the order, when b2 is smaller than b1 or viceversa
+ byte[] xor_digest = new BigInteger(1, b1).xor(new BigInteger(1, b2)).toByteArray();
+ return ConfigHelper.trim(xor_digest);
+ }
+
+ /**
+ * @returns The exponential residue (parameter A) to be sent to the server.
+ */
+ public byte[] exponential() {
+ byte[] Abytes = null;
+ if(A == null) {
+ /* If the random component of A has not been specified use a random
+ number */
+ if( a == null ) {
+ BigInteger one = BigInteger.ONE;
+ do {
+ a = new BigInteger(A_LEN, pseudoRng);
+ } while(a.compareTo(one) <= 0);
+ }
+ A = g.modPow(a, N);
+ Abytes = ConfigHelper.trim(A.toByteArray());
+ }
+ return Abytes;
+ }
+
+ /**
+ * Calculates the parameter M1, to be sent to the SRP server.
+ * It also updates hashes of client and server for further calculations in other methods.
+ * It uses a predefined k.
+ * @param salt_bytes
+ * @param Bbytes the parameter received from the server, in bytes
+ * @return the parameter M1
+ * @throws NoSuchAlgorithmException
+ */
+ public byte[] response(byte[] salt_bytes, byte[] Bbytes) throws NoSuchAlgorithmException {
+ // Calculate x = H(s | H(U | ':' | password))
+ byte[] xb = calculatePasswordHash(username, password, ConfigHelper.trim(salt_bytes));
+ this.x = new BigInteger(1, xb);
+
+ // Calculate v = kg^x mod N
+ String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0";
+ this.v = calculateV(k_string);
+
+ // H(N)
+ byte[] digest_of_n = newDigest().digest(N_bytes);
+
+ // H(g)
+ byte[] digest_of_g = newDigest().digest(params.g);
+
+ // clientHash = H(N) xor H(g)
+ byte[] xor_digest = xor(digest_of_n, digest_of_g);
+ clientHash.update(xor_digest);
+
+ // clientHash = H(N) xor H(g) | H(U)
+ byte[] username_digest = newDigest().digest(ConfigHelper.trim(username.getBytes()));
+ username_digest = ConfigHelper.trim(username_digest);
+ clientHash.update(username_digest);
+
+ // clientHash = H(N) xor H(g) | H(U) | s
+ clientHash.update(ConfigHelper.trim(salt_bytes));
+
+ K = null;
+
+ // clientHash = H(N) xor H(g) | H(U) | A
+ byte[] Abytes = ConfigHelper.trim(A.toByteArray());
+ clientHash.update(Abytes);
+
+ // clientHash = H(N) xor H(g) | H(U) | s | A | B
+ Bbytes = ConfigHelper.trim(Bbytes);
+ clientHash.update(Bbytes);
+
+ // Calculate S = (B - kg^x) ^ (a + u * x) % N
+ BigInteger S = calculateS(Bbytes);
+ byte[] S_bytes = ConfigHelper.trim(S.toByteArray());
+
+ // K = SessionHash(S)
+ String hash_algorithm = params.hashAlgorithm;
+ MessageDigest sessionDigest = MessageDigest.getInstance(hash_algorithm);
+ K = ConfigHelper.trim(sessionDigest.digest(S_bytes));
+
+ // clientHash = H(N) xor H(g) | H(U) | A | B | K
+ clientHash.update(K);
+
+ byte[] M1 = ConfigHelper.trim(clientHash.digest());
+
+ // serverHash = Astr + M + K
+ serverHash.update(Abytes);
+ serverHash.update(M1);
+ serverHash.update(K);
+
+ return M1;
+ }
+
+ /**
+ * It calculates the parameter S used by response() to obtain session hash K.
+ * @param Bbytes the parameter received from the server, in bytes
+ * @return the parameter S
+ */
+ private BigInteger calculateS(byte[] Bbytes) {
+ byte[] Abytes = ConfigHelper.trim(A.toByteArray());
+ Bbytes = ConfigHelper.trim(Bbytes);
+ byte[] u_bytes = getU(Abytes, Bbytes);
+
+ BigInteger B = new BigInteger(1, Bbytes);
+ BigInteger u = new BigInteger(1, u_bytes);
+
+ BigInteger B_minus_v = B.subtract(v);
+ BigInteger a_ux = a.add(u.multiply(x));
+ BigInteger S = B_minus_v.modPow(a_ux, N);
+ return S;
+ }
+
+ /**
+ * It calculates the parameter u used by calculateS to obtain S.
+ * @param Abytes the exponential residue sent to the server
+ * @param Bbytes the parameter received from the server, in bytes
+ * @return
+ */
+ public byte[] getU(byte[] Abytes, byte[] Bbytes) {
+ MessageDigest u_digest = newDigest();
+ u_digest.update(ConfigHelper.trim(Abytes));
+ u_digest.update(ConfigHelper.trim(Bbytes));
+ byte[] u_digest_bytes = u_digest.digest();
+ return ConfigHelper.trim(new BigInteger(1, u_digest_bytes).toByteArray());
+ }
+
+ /**
+ * @param M2 The server's response to the client's challenge
+ * @returns True if and only if the server's response was correct.
+ */
+ public boolean verify(byte[] M2)
+ {
+ // M2 = H(A | M1 | K)
+ M2 = ConfigHelper.trim(M2);
+ byte[] myM2 = ConfigHelper.trim(serverHash.digest());
+ boolean valid = Arrays.equals(M2, myM2);
+ return valid;
+ }
+
+ /**
+ * @return a new SHA-256 digest.
+ */
+ public MessageDigest newDigest()
+ {
+ MessageDigest md = null;
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return md;
+ }
+}
diff --git a/src/se/leap/leapclient/LogInDialog.java b/src/se/leap/leapclient/LogInDialog.java
new file mode 100644
index 00000000..8b3f9e80
--- /dev/null
+++ b/src/se/leap/leapclient/LogInDialog.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import se.leap.leapclient.R;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+/**
+ * Implements the log in dialog, currently without progress dialog.
+ *
+ * It returns to the previous fragment when finished, and sends username and password to the authenticate method.
+ *
+ * It also notifies the user if the password is not valid.
+ *
+ * @author parmegv
+ *
+ */
+public class LogInDialog extends DialogFragment {
+
+ public AlertDialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View log_in_dialog_view = inflater.inflate(R.layout.log_in_dialog, null);
+
+ final TextView user_message = (TextView)log_in_dialog_view.findViewById(R.id.user_message);
+ if(getArguments() != null && getArguments().containsKey(getResources().getString(R.string.user_message))) {
+ user_message.setText(getArguments().getString(getResources().getString(R.string.user_message)));
+ } else user_message.setVisibility(View.GONE);
+ final EditText username_field = (EditText)log_in_dialog_view.findViewById(R.id.username_entered);
+ final EditText password_field = (EditText)log_in_dialog_view.findViewById(R.id.password_entered);
+
+ builder.setView(log_in_dialog_view)
+ .setPositiveButton(R.string.login_button, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ String username = username_field.getText().toString().trim();
+ String password = password_field.getText().toString().trim();
+ interface_with_Dashboard.authenticate(username, password);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+
+ return builder.create();
+ }
+
+ /**
+ * Interface used to communicate LogInDialog with Dashboard.
+ *
+ * @author parmegv
+ *
+ */
+ public interface LogInDialogInterface {
+ /**
+ * Starts authentication process.
+ * @param username
+ * @param password
+ */
+ public void authenticate(String username, String password);
+ }
+
+ LogInDialogInterface interface_with_Dashboard;
+
+ /**
+ * @return a new instance of this DialogFragment.
+ */
+ public static DialogFragment newInstance() {
+ LogInDialog dialog_fragment = new LogInDialog();
+ return dialog_fragment;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ interface_with_Dashboard = (LogInDialogInterface) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement LogInDialogListener");
+ }
+ }
+}
diff --git a/src/se/leap/leapclient/NewProviderDialog.java b/src/se/leap/leapclient/NewProviderDialog.java
new file mode 100644
index 00000000..0b9d8fd0
--- /dev/null
+++ b/src/se/leap/leapclient/NewProviderDialog.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import se.leap.leapclient.R;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.Toast;
+
+/**
+ * Implements the new custom provider dialog.
+ *
+ * @author parmegv
+ *
+ */
+public class NewProviderDialog extends DialogFragment {
+
+ public interface NewProviderDialogInterface {
+ public void saveAndSelectProvider(String url_provider, boolean danger_on);
+ }
+
+ NewProviderDialogInterface interface_with_ConfigurationWizard;
+
+ /**
+ * @return a new instance of this DialogFragment.
+ */
+ public static DialogFragment newInstance() {
+ NewProviderDialog dialog_fragment = new NewProviderDialog();
+ return dialog_fragment;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ interface_with_ConfigurationWizard = (NewProviderDialogInterface) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement NoticeDialogListener");
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View new_provider_dialog_view = inflater.inflate(R.layout.new_provider_dialog, null);
+ final EditText url_input_field = (EditText)new_provider_dialog_view.findViewById(R.id.new_provider_url);
+ final CheckBox danger_checkbox = (CheckBox)new_provider_dialog_view.findViewById(R.id.danger_checkbox);
+
+ builder.setView(new_provider_dialog_view)
+ .setMessage(R.string.introduce_new_provider)
+ .setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ String entered_url = url_input_field.getText().toString().trim();
+ if(!entered_url.startsWith("https://")) {
+ entered_url = "https://".concat(entered_url);
+ }
+ boolean danger_on = danger_checkbox.isChecked();
+ if(validURL(entered_url)) {
+ interface_with_ConfigurationWizard.saveAndSelectProvider(entered_url, danger_on);
+ Toast.makeText(getActivity().getApplicationContext(), R.string.valid_url_entered, Toast.LENGTH_LONG).show();
+ } else {
+ url_input_field.setText("");
+ Toast.makeText(getActivity().getApplicationContext(), R.string.not_valid_url_entered, Toast.LENGTH_LONG).show();
+ }
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ // Create the AlertDialog object and return it
+ return builder.create();
+ }
+
+ /**
+ * Checks if the entered url is valid or not.
+ * @param entered_url
+ * @return true if it's not empty nor contains only the protocol.
+ */
+ boolean validURL(String entered_url) {
+ return !entered_url.isEmpty() && entered_url.matches("http[s]?://.+") && !entered_url.replaceFirst("http[s]?://", "").isEmpty();
+ }
+}
diff --git a/src/se/leap/leapclient/Provider.java b/src/se/leap/leapclient/Provider.java
new file mode 100644
index 00000000..cdcd56c5
--- /dev/null
+++ b/src/se/leap/leapclient/Provider.java
@@ -0,0 +1,194 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Locale;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.app.Activity;
+import android.content.SharedPreferences;
+
+/**
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ *
+ */
+public final class Provider implements Serializable {
+
+ private static final long serialVersionUID = 6003835972151761353L;
+
+ private static Provider instance = null;
+
+ // We'll access our preferences here
+ private static SharedPreferences preferences = null;
+ // Represents our Provider's provider.json
+ private static JSONObject definition = null;
+
+
+ // Array of what API versions we understand
+ protected static final String[] API_VERSIONS = {"1"}; // I assume we might encounter arbitrary version "numbers"
+ // Some API pieces we want to know about
+ private static final String API_TERM_SERVICES = "services";
+ private static final String API_TERM_NAME = "name";
+ private static final String API_TERM_DOMAIN = "domain";
+ private static final String API_TERM_DEFAULT_LANGUAGE = "default_language";
+ protected static final String[] API_EIP_TYPES = {"openvpn"};
+
+ private static final String PREFS_EIP_NAME = null;
+
+
+
+ // What, no individual fields?! We're going to gamble on org.json.JSONObject and JSONArray
+ // Supporting multiple API versions will probably break this paradigm,
+ // Forcing me to write a real constructor and rewrite getters/setters
+ // Also will refactor if i'm instantiating the same local variables all the time
+
+ /**
+ *
+ */
+ private Provider() {}
+
+ protected static Provider getInstance(){
+ if(instance==null){
+ instance = new Provider();
+ }
+ return instance;
+ }
+
+ protected void init(Activity activity) {
+
+ // Load our preferences from SharedPreferences
+ // If there's nothing there, we will end up returning a rather empty object
+ // to whoever called getInstance() and they can run the First Run Wizard
+ //preferences = context.getgetPreferences(0); // 0 == MODE_PRIVATE, but we don't extend Android's classes...
+
+ // Load SharedPreferences
+ preferences = activity.getSharedPreferences(ConfigHelper.PREFERENCES_KEY,Context.MODE_PRIVATE);
+ // Inflate our provider.json data
+ try {
+ definition = new JSONObject( preferences.getString("provider", "") );
+ } catch (JSONException e) {
+ // TODO: handle exception
+
+ // FIXME!! We want "real" data!!
+ }
+ }
+
+ protected String getDomain(){
+ String domain = "Null";
+ try {
+ domain = definition.getString(API_TERM_DOMAIN);
+ } catch (JSONException e) {
+ domain = "Null";
+ e.printStackTrace();
+ }
+ return domain;
+ }
+
+ protected String getName(){
+ // Should we pass the locale in, or query the system here?
+ String lang = Locale.getDefault().getLanguage();
+ String name = "Null"; // Should it actually /be/ null, for error conditions?
+ try {
+ name = definition.getJSONObject(API_TERM_NAME).getString(lang);
+ } catch (JSONException e) {
+ // TODO: Nesting try/catch blocks? Crazy
+ // Maybe you should actually handle exception?
+ try {
+ name = definition.getJSONObject(API_TERM_NAME).getString( definition.getString(API_TERM_DEFAULT_LANGUAGE) );
+ } catch (JSONException e2) {
+ // TODO: Will you handle the exception already?
+ }
+ }
+
+ return name;
+ }
+
+ protected String getDescription(){
+ String lang = Locale.getDefault().getLanguage();
+ String desc = null;
+ try {
+ desc = definition.getJSONObject("description").getString(lang);
+ } catch (JSONException e) {
+ // TODO: handle exception!!
+ try {
+ desc = definition.getJSONObject("description").getString( definition.getString("default_language") );
+ } catch (JSONException e2) {
+ // TODO: i can't believe you're doing it again!
+ }
+ }
+
+ return desc;
+ }
+
+ protected boolean hasEIP() {
+ JSONArray services = null;
+ try {
+ services = definition.getJSONArray(API_TERM_SERVICES); // returns ["openvpn"]
+ } catch (Exception e) {
+ // TODO: handle exception
+ }
+ for (int i=0;i<API_EIP_TYPES.length+1;i++){
+ try {
+ // Walk the EIP types array looking for matches in provider's service definitions
+ if ( Arrays.asList(API_EIP_TYPES).contains( services.getString(i) ) )
+ return true;
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ return false;
+ }
+
+ protected String getEIPType() {
+ // FIXME!!!!! We won't always be providing /only/ OpenVPN, will we?
+ // This will have to hook into some saved choice of EIP transport
+ if ( instance.hasEIP() )
+ return "OpenVPN";
+ else
+ return null;
+ }
+
+ protected JSONObject getEIP() {
+ // FIXME!!!!! We won't always be providing /only/ OpenVPN, will we?
+ // This will have to hook into some saved choice of EIP transport, cluster, gateway
+ // with possible "choose at random" preference
+ if ( instance.hasEIP() ){
+ // TODO Might need an EIP class, but we've only got OpenVPN type right now,
+ // and only one gateway for our only provider...
+ // TODO We'll try to load from preferences, have to call ProviderAPI if we've got nothin...
+ JSONObject eipObject = null;
+ try {
+ eipObject = new JSONObject( preferences.getString(PREFS_EIP_NAME, "") );
+ } catch (JSONException e) {
+ // TODO ConfigHelper.rescueJSON()
+ // Still nothing?
+ // TODO ProviderAPI.getEIP()
+ e.printStackTrace();
+ }
+
+ return eipObject;
+ } else
+ return null;
+ }
+}
diff --git a/src/se/leap/leapclient/ProviderAPI.java b/src/se/leap/leapclient/ProviderAPI.java
new file mode 100644
index 00000000..944a0e7f
--- /dev/null
+++ b/src/se/leap/leapclient/ProviderAPI.java
@@ -0,0 +1,733 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.SecureRandom;
+import javax.net.ssl.KeyManager;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.HttpCookie;
+import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+import java.util.Scanner;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.jboss.security.srp.SRPParameters;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import se.leap.leapclient.ProviderListContent.ProviderItem;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.util.Base64;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Implements HTTP api methods used to manage communications with the provider server.
+ *
+ * It's an IntentService because it downloads data from the Internet, so it operates in the background.
+ *
+ * @author parmegv
+ * @author MeanderingCode
+ *
+ */
+public class ProviderAPI extends IntentService {
+
+ private Handler mHandler;
+
+ public ProviderAPI() {
+ super("ProviderAPI");
+ Log.v("ClassName", "Provider API");
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler();
+ }
+
+ private void displayToast(final int toast_string_id) {
+ mHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ Toast.makeText(ProviderAPI.this, toast_string_id, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+
+ @Override
+ protected void onHandleIntent(Intent task_for) {
+ final ResultReceiver receiver = task_for.getParcelableExtra("receiver");
+
+ Bundle task;
+ if((task = task_for.getBundleExtra(ConfigHelper.DOWNLOAD_JSON_FILES_BUNDLE_EXTRA)) != null) {
+ if(!downloadJsonFiles(task)) {
+ receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY);
+ } else {
+ receiver.send(ConfigHelper.CORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.UPDATE_PROVIDER_DOTJSON)) != null) {
+ Bundle result = updateProviderDotJSON(task);
+ if(result.getBoolean(ConfigHelper.RESULT_KEY)) {
+ receiver.send(ConfigHelper.CORRECTLY_UPDATED_PROVIDER_DOT_JSON, result);
+ } else {
+ receiver.send(ConfigHelper.INCORRECTLY_UPDATED_PROVIDER_DOT_JSON, Bundle.EMPTY);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.DOWNLOAD_NEW_PROVIDER_DOTJSON)) != null) {
+ Bundle result = downloadNewProviderDotJSON(task);
+ if(result.getBoolean(ConfigHelper.RESULT_KEY)) {
+ receiver.send(ConfigHelper.CORRECTLY_UPDATED_PROVIDER_DOT_JSON, result);
+ } else {
+ receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_JSON_FILES, Bundle.EMPTY);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.SRP_AUTH)) != null) {
+ Bundle session_id_bundle = authenticateBySRP(task);
+ if(session_id_bundle.getBoolean(ConfigHelper.RESULT_KEY)) {
+ receiver.send(ConfigHelper.SRP_AUTHENTICATION_SUCCESSFUL, session_id_bundle);
+ } else {
+ Bundle user_message_bundle = new Bundle();
+ String user_message_key = getResources().getString(R.string.user_message);
+ user_message_bundle.putString(user_message_key, session_id_bundle.getString(user_message_key));
+ receiver.send(ConfigHelper.SRP_AUTHENTICATION_FAILED, user_message_bundle);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.LOG_OUT)) != null) {
+ if(logOut(task)) {
+ receiver.send(ConfigHelper.LOGOUT_SUCCESSFUL, Bundle.EMPTY);
+ } else {
+ receiver.send(ConfigHelper.LOGOUT_FAILED, Bundle.EMPTY);
+ }
+ }
+ else if ((task = task_for.getBundleExtra(ConfigHelper.DOWNLOAD_CERTIFICATE)) != null) {
+ if(getNewCert(task)) {
+ receiver.send(ConfigHelper.CORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY);
+ } else {
+ receiver.send(ConfigHelper.INCORRECTLY_DOWNLOADED_CERTIFICATE, Bundle.EMPTY);
+ }
+ }
+ }
+
+ /**
+ * Downloads the main cert and the eip-service.json files given through the task parameter
+ * @param task
+ * @return true if eip-service.json was parsed as a JSON object correctly.
+ */
+ private boolean downloadJsonFiles(Bundle task) {
+ String cert_url = task.getString(ConfigHelper.MAIN_CERT_KEY);
+ String eip_service_json_url = task.getString(ConfigHelper.EIP_SERVICE_KEY);
+ boolean danger_on = task.getBoolean(ConfigHelper.DANGER_ON);
+ try {
+ String cert_string = getStringFromProvider(cert_url, danger_on);
+ ConfigHelper.saveSharedPref(ConfigHelper.MAIN_CERT_KEY, cert_string);
+ JSONObject eip_service_json = getJSONFromProvider(eip_service_json_url, danger_on);
+ ConfigHelper.saveSharedPref(ConfigHelper.EIP_SERVICE_KEY, eip_service_json);
+ return true;
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Starts the authentication process using SRP protocol.
+ *
+ * @param task containing: username, password and api url.
+ * @return a bundle with a boolean value mapped to a key named ConfigHelper.RESULT_KEY, and which is true if authentication was successful.
+ */
+ private Bundle authenticateBySRP(Bundle task) {
+ Bundle session_id_bundle = new Bundle();
+
+ String username = (String) task.get(ConfigHelper.USERNAME_KEY);
+ String password = (String) task.get(ConfigHelper.PASSWORD_KEY);
+ if(wellFormedPassword(password)) {
+ String authentication_server = (String) task.get(ConfigHelper.API_URL_KEY);
+
+ SRPParameters params = new SRPParameters(new BigInteger(ConfigHelper.NG_1024, 16).toByteArray(), ConfigHelper.G.toByteArray(), BigInteger.ZERO.toByteArray(), "SHA-256");
+ LeapSRPSession client = new LeapSRPSession(username, password, params);
+ byte[] A = client.exponential();
+ try {
+ JSONObject saltAndB = sendAToSRPServer(authentication_server, username, new BigInteger(1, A).toString(16));
+ if(saltAndB.length() > 0) {
+ String salt = saltAndB.getString(ConfigHelper.SALT_KEY);
+ byte[] Bbytes = new BigInteger(saltAndB.getString("B"), 16).toByteArray();
+ byte[] M1 = client.response(new BigInteger(salt, 16).toByteArray(), Bbytes);
+ JSONObject session_idAndM2 = sendM1ToSRPServer(authentication_server, username, M1);
+ if(session_idAndM2.has("M2") && client.verify((byte[])session_idAndM2.get("M2"))) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, true);
+ session_id_bundle.putString(ConfigHelper.SESSION_ID_KEY, session_idAndM2.getString(ConfigHelper.SESSION_ID_KEY));
+ session_id_bundle.putString(ConfigHelper.SESSION_ID_COOKIE_KEY, session_idAndM2.getString(ConfigHelper.SESSION_ID_COOKIE_KEY));
+ } else {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ session_id_bundle.putString(getResources().getString(R.string.user_message), getResources().getString(R.string.error_bad_user_password_user_message));
+ }
+ } else {
+ session_id_bundle.putString(getResources().getString(R.string.user_message), getResources().getString(R.string.error_bad_user_password_user_message));
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ }
+ } catch (ClientProtocolException e) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ session_id_bundle.putString(getResources().getString(R.string.user_message), getResources().getString(R.string.error_client_http_user_message));
+ } catch (IOException e) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ session_id_bundle.putString(getResources().getString(R.string.user_message), getResources().getString(R.string.error_io_exception_user_message));
+ } catch (JSONException e) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ session_id_bundle.putString(getResources().getString(R.string.user_message), getResources().getString(R.string.error_json_exception_user_message));
+ } catch (NoSuchAlgorithmException e) {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ session_id_bundle.putString(getResources().getString(R.string.user_message), getResources().getString(R.string.error_no_such_algorithm_exception_user_message));
+ }
+ } else {
+ session_id_bundle.putBoolean(ConfigHelper.RESULT_KEY, false);
+ session_id_bundle.putString(getResources().getString(R.string.user_message), getResources().getString(R.string.error_not_valid_password_user_message));
+ }
+
+ return session_id_bundle;
+ }
+
+ /**
+ * Validates a password
+ * @param entered_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;
+ }
+
+ /**
+ * Sends an HTTP POST request to the authentication server with the SRP Parameter A.
+ * @param server_url
+ * @param username
+ * @param clientA First SRP parameter sent
+ * @return response from authentication server
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws JSONException
+ */
+ private JSONObject sendAToSRPServer(String server_url, String username, String clientA) throws ClientProtocolException, IOException, JSONException {
+ HttpPost post = new HttpPost(server_url + "/sessions.json" + "?" + "login=" + username + "&&" + "A=" + clientA);
+ return sendToServer(post);
+ }
+
+ /**
+ * Sends an HTTP PUT request to the authentication server with the SRP Parameter M1 (or simply M).
+ * @param server_url
+ * @param username
+ * @param m1 Second SRP parameter sent
+ * @return response from authentication server
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws JSONException
+ */
+ private JSONObject sendM1ToSRPServer(String server_url, String username, byte[] m1) throws ClientProtocolException, IOException, JSONException {
+ HttpPut put = new HttpPut(server_url + "/sessions/" + username +".json" + "?" + "client_auth" + "=" + new BigInteger(1, ConfigHelper.trim(m1)).toString(16));
+ JSONObject json_response = sendToServer(put);
+
+ JSONObject session_idAndM2 = new JSONObject();
+ if(json_response.length() > 0) {
+ byte[] M2_not_trimmed = new BigInteger(json_response.getString(ConfigHelper.M2_KEY), 16).toByteArray();
+ Cookie session_id_cookie = LeapHttpClient.getInstance(getApplicationContext()).getCookieStore().getCookies().get(0);
+ session_idAndM2.put(ConfigHelper.SESSION_ID_COOKIE_KEY, session_id_cookie.getName());
+ session_idAndM2.put(ConfigHelper.SESSION_ID_KEY, session_id_cookie.getValue());
+ session_idAndM2.put(ConfigHelper.M2_KEY, ConfigHelper.trim(M2_not_trimmed));
+ }
+ return session_idAndM2;
+ }
+
+ /**
+ * Executes an HTTP request expecting a JSON response.
+ * @param request
+ * @return response from authentication server
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws JSONException
+ */
+ private JSONObject sendToServer(HttpUriRequest request) throws ClientProtocolException, IOException, JSONException {
+ DefaultHttpClient client = LeapHttpClient.getInstance(getApplicationContext());
+ HttpContext localContext = new BasicHttpContext();
+ localContext.setAttribute(ClientContext.COOKIE_STORE, client.getCookieStore());
+
+ HttpResponse getResponse = client.execute(request, localContext);
+ HttpEntity responseEntity = getResponse.getEntity();
+ String plain_response = new Scanner(responseEntity.getContent()).useDelimiter("\\A").next();
+ JSONObject json_response = new JSONObject(plain_response);
+ if(!json_response.isNull(ConfigHelper.ERRORS_KEY) || json_response.has(ConfigHelper.ERRORS_KEY)) {
+ return new JSONObject();
+ }
+
+ return json_response;
+ }
+
+ /**
+ * Downloads a provider.json from a given URL, adding a new provider using the given name.
+ * @param task containing a boolean meaning if the provider is custom or not, another boolean meaning if the user completely trusts this provider, the provider name and its provider.json url.
+ * @return a bundle with a boolean value mapped to a key named ConfigHelper.RESULT_KEY, and which is true if the update was successful.
+ */
+ private Bundle updateProviderDotJSON(Bundle task) {
+ Bundle result = new Bundle();
+ boolean custom = task.getBoolean(ConfigHelper.CUSTOM);
+ boolean danger_on = task.getBoolean(ConfigHelper.DANGER_ON);
+ String provider_json_url = task.getString(ConfigHelper.PROVIDER_JSON_URL);
+ String provider_name = task.getString(ConfigHelper.PROVIDER_NAME);
+
+ try {
+ JSONObject provider_json = getJSONFromProvider(provider_json_url, danger_on);
+ if(provider_json == null) {
+ result.putBoolean(ConfigHelper.RESULT_KEY, false);
+ } else {
+ ConfigHelper.saveSharedPref(ConfigHelper.ALLOWED_ANON, provider_json.getJSONObject(ConfigHelper.SERVICE_KEY).getBoolean(ConfigHelper.ALLOWED_ANON));
+
+ //ProviderListContent.addItem(new ProviderItem(provider_name, provider_json_url, provider_json, custom, danger_on));
+ result.putBoolean(ConfigHelper.RESULT_KEY, true);
+ result.putString(ConfigHelper.PROVIDER_KEY, provider_json.toString());
+ result.putBoolean(ConfigHelper.DANGER_ON, danger_on);
+ }
+ } catch (JSONException e) {
+ result.putBoolean(ConfigHelper.RESULT_KEY, false);
+ }
+
+ return result;
+ }
+
+ /**
+ * Downloads a custom provider provider.json file
+ * @param task containing a boolean meaning if the user completely trusts this provider, and the provider main url entered in the new custom provider dialog.
+ * @return true if provider.json file was successfully parsed as a JSON object.
+ */
+ private Bundle downloadNewProviderDotJSON(Bundle task) {
+ Bundle result = new Bundle();
+ boolean custom = true;
+ boolean danger_on = task.getBoolean(ConfigHelper.DANGER_ON);
+
+ String provider_main_url = (String) task.get(ConfigHelper.PROVIDER_MAIN_URL);
+ String provider_name = provider_main_url.replaceFirst("http[s]?://", "").replaceFirst("\\/", "_");
+ String provider_json_url = guessProviderDotJsonURL(provider_main_url);
+
+ JSONObject provider_json;
+ try {
+ provider_json = getJSONFromProvider(provider_json_url, danger_on);
+ if(provider_json == null) {
+ result.putBoolean(ConfigHelper.RESULT_KEY, false);
+ } else {
+
+ ConfigHelper.saveSharedPref(ConfigHelper.PROVIDER_KEY, provider_json);
+ ConfigHelper.saveSharedPref(ConfigHelper.DANGER_ON, danger_on);
+ ConfigHelper.saveSharedPref(ConfigHelper.ALLOWED_ANON, provider_json.getJSONObject(ConfigHelper.SERVICE_KEY).getBoolean(ConfigHelper.ALLOWED_ANON));
+ ProviderItem added_provider = new ProviderItem(provider_name, provider_json_url, provider_json, custom, danger_on);
+ ProviderListContent.addItem(added_provider);
+
+ result.putString(ConfigHelper.PROVIDER_ID, added_provider.getId());
+ result.putBoolean(ConfigHelper.RESULT_KEY, true);
+ result.putString(ConfigHelper.PROVIDER_KEY, provider_json.toString());
+ result.putBoolean(ConfigHelper.DANGER_ON, danger_on);
+ }
+ } catch (JSONException e) {
+ result.putBoolean(ConfigHelper.RESULT_KEY, false);
+ }
+
+ return result;
+ }
+
+ /**
+ * Tries to download whatever is pointed by the string_url.
+ *
+ * If danger_on flag is true, SSL exceptions will be managed by futher methods that will try to use some bypass methods.
+ * @param string_url
+ * @param danger_on if the user completely trusts this provider
+ * @return
+ */
+ private String getStringFromProvider(String string_url, boolean danger_on) {
+
+ String json_file_content = "";
+
+ URL provider_url = null;
+ int seconds_of_timeout = 1;
+ try {
+ provider_url = new URL(string_url);
+ URLConnection url_connection = provider_url.openConnection();
+ url_connection.setConnectTimeout(seconds_of_timeout*1000);
+ json_file_content = new Scanner(url_connection.getInputStream()).useDelimiter("\\A").next();
+ } catch (MalformedURLException e) {
+ displayToast(R.string.malformed_url);
+ } catch(SocketTimeoutException e) {
+ displayToast(R.string.server_is_down_message);
+ } catch (IOException e) {
+ if(provider_url != null) {
+ json_file_content = getStringFromProviderWithCACertAdded(provider_url, danger_on);
+ } else {
+ displayToast(R.string.certificate_error);
+ }
+ } catch (Exception e) {
+ if(provider_url != null && danger_on) {
+ json_file_content = getStringFromProviderWithCACertAdded(provider_url, danger_on);
+ }
+ }
+
+ return json_file_content;
+ }
+
+ /**
+ * Tries to download a string from given url without verifying the hostname.
+ *
+ * If a IOException still occurs, it tries with another bypass method: getStringFromProviderWithCACertAdded.
+ * @param string_url
+ * @return an empty string if everything fails, the url content if not.
+ */
+ private String getStringFromProviderWithoutValidate(
+ URL string_url) {
+
+ String json_string = "";
+ HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ };
+
+ try {
+ HttpsURLConnection urlConnection =
+ (HttpsURLConnection)string_url.openConnection();
+ urlConnection.setHostnameVerifier(hostnameVerifier);
+ json_string = new Scanner(urlConnection.getInputStream()).useDelimiter("\\A").next();
+ } catch (MalformedURLException e) {
+ displayToast(R.string.malformed_url);
+ } catch (IOException e) {
+ json_string = getStringFromProviderIgnoringCertificate(string_url);
+ }
+
+ return json_string;
+ }
+
+ /**
+ * Tries to download the contents of the provided url using main certificate from choosen provider.
+ * @param url
+ * @param danger_on true to download CA certificate in case it has not been downloaded.
+ * @return an empty string if it fails, the url content if not.
+ */
+ private String getStringFromProviderWithCACertAdded(URL url, boolean danger_on) {
+ String json_file_content = "";
+
+ // Load CAs from an InputStream
+ // (could be from a resource or ByteArrayInputStream or ...)
+ String cert_string = ConfigHelper.getStringFromSharedPref(ConfigHelper.MAIN_CERT_KEY);
+ if(cert_string.isEmpty() && danger_on) {
+ cert_string = downloadCertificateWithoutTrusting(url.getProtocol() + "://" + url.getHost() + "/" + "ca.crt");
+ ConfigHelper.saveSharedPref(ConfigHelper.MAIN_CERT_KEY, cert_string);
+ }
+
+ try {
+ java.security.cert.Certificate dangerous_certificate = ConfigHelper.parseX509CertificateFromString(cert_string);
+
+ // Create a KeyStore containing our trusted CAs
+ String keyStoreType = KeyStore.getDefaultType();
+ KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+ keyStore.load(null, null);
+ keyStore.setCertificateEntry("provider_ca_certificate", dangerous_certificate);
+
+ // Create a TrustManager that trusts the CAs in our KeyStore
+ String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
+ tmf.init(keyStore);
+
+ // Create an SSLContext that uses our TrustManager
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, tmf.getTrustManagers(), null);
+
+ // Tell the URLConnection to use a SocketFactory from our SSLContext
+ HttpsURLConnection urlConnection =
+ (HttpsURLConnection)url.openConnection();
+ urlConnection.setSSLSocketFactory(context.getSocketFactory());
+ json_file_content = new Scanner(urlConnection.getInputStream()).useDelimiter("\\A").next();
+ } catch (CertificateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (UnknownHostException e) {
+ displayToast(R.string.server_is_down_message);
+ } catch (IOException e) {
+ // The downloaded certificate doesn't validate our https connection.
+ if(danger_on) {
+ json_file_content = getStringFromProviderWithoutValidate(url);
+ } else {
+ displayToast(R.string.certificate_error);
+ }
+ } catch (KeyStoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return json_file_content;
+ }
+
+ /**
+ * Downloads the string that's in the url without regarding certificate validity
+ */
+ private String getStringFromProviderIgnoringCertificate(URL url) {
+ String string = "";
+ try {
+ class DefaultTrustManager implements X509TrustManager {
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ }
+
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
+
+ HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
+ urlConnection.setSSLSocketFactory(context.getSocketFactory());
+ urlConnection.setHostnameVerifier(new HostnameVerifier() {
+ @Override
+ public boolean verify(String arg0, SSLSession arg1) {
+ return true;
+ }
+ });
+ string = new Scanner(urlConnection.getInputStream()).useDelimiter("\\A").next();
+ System.out.println("String ignoring certificate = " + string);
+ } catch (IOException e) {
+ // The downloaded certificate doesn't validate our https connection.
+ e.printStackTrace();
+ displayToast(R.string.certificate_error);
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return string;
+ }
+
+ /**
+ * Downloads the certificate from the parameter url bypassing self signed certificate SSL errors.
+ * @param certificate_url_string
+ * @return the certificate, as a string
+ */
+ private String downloadCertificateWithoutTrusting(String certificate_url_string) {
+
+ String cert_string = "";
+ HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+ @Override
+ public boolean verify(String hostname, SSLSession session) {
+ return true;
+ }
+ };
+
+ TrustManager[] trustAllCerts = new TrustManager[]{
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) {
+ }
+ public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) {
+ }
+ }
+ };
+
+ try {
+ URL certificate_url = new URL(certificate_url_string);
+ HttpsURLConnection urlConnection =
+ (HttpsURLConnection)certificate_url.openConnection();
+ urlConnection.setHostnameVerifier(hostnameVerifier);
+
+ SSLContext sc = SSLContext.getInstance("TLS");
+ sc.init(null, trustAllCerts, new java.security.SecureRandom());
+
+ urlConnection.setSSLSocketFactory(sc.getSocketFactory());
+
+ cert_string = new Scanner(urlConnection.getInputStream()).useDelimiter("\\A").next();
+
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // This should never happen
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return cert_string;
+ }
+
+ /**
+ * Downloads a JSON object from the given url.
+ *
+ * It first downloads the JSON object as a String, and then parses it to JSON object.
+ * @param json_url
+ * @param danger_on if the user completely trusts the certificate of the url address.
+ * @return
+ * @throws JSONException
+ */
+ private JSONObject getJSONFromProvider(String json_url, boolean danger_on) throws JSONException {
+ String json_file_content = getStringFromProvider(json_url, danger_on);
+ return new JSONObject(json_file_content);
+ }
+
+ /**
+ * Tries to guess the provider.json url given the main provider url.
+ * @param provider_main_url
+ * @return the guessed provider.json url
+ */
+ private String guessProviderDotJsonURL(String provider_main_url) {
+ return provider_main_url + "/provider.json";
+ }
+
+ /**
+ * Logs out from the api url retrieved from the task.
+ * @param task containing api url from which the user will log out
+ * @return true if there were no exceptions
+ */
+ private boolean logOut(Bundle task) {
+ DefaultHttpClient client = LeapHttpClient.getInstance(getApplicationContext());
+ int session_id_index = 0;
+ //String delete_url = task.getString(ConfigHelper.srp_server_url_key) + "/sessions/" + client.getCookieStore().getCookies().get(0).getValue();
+ try {
+ String delete_url = task.getString(ConfigHelper.API_URL_KEY) + "/logout" + "?authenticity_token=" + client.getCookieStore().getCookies().get(session_id_index).getValue();
+ HttpDelete delete = new HttpDelete(delete_url);
+ HttpResponse getResponse = client.execute(delete);
+ HttpEntity responseEntity = getResponse.getEntity();
+ responseEntity.consumeContent();
+ } 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;
+ }
+ return true;
+ }
+
+ /**
+ * Downloads a new OpenVPN certificate, attaching authenticated cookie for authenticated certificate.
+ *
+ * @param task containing the type of the certificate to be downloaded
+ * @return true if certificate was downloaded correctly, false if provider.json or danger_on flag are not present in SharedPreferences, or if the certificate url could not be parsed as a URI, or if there was an SSL error.
+ */
+ private boolean getNewCert(Bundle task) {
+ String type_of_certificate = task.getString(ConfigHelper.TYPE_OF_CERTIFICATE);
+ try {
+ JSONObject provider_json = ConfigHelper.getJsonFromSharedPref(ConfigHelper.PROVIDER_KEY);
+ URL provider_main_url = new URL(provider_json.getString(ConfigHelper.API_URL_KEY));
+ String new_cert_string_url = provider_main_url.toString() + "/" + provider_json.getString(ConfigHelper.API_VERSION_KEY) + "/" + ConfigHelper.CERT_KEY;
+
+ if(type_of_certificate.equalsIgnoreCase(ConfigHelper.AUTHED_CERTIFICATE)) {
+ HttpCookie session_id_cookie = new HttpCookie(task.getString(ConfigHelper.SESSION_ID_COOKIE_KEY), task.getString(ConfigHelper.SESSION_ID_KEY));
+
+ CookieManager cookieManager = new CookieManager();
+ cookieManager.getCookieStore().add(provider_main_url.toURI(), session_id_cookie);
+ CookieHandler.setDefault(cookieManager);
+ }
+
+ boolean danger_on = ConfigHelper.getBoolFromSharedPref(ConfigHelper.DANGER_ON);
+ String cert_string = getStringFromProvider(new_cert_string_url, danger_on);
+ if(!cert_string.isEmpty()) {
+ // API returns concatenated cert & key. Split them for OpenVPN options
+ String certificate = null, key = null;
+ String[] certAndKey = cert_string.split("(?<=-\n)");
+ for (int i=0; i < certAndKey.length-1; i++){
+ if ( certAndKey[i].contains("KEY") )
+ key = certAndKey[i++] + certAndKey[i];
+ else if ( certAndKey[i].contains("CERTIFICATE") )
+ certificate = certAndKey[i++] + certAndKey[i];
+ }
+ ConfigHelper.saveSharedPref(ConfigHelper.CERT_KEY, certificate);
+ ConfigHelper.saveSharedPref(ConfigHelper.KEY_KEY, key);
+ return true;
+ } else {
+ return false;
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ } catch (URISyntaxException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/src/se/leap/leapclient/ProviderAPIResultReceiver.java b/src/se/leap/leapclient/ProviderAPIResultReceiver.java
new file mode 100644
index 00000000..d82484c8
--- /dev/null
+++ b/src/se/leap/leapclient/ProviderAPIResultReceiver.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+
+/**
+ * Implements the ResultReceiver needed by Activities using ProviderAPI to receive the results of its operations.
+ * @author parmegv
+ *
+ */
+public class ProviderAPIResultReceiver extends ResultReceiver {
+ private Receiver mReceiver;
+
+ public ProviderAPIResultReceiver(Handler handler) {
+ super(handler);
+ // TODO Auto-generated constructor stub
+ }
+
+ public void setReceiver(Receiver receiver) {
+ mReceiver = receiver;
+ }
+
+ /**
+ * Interface to enable ProviderAPIResultReceiver to receive results from the ProviderAPI IntentService.
+ * @author parmegv
+ *
+ */
+ public interface Receiver {
+ public void onReceiveResult(int resultCode, Bundle resultData);
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (mReceiver != null) {
+ mReceiver.onReceiveResult(resultCode, resultData);
+ }
+ }
+
+}
diff --git a/src/se/leap/leapclient/ProviderDetailFragment.java b/src/se/leap/leapclient/ProviderDetailFragment.java
new file mode 100644
index 00000000..600be58f
--- /dev/null
+++ b/src/se/leap/leapclient/ProviderDetailFragment.java
@@ -0,0 +1,108 @@
+package se.leap.leapclient;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+public class ProviderDetailFragment extends DialogFragment {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ try {
+
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View provider_detail_view = inflater.inflate(R.layout.provider_detail_fragment, null);
+
+ JSONObject provider_json = ConfigHelper.getJsonFromSharedPref(ConfigHelper.PROVIDER_KEY);
+
+ final TextView domain = (TextView)provider_detail_view.findViewById(R.id.provider_detail_domain);
+ domain.setText(provider_json.getString(ConfigHelper.DOMAIN));
+ final TextView name = (TextView)provider_detail_view.findViewById(R.id.provider_detail_name);
+ name.setText(provider_json.getJSONObject(ConfigHelper.NAME).getString("en"));
+ final TextView description = (TextView)provider_detail_view.findViewById(R.id.provider_detail_description);
+ description.setText(provider_json.getJSONObject(ConfigHelper.DESCRIPTION).getString("en"));
+
+ builder.setView(provider_detail_view);
+ builder.setTitle(R.string.provider_details_fragment_title);
+
+ if(anon_allowed(provider_json)) {
+ builder.setPositiveButton(R.string.use_anonymously_button, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ interface_with_configuration_wizard.use_anonymously();
+ }
+ });
+ }
+
+ if(registration_allowed(provider_json)) {
+ builder.setNegativeButton(R.string.login_button, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ interface_with_configuration_wizard.login();
+ }
+ });
+ }
+
+ return builder.create();
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+
+ private boolean anon_allowed(JSONObject provider_json) {
+ try {
+ JSONObject service_description = provider_json.getJSONObject(ConfigHelper.SERVICE_KEY);
+ return service_description.has(ConfigHelper.ALLOWED_ANON) && service_description.getBoolean(ConfigHelper.ALLOWED_ANON);
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ private boolean registration_allowed(JSONObject provider_json) {
+ try {
+ JSONObject service_description = provider_json.getJSONObject(ConfigHelper.SERVICE_KEY);
+ return service_description.has(ConfigHelper.ALLOW_REGISTRATION_KEY) && service_description.getBoolean(ConfigHelper.ALLOW_REGISTRATION_KEY);
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ ConfigHelper.removeFromSharedPref(ConfigHelper.PROVIDER_KEY);
+ ConfigHelper.removeFromSharedPref(ConfigHelper.DANGER_ON);
+ ConfigHelper.removeFromSharedPref(ConfigHelper.ALLOWED_ANON);
+ ConfigHelper.removeFromSharedPref(ConfigHelper.EIP_SERVICE_KEY);
+ }
+
+ public static DialogFragment newInstance() {
+ ProviderDetailFragment provider_detail_fragment = new ProviderDetailFragment();
+ return provider_detail_fragment;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ interface_with_configuration_wizard = (ProviderDetailFragmentInterface) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement LogInDialogListener");
+ }
+ }
+
+ public interface ProviderDetailFragmentInterface {
+ public void login();
+ public void use_anonymously();
+ }
+
+ ProviderDetailFragmentInterface interface_with_configuration_wizard;
+}
diff --git a/src/se/leap/leapclient/ProviderListContent.java b/src/se/leap/leapclient/ProviderListContent.java
new file mode 100644
index 00000000..714ed5f4
--- /dev/null
+++ b/src/se/leap/leapclient/ProviderListContent.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Models the provider list shown in the ConfigurationWizard.
+ *
+ * @author parmegv
+ *
+ */
+public class ProviderListContent {
+
+ public static List<ProviderItem> ITEMS = new ArrayList<ProviderItem>();
+
+ public static Map<String, ProviderItem> ITEM_MAP = new HashMap<String, ProviderItem>();
+
+ /**
+ * Adds a new provider item to the end of the items map, and to the items list.
+ * @param item
+ */
+ public static void addItem(ProviderItem item) {
+ ITEMS.add(item);
+ ITEM_MAP.put(String.valueOf(ITEMS.size()), item);
+ }
+
+ /**
+ * A provider item.
+ */
+ public static class ProviderItem {
+ public boolean custom = false;
+ public String id;
+ public String name;
+ public String domain;
+ public String provider_json_url;
+ public JSONObject provider_json;
+ public String provider_json_filename;
+ public String eip_service_json_url;
+ public String cert_json_url;
+ public boolean danger_on = false;
+
+ /**
+ * @param name of the provider
+ * @param urls_file_input_stream file input stream linking with the assets url file
+ * @param custom if it's a new provider entered by the user or not
+ * @param danger_on if the user trusts completely the new provider
+ */
+ public ProviderItem(String name, InputStream urls_file_input_stream, boolean custom, boolean danger_on) {
+
+ try {
+ byte[] urls_file_bytes = new byte[urls_file_input_stream.available()];
+ urls_file_input_stream.read(urls_file_bytes);
+ String urls_file_content = new String(urls_file_bytes);
+ JSONObject file_contents = new JSONObject(urls_file_content);
+ id = name;
+ this.name = name;
+ provider_json_url = file_contents.getString(ConfigHelper.PROVIDER_JSON_URL);
+ domain = new URL(provider_json_url).getHost();
+ //provider_json_filename = file_contents.getString("assets_json_provider");
+ eip_service_json_url = file_contents.getString("json_eip_service");
+ cert_json_url = file_contents.getString(ConfigHelper.CERT_KEY);
+ this.custom = custom;
+ this.danger_on = danger_on;
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @param name of the provider
+ * @param provider_json_url used to download provider.json file of the provider
+ * @param provider_json already downloaded
+ * @param custom if it's a new provider entered by the user or not
+ * @param danger_on if the user trusts completely the new provider
+ */
+ public ProviderItem(String name, String provider_json_url, JSONObject provider_json, boolean custom, boolean danger_on) {
+
+ try {
+ id = name;
+ //this.name = name;
+ this.provider_json_url = provider_json_url;
+ this.provider_json = provider_json;
+ this.name = provider_json.getJSONObject("name").getString("en");
+ domain = new URL(provider_json_url).getHost();
+ eip_service_json_url = provider_json.getString(ConfigHelper.API_URL_KEY) + "/" + provider_json.getString(ConfigHelper.API_VERSION_KEY) + "/" + ConfigHelper.EIP_SERVICE_API_PATH;
+ cert_json_url = provider_json.getString("ca_cert_uri");
+ this.custom = custom;
+ this.danger_on = danger_on;
+ if(custom)
+ provider_json_filename = name + "_provider.json".replaceFirst("__", "_");
+ } catch (MalformedURLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public String getId() {
+ return id;
+ }
+ }
+}
diff --git a/src/se/leap/leapclient/ProviderListFragment.java b/src/se/leap/leapclient/ProviderListFragment.java
new file mode 100644
index 00000000..2fca20e2
--- /dev/null
+++ b/src/se/leap/leapclient/ProviderListFragment.java
@@ -0,0 +1,191 @@
+/**
+ * Copyright (c) 2013 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.leapclient;
+
+import se.leap.leapclient.ProviderListContent.ProviderItem;
+import android.app.Activity;
+import android.app.ListFragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.content.Context;
+import android.widget.TwoLineListItem;
+
+/**
+ * A list fragment representing a list of Providers. This fragment
+ * also supports tablet devices by allowing list items to be given an
+ * 'activated' state upon selection. This helps indicate which item is
+ * currently being viewed in a {@link DashboardFragment}.
+ * <p>
+ * Activities containing this fragment MUST implement the {@link Callbacks}
+ * interface.
+ */
+public class ProviderListFragment extends ListFragment {
+
+ private ArrayAdapter<ProviderItem> content_adapter;
+
+ /**
+ * The serialization (saved instance state) Bundle key representing the
+ * activated item position. Only used on tablets.
+ */
+ private static final String STATE_ACTIVATED_POSITION = "activated_position";
+
+ /**
+ * The fragment's current callback object, which is notified of list item
+ * clicks.
+ */
+ private Callbacks mCallbacks = sDummyCallbacks;
+
+ /**
+ * The current activated item position. Only used on tablets.
+ */
+ private int mActivatedPosition = ListView.INVALID_POSITION;
+
+ /**
+ * A callback interface that all activities containing this fragment must
+ * implement. This mechanism allows activities to be notified of item
+ * selections.
+ */
+ public interface Callbacks {
+ /**
+ * Callback for when an item has been selected.
+ */
+ public void onItemSelected(String id);
+ }
+
+ /**
+ * A dummy implementation of the {@link Callbacks} interface that does
+ * nothing. Used only when this fragment is not attached to an activity.
+ */
+ private static Callbacks sDummyCallbacks = new Callbacks() {
+ @Override
+ public void onItemSelected(String id) {
+ }
+ };
+
+ /**
+ * Mandatory empty constructor for the fragment manager to instantiate the
+ * fragment (e.g. upon screen orientation changes).
+ */
+ public ProviderListFragment() {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ content_adapter = new ArrayAdapter<ProviderListContent.ProviderItem>(
+ getActivity(),
+ android.R.layout.simple_list_item_activated_2,
+ ProviderListContent.ITEMS) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent){
+ TwoLineListItem row;
+ if (convertView == null) {
+ LayoutInflater inflater = (LayoutInflater)getActivity().getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ row = (TwoLineListItem)inflater.inflate(android.R.layout.simple_list_item_2, null);
+ } else {
+ row = (TwoLineListItem)convertView;
+ }
+ ProviderListContent.ProviderItem data = ProviderListContent.ITEMS.get(position);
+ row.getText1().setText(data.domain);
+ row.getText2().setText(data.name);
+
+ return row;
+ }
+ };
+ setListAdapter(content_adapter);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ return inflater.inflate(R.layout.provider_list_fragment, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // Restore the previously serialized activated item position.
+ if (savedInstanceState != null
+ && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
+ setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
+ }
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ // Activities containing this fragment must implement its callbacks.
+ if (!(activity instanceof Callbacks)) {
+ throw new IllegalStateException("Activity must implement fragment's callbacks.");
+ }
+
+ mCallbacks = (Callbacks) activity;
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+
+ // Reset the active callbacks interface to the dummy implementation.
+ mCallbacks = sDummyCallbacks;
+ }
+
+ @Override
+ public void onListItemClick(ListView listView, View view, int position, long id) {
+ super.onListItemClick(listView, view, position, id);
+
+ // Notify the active callbacks interface (the activity, if the
+ // fragment is attached to one) that an item has been selected.
+ mCallbacks.onItemSelected(ProviderListContent.ITEMS.get(position).id);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mActivatedPosition != ListView.INVALID_POSITION) {
+ // Serialize and persist the activated item position.
+ outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
+ }
+ }
+
+ /**
+ * Turns on activate-on-click mode. When this mode is on, list items will be
+ * given the 'activated' state when touched.
+ */
+ public void setActivateOnItemClick(boolean activateOnItemClick) {
+ // When setting CHOICE_MODE_SINGLE, ListView will automatically
+ // give items the 'activated' state when touched.
+ getListView().setChoiceMode(activateOnItemClick
+ ? ListView.CHOICE_MODE_SINGLE
+ : ListView.CHOICE_MODE_NONE);
+ }
+
+ private void setActivatedPosition(int position) {
+ if (position == ListView.INVALID_POSITION) {
+ getListView().setItemChecked(mActivatedPosition, false);
+ } else {
+ getListView().setItemChecked(position, true);
+ }
+
+ mActivatedPosition = position;
+ }
+}
diff --git a/src/se/leap/openvpn/ConfigParser.java b/src/se/leap/openvpn/ConfigParser.java
index f57bbae9..3d369fa6 100644
--- a/src/se/leap/openvpn/ConfigParser.java
+++ b/src/se/leap/openvpn/ConfigParser.java
@@ -47,6 +47,9 @@ public class ConfigParser {
options.get(optionname).add(args);
}
}
+ public void setDefinition(HashMap<String,Vector<Vector<String>>> args) {
+ options = args;
+ }
private void checkinlinefile(Vector<String> args, BufferedReader br) throws IOException, ConfigParseError {
String arg0 = args.get(0);
@@ -247,7 +250,8 @@ public class ConfigParser {
// Pull, client, tls-client
np.clearDefaults();
- if(options.containsKey("client") || options.containsKey("pull")) {
+ // XXX we are always client
+ if(/*options.containsKey("client") || options.containsKey("pull")*/ true) {
np.mUsePull=true;
options.remove("pull");
options.remove("client");
diff --git a/src/se/leap/openvpn/LICENSE.txt b/src/se/leap/openvpn/LICENSE.txt
new file mode 100644
index 00000000..d897edea
--- /dev/null
+++ b/src/se/leap/openvpn/LICENSE.txt
@@ -0,0 +1,24 @@
+License for OpenVPN for Android. Please note that the thirdparty libraries/executables may have other license (OpenVPN, lzo, OpenSSL, Google Breakpad)
+
+Copyright (c) 2012-2013, Arne Schwabe
+ All rights reserved.
+
+If you need a non GPLv2 license of the source please contact me.
+
+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 2
+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, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+In addition, as a special exception, the copyright holders give
+permission to link the code of portions of this program with the
+OpenSSL library.
diff --git a/src/se/leap/openvpn/LaunchVPN.java b/src/se/leap/openvpn/LaunchVPN.java
index 3df2aacb..1df6be96 100644
--- a/src/se/leap/openvpn/LaunchVPN.java
+++ b/src/se/leap/openvpn/LaunchVPN.java
@@ -19,6 +19,9 @@ package se.leap.openvpn;
import java.io.IOException;
import java.util.Collection;
import java.util.Vector;
+
+import se.leap.leapclient.ConfigHelper;
+import se.leap.leapclient.EIP;
import se.leap.leapclient.R;
import android.app.Activity;
@@ -32,6 +35,7 @@ import android.content.SharedPreferences;
import android.net.VpnService;
import android.os.Bundle;
import android.os.Parcelable;
+import android.os.ResultReceiver;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
@@ -74,8 +78,10 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener {
public static final String EXTRA_NAME = "se.leap.openvpn.shortcutProfileName";
public static final String EXTRA_HIDELOG = "se.leap.openvpn.showNoLogWindow";;
- private static final int START_VPN_PROFILE= 70;
+ public static final int START_VPN_PROFILE= 70;
+ // Dashboard, maybe more, want to know!
+ private ResultReceiver mReceiver;
private ProfileManager mPM;
private VpnProfile mSelectedProfile;
@@ -99,6 +105,9 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener {
final Intent intent = getIntent();
final String action = intent.getAction();
+ // If something wants feedback, they sent us a Receiver
+ mReceiver = intent.getParcelableExtra(ConfigHelper.RECEIVER_TAG);
+
// If the intent is a request to create a shortcut, we'll do that and exit
@@ -266,14 +275,18 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener {
askForPW(needpw);
} else {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- boolean showlogwindow = prefs.getBoolean("showlogwindow", true);
+ boolean showlogwindow = prefs.getBoolean("showlogwindow", false);
if(!mhideLog && showlogwindow)
showLogWindow();
new startOpenVpnThread().start();
}
} else if (resultCode == Activity.RESULT_CANCELED) {
- // User does not want us to start, so we just vanish
+ // User does not want us to start, so we just vanish (well, now we tell our receiver, then vanish)
+ Bundle resultData = new Bundle();
+ // For now, nothing else is calling, so this "request" string is good enough
+ resultData.putString(ConfigHelper.REQUEST_TAG, EIP.ACTION_START_EIP);
+ mReceiver.send(RESULT_CANCELED, resultData);
finish();
}
}
@@ -357,6 +370,11 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener {
@Override
public void run() {
VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext());
+ // Tell whom-it-may-concern that we started VPN
+ Bundle resultData = new Bundle();
+ // For now, nothing else is calling, so this "request" string is good enough
+ resultData.putString(ConfigHelper.REQUEST_TAG, EIP.ACTION_START_EIP);
+ mReceiver.send(RESULT_OK, resultData);
finish();
}
diff --git a/src/se/leap/openvpn/OpenVPN.java b/src/se/leap/openvpn/OpenVPN.java
index 152cf2d8..3ffc47cb 100644
--- a/src/se/leap/openvpn/OpenVPN.java
+++ b/src/se/leap/openvpn/OpenVPN.java
@@ -204,10 +204,10 @@ public class OpenVPN {
mLaststate= state;
mLaststatemsg = msg;
mLastStateresid = resid;
- }
- for (StateListener sl : stateListener) {
- sl.updateState(state,msg,resid);
+ for (StateListener sl : stateListener) {
+ sl.updateState(state,msg,resid);
+ }
}
}
diff --git a/src/se/leap/openvpn/OpenVpnService.java b/src/se/leap/openvpn/OpenVpnService.java
index c745ee3b..3ac80497 100644
--- a/src/se/leap/openvpn/OpenVpnService.java
+++ b/src/se/leap/openvpn/OpenVpnService.java
@@ -1,25 +1,11 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
package se.leap.openvpn;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Vector;
+
+import se.leap.leapclient.Dashboard;
import se.leap.leapclient.R;
import android.annotation.TargetApi;
@@ -35,7 +21,6 @@ import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.VpnService;
import android.os.Binder;
-import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Build;
import android.os.IBinder;
@@ -45,6 +30,7 @@ import se.leap.openvpn.OpenVPN.StateListener;
public class OpenVpnService extends VpnService implements StateListener, Callback {
public static final String START_SERVICE = "se.leap.openvpn.START_SERVICE";
+ public static final String RETRIEVE_SERVICE = "se.leap.openvpn.RETRIEVE_SERVICE";
private Thread mProcessThread=null;
@@ -65,6 +51,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
private int mMtu;
private String mLocalIPv6=null;
private NetworkSateReceiver mNetworkStateReceiver;
+ private NotificationManager mNotificationManager;
private boolean mDisplayBytecount=false;
@@ -89,7 +76,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
@Override
public IBinder onBind(Intent intent) {
String action = intent.getAction();
- if( action !=null && action.equals(START_SERVICE))
+ if( action !=null && (action.equals(START_SERVICE) || action.equals(RETRIEVE_SERVICE)) )
return mBinder;
else
return super.onBind(intent);
@@ -116,9 +103,9 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
}
}
- private void showNotification(String msg, String tickerText, boolean lowpriority, long when) {
+ private void showNotification(String msg, String tickerText, boolean lowpriority, long when, boolean persistant) {
String ns = Context.NOTIFICATION_SERVICE;
- NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
+ mNotificationManager = (NotificationManager) getSystemService(ns);
int icon = R.drawable.ic_stat_vpn;
@@ -127,7 +114,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
nbuilder.setContentTitle(getString(R.string.notifcation_title,mProfile.mName));
nbuilder.setContentText(msg);
nbuilder.setOnlyAlertOnce(true);
- nbuilder.setOngoing(true);
+ nbuilder.setOngoing(persistant);
nbuilder.setContentIntent(getLogPendingIntent());
nbuilder.setSmallIcon(icon);
if(when !=0)
@@ -144,7 +131,6 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mNotificationManager.notify(OPENVPN_STATUS, notification);
- startForeground(OPENVPN_STATUS, notification);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@@ -173,10 +159,10 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
}
PendingIntent getLogPendingIntent() {
- // Let the configure Button show the Log
- Intent intent = new Intent(getBaseContext(),LogWindow.class);
+ // Let the configure Button show the Dashboard
+ Intent intent = new Intent(Dashboard.getAppContext(),Dashboard.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- PendingIntent startLW = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
+ PendingIntent startLW = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
return startLW;
@@ -223,7 +209,8 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if(intent != null && intent.getAction() !=null &&intent.getAction().equals(START_SERVICE))
+ if( intent != null && intent.getAction() !=null &&
+ (intent.getAction().equals(START_SERVICE) || intent.getAction().equals(RETRIEVE_SERVICE)) )
return START_NOT_STICKY;
@@ -235,7 +222,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mProfile = ProfileManager.get(profileUUID);
- showNotification("Starting VPN " + mProfile.mName,"Starting VPN " + mProfile.mName, false,0);
+ //showNotification("Starting VPN " + mProfile.mName,"Starting VPN " + mProfile.mName, false,0);
OpenVPN.addStateListener(this);
@@ -466,6 +453,13 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
mLocalIPv6 = ipv6addr;
}
+ public boolean isRunning() {
+ if (mStarting == true || mProcessThread != null)
+ return true;
+ else
+ return false;
+ }
+
@Override
public void updateState(String state,String logmessage, int resid) {
// If the process is not running, ignore any state,
@@ -473,26 +467,16 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac
if(mProcessThread==null)
return;
- // Display byte count only after being connected
-
- if("BYTECOUNT".equals(state)) {
- if(mDisplayBytecount) {
- showNotification(logmessage,null,true,mConnecttime);
- }
- } else {
- if("CONNECTED".equals(state)) {
- mDisplayBytecount = true;
- mConnecttime = System.currentTimeMillis();
- } else {
- mDisplayBytecount = false;
- }
+ if("CONNECTED".equals(state)) {
+ mNotificationManager.cancel(OPENVPN_STATUS);
+ } else if(!"BYTECOUNT".equals(state)) {
// Other notifications are shown,
// This also mean we are no longer connected, ignore bytecount messages until next
// CONNECTED
String ticker = getString(resid);
- showNotification(getString(resid) +" " + logmessage,ticker,false,0);
-
+ boolean persist = ("NOPROCESS".equals(state)) ? false : true;
+ showNotification(getString(resid) +" " + logmessage,ticker,false,0,persist);
}
}
diff --git a/src/se/leap/openvpn/VpnProfile.java b/src/se/leap/openvpn/VpnProfile.java
index 38ee3c83..2262f565 100644
--- a/src/se/leap/openvpn/VpnProfile.java
+++ b/src/se/leap/openvpn/VpnProfile.java
@@ -20,6 +20,8 @@ import java.util.Vector;
import org.spongycastle.util.io.pem.PemObject;
import org.spongycastle.util.io.pem.PemWriter;
+
+import se.leap.leapclient.ConfigHelper;
import se.leap.leapclient.R;
import android.content.Context;
@@ -62,7 +64,7 @@ public class VpnProfile implements Serializable{
// Public attributes, since I got mad with getter/setter
// set members to default values
private UUID mUuid;
- public int mAuthenticationType = TYPE_KEYSTORE ;
+ public int mAuthenticationType = TYPE_CERTIFICATES ;
public String mName;
public String mAlias;
public String mClientCertFilename;
@@ -236,13 +238,18 @@ public class VpnProfile implements Serializable{
case VpnProfile.TYPE_USERPASS_CERTIFICATES:
cfg+="auth-user-pass\n";
case VpnProfile.TYPE_CERTIFICATES:
- // Ca
+ /*// Ca
cfg+=insertFileData("ca",mCaFilename);
// Client Cert + Key
cfg+=insertFileData("key",mClientKeyFilename);
cfg+=insertFileData("cert",mClientCertFilename);
-
+*/
+ // FIXME This is all we need...The whole switch statement can go...
+ cfg+="<ca>\n"+ConfigHelper.getStringFromSharedPref(ConfigHelper.MAIN_CERT_KEY)+"\n</ca>\n";
+ cfg+="<key>\n"+ConfigHelper.getStringFromSharedPref(ConfigHelper.KEY_KEY)+"\n</key>\n";
+ cfg+="<cert>\n"+ConfigHelper.getStringFromSharedPref(ConfigHelper.CERT_KEY)+"\n</cert>\n";
+
break;
case VpnProfile.TYPE_USERPASS_PKCS12:
cfg+="auth-user-pass\n";
@@ -492,8 +499,8 @@ public class VpnProfile implements Serializable{
Intent intent = new Intent(context,OpenVpnService.class);
if(mAuthenticationType == VpnProfile.TYPE_KEYSTORE || mAuthenticationType == VpnProfile.TYPE_USERPASS_KEYSTORE) {
- if(!saveCertificates(context))
- return null;
+ /*if(!saveCertificates(context))
+ return null;*/
}
intent.putExtra(prefix + ".ARGV" , buildOpenvpnArgv(context.getCacheDir()));
@@ -597,10 +604,10 @@ public class VpnProfile implements Serializable{
//! Return an error if somethign is wrong
public int checkProfile(Context context) {
- if(mAuthenticationType==TYPE_KEYSTORE || mAuthenticationType==TYPE_USERPASS_KEYSTORE) {
+/* if(mAuthenticationType==TYPE_KEYSTORE || mAuthenticationType==TYPE_USERPASS_KEYSTORE) {
if(mAlias==null)
return R.string.no_keystore_cert_selected;
- }
+ }*/
if(!mUsePull) {
if(mIPv4Address == null || cidrToIPAndNetmask(mIPv4Address) == null)
diff --git a/todo.txt b/todo.txt
deleted file mode 100644
index a8da2103..00000000
--- a/todo.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-Ideas:
-
-- Do cert+key+ca => p12 inside the app, import directly to keystore
-
-- general settings dialog
- - encryption of profiles
-
-- Give the notification a nice speed bar
-
-- The app https://play.google.com/store/apps/details?id=org.proxydroid
- has a nice feature: auto connect. Run a profile if a specified
- network is available (a user defined WIFI profile and/or "WIFI/2G/3G"
- and/or "WIFI" and/or "2G/3G" - selection of more than one network type
- is possible)
-
-- Copy/Duplicate an existing profile (to create a new one with nearly
- exact settings)
-
-- implement an encryption for profiles, so no sensitive data has be stored in plain text
- - encrypt/decrypt with android private storage key (+no user input required)
-
-Bugfixes:
- - startpath file explorer
- - hibernate when screen off and < 50 kB min
-
-Missing configuration options:
-
-Tap support:
-- Actually it is possible to emulate tap with tun device, a minimal implementation would have to do:
- - generate random mac
- - strip macs header on receive
- - append mac header on send
- - implement arp, possible the most difficult task ...
- - need to chose right mac of receiver
-
diff --git a/vim-plugin/basic-adt.vim b/vim-plugin/basic-adt.vim
new file mode 100644
index 00000000..366b3f20
--- /dev/null
+++ b/vim-plugin/basic-adt.vim
@@ -0,0 +1,47 @@
+function! AndroidRun()
+ let new_project_root = s:Find_in_parent("AndroidManifest.xml", s:windowdir() ,$HOME)
+ echo "Found Android Project at: " . new_project_root
+ echo "Windowdir: " . s:windowdir()
+ if new_project_root != "Nothing"
+ let b:project_root = new_project_root
+ new
+ set buftype=nofile
+ silent! exec "r!../run.sh SII b:project_root
+ endif
+endfunc
+
+" Find_in_parent
+" " find the file argument and returns the path to it.
+" " Starting with the current working dir, it walks up the parent folders
+" " until it finds the file, or it hits the stop dir.
+" " If it doesn't find it, it returns "Nothing"
+function s:Find_in_parent(fln,flsrt,flstp)
+ let here = a:flsrt
+ while ( strlen( here) > 0 )
+ if filereadable( here . "/" . a:fln )
+ return here
+ endif
+ let fr = match(here, "/[^/]*$")
+ if fr == -1
+ break
+ endif
+ let here = strpart(here, 0, fr)
+ if here == a:flstp
+ break
+ endif
+ endwhile
+ return "Nothing"
+endfunc
+
+" windowdir
+" " Gets the directory for the file in the current window
+" " Or the current working dir if there isn't one for the window.
+" " Use tr to allow that other OS paths, too
+function s:windowdir()
+ if winbufnr(0) == -1
+ let unislash = getcwd()
+ else
+ let unislash = fnamemodify(bufname(winbufnr(0)), ':p:h')
+ endif
+ return tr(unislash, '\', '/')
+endfunc