From: David A. Velasco Date: Mon, 18 Nov 2013 13:10:16 +0000 (+0100) Subject: Merge branch 'develop' into refactor_remote_operation_to_create_folder X-Git-Tag: oc-android-1.5.5~126^2~1 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/e1245bbda7894e1a3028d2b54a6918e4bdd46ae2?hp=d7559f22a9197b7c2810f6e2aa7e91e796926bd1 Merge branch 'develop' into refactor_remote_operation_to_create_folder --- diff --git a/.gitignore b/.gitignore index ac30011b..9b9bd8e3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,18 +13,22 @@ bin/ gen/ target/ -# Local configuration file (sdk path, etc) +# Local configuration files (sdk path, etc) local.properties +oc_workaround/local.properties +oc_framework/local.properties +oc_framework-test-project/local.properties +tests/local.properties # Mac .DS_Store files .DS_Store -# These files are created automatically by Eclipse: -tests/proguard-project.txt -tests/project.properties -tests/ant.properties -tests/build.xml +# Proguard README proguard-project.txt +oc_workaround/proguard-project.txt +oc_framework/proguard-project.txt +oc_framework-test-project/proguard-project.txt +tests/proguard-project.txt # Should not be commited inside this repo: actionbarsherlock/ \ No newline at end of file diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f1900f8c..bdd792b0 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -63,7 +63,7 @@ - + diff --git a/SETUP.md b/SETUP.md index 6e7b7f62..64701577 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,56 +1,73 @@ - If you want to start help developing ownCloud please follow the [contribution guidlines][0] and observe these instructions: +If you want to start help developing ownCloud please follow the [contribution guidelines][0] and observe these instructions: - 1. Fork and download android/develop repository: - - - NOTE: You must have git in your enviroment path - - Navigate to https://github.com/owncloud/android, click fork. - - Clone your new repo: "git clone git@github.com:YOURGITHUBNAME/android.git" - - "cd android" - - Checkout remote develop branch: "git checkout -b develop remotes/origin/develop" - - Pull changes from your develop branch: "git pull origin develop" - - Make sure to get the latest changes from official android/develop branch: - - Make official owncloud repo known as upstream: "git remote add upstream git@github.com:owncloud/android.git" - - Pull latest changes from upstream: "git pull upstream develop" - - 2. Building with console/maven: - - - OPTIONAL, CONTINUE WITH STEP 3 IF NOT REQUIRED! - - NOTE: You must have mvn in your enviroment path - - Download/install Android plugin for Maven, then build ownCloud with mvn: - - "cd .." - - "git clone https://github.com/mosabua/maven-android-sdk-deployer.git" - - "cd maven-android-sdk-deployer" - - "mvn -pl com.simpligility.android.sdk-deployer:android-17 -am install" - - "cd ../android" - - Now you can create APK using "mvn package" - - 3. Building with Eclipse: - - - NOTE: You must have android/tools, and 'platforms-tools' in your enviroment path - - Prepare building with Eclipse: - - "setup_env.bat" or "./setup_env.sh" - - Open Eclipse and create new "Android Project from Existing Code". As root choose android/actionbarsherlock/library - - Increase Android API level until project compiles. 14 should work. - - Clean project and compile. - - Make sure android/actionbarsherlock/library/bin/library.jar was created! - - Import OwnCloud Android project. - - Increase Android API level to 17. - - Clean project and compile. - - After those actions you should be good to go. HAVE FUN! - - NOTE: Even though API level is set to 17, APK also runs on older devices because in AndroidManifest.xml minSdkVersion is set to 8. - - 4. Create pull request: +### 1. Fork and download android/develop repository: + +NOTE: You must have git in your environment path variable to perform the next operations. + +* Navigate to https://github.com/owncloud/android, click fork. +* Clone your new repo: "git clone git@github.com:YOURGITHUBNAME/android.git" +* Move to the project folder with "cd android" +* Checkout remote develop branch: "git checkout -b develop remotes/origin/develop" +* Pull changes from your develop branch: "git pull origin develop" +* Make official ownCloud repo known as upstream: "git remote add upstream git@github.com:owncloud/android.git" +* Make sure to get the latest changes from official android/develop branch: "git pull upstream develop" + +At this point you can continue using different tools to build the project. Section 2, 3 and 4 describe some of the existing alternatives. + +### 2. Building with Ant: + +NOTE: You must have the Android SDK 'tools/', and 'platforms-tools/' folders in your environment path variable. + +* Complete the setup of project properties and resolve pending dependencies running "setup_env.bat" or "./setup_env.sh" . +* Run "ant clean" . +* Run "ant debug" to generate a debuggable version of the ownCkoud app. + +### 3. Building with console/maven: + +NOTE: You must have mvn in your environment path + +* Download/install Android plugin for Maven, then build ownCloud with mvn: +* "cd .." +* "git clone https://github.com/mosabua/maven-android-sdk-deployer.git" +* "cd maven-android-sdk-deployer" +* "mvn -pl com.simpligility.android.sdk-deployer:android-17 -am install" +* "cd ../android" +* Now you can create APK using "mvn package" + +### 4. Building with Eclipse: + +NOTE: You must have the Android SDK 'tools/', and 'platforms-tools/' folders in your environment path variable. + +* Complete the setup of project properties and resolve pending dependencies running "setup_env.bat" or "./setup_env.sh" . +* Open Eclipse and create new "Android Project from Existing Code". Choose android/actionbarsherlock/library as root. +* Clean project and compile. +* If any error appear, check the project properties; in the 'Android' section, API Level should be greater or equal than 14. +* Make sure android/actionbarsherlock/library/bin/library.jar was created. +* Create a new "Android Project from Existing Code". Choose android/oc_framework/library as root. +* Clean project and compile. +* If any error appear, check the project properties; in the 'Android' section, API Level should be 19 or greater. +* Make sure android/oc_framework/bin/classes.jar was created. +* Import ownCloud Android project. +* Clean project and compile. +* If any error appears, check the project properties; in the 'Android' section: + - API Level should be 19 or greater. + - Two library projects should appear referred in the bottom square: actionbarsherlock/library and oc_framework. Add them if needed. +* After those actions you should be good to go. HAVE FUN! + +NOTE: Even though API level is set to 19, APK also runs on older devices because in AndroidManifest.xml minSdkVersion is set to 8. + +### 5. Create pull request: - - NOTE: You must sign the [Contributor Agreement][1] before your changes can be accepted! - - Commit your changes locally: "git commit -a" - - Push your changes to your Github repo: "git push" - - Browse to https://github.com/YOURGITHUBNAME/android/pulls and issue pull request - - Click "Edit" and set "base:develop" - - Again, click "Edit" and set "compare:develop" - - Enter description and send pull request. +NOTE: You must sign the [Contributor Agreement][1] before your changes can be accepted! + +* Commit your changes locally: "git commit -a" +* Push your changes to your Github repo: "git push" +* Browse to https://github.com/YOURGITHUBNAME/android/pulls and issue pull request +* Click "Edit" and set "base:develop" +* Again, click "Edit" and set "compare:develop" +* Enter description and send pull request. [0]: https://github.com/owncloud/android/blob/master/CONTRIBUTING.md [1]: http://owncloud.org/about/contributor-agreement/ - diff --git a/oc_framework-test-project/.classpath b/oc_framework-test-project/.classpath new file mode 100644 index 00000000..d3d9f2d8 --- /dev/null +++ b/oc_framework-test-project/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/oc_framework-test-project/.project b/oc_framework-test-project/.project new file mode 100644 index 00000000..8c7df640 --- /dev/null +++ b/oc_framework-test-project/.project @@ -0,0 +1,33 @@ + + + oc_framework-test-project + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/oc_framework-test-project/.settings/org.eclipse.jdt.core.prefs b/oc_framework-test-project/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b080d2dd --- /dev/null +++ b/oc_framework-test-project/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/oc_framework-test-project/AndroidManifest.xml b/oc_framework-test-project/AndroidManifest.xml new file mode 100644 index 00000000..c913bf06 --- /dev/null +++ b/oc_framework-test-project/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/oc_framework-test-project/ic_launcher-web.png b/oc_framework-test-project/ic_launcher-web.png new file mode 100644 index 00000000..a18cbb48 Binary files /dev/null and b/oc_framework-test-project/ic_launcher-web.png differ diff --git a/oc_framework-test-project/libs/android-support-v4.jar b/oc_framework-test-project/libs/android-support-v4.jar new file mode 100644 index 00000000..9056828a Binary files /dev/null and b/oc_framework-test-project/libs/android-support-v4.jar differ diff --git a/oc_framework-test-project/oc_framework-test-test/.classpath b/oc_framework-test-project/oc_framework-test-test/.classpath new file mode 100644 index 00000000..49a8ebcf --- /dev/null +++ b/oc_framework-test-project/oc_framework-test-test/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/oc_framework-test-project/oc_framework-test-test/.project b/oc_framework-test-project/oc_framework-test-test/.project new file mode 100644 index 00000000..c490827b --- /dev/null +++ b/oc_framework-test-project/oc_framework-test-test/.project @@ -0,0 +1,34 @@ + + + oc_framework-test + + + oc_framework-test-project + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/oc_framework-test-project/oc_framework-test-test/.settings/org.eclipse.jdt.core.prefs b/oc_framework-test-project/oc_framework-test-test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b080d2dd --- /dev/null +++ b/oc_framework-test-project/oc_framework-test-test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/oc_framework-test-project/oc_framework-test-test/AndroidManifest.xml b/oc_framework-test-project/oc_framework-test-test/AndroidManifest.xml new file mode 100644 index 00000000..294f271b --- /dev/null +++ b/oc_framework-test-project/oc_framework-test-test/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/oc_framework-test-project/oc_framework-test-test/project.properties b/oc_framework-test-project/oc_framework-test-test/project.properties new file mode 100644 index 00000000..4ab12569 --- /dev/null +++ b/oc_framework-test-project/oc_framework-test-test/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 diff --git a/oc_framework-test-project/oc_framework-test-test/res/drawable-hdpi/ic_launcher.png b/oc_framework-test-project/oc_framework-test-test/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..96a442e5 Binary files /dev/null and b/oc_framework-test-project/oc_framework-test-test/res/drawable-hdpi/ic_launcher.png differ diff --git a/oc_framework-test-project/oc_framework-test-test/res/drawable-ldpi/ic_launcher.png b/oc_framework-test-project/oc_framework-test-test/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 00000000..99238729 Binary files /dev/null and b/oc_framework-test-project/oc_framework-test-test/res/drawable-ldpi/ic_launcher.png differ diff --git a/oc_framework-test-project/oc_framework-test-test/res/drawable-mdpi/ic_launcher.png b/oc_framework-test-project/oc_framework-test-test/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..359047df Binary files /dev/null and b/oc_framework-test-project/oc_framework-test-test/res/drawable-mdpi/ic_launcher.png differ diff --git a/oc_framework-test-project/oc_framework-test-test/res/drawable-xhdpi/ic_launcher.png b/oc_framework-test-project/oc_framework-test-test/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000..71c6d760 Binary files /dev/null and b/oc_framework-test-project/oc_framework-test-test/res/drawable-xhdpi/ic_launcher.png differ diff --git a/oc_framework-test-project/oc_framework-test-test/res/values/strings.xml b/oc_framework-test-project/oc_framework-test-test/res/values/strings.xml new file mode 100644 index 00000000..657f31dd --- /dev/null +++ b/oc_framework-test-project/oc_framework-test-test/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + Oc_framework-testTest + + diff --git a/oc_framework-test-project/oc_framework-test-test/src/com/owncloud/android/oc_framework_test_project/test/CreateFolderTest.java b/oc_framework-test-project/oc_framework-test-test/src/com/owncloud/android/oc_framework_test_project/test/CreateFolderTest.java new file mode 100644 index 00000000..0c4b8efa --- /dev/null +++ b/oc_framework-test-project/oc_framework-test-test/src/com/owncloud/android/oc_framework_test_project/test/CreateFolderTest.java @@ -0,0 +1,96 @@ +package com.owncloud.android.oc_framework_test_project.test; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework_test_project.TestActivity; + +import android.test.ActivityInstrumentationTestCase2; + +/** + * Class to test Create Folder Operation + * @author masensio + * + */ +public class CreateFolderTest extends ActivityInstrumentationTestCase2 { + + private TestActivity mActivity; + private String mCurrentDate; + + public CreateFolderTest() { + super(TestActivity.class); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + mCurrentDate = sdf.format(new Date()); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(false); + mActivity = getActivity(); + } + + /** + * Test Create Folder + */ + public void testCreateFolder() { + + String remotePath = "/testCreateFolder" + mCurrentDate; + boolean createFullPath = true; + + RemoteOperationResult result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.isSuccess() || result.getCode() == ResultCode.TIMEOUT); + + // Create Subfolder + remotePath = "/testCreateFolder" + mCurrentDate + "/" + "testCreateFolder" + mCurrentDate; + createFullPath = true; + + result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.isSuccess() || result.getCode() == ResultCode.TIMEOUT); + } + + + /** + * Test to Create Folder with special characters + */ + public void testCreateFolderSpecialCharacters() { + boolean createFullPath = true; + + String remotePath = "/testSpecialCharacters_\\" + mCurrentDate; + RemoteOperationResult result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + + remotePath = "/testSpecialCharacters_<" + mCurrentDate; + result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + + remotePath = "/testSpecialCharacters_>" + mCurrentDate; + result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + + remotePath = "/testSpecialCharacters_:" + mCurrentDate; + result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + + remotePath = "/testSpecialCharacters_\"" + mCurrentDate; + result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + + remotePath = "/testSpecialCharacters_|" + mCurrentDate; + result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + + remotePath = "/testSpecialCharacters_?" + mCurrentDate; + result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + + remotePath = "/testSpecialCharacters_*" + mCurrentDate; + result = mActivity.createFolder(remotePath, createFullPath); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + } + + +} diff --git a/oc_framework-test-project/project.properties b/oc_framework-test-project/project.properties new file mode 100644 index 00000000..153d36b6 --- /dev/null +++ b/oc_framework-test-project/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 +android.library.reference.1=../oc_framework diff --git a/oc_framework-test-project/res/drawable-hdpi/ic_launcher.png b/oc_framework-test-project/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..288b6655 Binary files /dev/null and b/oc_framework-test-project/res/drawable-hdpi/ic_launcher.png differ diff --git a/oc_framework-test-project/res/drawable-mdpi/ic_launcher.png b/oc_framework-test-project/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..6ae570b4 Binary files /dev/null and b/oc_framework-test-project/res/drawable-mdpi/ic_launcher.png differ diff --git a/oc_framework-test-project/res/drawable-xhdpi/ic_launcher.png b/oc_framework-test-project/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000..d4fb7cd9 Binary files /dev/null and b/oc_framework-test-project/res/drawable-xhdpi/ic_launcher.png differ diff --git a/oc_framework-test-project/res/drawable-xxhdpi/ic_launcher.png b/oc_framework-test-project/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..85a60815 Binary files /dev/null and b/oc_framework-test-project/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/oc_framework-test-project/res/layout/activity_test.xml b/oc_framework-test-project/res/layout/activity_test.xml new file mode 100644 index 00000000..42c4fbda --- /dev/null +++ b/oc_framework-test-project/res/layout/activity_test.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/oc_framework-test-project/res/menu/test.xml b/oc_framework-test-project/res/menu/test.xml new file mode 100644 index 00000000..c0020282 --- /dev/null +++ b/oc_framework-test-project/res/menu/test.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/oc_framework-test-project/res/values-sw600dp/dimens.xml b/oc_framework-test-project/res/values-sw600dp/dimens.xml new file mode 100644 index 00000000..44f01db7 --- /dev/null +++ b/oc_framework-test-project/res/values-sw600dp/dimens.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/oc_framework-test-project/res/values-sw720dp-land/dimens.xml b/oc_framework-test-project/res/values-sw720dp-land/dimens.xml new file mode 100644 index 00000000..61e3fa8f --- /dev/null +++ b/oc_framework-test-project/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,9 @@ + + + + 128dp + + diff --git a/oc_framework-test-project/res/values-v11/styles.xml b/oc_framework-test-project/res/values-v11/styles.xml new file mode 100644 index 00000000..3c02242a --- /dev/null +++ b/oc_framework-test-project/res/values-v11/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/oc_framework-test-project/res/values-v14/styles.xml b/oc_framework-test-project/res/values-v14/styles.xml new file mode 100644 index 00000000..a91fd037 --- /dev/null +++ b/oc_framework-test-project/res/values-v14/styles.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/oc_framework-test-project/res/values/dimens.xml b/oc_framework-test-project/res/values/dimens.xml new file mode 100644 index 00000000..55c1e590 --- /dev/null +++ b/oc_framework-test-project/res/values/dimens.xml @@ -0,0 +1,7 @@ + + + + 16dp + 16dp + + diff --git a/oc_framework-test-project/res/values/strings.xml b/oc_framework-test-project/res/values/strings.xml new file mode 100644 index 00000000..e50b40df --- /dev/null +++ b/oc_framework-test-project/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + oc_framework-test-project + Settings + Hello world! + + diff --git a/oc_framework-test-project/res/values/styles.xml b/oc_framework-test-project/res/values/styles.xml new file mode 100644 index 00000000..6ce89c7b --- /dev/null +++ b/oc_framework-test-project/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/oc_framework-test-project/src/com/owncloud/android/oc_framework_test_project/TestActivity.java b/oc_framework-test-project/src/com/owncloud/android/oc_framework_test_project/TestActivity.java new file mode 100644 index 00000000..082557de --- /dev/null +++ b/oc_framework-test-project/src/com/owncloud/android/oc_framework_test_project/TestActivity.java @@ -0,0 +1,102 @@ +package com.owncloud.android.oc_framework_test_project; + +import java.io.IOException; + +import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException; +import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.remote.CreateRemoteFolderOperation; + +import android.os.Bundle; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Activity; +import android.util.Log; +import android.view.Menu; + +/** + * Activity to test OC framework + * @author masensio + * + */ +public class TestActivity extends Activity { + + private static final String TAG = "TestActivity"; + + private Account mAccount = null; + private WebdavClient mClient; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_test); + + // This account must exists on the simulator / device + String accountHost = "beta.owncloud.com"; + String accountUser = "testandroid"; + String accountName = accountUser + "@"+ accountHost; + String accountPass = "testandroid"; + String accountType = "owncloud"; + + AccountManager am = AccountManager.get(this); + + Account[] ocAccounts = am.getAccountsByType(accountType); + for (Account ac : ocAccounts) { + if (ac.name.equals(accountName)) { + mAccount = ac; + break; + } + } + +// if (mAccount == null) { +// mAccount = new Account(accountName, accountType); +// am.addAccountExplicitly(mAccount, accountPass, null); +// am.setUserData(mAccount, "oc_version", "5.0.14"); +// am.setUserData(mAccount, "oc_base_url", "http://beta.owncloud.com/owncloud"); +// } else { + Log.d(TAG, "oc_version --->"+ am.getUserData(mAccount, "oc_version") ); + Log.d(TAG, "oc_base_url --->"+ am.getUserData(mAccount, "oc_base_url") ); +// } + + + try { + mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, this.getApplicationContext()); + } catch (OperationCanceledException e) { + Log.e(TAG, "Error while trying to access to " + mAccount.name, e); + e.printStackTrace(); + } catch (AuthenticatorException e) { + Log.e(TAG, "Error while trying to access to " + mAccount.name, e); + e.printStackTrace(); + } catch (AccountNotFoundException e) { + Log.e(TAG, "Error while trying to access to " + mAccount.name, e); + e.printStackTrace(); + } catch (IOException e) { + Log.e(TAG, "Error while trying to access to " + mAccount.name, e); + e.printStackTrace(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.test, menu); + return true; + } + + /** + * Access to the library method to Create a Folder + * @param remotePath + * @param createFullPath + * @return + */ + public RemoteOperationResult createFolder(String remotePath, boolean createFullPath) { + + CreateRemoteFolderOperation createOperation = new CreateRemoteFolderOperation(remotePath, createFullPath); + RemoteOperationResult result = createOperation.execute(mClient); + + return result; + } +} diff --git a/oc_framework/.classpath b/oc_framework/.classpath new file mode 100644 index 00000000..51769745 --- /dev/null +++ b/oc_framework/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/oc_framework/.project b/oc_framework/.project new file mode 100644 index 00000000..18812a0d --- /dev/null +++ b/oc_framework/.project @@ -0,0 +1,33 @@ + + + oc_framework + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/oc_framework/.settings/org.eclipse.jdt.core.prefs b/oc_framework/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b080d2dd --- /dev/null +++ b/oc_framework/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/oc_framework/AndroidManifest.xml b/oc_framework/AndroidManifest.xml new file mode 100644 index 00000000..836b4a0a --- /dev/null +++ b/oc_framework/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/oc_framework/build.xml b/oc_framework/build.xml new file mode 100644 index 00000000..6b112f43 --- /dev/null +++ b/oc_framework/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/oc_framework/libs/jackrabbit-webdav-2.2.5-jar-with-dependencies.jar b/oc_framework/libs/jackrabbit-webdav-2.2.5-jar-with-dependencies.jar new file mode 100644 index 00000000..2dd374f9 Binary files /dev/null and b/oc_framework/libs/jackrabbit-webdav-2.2.5-jar-with-dependencies.jar differ diff --git a/oc_framework/project.properties b/oc_framework/project.properties new file mode 100644 index 00000000..91d2b024 --- /dev/null +++ b/oc_framework/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 +android.library=true diff --git a/oc_framework/src/com/owncloud/android/oc_framework/accounts/AccountTypeUtils.java b/oc_framework/src/com/owncloud/android/oc_framework/accounts/AccountTypeUtils.java new file mode 100644 index 00000000..2c9db68d --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/accounts/AccountTypeUtils.java @@ -0,0 +1,43 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.accounts; + +/** + * @author masensio + * @author David A. Velasco + */ +public class AccountTypeUtils { + + public static String getAuthTokenTypePass(String accountType) { + return accountType + ".password"; + } + + public static String getAuthTokenTypeAccessToken(String accountType) { + return accountType + ".oauth2.access_token"; + } + + public static String getAuthTokenTypeRefreshToken(String accountType) { + return accountType + ".oauth2.refresh_token"; + } + + public static String getAuthTokenTypeSamlSessionCookie(String accountType) { + return accountType + ".saml.web_sso.session_cookie"; + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/accounts/AccountUtils.java b/oc_framework/src/com/owncloud/android/oc_framework/accounts/AccountUtils.java new file mode 100644 index 00000000..810f3eba --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/accounts/AccountUtils.java @@ -0,0 +1,129 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.accounts; + +import com.owncloud.android.oc_framework.utils.OwnCloudVersion; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountsException; +import android.content.Context; + +public class AccountUtils { + public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php"; + public static final String WEBDAV_PATH_2_0 = "/files/webdav.php"; + public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav"; + private static final String ODAV_PATH = "/remote.php/odav"; + private static final String SAML_SSO_PATH = "/remote.php/webdav"; + public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php"; + public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php"; + public static final String STATUS_PATH = "/status.php"; + + /** + * + * @param version version of owncloud + * @return webdav path for given OC version, null if OC version unknown + */ + public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth, boolean supportsSamlSso) { + if (version != null) { + if (supportsOAuth) { + return ODAV_PATH; + } + if (supportsSamlSso) { + return SAML_SSO_PATH; + } + if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0) + return WEBDAV_PATH_4_0; + if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0 + || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0) + return WEBDAV_PATH_2_0; + if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0) + return WEBDAV_PATH_1_2; + } + return null; + } + +// /** +// * Returns the proper URL path to access the WebDAV interface of an ownCloud server, +// * according to its version and the authorization method used. +// * +// * @param version Version of ownCloud server. +// * @param authTokenType Authorization token type, matching some of the AUTH_TOKEN_TYPE_* constants in {@link AccountAuthenticator}. +// * @return WebDAV path for given OC version and authorization method, null if OC version is unknown. +// */ +// public static String getWebdavPath(OwnCloudVersion version, String authTokenType) { +// if (version != null) { +// if (MainApp.getAuthTokenTypeAccessToken().equals(authTokenType)) { +// return ODAV_PATH; +// } +// if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(authTokenType)) { +// return SAML_SSO_PATH; +// } +// if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0) +// return WEBDAV_PATH_4_0; +// if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0 +// || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0) +// return WEBDAV_PATH_2_0; +// if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0) +// return WEBDAV_PATH_1_2; +// } +// return null; +// } + + /** + * Constructs full url to host and webdav resource basing on host version + * @param context + * @param account + * @return url or null on failure + * @throws AccountNotFoundException When 'account' is unknown for the AccountManager + */ + public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException { + AccountManager ama = AccountManager.get(context); + String baseurl = ama.getUserData(account, OwnCloudAccount.Constants.KEY_OC_BASE_URL); + String strver = ama.getUserData(account, OwnCloudAccount.Constants.KEY_OC_VERSION); + boolean supportsOAuth = (ama.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_OAUTH2) != null); + boolean supportsSamlSso = (ama.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null); + OwnCloudVersion ver = new OwnCloudVersion(strver); + String webdavpath = getWebdavPath(ver, supportsOAuth, supportsSamlSso); + + if (baseurl == null || webdavpath == null) + throw new AccountNotFoundException(account, "Account not found", null); + + return baseurl + webdavpath; + } + + + public static class AccountNotFoundException extends AccountsException { + + /** Generated - should be refreshed every time the class changes!! */ + private static final long serialVersionUID = -9013287181793186830L; + + private Account mFailedAccount; + + public AccountNotFoundException(Account failedAccount, String message, Throwable cause) { + super(message, cause); + mFailedAccount = failedAccount; + } + + public Account getFailedAccount() { + return mFailedAccount; + } + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/accounts/OwnCloudAccount.java b/oc_framework/src/com/owncloud/android/oc_framework/accounts/OwnCloudAccount.java new file mode 100644 index 00000000..73fdb652 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/accounts/OwnCloudAccount.java @@ -0,0 +1,105 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.accounts; + +import android.accounts.Account; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Account with extra information specific for ownCloud accounts. + * + * TODO integrate in the main app + * + * @author David A. Velasco + */ +public class OwnCloudAccount extends Account { + + public static class Constants { + /** + * Value under this key should handle path to webdav php script. Will be + * removed and usage should be replaced by combining + * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and + * {@link com.owncloud.android.oc_framework.utils.utils.OwnCloudVersion} + * + * @deprecated + */ + public static final String KEY_OC_URL = "oc_url"; + /** + * Version should be 3 numbers separated by dot so it can be parsed by + * {@link com.owncloud.android.oc_framework.utils.utils.OwnCloudVersion} + */ + public static final String KEY_OC_VERSION = "oc_version"; + /** + * Base url should point to owncloud installation without trailing / ie: + * http://server/path or https://owncloud.server + */ + public static final String KEY_OC_BASE_URL = "oc_base_url"; + /** + * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens. + */ + public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2"; + /** + * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on. + */ + public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso"; + } + + private String mAuthTokenType; + + public OwnCloudAccount(String name, String type, String authTokenType) { + super(name, type); + // TODO validate authTokentype as supported + mAuthTokenType = authTokenType; + } + + /** + * Reconstruct from parcel + * + * @param source The source parcel + */ + public OwnCloudAccount(Parcel source) { + super(source); + mAuthTokenType = source.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mAuthTokenType); + } + + + public String getAuthTokenType() { + return mAuthTokenType; + } + + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public OwnCloudAccount createFromParcel(Parcel source) { + return new OwnCloudAccount(source); + } + + @Override + public OwnCloudAccount [] newArray(int size) { + return new OwnCloudAccount[size]; + } + }; + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java b/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java new file mode 100644 index 00000000..e24fa364 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedSslSocketFactory.java @@ -0,0 +1,242 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.network; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.security.cert.X509Certificate; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import org.apache.commons.httpclient.ConnectTimeoutException; +import org.apache.commons.httpclient.params.HttpConnectionParams; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; +import org.apache.http.conn.ssl.X509HostnameVerifier; + +import android.util.Log; + + + +/** + * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with + * a custom SSLContext and an optional Hostname Verifier. + * + * @author David A. Velasco + */ + +public class AdvancedSslSocketFactory implements ProtocolSocketFactory { + + private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName(); + + private SSLContext mSslContext = null; + private AdvancedX509TrustManager mTrustManager = null; + private X509HostnameVerifier mHostnameVerifier = null; + + public SSLContext getSslContext() { + return mSslContext; + } + + /** + * Constructor for AdvancedSSLProtocolSocketFactory. + */ + public AdvancedSslSocketFactory(SSLContext sslContext, AdvancedX509TrustManager trustManager, X509HostnameVerifier hostnameVerifier) { + if (sslContext == null) + throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null SSLContext"); + if (trustManager == null) + throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null Trust Manager"); + mSslContext = sslContext; + mTrustManager = trustManager; + mHostnameVerifier = hostnameVerifier; + } + + /** + * @see ProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) + */ + public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { + Socket socket = mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort); + verifyPeerIdentity(host, port, socket); + return socket; + } + + + /** + * Attempts to get a new socket connection to the given host within the + * given time limit. + * + * @param host the host name/IP + * @param port the port on the host + * @param clientHost the local host name/IP to bind the socket to + * @param clientPort the port on the local machine + * @param params {@link HttpConnectionParams Http connection parameters} + * + * @return Socket a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + * @throws UnknownHostException if the IP address of the host cannot be + * determined + */ + public Socket createSocket(final String host, final int port, + final InetAddress localAddress, final int localPort, + final HttpConnectionParams params) throws IOException, + UnknownHostException, ConnectTimeoutException { + Log.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params); + if (params == null) { + throw new IllegalArgumentException("Parameters may not be null"); + } + int timeout = params.getConnectionTimeout(); + SocketFactory socketfactory = mSslContext.getSocketFactory(); + Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout()); + Socket socket = socketfactory.createSocket(); + SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); + SocketAddress remoteaddr = new InetSocketAddress(host, port); + socket.setSoTimeout(params.getSoTimeout()); + socket.bind(localaddr); + socket.connect(remoteaddr, timeout); + verifyPeerIdentity(host, port, socket); + return socket; + } + + /** + * @see ProtocolSocketFactory#createSocket(java.lang.String,int) + */ + public Socket createSocket(String host, int port) throws IOException, + UnknownHostException { + Log.d(TAG, "Creating SSL Socket with remote " + host + ":" + port); + Socket socket = mSslContext.getSocketFactory().createSocket(host, port); + verifyPeerIdentity(host, port, socket); + return socket; + } + + public boolean equals(Object obj) { + return ((obj != null) && obj.getClass().equals( + AdvancedSslSocketFactory.class)); + } + + public int hashCode() { + return AdvancedSslSocketFactory.class.hashCode(); + } + + + public X509HostnameVerifier getHostNameVerifier() { + return mHostnameVerifier; + } + + + public void setHostNameVerifier(X509HostnameVerifier hostnameVerifier) { + mHostnameVerifier = hostnameVerifier; + } + + /** + * Verifies the identity of the server. + * + * The server certificate is verified first. + * + * Then, the host name is compared with the content of the server certificate using the current host name verifier, if any. + * @param socket + */ + private void verifyPeerIdentity(String host, int port, Socket socket) throws IOException { + try { + CertificateCombinedException failInHandshake = null; + /// 1. VERIFY THE SERVER CERTIFICATE through the registered TrustManager (that should be an instance of AdvancedX509TrustManager) + try { + SSLSocket sock = (SSLSocket) socket; // a new SSLSession instance is created as a "side effect" + sock.startHandshake(); + + } catch (RuntimeException e) { + + if (e instanceof CertificateCombinedException) { + failInHandshake = (CertificateCombinedException) e; + } else { + Throwable cause = e.getCause(); + Throwable previousCause = null; + while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) { + previousCause = cause; + cause = cause.getCause(); + } + if (cause != null && cause instanceof CertificateCombinedException) { + failInHandshake = (CertificateCombinedException)cause; + } + } + if (failInHandshake == null) { + throw e; + } + failInHandshake.setHostInUrl(host); + + } + + /// 2. VERIFY HOSTNAME + SSLSession newSession = null; + boolean verifiedHostname = true; + if (mHostnameVerifier != null) { + if (failInHandshake != null) { + /// 2.1 : a new SSLSession instance was NOT created in the handshake + X509Certificate serverCert = failInHandshake.getServerCertificate(); + try { + mHostnameVerifier.verify(host, serverCert); + } catch (SSLException e) { + verifiedHostname = false; + } + + } else { + /// 2.2 : a new SSLSession instance was created in the handshake + newSession = ((SSLSocket)socket).getSession(); + if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) { + verifiedHostname = mHostnameVerifier.verify(host, newSession); + } + } + } + + /// 3. Combine the exceptions to throw, if any + if (!verifiedHostname) { + SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException("Names in the server certificate do not match to " + host + " in the URL"); + if (failInHandshake == null) { + failInHandshake = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]); + failInHandshake.setHostInUrl(host); + } + failInHandshake.setSslPeerUnverifiedException(pue); + pue.initCause(failInHandshake); + throw pue; + + } else if (failInHandshake != null) { + SSLHandshakeException hse = new SSLHandshakeException("Server certificate could not be verified"); + hse.initCause(failInHandshake); + throw hse; + } + + } catch (IOException io) { + try { + socket.close(); + } catch (Exception x) { + // NOTHING - irrelevant exception for the caller + } + throw io; + } + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedX509TrustManager.java b/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedX509TrustManager.java new file mode 100644 index 00000000..4bad3ef0 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/AdvancedX509TrustManager.java @@ -0,0 +1,148 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.network; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertStoreException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import android.util.Log; + + + +/** + * @author David A. Velasco + */ +public class AdvancedX509TrustManager implements X509TrustManager { + + private static final String TAG = AdvancedX509TrustManager.class.getSimpleName(); + + private X509TrustManager mStandardTrustManager = null; + private KeyStore mKnownServersKeyStore; + + /** + * Constructor for AdvancedX509TrustManager + * + * @param knownServersCertStore Local certificates store with server certificates explicitly trusted by the user. + * @throws CertStoreException When no default X509TrustManager instance was found in the system. + */ + public AdvancedX509TrustManager(KeyStore knownServersKeyStore) + throws NoSuchAlgorithmException, KeyStoreException, CertStoreException { + super(); + TrustManagerFactory factory = TrustManagerFactory + .getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init((KeyStore)null); + mStandardTrustManager = findX509TrustManager(factory); + + mKnownServersKeyStore = knownServersKeyStore; + } + + + /** + * Locates the first X509TrustManager provided by a given TrustManagerFactory + * @param factory TrustManagerFactory to inspect in the search for a X509TrustManager + * @return The first X509TrustManager found in factory. + * @throws CertStoreException When no X509TrustManager instance was found in factory + */ + private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException { + TrustManager tms[] = factory.getTrustManagers(); + for (int i = 0; i < tms.length; i++) { + if (tms[i] instanceof X509TrustManager) { + return (X509TrustManager) tms[i]; + } + } + return null; + } + + + /** + * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[], + * String authType) + */ + public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException { + mStandardTrustManager.checkClientTrusted(certificates, authType); + } + + + /** + * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], + * String authType) + */ + public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException { + if (!isKnownServer(certificates[0])) { + CertificateCombinedException result = new CertificateCombinedException(certificates[0]); + try { + certificates[0].checkValidity(); + } catch (CertificateExpiredException c) { + result.setCertificateExpiredException(c); + + } catch (CertificateNotYetValidException c) { + result.setCertificateNotYetException(c); + } + + try { + mStandardTrustManager.checkServerTrusted(certificates, authType); + } catch (CertificateException c) { + Throwable cause = c.getCause(); + Throwable previousCause = null; + while (cause != null && cause != previousCause && !(cause instanceof CertPathValidatorException)) { // getCause() is not funny + previousCause = cause; + cause = cause.getCause(); + } + if (cause != null && cause instanceof CertPathValidatorException) { + result.setCertPathValidatorException((CertPathValidatorException)cause); + } else { + result.setOtherCertificateException(c); + } + } + + if (result.isException()) + throw result; + + } + } + + + /** + * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() + */ + public X509Certificate[] getAcceptedIssuers() { + return mStandardTrustManager.getAcceptedIssuers(); + } + + + public boolean isKnownServer(X509Certificate cert) { + try { + return (mKnownServersKeyStore.getCertificateAlias(cert) != null); + } catch (KeyStoreException e) { + Log.d(TAG, "Fail while checking certificate in the known-servers store"); + return false; + } + } + +} \ No newline at end of file diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/BearerAuthScheme.java b/oc_framework/src/com/owncloud/android/oc_framework/network/BearerAuthScheme.java new file mode 100644 index 00000000..37698d68 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/BearerAuthScheme.java @@ -0,0 +1,272 @@ +package com.owncloud.android.oc_framework.network; +/* ownCloud Android client application + * Copyright (C) 2012 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + + + +import java.util.Map; + +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.auth.AuthChallengeParser; +import org.apache.commons.httpclient.auth.AuthScheme; +import org.apache.commons.httpclient.auth.AuthenticationException; +import org.apache.commons.httpclient.auth.InvalidCredentialsException; +import org.apache.commons.httpclient.auth.MalformedChallengeException; + +import android.util.Log; + + + +/** + * Bearer authentication scheme as defined in RFC 6750. + * + * @author David A. Velasco + */ + +public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { + + private static final String TAG = BearerAuthScheme.class.getSimpleName(); + + public static final String AUTH_POLICY = "Bearer"; + + /** Whether the bearer authentication process is complete */ + private boolean mComplete; + + /** Authentication parameter map */ + @SuppressWarnings("rawtypes") + private Map mParams = null; + + + /** + * Default constructor for the bearer authentication scheme. + */ + public BearerAuthScheme() { + mComplete = false; + } + + /** + * Constructor for the basic authentication scheme. + * + * @param challenge Authentication challenge + * + * @throws MalformedChallengeException Thrown if the authentication challenge is malformed + * + * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method + */ + public BearerAuthScheme(final String challenge) throws MalformedChallengeException { + processChallenge(challenge); + mComplete = true; + } + + /** + * Returns textual designation of the bearer authentication scheme. + * + * @return "Bearer" + */ + public String getSchemeName() { + return "bearer"; + } + + /** + * Processes the Bearer challenge. + * + * @param challenge The challenge string + * + * @throws MalformedChallengeException Thrown if the authentication challenge is malformed + */ + public void processChallenge(String challenge) throws MalformedChallengeException { + String s = AuthChallengeParser.extractScheme(challenge); + if (!s.equalsIgnoreCase(getSchemeName())) { + throw new MalformedChallengeException( + "Invalid " + getSchemeName() + " challenge: " + challenge); + } + mParams = AuthChallengeParser.extractParams(challenge); + mComplete = true; + } + + /** + * Tests if the Bearer authentication process has been completed. + * + * @return 'true' if Bearer authorization has been processed, 'false' otherwise. + */ + public boolean isComplete() { + return this.mComplete; + } + + /** + * Produces bearer authorization string for the given set of + * {@link Credentials}. + * + * @param credentials The set of credentials to be used for authentication + * @param method Method name is ignored by the bearer authentication scheme + * @param uri URI is ignored by the bearer authentication scheme + * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable + * for this authentication scheme + * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure + * @return A bearer authorization string + * + * @deprecated Use {@link #authenticate(Credentials, HttpMethod)} + */ + public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException { + Log.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)"); + + BearerCredentials bearer = null; + try { + bearer = (BearerCredentials) credentials; + } catch (ClassCastException e) { + throw new InvalidCredentialsException( + "Credentials cannot be used for bearer authentication: " + + credentials.getClass().getName()); + } + return BearerAuthScheme.authenticate(bearer); + } + + + /** + * Returns 'false'. Bearer authentication scheme is request based. + * + * @return 'false'. + */ + public boolean isConnectionBased() { + return false; + } + + /** + * Produces bearer authorization string for the given set of {@link Credentials}. + * + * @param credentials The set of credentials to be used for authentication + * @param method The method being authenticated + * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable for this authentication + * scheme. + * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure. + * + * @return a basic authorization string + */ + public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException { + Log.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)"); + + if (method == null) { + throw new IllegalArgumentException("Method may not be null"); + } + BearerCredentials bearer = null; + try { + bearer = (BearerCredentials) credentials; + } catch (ClassCastException e) { + throw new InvalidCredentialsException( + "Credentials cannot be used for bearer authentication: " + + credentials.getClass().getName()); + } + return BearerAuthScheme.authenticate( + bearer, + method.getParams().getCredentialCharset()); + } + + /** + * @deprecated Use {@link #authenticate(BearerCredentials, String)} + * + * Returns a bearer Authorization header value for the given + * {@link BearerCredentials}. + * + * @param credentials The credentials to encode. + * + * @return A bearer authorization string + */ + public static String authenticate(BearerCredentials credentials) { + return authenticate(credentials, "ISO-8859-1"); + } + + /** + * Returns a bearer Authorization header value for the given + * {@link BearerCredentials} and charset. + * + * @param credentials The credentials to encode. + * @param charset The charset to use for encoding the credentials + * + * @return A bearer authorization string + * + * @since 3.0 + */ + public static String authenticate(BearerCredentials credentials, String charset) { + Log.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)"); + + if (credentials == null) { + throw new IllegalArgumentException("Credentials may not be null"); + } + if (charset == null || charset.length() == 0) { + throw new IllegalArgumentException("charset may not be null or empty"); + } + StringBuffer buffer = new StringBuffer(); + buffer.append(credentials.getAccessToken()); + + //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset)); + return "Bearer " + buffer.toString(); + } + + /** + * Returns a String identifying the authentication challenge. This is + * used, in combination with the host and port to determine if + * authorization has already been attempted or not. Schemes which + * require multiple requests to complete the authentication should + * return a different value for each stage in the request. + * + * Additionally, the ID should take into account any changes to the + * authentication challenge and return a different value when appropriate. + * For example when the realm changes in basic authentication it should be + * considered a different authentication attempt and a different value should + * be returned. + * + * This method simply returns the realm for the challenge. + * + * @return String a String identifying the authentication challenge. + * + * @deprecated no longer used + */ + @Override + public String getID() { + return getRealm(); + } + + /** + * Returns authentication parameter with the given name, if available. + * + * @param name The name of the parameter to be returned + * + * @return The parameter with the given name + */ + @Override + public String getParameter(String name) { + if (name == null) { + throw new IllegalArgumentException("Parameter name may not be null"); + } + if (mParams == null) { + return null; + } + return (String) mParams.get(name.toLowerCase()); + } + + /** + * Returns authentication realm. The realm may not be null. + * + * @return The authentication realm + */ + @Override + public String getRealm() { + return getParameter("realm"); + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/BearerCredentials.java b/oc_framework/src/com/owncloud/android/oc_framework/network/BearerCredentials.java new file mode 100644 index 00000000..5b18e62d --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/BearerCredentials.java @@ -0,0 +1,98 @@ +package com.owncloud.android.oc_framework.network; +/* ownCloud Android client application + * Copyright (C) 2012 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + + + +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.util.LangUtils; + +/** + * Bearer token {@link Credentials} + * + * @author David A. Velasco + */ +public class BearerCredentials implements Credentials { + + + private String mAccessToken; + + + /** + * The constructor with the bearer token + * + * @param token The bearer token + */ + public BearerCredentials(String token) { + /*if (token == null) { + throw new IllegalArgumentException("Bearer token may not be null"); + }*/ + mAccessToken = (token == null) ? "" : token; + } + + + /** + * Returns the access token + * + * @return The access token + */ + public String getAccessToken() { + return mAccessToken; + } + + + /** + * Get this object string. + * + * @return The access token + */ + public String toString() { + return mAccessToken; + } + + /** + * Does a hash of the access token. + * + * @return The hash code of the access token + */ + public int hashCode() { + int hash = LangUtils.HASH_SEED; + hash = LangUtils.hashCode(hash, mAccessToken); + return hash; + } + + /** + * These credentials are assumed equal if accessToken is the same. + * + * @param o The other object to compare with. + * + * @return 'True' if the object is equivalent. + */ + public boolean equals(Object o) { + if (o == null) return false; + if (this == o) return true; + if (this.getClass().equals(o.getClass())) { + BearerCredentials that = (BearerCredentials) o; + if (LangUtils.equals(mAccessToken, that.mAccessToken)) { + return true; + } + } + return false; + } + +} + diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/CertificateCombinedException.java b/oc_framework/src/com/owncloud/android/oc_framework/network/CertificateCombinedException.java new file mode 100644 index 00000000..1b262bc2 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/CertificateCombinedException.java @@ -0,0 +1,131 @@ +package com.owncloud.android.oc_framework.network; +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + + + +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLPeerUnverifiedException; + +/** + * Exception joining all the problems that {@link AdvancedX509TrustManager} can find in + * a certificate chain for a server. + * + * This was initially created as an extension of CertificateException, but some + * implementations of the SSL socket layer in existing devices are REPLACING the CertificateException + * instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)} + * with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it. + * + * Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException + * instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}. + * + * BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it + * in client methods. Be sure to use it only when you know exactly where it will go. + * + * @author David A. Velasco + */ +public class CertificateCombinedException extends RuntimeException { + + /** Generated - to refresh every time the class changes */ + private static final long serialVersionUID = -8875782030758554999L; + + private X509Certificate mServerCert = null; + private String mHostInUrl; + + private CertificateExpiredException mCertificateExpiredException = null; + private CertificateNotYetValidException mCertificateNotYetValidException = null; + private CertPathValidatorException mCertPathValidatorException = null; + private CertificateException mOtherCertificateException = null; + private SSLPeerUnverifiedException mSslPeerUnverifiedException = null; + + public CertificateCombinedException(X509Certificate x509Certificate) { + mServerCert = x509Certificate; + } + + public X509Certificate getServerCertificate() { + return mServerCert; + } + + public String getHostInUrl() { + return mHostInUrl; + } + + public void setHostInUrl(String host) { + mHostInUrl = host; + } + + public CertificateExpiredException getCertificateExpiredException() { + return mCertificateExpiredException; + } + + public void setCertificateExpiredException(CertificateExpiredException c) { + mCertificateExpiredException = c; + } + + public CertificateNotYetValidException getCertificateNotYetValidException() { + return mCertificateNotYetValidException; + } + + public void setCertificateNotYetException(CertificateNotYetValidException c) { + mCertificateNotYetValidException = c; + } + + public CertPathValidatorException getCertPathValidatorException() { + return mCertPathValidatorException; + } + + public void setCertPathValidatorException(CertPathValidatorException c) { + mCertPathValidatorException = c; + } + + public CertificateException getOtherCertificateException() { + return mOtherCertificateException; + } + + public void setOtherCertificateException(CertificateException c) { + mOtherCertificateException = c; + } + + public SSLPeerUnverifiedException getSslPeerUnverifiedException() { + return mSslPeerUnverifiedException ; + } + + public void setSslPeerUnverifiedException(SSLPeerUnverifiedException s) { + mSslPeerUnverifiedException = s; + } + + public boolean isException() { + return (mCertificateExpiredException != null || + mCertificateNotYetValidException != null || + mCertPathValidatorException != null || + mOtherCertificateException != null || + mSslPeerUnverifiedException != null); + } + + public boolean isRecoverable() { + return (mCertificateExpiredException != null || + mCertificateNotYetValidException != null || + mCertPathValidatorException != null || + mSslPeerUnverifiedException != null); + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/NetworkUtils.java b/oc_framework/src/com/owncloud/android/oc_framework/network/NetworkUtils.java new file mode 100644 index 00000000..ac2e015e --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/NetworkUtils.java @@ -0,0 +1,168 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ +package com.owncloud.android.oc_framework.network; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; +import org.apache.http.conn.ssl.X509HostnameVerifier; + +import android.content.Context; +import android.util.Log; + +public class NetworkUtils { + + final private static String TAG = NetworkUtils.class.getSimpleName(); + + /** Default timeout for waiting data from the server */ + public static final int DEFAULT_DATA_TIMEOUT = 60000; + + /** Default timeout for establishing a connection */ + public static final int DEFAULT_CONNECTION_TIMEOUT = 60000; + + /** Connection manager for all the WebdavClients */ + private static MultiThreadedHttpConnectionManager mConnManager = null; + + private static Protocol mDefaultHttpsProtocol = null; + + private static AdvancedSslSocketFactory mAdvancedSslSocketFactory = null; + + private static X509HostnameVerifier mHostnameVerifier = null; + + + /** + * Registers or unregisters the proper components for advanced SSL handling. + * @throws IOException + */ + public static void registerAdvancedSslContext(boolean register, Context context) throws GeneralSecurityException, IOException { + Protocol pr = null; + try { + pr = Protocol.getProtocol("https"); + if (pr != null && mDefaultHttpsProtocol == null) { + mDefaultHttpsProtocol = pr; + } + } catch (IllegalStateException e) { + // nothing to do here; really + } + boolean isRegistered = (pr != null && pr.getSocketFactory() instanceof AdvancedSslSocketFactory); + if (register && !isRegistered) { + Protocol.registerProtocol("https", new Protocol("https", getAdvancedSslSocketFactory(context), 443)); + + } else if (!register && isRegistered) { + if (mDefaultHttpsProtocol != null) { + Protocol.registerProtocol("https", mDefaultHttpsProtocol); + } + } + } + + public static AdvancedSslSocketFactory getAdvancedSslSocketFactory(Context context) throws GeneralSecurityException, IOException { + if (mAdvancedSslSocketFactory == null) { + KeyStore trustStore = getKnownServersStore(context); + AdvancedX509TrustManager trustMgr = new AdvancedX509TrustManager(trustStore); + TrustManager[] tms = new TrustManager[] { trustMgr }; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, tms, null); + + mHostnameVerifier = new BrowserCompatHostnameVerifier(); + mAdvancedSslSocketFactory = new AdvancedSslSocketFactory(sslContext, trustMgr, mHostnameVerifier); + } + return mAdvancedSslSocketFactory; + } + + + private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks"; + + private static String LOCAL_TRUSTSTORE_PASSWORD = "password"; + + private static KeyStore mKnownServersStore = null; + + /** + * Returns the local store of reliable server certificates, explicitly accepted by the user. + * + * Returns a KeyStore instance with empty content if the local store was never created. + * + * Loads the store from the storage environment if needed. + * + * @param context Android context where the operation is being performed. + * @return KeyStore instance with explicitly-accepted server certificates. + * @throws KeyStoreException When the KeyStore instance could not be created. + * @throws IOException When an existing local trust store could not be loaded. + * @throws NoSuchAlgorithmException When the existing local trust store was saved with an unsupported algorithm. + * @throws CertificateException When an exception occurred while loading the certificates from the local trust store. + */ + private static KeyStore getKnownServersStore(Context context) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { + if (mKnownServersStore == null) { + //mKnownServersStore = KeyStore.getInstance("BKS"); + mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType()); + File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME); + Log.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath()); + if (localTrustStoreFile.exists()) { + InputStream in = new FileInputStream(localTrustStoreFile); + try { + mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); + } finally { + in.close(); + } + } else { + mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); // necessary to initialize an empty KeyStore instance + } + } + return mKnownServersStore; + } + + + public static void addCertToKnownServersStore(Certificate cert, Context context) throws KeyStoreException, NoSuchAlgorithmException, + CertificateException, IOException { + KeyStore knownServers = getKnownServersStore(context); + knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert); + FileOutputStream fos = null; + try { + fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE); + knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); + } finally { + fos.close(); + } + } + + + static public MultiThreadedHttpConnectionManager getMultiThreadedConnManager() { + if (mConnManager == null) { + mConnManager = new MultiThreadedHttpConnectionManager(); + mConnManager.getParams().setDefaultMaxConnectionsPerHost(5); + mConnManager.getParams().setMaxTotalConnections(5); + } + return mConnManager; + } + + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/ProgressiveDataTransferer.java b/oc_framework/src/com/owncloud/android/oc_framework/network/ProgressiveDataTransferer.java new file mode 100644 index 00000000..3a21d5f7 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/ProgressiveDataTransferer.java @@ -0,0 +1,34 @@ +package com.owncloud.android.oc_framework.network; +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + + + +import java.util.Collection; + +import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener; + + +public interface ProgressiveDataTransferer { + + public void addDatatransferProgressListener (OnDatatransferProgressListener listener); + + public void addDatatransferProgressListeners(Collection listeners); + + public void removeDatatransferProgressListener(OnDatatransferProgressListener listener); + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/ChunkFromFileChannelRequestEntity.java b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/ChunkFromFileChannelRequestEntity.java new file mode 100644 index 00000000..6a4200df --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/ChunkFromFileChannelRequestEntity.java @@ -0,0 +1,145 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.network.webdav; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.apache.commons.httpclient.methods.RequestEntity; + +import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer; + +import android.util.Log; + + +/** + * A RequestEntity that represents a PIECE of a file. + * + * @author David A. Velasco + */ +public class ChunkFromFileChannelRequestEntity implements RequestEntity, ProgressiveDataTransferer { + + private static final String TAG = ChunkFromFileChannelRequestEntity.class.getSimpleName(); + + //private final File mFile; + private final FileChannel mChannel; + private final String mContentType; + private final long mChunkSize; + private final File mFile; + private long mOffset; + private long mTransferred; + Set mDataTransferListeners = new HashSet(); + private ByteBuffer mBuffer = ByteBuffer.allocate(4096); + + public ChunkFromFileChannelRequestEntity(final FileChannel channel, final String contentType, long chunkSize, final File file) { + super(); + if (channel == null) { + throw new IllegalArgumentException("File may not be null"); + } + if (chunkSize <= 0) { + throw new IllegalArgumentException("Chunk size must be greater than zero"); + } + mChannel = channel; + mContentType = contentType; + mChunkSize = chunkSize; + mFile = file; + mOffset = 0; + mTransferred = 0; + } + + public void setOffset(long offset) { + mOffset = offset; + } + + public long getContentLength() { + try { + return Math.min(mChunkSize, mChannel.size() - mChannel.position()); + } catch (IOException e) { + return mChunkSize; + } + } + + public String getContentType() { + return mContentType; + } + + public boolean isRepeatable() { + return true; + } + + @Override + public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.add(listener); + } + } + + @Override + public void addDatatransferProgressListeners(Collection listeners) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.addAll(listeners); + } + } + + @Override + public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.remove(listener); + } + } + + + public void writeRequest(final OutputStream out) throws IOException { + int readCount = 0; + Iterator it = null; + + try { + mChannel.position(mOffset); + long size = mFile.length(); + if (size == 0) size = -1; + long maxCount = Math.min(mOffset + mChunkSize, mChannel.size()); + while (mChannel.position() < maxCount) { + readCount = mChannel.read(mBuffer); + out.write(mBuffer.array(), 0, readCount); + mBuffer.clear(); + if (mTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks + mTransferred += readCount; + } + synchronized (mDataTransferListeners) { + it = mDataTransferListeners.iterator(); + while (it.hasNext()) { + it.next().onTransferProgress(readCount, mTransferred, size, mFile.getName()); + } + } + } + + } catch (IOException io) { + Log.e(TAG, io.getMessage()); + throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io); + + } + } + +} \ No newline at end of file diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/FileRequestEntity.java b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/FileRequestEntity.java new file mode 100644 index 00000000..3f066f94 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/FileRequestEntity.java @@ -0,0 +1,132 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.network.webdav; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.apache.commons.httpclient.methods.RequestEntity; + +import android.util.Log; + +import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer; + + +/** + * A RequestEntity that represents a File. + * + */ +public class FileRequestEntity implements RequestEntity, ProgressiveDataTransferer { + + final File mFile; + final String mContentType; + Set mDataTransferListeners = new HashSet(); + + public FileRequestEntity(final File file, final String contentType) { + super(); + this.mFile = file; + this.mContentType = contentType; + if (file == null) { + throw new IllegalArgumentException("File may not be null"); + } + } + + @Override + public long getContentLength() { + return mFile.length(); + } + + @Override + public String getContentType() { + return mContentType; + } + + @Override + public boolean isRepeatable() { + return true; + } + + @Override + public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.add(listener); + } + } + + @Override + public void addDatatransferProgressListeners(Collection listeners) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.addAll(listeners); + } + } + + @Override + public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { + synchronized (mDataTransferListeners) { + mDataTransferListeners.remove(listener); + } + } + + + @Override + public void writeRequest(final OutputStream out) throws IOException { + //byte[] tmp = new byte[4096]; + ByteBuffer tmp = ByteBuffer.allocate(4096); + int readResult = 0; + + // TODO(bprzybylski): each mem allocation can throw OutOfMemoryError we need to handle it + // globally in some fashionable manner + RandomAccessFile raf = new RandomAccessFile(mFile, "r"); + FileChannel channel = raf.getChannel(); + Iterator it = null; + long transferred = 0; + long size = mFile.length(); + if (size == 0) size = -1; + try { + while ((readResult = channel.read(tmp)) >= 0) { + out.write(tmp.array(), 0, readResult); + tmp.clear(); + transferred += readResult; + synchronized (mDataTransferListeners) { + it = mDataTransferListeners.iterator(); + while (it.hasNext()) { + it.next().onTransferProgress(readResult, transferred, size, mFile.getName()); + } + } + } + + } catch (IOException io) { + Log.e("FileRequestException", io.getMessage()); + throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io); + + } finally { + channel.close(); + raf.close(); + } + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/OnDatatransferProgressListener.java b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/OnDatatransferProgressListener.java new file mode 100644 index 00000000..06f32b59 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/OnDatatransferProgressListener.java @@ -0,0 +1,24 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.network.webdav; + +public interface OnDatatransferProgressListener { + public void onTransferProgress(long progressRate); + public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName); +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/OwnCloudClientFactory.java b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/OwnCloudClientFactory.java new file mode 100644 index 00000000..0c0c2072 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/OwnCloudClientFactory.java @@ -0,0 +1,151 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ +package com.owncloud.android.oc_framework.network.webdav; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +import com.owncloud.android.oc_framework.accounts.AccountTypeUtils; +import com.owncloud.android.oc_framework.accounts.AccountUtils; +import com.owncloud.android.oc_framework.accounts.OwnCloudAccount; +import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException; +import com.owncloud.android.oc_framework.network.NetworkUtils; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +public class OwnCloudClientFactory { + + final private static String TAG = OwnCloudClientFactory.class.getSimpleName(); + + /** Default timeout for waiting data from the server */ + public static final int DEFAULT_DATA_TIMEOUT = 60000; + + /** Default timeout for establishing a connection */ + public static final int DEFAULT_CONNECTION_TIMEOUT = 60000; + + + /** + * Creates a WebdavClient setup for an ownCloud account + * + * Do not call this method from the main thread. + * + * @param account The ownCloud account + * @param appContext Android application context + * @return A WebdavClient object ready to be used + * @throws AuthenticatorException If the authenticator failed to get the authorization token for the account. + * @throws OperationCanceledException If the authenticator operation was cancelled while getting the authorization token for the account. + * @throws IOException If there was some I/O error while getting the authorization token for the account. + * @throws AccountNotFoundException If 'account' is unknown for the AccountManager + */ + public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException { + //Log_OC.d(TAG, "Creating WebdavClient associated to " + account.name); + + Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account)); + AccountManager am = AccountManager.get(appContext); + boolean isOauth2 = am.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here + boolean isSamlSso = am.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null; + WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso); + if (isOauth2) { + String accessToken = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), false); + client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token + + } else if (isSamlSso) { // TODO avoid a call to getUserData here + String accessToken = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), false); + client.setSsoSessionCookie(accessToken); + + } else { + String username = account.name.substring(0, account.name.lastIndexOf('@')); + String password = am.getPassword(account); + //String password = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypePass(account.type), false); + client.setBasicCredentials(username, password); + } + + return client; + } + + + public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException { + Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account)); + AccountManager am = AccountManager.get(appContext); + boolean isOauth2 = am.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here + boolean isSamlSso = am.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null; + WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso); + + if (isOauth2) { // TODO avoid a call to getUserData here + AccountManagerFuture future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), null, currentActivity, null, null); + Bundle result = future.getResult(); + String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN); + if (accessToken == null) throw new AuthenticatorException("WTF!"); + client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token + + } else if (isSamlSso) { // TODO avoid a call to getUserData here + AccountManagerFuture future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), null, currentActivity, null, null); + Bundle result = future.getResult(); + String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN); + if (accessToken == null) throw new AuthenticatorException("WTF!"); + client.setSsoSessionCookie(accessToken); + + } else { + String username = account.name.substring(0, account.name.lastIndexOf('@')); + //String password = am.getPassword(account); + //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false); + AccountManagerFuture future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypePass(account.type), null, currentActivity, null, null); + Bundle result = future.getResult(); + String password = result.getString(AccountManager.KEY_AUTHTOKEN); + client.setBasicCredentials(username, password); + } + + return client; + } + + /** + * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections. + * + * @param uri URL to the ownCloud server + * @param context Android context where the WebdavClient is being created. + * @return A WebdavClient object ready to be used + */ + public static WebdavClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) { + try { + NetworkUtils.registerAdvancedSslContext(true, context); + } catch (GeneralSecurityException e) { + Log.e(TAG, "Advanced SSL Context could not be loaded. Default SSL management in the system will be used for HTTPS connections", e); + + } catch (IOException e) { + Log.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e); + } + + WebdavClient client = new WebdavClient(NetworkUtils.getMultiThreadedConnManager()); + + client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); + client.setBaseUri(uri); + client.setFollowRedirects(followRedirects); + + return client; + } + + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavClient.java b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavClient.java new file mode 100644 index 00000000..543374cb --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavClient.java @@ -0,0 +1,245 @@ +/* ownCloud Android client application + * Copyright (C) 2011 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.network.webdav; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpConnectionManager; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.HttpVersion; +import org.apache.commons.httpclient.URI; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthPolicy; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.cookie.CookiePolicy; +import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.params.HttpMethodParams; +import org.apache.http.HttpStatus; +import org.apache.http.params.CoreProtocolPNames; + +import com.owncloud.android.oc_framework.network.BearerAuthScheme; +import com.owncloud.android.oc_framework.network.BearerCredentials; + +import android.net.Uri; +import android.util.Log; + +public class WebdavClient extends HttpClient { + private static final int MAX_REDIRECTIONS_COUNT = 3; + + private Uri mUri; + private Credentials mCredentials; + private boolean mFollowRedirects; + private String mSsoSessionCookie; + final private static String TAG = WebdavClient.class.getSimpleName(); + public static final String USER_AGENT = "Android-ownCloud"; + + static private byte[] sExhaustBuffer = new byte[1024]; + + /** + * Constructor + */ + public WebdavClient(HttpConnectionManager connectionMgr) { + super(connectionMgr); + Log.d(TAG, "Creating WebdavClient"); + getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT); + getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1); + mFollowRedirects = true; + mSsoSessionCookie = null; + } + + public void setBearerCredentials(String accessToken) { + AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class); + + List authPrefs = new ArrayList(1); + authPrefs.add(BearerAuthScheme.AUTH_POLICY); + getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); + + mCredentials = new BearerCredentials(accessToken); + getState().setCredentials(AuthScope.ANY, mCredentials); + mSsoSessionCookie = null; + } + + public void setBasicCredentials(String username, String password) { + List authPrefs = new ArrayList(1); + authPrefs.add(AuthPolicy.BASIC); + getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); + + getParams().setAuthenticationPreemptive(true); + mCredentials = new UsernamePasswordCredentials(username, password); + getState().setCredentials(AuthScope.ANY, mCredentials); + mSsoSessionCookie = null; + } + + public void setSsoSessionCookie(String accessToken) { + getParams().setAuthenticationPreemptive(false); + getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES); + mSsoSessionCookie = accessToken; + mCredentials = null; + } + + + /** + * Check if a file exists in the OC server + * + * TODO replace with ExistenceOperation + * + * @return 'true' if the file exists; 'false' it doesn't exist + * @throws Exception When the existence could not be determined + */ + public boolean existsFile(String path) throws IOException, HttpException { + HeadMethod head = new HeadMethod(mUri.toString() + WebdavUtils.encodePath(path)); + try { + int status = executeMethod(head); + Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":"")); + exhaustResponse(head.getResponseBodyAsStream()); + return (status == HttpStatus.SC_OK); + + } finally { + head.releaseConnection(); // let the connection available for other methods + } + } + + /** + * Requests the received method with the received timeout (milliseconds). + * + * Executes the method through the inherited HttpClient.executedMethod(method). + * + * Sets the socket and connection timeouts only for the method received. + * + * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default' + * + * @param method HTTP method request. + * @param readTimeout Timeout to set for data reception + * @param conntionTimout Timeout to set for connection establishment + */ + public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException { + int oldSoTimeout = getParams().getSoTimeout(); + int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout(); + try { + if (readTimeout >= 0) { + method.getParams().setSoTimeout(readTimeout); // this should be enough... + getParams().setSoTimeout(readTimeout); // ... but this looks like necessary for HTTPS + } + if (connectionTimeout >= 0) { + getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout); + } + return executeMethod(method); + } finally { + getParams().setSoTimeout(oldSoTimeout); + getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout); + } + } + + + @Override + public int executeMethod(HttpMethod method) throws IOException, HttpException { + boolean customRedirectionNeeded = false; + try { + method.setFollowRedirects(mFollowRedirects); + } catch (Exception e) { + //if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used if needed"); + customRedirectionNeeded = mFollowRedirects; + } + if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) { + method.setRequestHeader("Cookie", mSsoSessionCookie); + } + int status = super.executeMethod(method); + int redirectionsCount = 0; + while (customRedirectionNeeded && + redirectionsCount < MAX_REDIRECTIONS_COUNT && + ( status == HttpStatus.SC_MOVED_PERMANENTLY || + status == HttpStatus.SC_MOVED_TEMPORARILY || + status == HttpStatus.SC_TEMPORARY_REDIRECT) + ) { + + Header location = method.getResponseHeader("Location"); + if (location != null) { + Log.d(TAG, "Location to redirect: " + location.getValue()); + method.setURI(new URI(location.getValue(), true)); + status = super.executeMethod(method); + redirectionsCount++; + + } else { + Log.d(TAG, "No location to redirect!"); + status = HttpStatus.SC_NOT_FOUND; + } + } + + return status; + } + + + /** + * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. + * + * @param responseBodyAsStream InputStream with the HTTP response to exhaust. + */ + public void exhaustResponse(InputStream responseBodyAsStream) { + if (responseBodyAsStream != null) { + try { + while (responseBodyAsStream.read(sExhaustBuffer) >= 0); + responseBodyAsStream.close(); + + } catch (IOException io) { + Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io); + } + } + } + + /** + * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client. + */ + public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) { + getParams().setSoTimeout(defaultDataTimeout); + getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout); + } + + /** + * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs + * @param uri + */ + public void setBaseUri(Uri uri) { + mUri = uri; + } + + public Uri getBaseUri() { + return mUri; + } + + public final Credentials getCredentials() { + return mCredentials; + } + + public final String getSsoSessionCookie() { + return mSsoSessionCookie; + } + + public void setFollowRedirects(boolean followRedirects) { + mFollowRedirects = followRedirects; + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavEntry.java b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavEntry.java new file mode 100644 index 00000000..3bcfa36a --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavEntry.java @@ -0,0 +1,150 @@ +/* ownCloud Android client application + * Copyright (C) 2012 ownCloud + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ +package com.owncloud.android.oc_framework.network.webdav; + +import java.util.Date; + +import org.apache.jackrabbit.webdav.MultiStatusResponse; +import org.apache.jackrabbit.webdav.property.DavProperty; +import org.apache.jackrabbit.webdav.property.DavPropertyName; +import org.apache.jackrabbit.webdav.property.DavPropertySet; + + + +import android.net.Uri; +import android.util.Log; + +public class WebdavEntry { + private String mName, mPath, mUri, mContentType, mEtag; + private long mContentLength, mCreateTimestamp, mModifiedTimestamp; + + public WebdavEntry(MultiStatusResponse ms, String splitElement) { + resetData(); + if (ms.getStatus().length != 0) { + mUri = ms.getHref(); + + mPath = mUri.split(splitElement, 2)[1]; + + int status = ms.getStatus()[0].getStatusCode(); + DavPropertySet propSet = ms.getProperties(status); + @SuppressWarnings("rawtypes") + DavProperty prop = propSet.get(DavPropertyName.DISPLAYNAME); + if (prop != null) { + mName = (String) prop.getName().toString(); + mName = mName.substring(1, mName.length()-1); + } + else { + String[] tmp = mPath.split("/"); + if (tmp.length > 0) + mName = tmp[tmp.length - 1]; + } + + // use unknown mimetype as default behavior + mContentType = "application/octet-stream"; + prop = propSet.get(DavPropertyName.GETCONTENTTYPE); + if (prop != null) { + mContentType = (String) prop.getValue(); + // dvelasco: some builds of ownCloud server 4.0.x added a trailing ';' to the MIME type ; if looks fixed, but let's be cautious + if (mContentType.indexOf(";") >= 0) { + mContentType = mContentType.substring(0, mContentType.indexOf(";")); + } + } + + // check if it's a folder in the standard way: see RFC2518 12.2 . RFC4918 14.3 + prop = propSet.get(DavPropertyName.RESOURCETYPE); + if (prop!= null) { + Object value = prop.getValue(); + if (value != null) { + mContentType = "DIR"; // a specific attribute would be better, but this is enough; unless while we have no reason to distinguish MIME types for folders + } + } + + prop = propSet.get(DavPropertyName.GETCONTENTLENGTH); + if (prop != null) + mContentLength = Long.parseLong((String) prop.getValue()); + + prop = propSet.get(DavPropertyName.GETLASTMODIFIED); + if (prop != null) { + Date d = WebdavUtils + .parseResponseDate((String) prop.getValue()); + mModifiedTimestamp = (d != null) ? d.getTime() : 0; + } + + prop = propSet.get(DavPropertyName.CREATIONDATE); + if (prop != null) { + Date d = WebdavUtils + .parseResponseDate((String) prop.getValue()); + mCreateTimestamp = (d != null) ? d.getTime() : 0; + } + + prop = propSet.get(DavPropertyName.GETETAG); + if (prop != null) { + mEtag = (String) prop.getValue(); + mEtag = mEtag.substring(1, mEtag.length()-1); + } + + } else { + Log.e("WebdavEntry", + "General fuckup, no status for webdav response"); + } + } + + public String path() { + return mPath; + } + + public String decodedPath() { + return Uri.decode(mPath); + } + + public String name() { + return mName; + } + + public boolean isDirectory() { + return mContentType.equals("DIR"); + } + + public String contentType() { + return mContentType; + } + + public String uri() { + return mUri; + } + + public long contentLength() { + return mContentLength; + } + + public long createTimestamp() { + return mCreateTimestamp; + } + + public long modifiedTimestamp() { + return mModifiedTimestamp; + } + + public String etag() { + return mEtag; + } + + private void resetData() { + mName = mUri = mContentType = null; + mContentLength = mCreateTimestamp = mModifiedTimestamp = 0; + } +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavUtils.java b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavUtils.java new file mode 100644 index 00000000..f5681de5 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/network/webdav/WebdavUtils.java @@ -0,0 +1,76 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.network.webdav; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import android.net.Uri; + +public class WebdavUtils { + public static final SimpleDateFormat DISPLAY_DATE_FORMAT = new SimpleDateFormat( + "dd.MM.yyyy hh:mm"); + private static final SimpleDateFormat DATETIME_FORMATS[] = { + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US), + new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US), + new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US), + new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), + new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) }; + + public static String prepareXmlForPropFind() { + String ret = ""; + return ret; + } + + public static String prepareXmlForPatch() { + return ""; + } + + public static Date parseResponseDate(String date) { + Date returnDate = null; + for (int i = 0; i < DATETIME_FORMATS.length; ++i) { + try { + returnDate = DATETIME_FORMATS[i].parse(date); + return returnDate; + } catch (ParseException e) { + } + } + return null; + } + + /** + * Encodes a path according to URI RFC 2396. + * + * If the received path doesn't start with "/", the method adds it. + * + * @param remoteFilePath Path + * @return Encoded path according to RFC 2396, always starting with "/" + */ + public static String encodePath(String remoteFilePath) { + String encodedPath = Uri.encode(remoteFilePath, "/"); + if (!encodedPath.startsWith("/")) + encodedPath = "/" + encodedPath; + return encodedPath; + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/operations/OnRemoteOperationListener.java b/oc_framework/src/com/owncloud/android/oc_framework/operations/OnRemoteOperationListener.java new file mode 100644 index 00000000..a81d4017 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/operations/OnRemoteOperationListener.java @@ -0,0 +1,25 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.operations; + +public interface OnRemoteOperationListener { + + void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result); + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/operations/OperationCancelledException.java b/oc_framework/src/com/owncloud/android/oc_framework/operations/OperationCancelledException.java new file mode 100644 index 00000000..d5fd3782 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/operations/OperationCancelledException.java @@ -0,0 +1,28 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.operations; + +public class OperationCancelledException extends Exception { + + /** + * Generated serial version - to avoid Java warning + */ + private static final long serialVersionUID = -6350981497740424983L; + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/operations/RemoteOperation.java b/oc_framework/src/com/owncloud/android/oc_framework/operations/RemoteOperation.java new file mode 100644 index 00000000..e18ae53f --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/operations/RemoteOperation.java @@ -0,0 +1,287 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ +package com.owncloud.android.oc_framework.operations; + +import java.io.IOException; + +import org.apache.commons.httpclient.Credentials; + +import com.owncloud.android.oc_framework.network.BearerCredentials; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; + + + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountsException; +import android.app.Activity; +import android.content.Context; +import android.os.Handler; +import android.util.Log; + + +/** + * Operation which execution involves one or several interactions with an ownCloud server. + * + * Provides methods to execute the operation both synchronously or asynchronously. + * + * @author David A. Velasco + */ +public abstract class RemoteOperation implements Runnable { + + private static final String TAG = RemoteOperation.class.getSimpleName(); + + /** ownCloud account in the remote ownCloud server to operate */ + private Account mAccount = null; + + /** Android Application context */ + private Context mContext = null; + + /** Object to interact with the remote server */ + private WebdavClient mClient = null; + + /** Callback object to notify about the execution of the remote operation */ + private OnRemoteOperationListener mListener = null; + + /** Handler to the thread where mListener methods will be called */ + private Handler mListenerHandler = null; + + /** Activity */ + private Activity mCallerActivity; + + + /** + * Abstract method to implement the operation in derived classes. + */ + protected abstract RemoteOperationResult run(WebdavClient client); + + + /** + * Synchronously executes the remote operation on the received ownCloud account. + * + * Do not call this method from the main thread. + * + * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}. + * + * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation. + * @param context Android context for the component calling the method. + * @return Result of the operation. + */ + public final RemoteOperationResult execute(Account account, Context context) { + if (account == null) + throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account"); + if (context == null) + throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context"); + mAccount = account; + mContext = context.getApplicationContext(); + try { + mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext); + } catch (Exception e) { + Log.e(TAG, "Error while trying to access to " + mAccount.name, e); + return new RemoteOperationResult(e); + } + return run(mClient); + } + + + /** + * Synchronously executes the remote operation + * + * Do not call this method from the main thread. + * + * @param client Client object to reach an ownCloud server during the execution of the operation. + * @return Result of the operation. + */ + public final RemoteOperationResult execute(WebdavClient client) { + if (client == null) + throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient"); + mClient = client; + return run(client); + } + + + /** + * Asynchronously executes the remote operation + * + * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}. + * + * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation. + * @param context Android context for the component calling the method. + * @param listener Listener to be notified about the execution of the operation. + * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. + * @return Thread were the remote operation is executed. + */ + public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) { + if (account == null) + throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account"); + if (context == null) + throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context"); + mAccount = account; + mContext = context.getApplicationContext(); + mCallerActivity = callerActivity; + mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below + + mListener = listener; + + mListenerHandler = listenerHandler; + + Thread runnerThread = new Thread(this); + runnerThread.start(); + return runnerThread; + } + + + /** + * Asynchronously executes the remote operation + * + * @param client Client object to reach an ownCloud server during the execution of the operation. + * @param listener Listener to be notified about the execution of the operation. + * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. + * @return Thread were the remote operation is executed. + */ + public final Thread execute(WebdavClient client, OnRemoteOperationListener listener, Handler listenerHandler) { + if (client == null) { + throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient"); + } + mClient = client; + + if (listener == null) { + throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result"); + } + mListener = listener; + + if (listenerHandler == null) { + throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread"); + } + mListenerHandler = listenerHandler; + + Thread runnerThread = new Thread(this); + runnerThread.start(); + return runnerThread; + } + + /** + * Synchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient)} + * + * @param listener Listener to be notified about the execution of the operation. + * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. + * @return Thread were the remote operation is executed. + */ + public final RemoteOperationResult retry() { + return execute(mClient); + } + + /** + * Asynchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)} + * + * @param listener Listener to be notified about the execution of the operation. + * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. + * @return Thread were the remote operation is executed. + */ + public final Thread retry(OnRemoteOperationListener listener, Handler listenerHandler) { + return execute(mClient, listener, listenerHandler); + } + + + /** + * Asynchronous execution of the operation + * started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}, + * and result posting. + * + * TODO refactor && clean the code; now it's a mess + */ + @Override + public final void run() { + RemoteOperationResult result = null; + boolean repeat = false; + do { + try{ + if (mClient == null) { + if (mAccount != null && mContext != null) { + if (mCallerActivity != null) { + mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext, mCallerActivity); + } else { + mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext); + } + } else { + throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account"); + } + } + + } catch (IOException e) { + Log.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e)); + result = new RemoteOperationResult(e); + + } catch (AccountsException e) { + Log.e(TAG, "Error while trying to access to " + mAccount.name, e); + result = new RemoteOperationResult(e); + } + + if (result == null) + result = run(mClient); + + repeat = false; + if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() && +// (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) { + (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) { + /// possible fail due to lack of authorization in an operation performed in foreground + Credentials cred = mClient.getCredentials(); + String ssoSessionCookie = mClient.getSsoSessionCookie(); + if (cred != null || ssoSessionCookie != null) { + /// confirmed : unauthorized operation + AccountManager am = AccountManager.get(mContext); + boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials); + boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null); + if (bearerAuthorization) { + am.invalidateAuthToken(mAccount.type, ((BearerCredentials)cred).getAccessToken()); + } else if (samlBasedSsoAuthorization ) { + am.invalidateAuthToken(mAccount.type, ssoSessionCookie); + } else { + am.clearPassword(mAccount); + } + mClient = null; + repeat = true; // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity + result = null; + } + } + } while (repeat); + + final RemoteOperationResult resultToSend = result; + if (mListenerHandler != null && mListener != null) { + mListenerHandler.post(new Runnable() { + @Override + public void run() { + mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend); + } + }); + } + } + + + /** + * Returns the current client instance to access the remote server. + * + * @return Current client instance to access the remote server. + */ + public final WebdavClient getClient() { + return mClient; + } + + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/operations/RemoteOperationResult.java b/oc_framework/src/com/owncloud/android/oc_framework/operations/RemoteOperationResult.java new file mode 100644 index 00000000..f26988cc --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/operations/RemoteOperationResult.java @@ -0,0 +1,341 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.operations; + +import java.io.IOException; +import java.io.Serializable; +import java.net.MalformedURLException; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; + +import javax.net.ssl.SSLException; + +import org.apache.commons.httpclient.ConnectTimeoutException; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.jackrabbit.webdav.DavException; + +import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException; +import com.owncloud.android.oc_framework.network.CertificateCombinedException; + +import android.accounts.Account; +import android.accounts.AccountsException; +import android.util.Log; + + +/** + * The result of a remote operation required to an ownCloud server. + * + * Provides a common classification of remote operation results for all the + * application. + * + * @author David A. Velasco + */ +public class RemoteOperationResult implements Serializable { + + /** Generated - should be refreshed every time the class changes!! */ + private static final long serialVersionUID = -4415103901492836870L; + + + + private static final String TAG = "RemoteOperationResult"; + + public enum ResultCode { + OK, + OK_SSL, + OK_NO_SSL, + UNHANDLED_HTTP_CODE, + UNAUTHORIZED, + FILE_NOT_FOUND, + INSTANCE_NOT_CONFIGURED, + UNKNOWN_ERROR, + WRONG_CONNECTION, + TIMEOUT, + INCORRECT_ADDRESS, + HOST_NOT_AVAILABLE, + NO_NETWORK_CONNECTION, + SSL_ERROR, + SSL_RECOVERABLE_PEER_UNVERIFIED, + BAD_OC_VERSION, + CANCELLED, + INVALID_LOCAL_FILE_NAME, + INVALID_OVERWRITE, + CONFLICT, + OAUTH2_ERROR, + SYNC_CONFLICT, + LOCAL_STORAGE_FULL, + LOCAL_STORAGE_NOT_MOVED, + LOCAL_STORAGE_NOT_COPIED, + OAUTH2_ERROR_ACCESS_DENIED, + QUOTA_EXCEEDED, + ACCOUNT_NOT_FOUND, + ACCOUNT_EXCEPTION, + ACCOUNT_NOT_NEW, + ACCOUNT_NOT_THE_SAME, + INVALID_CHARACTER_IN_NAME + } + + private boolean mSuccess = false; + private int mHttpCode = -1; + private Exception mException = null; + private ResultCode mCode = ResultCode.UNKNOWN_ERROR; + private String mRedirectedLocation; + + public RemoteOperationResult(ResultCode code) { + mCode = code; + mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL); + } + + private RemoteOperationResult(boolean success, int httpCode) { + mSuccess = success; + mHttpCode = httpCode; + + if (success) { + mCode = ResultCode.OK; + + } else if (httpCode > 0) { + switch (httpCode) { + case HttpStatus.SC_UNAUTHORIZED: + mCode = ResultCode.UNAUTHORIZED; + break; + case HttpStatus.SC_NOT_FOUND: + mCode = ResultCode.FILE_NOT_FOUND; + break; + case HttpStatus.SC_INTERNAL_SERVER_ERROR: + mCode = ResultCode.INSTANCE_NOT_CONFIGURED; + break; + case HttpStatus.SC_CONFLICT: + mCode = ResultCode.CONFLICT; + break; + case HttpStatus.SC_INSUFFICIENT_STORAGE: + mCode = ResultCode.QUOTA_EXCEEDED; + break; + default: + mCode = ResultCode.UNHANDLED_HTTP_CODE; + Log.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode); + } + } + } + + public RemoteOperationResult(boolean success, int httpCode, Header[] headers) { + this(success, httpCode); + if (headers != null) { + Header current; + for (int i=0; i= HttpStatus.SC_INTERNAL_SERVER_ERROR); + } + + public boolean isException() { + return (mException != null); + } + + public boolean isTemporalRedirection() { + return (mHttpCode == 302 || mHttpCode == 307); + } + + public String getRedirectedLocation() { + return mRedirectedLocation; + } + + public boolean isIdPRedirection() { + return (mRedirectedLocation != null && + (mRedirectedLocation.toUpperCase().contains("SAML") || + mRedirectedLocation.toLowerCase().contains("wayf"))); + } + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/operations/remote/CreateRemoteFolderOperation.java b/oc_framework/src/com/owncloud/android/oc_framework/operations/remote/CreateRemoteFolderOperation.java new file mode 100644 index 00000000..0974d08b --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/operations/remote/CreateRemoteFolderOperation.java @@ -0,0 +1,94 @@ +package com.owncloud.android.oc_framework.operations.remote; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.jackrabbit.webdav.client.methods.MkColMethod; + +import android.util.Log; + +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.utils.FileUtils; + + + +/** + * Remote operation performing the creation of a new folder in the ownCloud server. + * + * @author David A. Velasco + * @author masensio + * + */ +public class CreateRemoteFolderOperation extends RemoteOperation { + + private static final String TAG = CreateRemoteFolderOperation.class.getSimpleName(); + + private static final int READ_TIMEOUT = 10000; + private static final int CONNECTION_TIMEOUT = 5000; + + + protected String mRemotePath; + protected boolean mCreateFullPath; + + /** + * Constructor + * + * @param remotePath Full path to the new directory to create in the remote server. + * @param createFullPath 'True' means that all the ancestor folders should be created if don't exist yet. + */ + public CreateRemoteFolderOperation(String remotePath, boolean createFullPath) { + mRemotePath = remotePath; + mCreateFullPath = createFullPath; + } + + /** + * Performs the operation + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Override + protected RemoteOperationResult run(WebdavClient client) { + RemoteOperationResult result = null; + MkColMethod mkcol = null; + + boolean noInvalidChars = FileUtils.isValidPath(mRemotePath); + if (noInvalidChars) { + try { + mkcol = new MkColMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath)); + int status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT); + if (!mkcol.succeeded() && mkcol.getStatusCode() == HttpStatus.SC_CONFLICT && mCreateFullPath) { + result = createParentFolder(FileUtils.getParentPath(mRemotePath), client); + status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT); // second (and last) try + } + + result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders()); + Log.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage()); + client.exhaustResponse(mkcol.getResponseBodyAsStream()); + + } catch (Exception e) { + result = new RemoteOperationResult(e); + Log.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e); + + } finally { + if (mkcol != null) + mkcol.releaseConnection(); + } + } else { + result = new RemoteOperationResult(ResultCode.INVALID_CHARACTER_IN_NAME); + } + + return result; + } + + + private RemoteOperationResult createParentFolder(String parentPath, WebdavClient client) { + RemoteOperation operation = new CreateRemoteFolderOperation(parentPath, + mCreateFullPath); + return operation.execute(client); + } + + + +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/utils/FileUtils.java b/oc_framework/src/com/owncloud/android/oc_framework/utils/FileUtils.java new file mode 100644 index 00000000..8fb01dce --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/utils/FileUtils.java @@ -0,0 +1,52 @@ +package com.owncloud.android.oc_framework.utils; + +import java.io.File; + +import android.util.Log; + +public class FileUtils { + + public static final String PATH_SEPARATOR = "/"; + + + public static String getParentPath(String remotePath) { + String parentPath = new File(remotePath).getParent(); + parentPath = parentPath.endsWith(PATH_SEPARATOR) ? parentPath : parentPath + PATH_SEPARATOR; + return parentPath; + } + + /** + * Validate the fileName to detect if contains any forbidden character: / , \ , < , > , : , " , | , ? , * + * @param fileName + * @return + */ + public static boolean isValidName(String fileName) { + boolean result = true; + + Log.d("FileUtils", "fileName =======" + fileName); + if (fileName.contains(PATH_SEPARATOR) || + fileName.contains("\\") || fileName.contains("<") || fileName.contains(">") || + fileName.contains(":") || fileName.contains("\"") || fileName.contains("|") || + fileName.contains("?") || fileName.contains("*")) { + result = false; + } + return result; + } + + /** + * Validate the path to detect if contains any forbidden character: \ , < , > , : , " , | , ? , * + * @param path + * @return + */ + public static boolean isValidPath(String path) { + boolean result = true; + + Log.d("FileUtils", "path ....... " + path); + if (path.contains("\\") || path.contains("<") || path.contains(">") || + path.contains(":") || path.contains("\"") || path.contains("|") || + path.contains("?") || path.contains("*")) { + result = false; + } + return result; + } +} diff --git a/oc_framework/src/com/owncloud/android/oc_framework/utils/OwnCloudVersion.java b/oc_framework/src/com/owncloud/android/oc_framework/utils/OwnCloudVersion.java new file mode 100644 index 00000000..5a9df4d3 --- /dev/null +++ b/oc_framework/src/com/owncloud/android/oc_framework/utils/OwnCloudVersion.java @@ -0,0 +1,85 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.oc_framework.utils; + +public class OwnCloudVersion implements Comparable { + public static final OwnCloudVersion owncloud_v1 = new OwnCloudVersion( + 0x010000); + public static final OwnCloudVersion owncloud_v2 = new OwnCloudVersion( + 0x020000); + public static final OwnCloudVersion owncloud_v3 = new OwnCloudVersion( + 0x030000); + public static final OwnCloudVersion owncloud_v4 = new OwnCloudVersion( + 0x040000); + public static final OwnCloudVersion owncloud_v4_5 = new OwnCloudVersion( + 0x040500); + + // format is in version + // 0xAABBCC + // for version AA.BB.CC + // ie version 2.0.3 will be stored as 0x030003 + private int mVersion; + private boolean mIsValid; + + public OwnCloudVersion(int version) { + mVersion = version; + mIsValid = true; + } + + public OwnCloudVersion(String version) { + mVersion = 0; + mIsValid = false; + parseVersionString(version); + } + + public String toString() { + return ((mVersion >> 16) % 256) + "." + ((mVersion >> 8) % 256) + "." + + ((mVersion) % 256); + } + + public boolean isVersionValid() { + return mIsValid; + } + + @Override + public int compareTo(OwnCloudVersion another) { + return another.mVersion == mVersion ? 0 + : another.mVersion < mVersion ? 1 : -1; + } + + private void parseVersionString(String version) { + try { + String[] nums = version.split("\\."); + if (nums.length > 0) { + mVersion += Integer.parseInt(nums[0]); + } + mVersion = mVersion << 8; + if (nums.length > 1) { + mVersion += Integer.parseInt(nums[1]); + } + mVersion = mVersion << 8; + if (nums.length > 2) { + mVersion += Integer.parseInt(nums[2]); + } + mIsValid = true; + } catch (Exception e) { + mIsValid = false; + } + } +} diff --git a/project.properties b/project.properties index c9e58408..84352610 100644 --- a/project.properties +++ b/project.properties @@ -9,4 +9,5 @@ # Project target. target=android-19 -android.library.reference.1=actionbarsherlock/library +android.library.reference.1=actionbarsherlock\\library +android.library.reference.2=oc_framework diff --git a/res/values/strings.xml b/res/values/strings.xml index c8cea0ba..05c7ecf8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -182,6 +182,7 @@ Remote file could not be checked File contents already synchronized Directory could not be created + Forbidden characters: / \\ < > : " | ? * Wait a moment "Unexpected problem ; please select the file from a different app" No file was selected diff --git a/setup_env.bat b/setup_env.bat index 265ff594..d5b78c93 100644 --- a/setup_env.bat +++ b/setup_env.bat @@ -1,6 +1,7 @@ call git submodule init call git submodule update call android.bat update project -p actionbarsherlock\library -n ActionBarSherlock +call android.bat update project -p oc_framework -n ownCloudFramework call android.bat update project -p . call android.bat update project -p oc_jb_workaround copy /Y third_party\android-support-library\android-support-v4.jar actionbarsherlock\library\libs\android-support-v4.jar diff --git a/setup_env.sh b/setup_env.sh index d18ce226..b13fe81d 100755 --- a/setup_env.sh +++ b/setup_env.sh @@ -3,6 +3,7 @@ git submodule init git submodule update android update project -p actionbarsherlock/library -n ActionBarSherlock +android update project -p oc_framework -n ownCloudFramework android update project -p . android update project -p oc_jb_workaround cp third_party/android-support-library/android-support-v4.jar actionbarsherlock/library/libs/android-support-v4.jar diff --git a/src/com/owncloud/android/DisplayUtils.java b/src/com/owncloud/android/DisplayUtils.java deleted file mode 100644 index 1ee898b3..00000000 --- a/src/com/owncloud/android/DisplayUtils.java +++ /dev/null @@ -1,190 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2011 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android; - -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -/** - * A helper class for some string operations. - * - * @author Bartek Przybylski - * @author David A. Velasco - */ -public class DisplayUtils { - - //private static String TAG = DisplayUtils.class.getSimpleName(); - - private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; - - private static HashMap mimeType2HUmanReadable; - static { - mimeType2HUmanReadable = new HashMap(); - // images - mimeType2HUmanReadable.put("image/jpeg", "JPEG image"); - mimeType2HUmanReadable.put("image/jpg", "JPEG image"); - mimeType2HUmanReadable.put("image/png", "PNG image"); - mimeType2HUmanReadable.put("image/bmp", "Bitmap image"); - mimeType2HUmanReadable.put("image/gif", "GIF image"); - mimeType2HUmanReadable.put("image/svg+xml", "JPEG image"); - mimeType2HUmanReadable.put("image/tiff", "TIFF image"); - // music - mimeType2HUmanReadable.put("audio/mpeg", "MP3 music file"); - mimeType2HUmanReadable.put("application/ogg", "OGG music file"); - - } - - private static final String TYPE_APPLICATION = "application"; - private static final String TYPE_AUDIO = "audio"; - private static final String TYPE_IMAGE = "image"; - private static final String TYPE_TXT = "text"; - private static final String TYPE_VIDEO = "video"; - - private static final String SUBTYPE_PDF = "pdf"; - private static final String[] SUBTYPES_DOCUMENT = { "msword", "mspowerpoint", "msexcel", - "vnd.oasis.opendocument.presentation", - "vnd.oasis.opendocument.spreadsheet", - "vnd.oasis.opendocument.text" - }; - private static Set SUBTYPES_DOCUMENT_SET = new HashSet(Arrays.asList(SUBTYPES_DOCUMENT)); - private static final String[] SUBTYPES_COMPRESSED = {"x-tar", "x-gzip", "zip"}; - private static final Set SUBTYPES_COMPRESSED_SET = new HashSet(Arrays.asList(SUBTYPES_COMPRESSED)); - - /** - * Converts the file size in bytes to human readable output. - * - * @param bytes Input file size - * @return Like something readable like "12 MB" - */ - public static String bytesToHumanReadable(long bytes) { - double result = bytes; - int attachedsuff = 0; - while (result > 1024 && attachedsuff < sizeSuffixes.length) { - result /= 1024.; - attachedsuff++; - } - result = ((int) (result * 100)) / 100.; - return result + " " + sizeSuffixes[attachedsuff]; - } - - /** - * Removes special HTML entities from a string - * - * @param s Input string - * @return A cleaned version of the string - */ - public static String HtmlDecode(String s) { - /* - * TODO: Perhaps we should use something more proven like: - * http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#unescapeHtml%28java.lang.String%29 - */ - - String ret = ""; - for (int i = 0; i < s.length(); ++i) { - if (s.charAt(i) == '%') { - ret += (char) Integer.parseInt(s.substring(i + 1, i + 3), 16); - i += 2; - } else { - ret += s.charAt(i); - } - } - return ret; - } - - /** - * Converts MIME types like "image/jpg" to more end user friendly output - * like "JPG image". - * - * @param mimetype MIME type to convert - * @return A human friendly version of the MIME type - */ - public static String convertMIMEtoPrettyPrint(String mimetype) { - if (mimeType2HUmanReadable.containsKey(mimetype)) { - return mimeType2HUmanReadable.get(mimetype); - } - if (mimetype.split("/").length >= 2) - return mimetype.split("/")[1].toUpperCase() + " file"; - return "Unknown type"; - } - - - /** - * Returns the resource identifier of an image resource to use as icon associated to a - * known MIME type. - * - * @param mimetype MIME type string. - * @return Resource identifier of an image resource. - */ - public static int getResourceId(String mimetype) { - - if (mimetype == null || "DIR".equals(mimetype)) { - return R.drawable.ic_menu_archive; - - } else { - String [] parts = mimetype.split("/"); - String type = parts[0]; - String subtype = (parts.length > 1) ? parts[1] : ""; - - if(TYPE_TXT.equals(type)) { - return R.drawable.file_doc; - - } else if(TYPE_IMAGE.equals(type)) { - return R.drawable.file_image; - - } else if(TYPE_VIDEO.equals(type)) { - return R.drawable.file_movie; - - } else if(TYPE_AUDIO.equals(type)) { - return R.drawable.file_sound; - - } else if(TYPE_APPLICATION.equals(type)) { - - if (SUBTYPE_PDF.equals(subtype)) { - return R.drawable.file_pdf; - - } else if (SUBTYPES_DOCUMENT_SET.contains(subtype)) { - return R.drawable.file_doc; - - } else if (SUBTYPES_COMPRESSED_SET.contains(subtype)) { - return R.drawable.file_zip; - } - - } - // problems: RAR, RTF, 3GP are send as application/octet-stream from the server ; extension in the filename should be explicitly reviewed - } - - // default icon - return R.drawable.file; - } - - - - /** - * Converts Unix time to human readable format - * @param miliseconds that have passed since 01/01/1970 - * @return The human readable time for the users locale - */ - public static String unixTimeToHumanReadable(long milliseconds) { - Date date = new Date(milliseconds); - return date.toLocaleString(); - } -} diff --git a/src/com/owncloud/android/Log_OC.java b/src/com/owncloud/android/Log_OC.java deleted file mode 100644 index 5a1be298..00000000 --- a/src/com/owncloud/android/Log_OC.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.owncloud.android; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -import android.util.Log; - - - -public class Log_OC { - - - private static boolean isEnabled = false; - private static File logFile; - private static File folder; - private static BufferedWriter buf; - - public static void i(String TAG, String message){ - // Printing the message to LogCat console - Log.i(TAG, message); - // Write the log message to the file - appendLog(TAG+" : "+message); - } - - public static void d(String TAG, String message){ - Log.d(TAG, message); - appendLog(TAG + " : " + message); - } - public static void d(String TAG, String message, Exception e) { - Log.d(TAG, message, e); - appendLog(TAG + " : " + message + " Exception : "+ e.getStackTrace()); - } - public static void e(String TAG, String message){ - Log.e(TAG, message); - appendLog(TAG + " : " + message); - } - - public static void e(String TAG, String message, Throwable e) { - Log.e(TAG, message, e); - appendLog(TAG+" : " + message +" Exception : " + e.getStackTrace()); - } - - public static void v(String TAG, String message){ - Log.v(TAG, message); - appendLog(TAG+" : "+ message); - } - - public static void w(String TAG, String message) { - Log.w(TAG,message); - appendLog(TAG+" : "+ message); - } - - public static void wtf(String TAG, String message) { - Log.wtf(TAG,message); - appendLog(TAG+" : "+ message); - } - - public static void startLogging(String logPath) { - folder = new File(logPath); - logFile = new File(folder + File.separator + "log.txt"); - - if (!folder.exists()) { - folder.mkdirs(); - } - if (logFile.exists()) { - logFile.delete(); - } - try { - logFile.createNewFile(); - buf = new BufferedWriter(new FileWriter(logFile, true)); - isEnabled = true; - appendPhoneInfo(); - }catch (IOException e){ - e.printStackTrace(); - } - } - - public static void stopLogging() { - SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()); - String currentDateandTime = sdf.format(new Date()); - if (logFile != null) { - logFile.renameTo(new File(folder + File.separator + MainApp.getLogName() + currentDateandTime+".log")); - - isEnabled = false; - try { - buf.close(); - } catch (IOException e) { - e.printStackTrace(); - } - - } - - } - - private static void appendPhoneInfo() { - appendLog("Model : " + android.os.Build.MODEL); - appendLog("Brand : " + android.os.Build.BRAND); - appendLog("Product : " + android.os.Build.PRODUCT); - appendLog("Device : " + android.os.Build.DEVICE); - appendLog("Version-Codename : " + android.os.Build.VERSION.CODENAME); - appendLog("Version-Release : " + android.os.Build.VERSION.RELEASE); - } - - private static void appendLog(String text) { - if (isEnabled) { - try { - buf.append(text); - buf.newLine(); - } catch (IOException e) { - e.printStackTrace(); - } - } -} - - - - - - - - -} diff --git a/src/com/owncloud/android/MainApp.java b/src/com/owncloud/android/MainApp.java index 6cd88fe1..85718ff6 100644 --- a/src/com/owncloud/android/MainApp.java +++ b/src/com/owncloud/android/MainApp.java @@ -57,30 +57,6 @@ public class MainApp extends Application { return getAppContext().getResources().getString(R.string.authority); } - // From AccountAuthenticator - // public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password"; - public static String getAuthTokenTypePass() { - return getAppContext().getResources().getString(R.string.account_type) + ".password"; - } - - // From AccountAuthenticator - // public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token"; - public static String getAuthTokenTypeAccessToken() { - return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.access_token"; - } - - // From AccountAuthenticator - // public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token"; - public static String getAuthTokenTypeRefreshToken() { - return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.refresh_token"; - } - - // From AccountAuthenticator - // public static final String AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie"; - public static String getAuthTokenTypeSamlSessionCookie() { - return getAppContext().getResources().getString(R.string.account_type) + ".saml.web_sso.session_cookie"; - } - // From ProviderMeta // public static final String DB_FILE = "owncloud.db"; public static String getDBFile() { diff --git a/src/com/owncloud/android/OwnCloudSession.java b/src/com/owncloud/android/OwnCloudSession.java deleted file mode 100644 index d7bb6094..00000000 --- a/src/com/owncloud/android/OwnCloudSession.java +++ /dev/null @@ -1,56 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2011 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ -package com.owncloud.android; - -/** - * Represents a session to an ownCloud instance - * - * @author Bartek Przybylski - * - */ -public class OwnCloudSession { - private String mSessionName; - private String mSessionUrl; - private int mEntryId; - - public OwnCloudSession(String name, String url, int entryId) { - mSessionName = name; - mSessionUrl = url; - mEntryId = entryId; - } - - public void setName(String name) { - mSessionName = name; - } - - public String getName() { - return mSessionName; - } - - public void setUrl(String url) { - mSessionUrl = url; - } - - public String getUrl() { - return mSessionUrl; - } - - public int getEntryId() { - return mEntryId; - } -} diff --git a/src/com/owncloud/android/Uploader.java b/src/com/owncloud/android/Uploader.java deleted file mode 100644 index 29a4d6a4..00000000 --- a/src/com/owncloud/android/Uploader.java +++ /dev/null @@ -1,427 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Stack; -import java.util.Vector; - -import com.owncloud.android.R; -import com.owncloud.android.authentication.AccountAuthenticator; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.files.services.FileUploader; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.AlertDialog; -import android.app.AlertDialog.Builder; -import android.app.Dialog; -import android.app.ListActivity; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.os.Parcelable; -import android.provider.MediaStore.Audio; -import android.provider.MediaStore.Images; -import android.provider.MediaStore.Video; -import android.view.View; -import android.view.Window; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.Button; -import android.widget.EditText; -import android.widget.SimpleAdapter; -import android.widget.Toast; - - -/** - * This can be used to upload things to an ownCloud instance. - * - * @author Bartek Przybylski - * - */ -public class Uploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener { - private static final String TAG = "ownCloudUploader"; - - private Account mAccount; - private AccountManager mAccountManager; - private Stack mParents; - private ArrayList mStreamsToUpload; - private boolean mCreateDir; - private String mUploadPath; - private FileDataStorageManager mStorageManager; - private OCFile mFile; - - private final static int DIALOG_NO_ACCOUNT = 0; - private final static int DIALOG_WAITING = 1; - private final static int DIALOG_NO_STREAM = 2; - private final static int DIALOG_MULTIPLE_ACCOUNT = 3; - - private final static int REQUEST_CODE_SETUP_ACCOUNT = 0; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().requestFeature(Window.FEATURE_NO_TITLE); - mParents = new Stack(); - mParents.add(""); - if (prepareStreamsToUpload()) { - mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE); - Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType()); - if (accounts.length == 0) { - Log_OC.i(TAG, "No ownCloud account is available"); - showDialog(DIALOG_NO_ACCOUNT); - } else if (accounts.length > 1) { - Log_OC.i(TAG, "More then one ownCloud is available"); - showDialog(DIALOG_MULTIPLE_ACCOUNT); - } else { - mAccount = accounts[0]; - mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); - populateDirectoryList(); - } - } else { - showDialog(DIALOG_NO_STREAM); - } - } - - @Override - protected Dialog onCreateDialog(final int id) { - final AlertDialog.Builder builder = new Builder(this); - switch (id) { - case DIALOG_WAITING: - ProgressDialog pDialog = new ProgressDialog(this); - pDialog.setIndeterminate(false); - pDialog.setCancelable(false); - pDialog.setMessage(getResources().getString(R.string.uploader_info_uploading)); - return pDialog; - case DIALOG_NO_ACCOUNT: - builder.setIcon(android.R.drawable.ic_dialog_alert); - builder.setTitle(R.string.uploader_wrn_no_account_title); - builder.setMessage(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name))); - builder.setCancelable(false); - builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) { - // using string value since in API7 this - // constatn is not defined - // in API7 < this constatant is defined in - // Settings.ADD_ACCOUNT_SETTINGS - // and Settings.EXTRA_AUTHORITIES - Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT); - intent.putExtra("authorities", new String[] { MainApp.getAuthTokenType() }); - startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT); - } else { - // since in API7 there is no direct call for - // account setup, so we need to - // show our own AccountSetupAcricity, get - // desired results and setup - // everything for ourself - Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class); - startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT); - } - } - }); - builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - return builder.create(); - case DIALOG_MULTIPLE_ACCOUNT: - CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(MainApp.getAccountType()).length]; - for (int i = 0; i < ac.length; ++i) { - ac[i] = mAccountManager.getAccountsByType(MainApp.getAccountType())[i].name; - } - builder.setTitle(R.string.common_choose_account); - builder.setItems(ac, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mAccount = mAccountManager.getAccountsByType(MainApp.getAccountType())[which]; - mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); - populateDirectoryList(); - } - }); - builder.setCancelable(true); - builder.setOnCancelListener(new OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - dialog.cancel(); - finish(); - } - }); - return builder.create(); - case DIALOG_NO_STREAM: - builder.setIcon(android.R.drawable.ic_dialog_alert); - builder.setTitle(R.string.uploader_wrn_no_content_title); - builder.setMessage(R.string.uploader_wrn_no_content_text); - builder.setCancelable(false); - builder.setNegativeButton(R.string.common_cancel, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - return builder.create(); - default: - throw new IllegalArgumentException("Unknown dialog id: " + id); - } - } - - class a implements OnClickListener { - String mPath; - EditText mDirname; - - public a(String path, EditText dirname) { - mPath = path; - mDirname = dirname; - } - - @Override - public void onClick(DialogInterface dialog, int which) { - Uploader.this.mUploadPath = mPath + mDirname.getText().toString(); - Uploader.this.mCreateDir = true; - uploadFiles(); - } - } - - @Override - public void onBackPressed() { - - if (mParents.size() <= 1) { - super.onBackPressed(); - return; - } else { - mParents.pop(); - populateDirectoryList(); - } - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - // click on folder in the list - Log_OC.d(TAG, "on item click"); - Vector tmpfiles = mStorageManager.getFolderContent(mFile); - if (tmpfiles.size() <= 0) return; - // filter on dirtype - Vector files = new Vector(); - for (OCFile f : tmpfiles) - if (f.isFolder()) - files.add(f); - if (files.size() < position) { - throw new IndexOutOfBoundsException("Incorrect item selected"); - } - mParents.push(files.get(position).getFileName()); - populateDirectoryList(); - } - - @Override - public void onClick(View v) { - // click on button - switch (v.getId()) { - case R.id.uploader_choose_folder: - mUploadPath = ""; // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix - for (String p : mParents) - mUploadPath += p + OCFile.PATH_SEPARATOR; - Log_OC.d(TAG, "Uploading file to dir " + mUploadPath); - - uploadFiles(); - - break; - default: - throw new IllegalArgumentException("Wrong element clicked"); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - Log_OC.i(TAG, "result received. req: " + requestCode + " res: " + resultCode); - if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) { - dismissDialog(DIALOG_NO_ACCOUNT); - if (resultCode == RESULT_CANCELED) { - finish(); - } - Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAuthTokenType()); - if (accounts.length == 0) { - showDialog(DIALOG_NO_ACCOUNT); - } else { - // there is no need for checking for is there more then one - // account at this point - // since account setup can set only one account at time - mAccount = accounts[0]; - populateDirectoryList(); - } - } - } - - private void populateDirectoryList() { - setContentView(R.layout.uploader_layout); - - String full_path = ""; - for (String a : mParents) - full_path += a + "/"; - - Log_OC.d(TAG, "Populating view with content of : " + full_path); - - mFile = mStorageManager.getFileByPath(full_path); - if (mFile != null) { - Vector files = mStorageManager.getFolderContent(mFile); - List> data = new LinkedList>(); - for (OCFile f : files) { - HashMap h = new HashMap(); - if (f.isFolder()) { - h.put("dirname", f.getFileName()); - data.add(h); - } - } - SimpleAdapter sa = new SimpleAdapter(this, - data, - R.layout.uploader_list_item_layout, - new String[] {"dirname"}, - new int[] {R.id.textView1}); - setListAdapter(sa); - Button btn = (Button) findViewById(R.id.uploader_choose_folder); - btn.setOnClickListener(this); - getListView().setOnItemClickListener(this); - } - } - - private boolean prepareStreamsToUpload() { - if (getIntent().getAction().equals(Intent.ACTION_SEND)) { - mStreamsToUpload = new ArrayList(); - mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM)); - } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) { - mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM); - } - return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null); - } - - public void uploadFiles() { - try { - //WebdavClient webdav = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext()); - - ArrayList local = new ArrayList(); - ArrayList remote = new ArrayList(); - - /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed - WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext()); - // create last directory in path if necessary - if (mCreateDir) { - wdc.createDirectory(mUploadPath); - } - */ - - // this checks the mimeType - for (Parcelable mStream : mStreamsToUpload) { - - Uri uri = (Uri) mStream; - if (uri !=null) { - if (uri.getScheme().equals("content")) { - - String mimeType = getContentResolver().getType(uri); - - if (mimeType.contains("image")) { - String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE}; - Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null); - c.moveToFirst(); - int index = c.getColumnIndex(Images.Media.DATA); - String data = c.getString(index); - local.add(data); - remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME))); - - } - else if (mimeType.contains("video")) { - String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE, Video.Media.DATE_MODIFIED }; - Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null); - c.moveToFirst(); - int index = c.getColumnIndex(Video.Media.DATA); - String data = c.getString(index); - local.add(data); - remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME))); - - } - else if (mimeType.contains("audio")) { - String[] CONTENT_PROJECTION = { Audio.Media.DATA, Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE, Audio.Media.SIZE }; - Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null); - c.moveToFirst(); - int index = c.getColumnIndex(Audio.Media.DATA); - String data = c.getString(index); - local.add(data); - remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME))); - - } - else { - String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", ""); - // cut everything whats before mnt. It occured to me that sometimes apps send their name into the URI - if (filePath.contains("mnt")) { - String splitedFilePath[] = filePath.split("/mnt"); - filePath = splitedFilePath[1]; - } - final File file = new File(filePath); - local.add(file.getAbsolutePath()); - remote.add(mUploadPath + file.getName()); - } - - } else if (uri.getScheme().equals("file")) { - String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", ""); - if (filePath.contains("mnt")) { - String splitedFilePath[] = filePath.split("/mnt"); - filePath = splitedFilePath[1]; - } - final File file = new File(filePath); - local.add(file.getAbsolutePath()); - remote.add(mUploadPath + file.getName()); - } - else { - throw new SecurityException(); - } - } - else { - throw new SecurityException(); - } - - Intent intent = new Intent(getApplicationContext(), FileUploader.class); - intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES); - intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()])); - intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote.toArray(new String[remote.size()])); - intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount); - startService(intent); - finish(); - } - - } catch (SecurityException e) { - String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name)); - Toast.makeText(this, message, Toast.LENGTH_LONG).show(); - } - } - -} diff --git a/src/com/owncloud/android/authentication/AccountAuthenticator.java b/src/com/owncloud/android/authentication/AccountAuthenticator.java index ff0782d5..78a8575e 100644 --- a/src/com/owncloud/android/authentication/AccountAuthenticator.java +++ b/src/com/owncloud/android/authentication/AccountAuthenticator.java @@ -18,7 +18,6 @@ package com.owncloud.android.authentication; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -29,7 +28,8 @@ import android.os.Bundle; import android.os.Handler; import android.widget.Toast; - +import com.owncloud.android.oc_framework.accounts.AccountTypeUtils; +import com.owncloud.android.utils.Log_OC; /** @@ -53,34 +53,6 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { public static final String KEY_LOGIN_OPTIONS = "loginOptions"; public static final String KEY_ACCOUNT = "account"; - /** - * Value under this key should handle path to webdav php script. Will be - * removed and usage should be replaced by combining - * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and - * {@link com.owncloud.android.utils.OwnCloudVersion} - * - * @deprecated - */ - public static final String KEY_OC_URL = "oc_url"; - /** - * Version should be 3 numbers separated by dot so it can be parsed by - * {@link com.owncloud.android.utils.OwnCloudVersion} - */ - public static final String KEY_OC_VERSION = "oc_version"; - /** - * Base url should point to owncloud installation without trailing / ie: - * http://server/path or https://owncloud.server - */ - public static final String KEY_OC_BASE_URL = "oc_base_url"; - /** - * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens. - */ - public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2"; - /** - * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on. - */ - public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso"; - private static final String TAG = AccountAuthenticator.class.getSimpleName(); private Context mContext; @@ -204,7 +176,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { /// check if required token is stored final AccountManager am = AccountManager.get(mContext); String accessToken; - if (authTokenType.equals(MainApp.getAuthTokenTypePass())) { + if (authTokenType.equals(AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()))) { accessToken = am.getPassword(account); } else { accessToken = am.peekAuthToken(account, authTokenType); @@ -285,10 +257,10 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator { private void validateAuthTokenType(String authTokenType) throws UnsupportedAuthTokenTypeException { if (!authTokenType.equals(MainApp.getAuthTokenType()) && - !authTokenType.equals(MainApp.getAuthTokenTypePass()) && - !authTokenType.equals(MainApp.getAuthTokenTypeAccessToken()) && - !authTokenType.equals(MainApp.getAuthTokenTypeRefreshToken()) && - !authTokenType.equals(MainApp.getAuthTokenTypeSamlSessionCookie())) { + !authTokenType.equals(AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType())) && + !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType())) && + !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeRefreshToken(MainApp.getAccountType())) && + !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()))) { throw new UnsupportedAuthTokenTypeException(); } } diff --git a/src/com/owncloud/android/authentication/AccountUtils.java b/src/com/owncloud/android/authentication/AccountUtils.java index 3b79c39b..33c7e6a9 100644 --- a/src/com/owncloud/android/authentication/AccountUtils.java +++ b/src/com/owncloud/android/authentication/AccountUtils.java @@ -19,11 +19,11 @@ package com.owncloud.android.authentication; import com.owncloud.android.MainApp; -import com.owncloud.android.utils.OwnCloudVersion; +import com.owncloud.android.oc_framework.accounts.AccountTypeUtils; +import com.owncloud.android.oc_framework.utils.OwnCloudVersion; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountsException; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; @@ -127,30 +127,6 @@ public class AccountUtils { } /** - * - * @param version version of owncloud - * @return webdav path for given OC version, null if OC version unknown - */ - public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth, boolean supportsSamlSso) { - if (version != null) { - if (supportsOAuth) { - return ODAV_PATH; - } - if (supportsSamlSso) { - return SAML_SSO_PATH; - } - if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0) - return WEBDAV_PATH_4_0; - if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0 - || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0) - return WEBDAV_PATH_2_0; - if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0) - return WEBDAV_PATH_1_2; - } - return null; - } - - /** * Returns the proper URL path to access the WebDAV interface of an ownCloud server, * according to its version and the authorization method used. * @@ -160,10 +136,10 @@ public class AccountUtils { */ public static String getWebdavPath(OwnCloudVersion version, String authTokenType) { if (version != null) { - if (MainApp.getAuthTokenTypeAccessToken().equals(authTokenType)) { + if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(authTokenType)) { return ODAV_PATH; } - if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(authTokenType)) { + if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(authTokenType)) { return SAML_SSO_PATH; } if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0) @@ -177,44 +153,4 @@ public class AccountUtils { return null; } - /** - * Constructs full url to host and webdav resource basing on host version - * @param context - * @param account - * @return url or null on failure - * @throws AccountNotFoundException When 'account' is unknown for the AccountManager - */ - public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException { - AccountManager ama = AccountManager.get(context); - String baseurl = ama.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL); - String strver = ama.getUserData(account, AccountAuthenticator.KEY_OC_VERSION); - boolean supportsOAuth = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null); - boolean supportsSamlSso = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null); - OwnCloudVersion ver = new OwnCloudVersion(strver); - String webdavpath = getWebdavPath(ver, supportsOAuth, supportsSamlSso); - - if (baseurl == null || webdavpath == null) - throw new AccountNotFoundException(account, "Account not found", null); - - return baseurl + webdavpath; - } - - - public static class AccountNotFoundException extends AccountsException { - - /** Generated - should be refreshed every time the class changes!! */ - private static final long serialVersionUID = -9013287181793186830L; - - private Account mFailedAccount; - - public AccountNotFoundException(Account failedAccount, String message, Throwable cause) { - super(message, cause); - mFailedAccount = failedAccount; - } - - public Account getFailedAccount() { - return mFailedAccount; - } - } - } diff --git a/src/com/owncloud/android/authentication/AuthenticatorActivity.java b/src/com/owncloud/android/authentication/AuthenticatorActivity.java index faeffd53..6928d414 100644 --- a/src/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/src/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -51,25 +51,25 @@ import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import com.actionbarsherlock.app.SherlockDialogFragment; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener; -import com.owncloud.android.network.OwnCloudClientUtils; +import com.owncloud.android.oc_framework.accounts.AccountTypeUtils; +import com.owncloud.android.oc_framework.accounts.OwnCloudAccount; +import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; import com.owncloud.android.operations.ExistenceCheckOperation; import com.owncloud.android.operations.OAuth2GetAccessToken; -import com.owncloud.android.operations.OnRemoteOperationListener; +import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener; import com.owncloud.android.operations.OwnCloudServerCheckOperation; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.dialog.SamlWebViewDialog; import com.owncloud.android.ui.dialog.SslValidatorDialog; import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener; -import com.owncloud.android.utils.OwnCloudVersion; - - -import eu.alefzero.webdav.WebdavClient; +import com.owncloud.android.utils.Log_OC; +import com.owncloud.android.oc_framework.utils.OwnCloudVersion; /** * This Activity is used to add an ownCloud account to the App @@ -236,11 +236,11 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList /// retrieve extras from intent mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT); if (mAccount != null) { - String ocVersion = mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION); + String ocVersion = mAccountMgr.getUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_VERSION); if (ocVersion != null) { mDiscoveredVersion = new OwnCloudVersion(ocVersion); } - mHostBaseUrl = normalizeUrl(mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL)); + mHostBaseUrl = normalizeUrl(mAccountMgr.getUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_BASE_URL)); mHostUrlInput.setText(mHostBaseUrl); String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@')); mUsernameInput.setText(userName); @@ -279,7 +279,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList mAccount = savedInstanceState.getParcelable(KEY_ACCOUNT); mAuthTokenType = savedInstanceState.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE); if (mAuthTokenType == null) { - mAuthTokenType = MainApp.getAuthTokenTypePass(); + mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()); } @@ -317,7 +317,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList if (mServerIsChecked && !mServerIsValid && refreshButtonEnabled) showRefreshButton(); mOkButton.setEnabled(mServerIsValid); // state not automatically recovered in configuration changes - if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) || + if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType) || !AUTH_OPTIONAL.equals(getString(R.string.auth_method_oauth2))) { mOAuth2Check.setVisibility(View.GONE); } @@ -369,7 +369,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList @Override public boolean onTouch(View view, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) && + if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType) && mHostUrlInput.hasFocus()) { checkOcServer(); } @@ -393,8 +393,8 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList if (mAuthTokenType == null) { if (mAccount != null) { /// same authentication method than the one used to create the account to update - oAuthRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null); - samlWebSsoRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null); + oAuthRequired = (mAccountMgr.getUserData(mAccount, OwnCloudAccount.Constants.KEY_SUPPORTS_OAUTH2) != null); + samlWebSsoRequired = (mAccountMgr.getUserData(mAccount, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null); } else { /// use the one set in setup.xml @@ -402,11 +402,11 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList samlWebSsoRequired = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso)); } if (oAuthRequired) { - mAuthTokenType = MainApp.getAuthTokenTypeAccessToken(); + mAuthTokenType = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()); } else if (samlWebSsoRequired) { - mAuthTokenType = MainApp.getAuthTokenTypeSamlSessionCookie(); + mAuthTokenType = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()); } else { - mAuthTokenType = MainApp.getAuthTokenTypePass(); + mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()); } } @@ -415,7 +415,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList mUsernameInput.setText(userName); } - mOAuth2Check.setChecked(MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)); + mOAuth2Check.setChecked(AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)); } @@ -488,10 +488,10 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList protected void onResume() { super.onResume(); if (mAction == ACTION_UPDATE_TOKEN && mJustCreated && getIntent().getBooleanExtra(EXTRA_ENFORCED_UPDATE, false)) { - if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) { + if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) { //Toast.makeText(this, R.string.auth_expired_oauth_token_toast, Toast.LENGTH_LONG).show(); showAuthMessage(getString(R.string.auth_expired_oauth_token_toast)); - } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { + } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { //Toast.makeText(this, R.string.auth_expired_saml_sso_token_toast, Toast.LENGTH_LONG).show(); showAuthMessage(getString(R.string.auth_expired_saml_sso_token_toast)); } else { @@ -527,7 +527,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList getString(R.string.oauth2_grant_type), queryParameters); //WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth2_url_endpoint_access)), getApplicationContext()); - WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mOAuthTokenEndpointText.getText().toString().trim()), getApplicationContext(), true); + WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mOAuthTokenEndpointText.getText().toString().trim()), getApplicationContext(), true); operation.execute(client, this, mHandler); } @@ -591,7 +591,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList mServerStatusIcon = R.drawable.progress_small; showServerStatus(); mOcServerChkOperation = new OwnCloudServerCheckOperation(uri, this); - WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this, true); + WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(uri), this, true); mOperationThread = mOcServerChkOperation.execute(client, this, mHandler); } else { mServerStatusText = 0; @@ -691,9 +691,9 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList return; } - if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) { + if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) { startOauthorization(); - } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { + } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { startSamlBasedFederatedSingleSignOnAuthorization(); } else { checkBasicAuthorization(); @@ -718,7 +718,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList /// test credentials accessing the root folder mAuthCheckOperation = new ExistenceCheckOperation("", this, false); - WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true); + WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true); client.setBasicCredentials(username, password); mOperationThread = mAuthCheckOperation.execute(client, this, mHandler); } @@ -767,7 +767,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList /// test credentials accessing the root folder mAuthCheckOperation = new ExistenceCheckOperation("", this, false); - WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false); + WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false); mOperationThread = mAuthCheckOperation.execute(client, this, mHandler); } @@ -787,7 +787,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result); } else if (operation instanceof ExistenceCheckOperation) { - if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { + if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { onSamlBasedFederatedSingleSignOnAuthorizationStart(operation, result); } else { @@ -1086,7 +1086,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList mAuthToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN); Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken); mAuthCheckOperation = new ExistenceCheckOperation("", this, false); - WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true); + WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true); client.setBearerCredentials(mAuthToken); mAuthCheckOperation.execute(client, this, mHandler); @@ -1172,12 +1172,12 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name); response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type); - if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) { + if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) { response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken); // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken); - } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { + } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { String username = getUserNameForSamlSso(); if (!mUsernameInput.getText().toString().equals(username)) { // fail - not a new account, but an existing one; disallow @@ -1212,8 +1212,8 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList */ private boolean createAccount() { /// create and save new ownCloud account - boolean isOAuth = MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType); - boolean isSaml = MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType); + boolean isOAuth = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType); + boolean isSaml = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType); Uri uri = Uri.parse(mHostBaseUrl); String username = mUsernameInput.getText().toString().trim(); @@ -1265,12 +1265,12 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken); } /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA - mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString()); - mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL, mHostBaseUrl); + mAccountMgr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_VERSION, mDiscoveredVersion.toString()); + mAccountMgr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_BASE_URL, mHostBaseUrl); if (isSaml) { - mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); + mAccountMgr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); } else if (isOAuth) { - mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE"); + mAccountMgr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_SUPPORTS_OAUTH2, "TRUE"); } setAccountAuthenticatorResult(intent.getExtras()); @@ -1482,9 +1482,9 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList public void onCheckClick(View view) { CheckBox oAuth2Check = (CheckBox)view; if (oAuth2Check.isChecked()) { - mAuthTokenType = MainApp.getAuthTokenTypeAccessToken(); + mAuthTokenType = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()); } else { - mAuthTokenType = MainApp.getAuthTokenTypePass(); + mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()); } adaptViewAccordingToAuthenticationMethod(); } @@ -1495,14 +1495,14 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList * the current authorization method. */ private void adaptViewAccordingToAuthenticationMethod () { - if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) { + if (AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType()).equals(mAuthTokenType)) { // OAuth 2 authorization mOAuthAuthEndpointText.setVisibility(View.VISIBLE); mOAuthTokenEndpointText.setVisibility(View.VISIBLE); mUsernameInput.setVisibility(View.GONE); mPasswordInput.setVisibility(View.GONE); - } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { + } else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { // SAML-based web Single Sign On mOAuthAuthEndpointText.setVisibility(View.GONE); mOAuthTokenEndpointText.setVisibility(View.GONE); @@ -1548,7 +1548,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList } } else if (actionId == EditorInfo.IME_ACTION_NEXT && inputField != null && inputField.equals(mHostUrlInput)) { - if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { + if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) { checkOcServer(); } } @@ -1647,7 +1647,7 @@ implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList @Override public boolean onTouchEvent(MotionEvent event) { - if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) && + if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType) && mHostUrlInput.hasFocus() && event.getAction() == MotionEvent.ACTION_DOWN) { checkOcServer(); } diff --git a/src/com/owncloud/android/authentication/SsoWebViewClient.java b/src/com/owncloud/android/authentication/SsoWebViewClient.java index 5c97931e..8d80e9b7 100644 --- a/src/com/owncloud/android/authentication/SsoWebViewClient.java +++ b/src/com/owncloud/android/authentication/SsoWebViewClient.java @@ -19,7 +19,7 @@ package com.owncloud.android.authentication; import java.lang.ref.WeakReference; -import com.owncloud.android.Log_OC; +import com.owncloud.android.utils.Log_OC; import android.graphics.Bitmap; diff --git a/src/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/com/owncloud/android/datamodel/FileDataStorageManager.java index 0ad985ee..fb55e933 100644 --- a/src/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -25,10 +25,10 @@ import java.util.Collections; import java.util.Iterator; import java.util.Vector; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; diff --git a/src/com/owncloud/android/datamodel/OCFile.java b/src/com/owncloud/android/datamodel/OCFile.java index 67de5009..a6e6a546 100644 --- a/src/com/owncloud/android/datamodel/OCFile.java +++ b/src/com/owncloud/android/datamodel/OCFile.java @@ -20,7 +20,7 @@ package com.owncloud.android.datamodel; import java.io.File; -import com.owncloud.android.Log_OC; +import com.owncloud.android.utils.Log_OC; import android.os.Parcel; diff --git a/src/com/owncloud/android/db/DbHandler.java b/src/com/owncloud/android/db/DbHandler.java index 0ba7b0c9..89864fea 100644 --- a/src/com/owncloud/android/db/DbHandler.java +++ b/src/com/owncloud/android/db/DbHandler.java @@ -17,8 +17,8 @@ */ package com.owncloud.android.db; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; +import com.owncloud.android.utils.Log_OC; import android.content.ContentValues; import android.content.Context; diff --git a/src/com/owncloud/android/files/BootupBroadcastReceiver.java b/src/com/owncloud/android/files/BootupBroadcastReceiver.java index 8a8c4306..e90e8d4a 100644 --- a/src/com/owncloud/android/files/BootupBroadcastReceiver.java +++ b/src/com/owncloud/android/files/BootupBroadcastReceiver.java @@ -18,8 +18,8 @@ package com.owncloud.android.files; -import com.owncloud.android.Log_OC; import com.owncloud.android.files.services.FileObserverService; +import com.owncloud.android.utils.Log_OC; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java b/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java index fbb6888b..1943d320 100644 --- a/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java +++ b/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java @@ -20,12 +20,12 @@ package com.owncloud.android.files; import java.io.File; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.db.DbHandler; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; diff --git a/src/com/owncloud/android/files/OwnCloudFileObserver.java b/src/com/owncloud/android/files/OwnCloudFileObserver.java index 964b61c3..68c97e4f 100644 --- a/src/com/owncloud/android/files/OwnCloudFileObserver.java +++ b/src/com/owncloud/android/files/OwnCloudFileObserver.java @@ -20,13 +20,13 @@ package com.owncloud.android.files; import java.io.File; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; import com.owncloud.android.operations.SynchronizeFileOperation; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.ConflictsResolveActivity; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.content.Context; diff --git a/src/com/owncloud/android/files/services/FileDownloader.java b/src/com/owncloud/android/files/services/FileDownloader.java index 2e479f4d..232a042a 100644 --- a/src/com/owncloud/android/files/services/FileDownloader.java +++ b/src/com/owncloud/android/files/services/FileDownloader.java @@ -28,23 +28,22 @@ import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import com.owncloud.android.Log_OC; -import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.network.OwnCloudClientUtils; + +import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener; +import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; import com.owncloud.android.operations.DownloadFileOperation; -import com.owncloud.android.operations.RemoteOperationResult; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.preview.PreviewImageActivity; import com.owncloud.android.ui.preview.PreviewImageFragment; - -import eu.alefzero.webdav.OnDatatransferProgressListener; - +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.accounts.AccountsException; @@ -62,8 +61,6 @@ import android.os.Message; import android.os.Process; import android.widget.RemoteViews; -import eu.alefzero.webdav.WebdavClient; - public class FileDownloader extends Service implements OnDatatransferProgressListener { public static final String EXTRA_ACCOUNT = "ACCOUNT"; @@ -355,7 +352,7 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) { mLastAccount = mCurrentDownload.getAccount(); mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver()); - mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext()); + mDownloadClient = OwnCloudClientFactory.createOwnCloudClient(mLastAccount, getApplicationContext()); } /// perform the download @@ -476,7 +473,8 @@ public class FileDownloader extends Service implements OnDatatransferProgressLis boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED || // (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection() (downloadResult.isIdPRedirection() - && MainApp.getAuthTokenTypeSamlSessionCookie().equals(mDownloadClient.getAuthTokenType()))); + && mDownloadClient.getCredentials() == null)); + //&& MainApp.getAuthTokenTypeSamlSessionCookie().equals(mDownloadClient.getAuthTokenType()))); if (needsToUpdateCredentials) { // let the user update credentials with one click Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class); diff --git a/src/com/owncloud/android/files/services/FileObserverService.java b/src/com/owncloud/android/files/services/FileObserverService.java index 502d4591..fa1fec24 100644 --- a/src/com/owncloud/android/files/services/FileObserverService.java +++ b/src/com/owncloud/android/files/services/FileObserverService.java @@ -22,13 +22,13 @@ import java.io.File; import java.util.HashMap; import java.util.Map; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.files.OwnCloudFileObserver; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; diff --git a/src/com/owncloud/android/files/services/FileUploader.java b/src/com/owncloud/android/files/services/FileUploader.java index 7f45b6b9..d9f3a32c 100644 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@ -33,7 +33,6 @@ import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.MultiStatus; import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountAuthenticator; @@ -41,27 +40,27 @@ import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.db.DbHandler; -import com.owncloud.android.network.OwnCloudClientUtils; import com.owncloud.android.operations.ChunkedUploadFileOperation; import com.owncloud.android.operations.CreateFolderOperation; import com.owncloud.android.operations.ExistenceCheckOperation; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; import com.owncloud.android.operations.UploadFileOperation; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.utils.OwnCloudVersion; +import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener; +import com.owncloud.android.oc_framework.accounts.OwnCloudAccount; +import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavEntry; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; import com.owncloud.android.ui.activity.FailedUploadActivity; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.activity.InstantUploadActivity; import com.owncloud.android.ui.preview.PreviewImageActivity; import com.owncloud.android.ui.preview.PreviewImageFragment; -import com.owncloud.android.utils.OwnCloudVersion; - - -import eu.alefzero.webdav.OnDatatransferProgressListener; -import eu.alefzero.webdav.WebdavEntry; -import eu.alefzero.webdav.WebdavUtils; - +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.accounts.AccountManager; @@ -82,7 +81,6 @@ import android.webkit.MimeTypeMap; import android.widget.RemoteViews; -import eu.alefzero.webdav.WebdavClient; public class FileUploader extends Service implements OnDatatransferProgressListener { @@ -261,8 +259,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe } } - OwnCloudVersion ocv = new OwnCloudVersion(AccountManager.get(this).getUserData(account, - AccountAuthenticator.KEY_OC_VERSION)); + OwnCloudVersion ocv = new OwnCloudVersion(AccountManager.get(this).getUserData(account, OwnCloudAccount.Constants.KEY_OC_VERSION)); boolean chunked = FileUploader.chunkedUploadIsSupported(ocv); AbstractList requestedUploads = new Vector(); String uploadKey = null; @@ -504,7 +501,7 @@ public class FileUploader extends Service implements OnDatatransferProgressListe if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) { mLastAccount = mCurrentUpload.getAccount(); mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver()); - mUploadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext()); + mUploadClient = OwnCloudClientFactory.createOwnCloudClient(mLastAccount, getApplicationContext()); } /// check the existence of the parent folder for the file to upload @@ -567,9 +564,9 @@ public class FileUploader extends Service implements OnDatatransferProgressListe RemoteOperation operation = new ExistenceCheckOperation(pathToGrant, this, false); RemoteOperationResult result = operation.execute(mUploadClient); if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) { - operation = new CreateFolderOperation( pathToGrant, - true, - mStorageManager ); + operation = new CreateFolderOperation( pathToGrant, + true, + mStorageManager ); result = operation.execute(mUploadClient); } if (result.isSuccess()) { @@ -823,7 +820,8 @@ public class FileUploader extends Service implements OnDatatransferProgressListe boolean needsToUpdateCredentials = (uploadResult.getCode() == ResultCode.UNAUTHORIZED || //(uploadResult.isTemporalRedirection() && uploadResult.isIdPRedirection() && (uploadResult.isIdPRedirection() && - MainApp.getAuthTokenTypeSamlSessionCookie().equals(mUploadClient.getAuthTokenType()))); + mUploadClient.getCredentials() == null)); + //MainApp.getAuthTokenTypeSamlSessionCookie().equals(mUploadClient.getAuthTokenType()))); if (needsToUpdateCredentials) { // let the user update credentials with one click Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class); diff --git a/src/com/owncloud/android/media/MediaService.java b/src/com/owncloud/android/media/MediaService.java index eb57222c..0a746355 100644 --- a/src/com/owncloud/android/media/MediaService.java +++ b/src/com/owncloud/android/media/MediaService.java @@ -37,11 +37,11 @@ import android.widget.Toast; import java.io.IOException; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; +import com.owncloud.android.utils.Log_OC; /** diff --git a/src/com/owncloud/android/media/MediaServiceBinder.java b/src/com/owncloud/android/media/MediaServiceBinder.java index ab018f67..d2a42c77 100644 --- a/src/com/owncloud/android/media/MediaServiceBinder.java +++ b/src/com/owncloud/android/media/MediaServiceBinder.java @@ -18,9 +18,9 @@ package com.owncloud.android.media; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.media.MediaService.State; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.content.Intent; diff --git a/src/com/owncloud/android/network/AdvancedSslSocketFactory.java b/src/com/owncloud/android/network/AdvancedSslSocketFactory.java deleted file mode 100644 index 6a698713..00000000 --- a/src/com/owncloud/android/network/AdvancedSslSocketFactory.java +++ /dev/null @@ -1,241 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.network; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketAddress; -import java.net.UnknownHostException; -import java.security.cert.X509Certificate; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; - -import org.apache.commons.httpclient.ConnectTimeoutException; -import org.apache.commons.httpclient.params.HttpConnectionParams; -import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; -import org.apache.http.conn.ssl.X509HostnameVerifier; - -import com.owncloud.android.Log_OC; - - -/** - * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with - * a custom SSLContext and an optional Hostname Verifier. - * - * @author David A. Velasco - */ - -public class AdvancedSslSocketFactory implements ProtocolSocketFactory { - - private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName(); - - private SSLContext mSslContext = null; - private AdvancedX509TrustManager mTrustManager = null; - private X509HostnameVerifier mHostnameVerifier = null; - - public SSLContext getSslContext() { - return mSslContext; - } - - /** - * Constructor for AdvancedSSLProtocolSocketFactory. - */ - public AdvancedSslSocketFactory(SSLContext sslContext, AdvancedX509TrustManager trustManager, X509HostnameVerifier hostnameVerifier) { - if (sslContext == null) - throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null SSLContext"); - if (trustManager == null) - throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null Trust Manager"); - mSslContext = sslContext; - mTrustManager = trustManager; - mHostnameVerifier = hostnameVerifier; - } - - /** - * @see ProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int) - */ - public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { - Socket socket = mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort); - verifyPeerIdentity(host, port, socket); - return socket; - } - - - /** - * Attempts to get a new socket connection to the given host within the - * given time limit. - * - * @param host the host name/IP - * @param port the port on the host - * @param clientHost the local host name/IP to bind the socket to - * @param clientPort the port on the local machine - * @param params {@link HttpConnectionParams Http connection parameters} - * - * @return Socket a new socket - * - * @throws IOException if an I/O error occurs while creating the socket - * @throws UnknownHostException if the IP address of the host cannot be - * determined - */ - public Socket createSocket(final String host, final int port, - final InetAddress localAddress, final int localPort, - final HttpConnectionParams params) throws IOException, - UnknownHostException, ConnectTimeoutException { - Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params); - if (params == null) { - throw new IllegalArgumentException("Parameters may not be null"); - } - int timeout = params.getConnectionTimeout(); - SocketFactory socketfactory = mSslContext.getSocketFactory(); - Log_OC.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout()); - Socket socket = socketfactory.createSocket(); - SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); - SocketAddress remoteaddr = new InetSocketAddress(host, port); - socket.setSoTimeout(params.getSoTimeout()); - socket.bind(localaddr); - socket.connect(remoteaddr, timeout); - verifyPeerIdentity(host, port, socket); - return socket; - } - - /** - * @see ProtocolSocketFactory#createSocket(java.lang.String,int) - */ - public Socket createSocket(String host, int port) throws IOException, - UnknownHostException { - Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port); - Socket socket = mSslContext.getSocketFactory().createSocket(host, port); - verifyPeerIdentity(host, port, socket); - return socket; - } - - public boolean equals(Object obj) { - return ((obj != null) && obj.getClass().equals( - AdvancedSslSocketFactory.class)); - } - - public int hashCode() { - return AdvancedSslSocketFactory.class.hashCode(); - } - - - public X509HostnameVerifier getHostNameVerifier() { - return mHostnameVerifier; - } - - - public void setHostNameVerifier(X509HostnameVerifier hostnameVerifier) { - mHostnameVerifier = hostnameVerifier; - } - - /** - * Verifies the identity of the server. - * - * The server certificate is verified first. - * - * Then, the host name is compared with the content of the server certificate using the current host name verifier, if any. - * @param socket - */ - private void verifyPeerIdentity(String host, int port, Socket socket) throws IOException { - try { - CertificateCombinedException failInHandshake = null; - /// 1. VERIFY THE SERVER CERTIFICATE through the registered TrustManager (that should be an instance of AdvancedX509TrustManager) - try { - SSLSocket sock = (SSLSocket) socket; // a new SSLSession instance is created as a "side effect" - sock.startHandshake(); - - } catch (RuntimeException e) { - - if (e instanceof CertificateCombinedException) { - failInHandshake = (CertificateCombinedException) e; - } else { - Throwable cause = e.getCause(); - Throwable previousCause = null; - while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) { - previousCause = cause; - cause = cause.getCause(); - } - if (cause != null && cause instanceof CertificateCombinedException) { - failInHandshake = (CertificateCombinedException)cause; - } - } - if (failInHandshake == null) { - throw e; - } - failInHandshake.setHostInUrl(host); - - } - - /// 2. VERIFY HOSTNAME - SSLSession newSession = null; - boolean verifiedHostname = true; - if (mHostnameVerifier != null) { - if (failInHandshake != null) { - /// 2.1 : a new SSLSession instance was NOT created in the handshake - X509Certificate serverCert = failInHandshake.getServerCertificate(); - try { - mHostnameVerifier.verify(host, serverCert); - } catch (SSLException e) { - verifiedHostname = false; - } - - } else { - /// 2.2 : a new SSLSession instance was created in the handshake - newSession = ((SSLSocket)socket).getSession(); - if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) { - verifiedHostname = mHostnameVerifier.verify(host, newSession); - } - } - } - - /// 3. Combine the exceptions to throw, if any - if (!verifiedHostname) { - SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException("Names in the server certificate do not match to " + host + " in the URL"); - if (failInHandshake == null) { - failInHandshake = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]); - failInHandshake.setHostInUrl(host); - } - failInHandshake.setSslPeerUnverifiedException(pue); - pue.initCause(failInHandshake); - throw pue; - - } else if (failInHandshake != null) { - SSLHandshakeException hse = new SSLHandshakeException("Server certificate could not be verified"); - hse.initCause(failInHandshake); - throw hse; - } - - } catch (IOException io) { - try { - socket.close(); - } catch (Exception x) { - // NOTHING - irrelevant exception for the caller - } - throw io; - } - } - -} diff --git a/src/com/owncloud/android/network/AdvancedX509TrustManager.java b/src/com/owncloud/android/network/AdvancedX509TrustManager.java deleted file mode 100644 index 81dcdbd7..00000000 --- a/src/com/owncloud/android/network/AdvancedX509TrustManager.java +++ /dev/null @@ -1,147 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.network; - -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertPathValidatorException; -import java.security.cert.CertStoreException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -import com.owncloud.android.Log_OC; - - -/** - * @author David A. Velasco - */ -public class AdvancedX509TrustManager implements X509TrustManager { - - private static final String TAG = AdvancedX509TrustManager.class.getSimpleName(); - - private X509TrustManager mStandardTrustManager = null; - private KeyStore mKnownServersKeyStore; - - /** - * Constructor for AdvancedX509TrustManager - * - * @param knownServersCertStore Local certificates store with server certificates explicitly trusted by the user. - * @throws CertStoreException When no default X509TrustManager instance was found in the system. - */ - public AdvancedX509TrustManager(KeyStore knownServersKeyStore) - throws NoSuchAlgorithmException, KeyStoreException, CertStoreException { - super(); - TrustManagerFactory factory = TrustManagerFactory - .getInstance(TrustManagerFactory.getDefaultAlgorithm()); - factory.init((KeyStore)null); - mStandardTrustManager = findX509TrustManager(factory); - - mKnownServersKeyStore = knownServersKeyStore; - } - - - /** - * Locates the first X509TrustManager provided by a given TrustManagerFactory - * @param factory TrustManagerFactory to inspect in the search for a X509TrustManager - * @return The first X509TrustManager found in factory. - * @throws CertStoreException When no X509TrustManager instance was found in factory - */ - private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException { - TrustManager tms[] = factory.getTrustManagers(); - for (int i = 0; i < tms.length; i++) { - if (tms[i] instanceof X509TrustManager) { - return (X509TrustManager) tms[i]; - } - } - return null; - } - - - /** - * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[], - * String authType) - */ - public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException { - mStandardTrustManager.checkClientTrusted(certificates, authType); - } - - - /** - * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], - * String authType) - */ - public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException { - if (!isKnownServer(certificates[0])) { - CertificateCombinedException result = new CertificateCombinedException(certificates[0]); - try { - certificates[0].checkValidity(); - } catch (CertificateExpiredException c) { - result.setCertificateExpiredException(c); - - } catch (CertificateNotYetValidException c) { - result.setCertificateNotYetException(c); - } - - try { - mStandardTrustManager.checkServerTrusted(certificates, authType); - } catch (CertificateException c) { - Throwable cause = c.getCause(); - Throwable previousCause = null; - while (cause != null && cause != previousCause && !(cause instanceof CertPathValidatorException)) { // getCause() is not funny - previousCause = cause; - cause = cause.getCause(); - } - if (cause != null && cause instanceof CertPathValidatorException) { - result.setCertPathValidatorException((CertPathValidatorException)cause); - } else { - result.setOtherCertificateException(c); - } - } - - if (result.isException()) - throw result; - - } - } - - - /** - * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() - */ - public X509Certificate[] getAcceptedIssuers() { - return mStandardTrustManager.getAcceptedIssuers(); - } - - - public boolean isKnownServer(X509Certificate cert) { - try { - return (mKnownServersKeyStore.getCertificateAlias(cert) != null); - } catch (KeyStoreException e) { - Log_OC.d(TAG, "Fail while checking certificate in the known-servers store"); - return false; - } - } - -} \ No newline at end of file diff --git a/src/com/owncloud/android/network/BearerAuthScheme.java b/src/com/owncloud/android/network/BearerAuthScheme.java deleted file mode 100644 index 16791c23..00000000 --- a/src/com/owncloud/android/network/BearerAuthScheme.java +++ /dev/null @@ -1,269 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.network; - -import java.util.Map; - -import org.apache.commons.httpclient.Credentials; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.auth.AuthChallengeParser; -import org.apache.commons.httpclient.auth.AuthScheme; -import org.apache.commons.httpclient.auth.AuthenticationException; -import org.apache.commons.httpclient.auth.InvalidCredentialsException; -import org.apache.commons.httpclient.auth.MalformedChallengeException; - -import com.owncloud.android.Log_OC; - - -/** - * Bearer authentication scheme as defined in RFC 6750. - * - * @author David A. Velasco - */ - -public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { - - private static final String TAG = BearerAuthScheme.class.getSimpleName(); - - public static final String AUTH_POLICY = "Bearer"; - - /** Whether the bearer authentication process is complete */ - private boolean mComplete; - - /** Authentication parameter map */ - private Map mParams = null; - - - /** - * Default constructor for the bearer authentication scheme. - */ - public BearerAuthScheme() { - mComplete = false; - } - - /** - * Constructor for the basic authentication scheme. - * - * @param challenge Authentication challenge - * - * @throws MalformedChallengeException Thrown if the authentication challenge is malformed - * - * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method - */ - public BearerAuthScheme(final String challenge) throws MalformedChallengeException { - processChallenge(challenge); - mComplete = true; - } - - /** - * Returns textual designation of the bearer authentication scheme. - * - * @return "Bearer" - */ - public String getSchemeName() { - return "bearer"; - } - - /** - * Processes the Bearer challenge. - * - * @param challenge The challenge string - * - * @throws MalformedChallengeException Thrown if the authentication challenge is malformed - */ - public void processChallenge(String challenge) throws MalformedChallengeException { - String s = AuthChallengeParser.extractScheme(challenge); - if (!s.equalsIgnoreCase(getSchemeName())) { - throw new MalformedChallengeException( - "Invalid " + getSchemeName() + " challenge: " + challenge); - } - mParams = AuthChallengeParser.extractParams(challenge); - mComplete = true; - } - - /** - * Tests if the Bearer authentication process has been completed. - * - * @return 'true' if Bearer authorization has been processed, 'false' otherwise. - */ - public boolean isComplete() { - return this.mComplete; - } - - /** - * Produces bearer authorization string for the given set of - * {@link Credentials}. - * - * @param credentials The set of credentials to be used for authentication - * @param method Method name is ignored by the bearer authentication scheme - * @param uri URI is ignored by the bearer authentication scheme - * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable - * for this authentication scheme - * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure - * @return A bearer authorization string - * - * @deprecated Use {@link #authenticate(Credentials, HttpMethod)} - */ - public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException { - Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)"); - - BearerCredentials bearer = null; - try { - bearer = (BearerCredentials) credentials; - } catch (ClassCastException e) { - throw new InvalidCredentialsException( - "Credentials cannot be used for bearer authentication: " - + credentials.getClass().getName()); - } - return BearerAuthScheme.authenticate(bearer); - } - - - /** - * Returns 'false'. Bearer authentication scheme is request based. - * - * @return 'false'. - */ - public boolean isConnectionBased() { - return false; - } - - /** - * Produces bearer authorization string for the given set of {@link Credentials}. - * - * @param credentials The set of credentials to be used for authentication - * @param method The method being authenticated - * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable for this authentication - * scheme. - * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure. - * - * @return a basic authorization string - */ - public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException { - Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)"); - - if (method == null) { - throw new IllegalArgumentException("Method may not be null"); - } - BearerCredentials bearer = null; - try { - bearer = (BearerCredentials) credentials; - } catch (ClassCastException e) { - throw new InvalidCredentialsException( - "Credentials cannot be used for bearer authentication: " - + credentials.getClass().getName()); - } - return BearerAuthScheme.authenticate( - bearer, - method.getParams().getCredentialCharset()); - } - - /** - * @deprecated Use {@link #authenticate(BearerCredentials, String)} - * - * Returns a bearer Authorization header value for the given - * {@link BearerCredentials}. - * - * @param credentials The credentials to encode. - * - * @return A bearer authorization string - */ - public static String authenticate(BearerCredentials credentials) { - return authenticate(credentials, "ISO-8859-1"); - } - - /** - * Returns a bearer Authorization header value for the given - * {@link BearerCredentials} and charset. - * - * @param credentials The credentials to encode. - * @param charset The charset to use for encoding the credentials - * - * @return A bearer authorization string - * - * @since 3.0 - */ - public static String authenticate(BearerCredentials credentials, String charset) { - Log_OC.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)"); - - if (credentials == null) { - throw new IllegalArgumentException("Credentials may not be null"); - } - if (charset == null || charset.length() == 0) { - throw new IllegalArgumentException("charset may not be null or empty"); - } - StringBuffer buffer = new StringBuffer(); - buffer.append(credentials.getAccessToken()); - - //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset)); - return "Bearer " + buffer.toString(); - } - - /** - * Returns a String identifying the authentication challenge. This is - * used, in combination with the host and port to determine if - * authorization has already been attempted or not. Schemes which - * require multiple requests to complete the authentication should - * return a different value for each stage in the request. - * - * Additionally, the ID should take into account any changes to the - * authentication challenge and return a different value when appropriate. - * For example when the realm changes in basic authentication it should be - * considered a different authentication attempt and a different value should - * be returned. - * - * This method simply returns the realm for the challenge. - * - * @return String a String identifying the authentication challenge. - * - * @deprecated no longer used - */ - @Override - public String getID() { - return getRealm(); - } - - /** - * Returns authentication parameter with the given name, if available. - * - * @param name The name of the parameter to be returned - * - * @return The parameter with the given name - */ - @Override - public String getParameter(String name) { - if (name == null) { - throw new IllegalArgumentException("Parameter name may not be null"); - } - if (mParams == null) { - return null; - } - return (String) mParams.get(name.toLowerCase()); - } - - /** - * Returns authentication realm. The realm may not be null. - * - * @return The authentication realm - */ - @Override - public String getRealm() { - return getParameter("realm"); - } - -} diff --git a/src/com/owncloud/android/network/BearerCredentials.java b/src/com/owncloud/android/network/BearerCredentials.java deleted file mode 100644 index 50799b02..00000000 --- a/src/com/owncloud/android/network/BearerCredentials.java +++ /dev/null @@ -1,97 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.network; - -import org.apache.commons.httpclient.Credentials; -import org.apache.commons.httpclient.util.LangUtils; - -/** - * Bearer token {@link Credentials} - * - * @author David A. Velasco - */ -public class BearerCredentials implements Credentials { - - - private String mAccessToken; - - - /** - * The constructor with the bearer token - * - * @param token The bearer token - */ - public BearerCredentials(String token) { - /*if (token == null) { - throw new IllegalArgumentException("Bearer token may not be null"); - }*/ - mAccessToken = (token == null) ? "" : token; - } - - - /** - * Returns the access token - * - * @return The access token - */ - public String getAccessToken() { - return mAccessToken; - } - - - /** - * Get this object string. - * - * @return The access token - */ - public String toString() { - return mAccessToken; - } - - /** - * Does a hash of the access token. - * - * @return The hash code of the access token - */ - public int hashCode() { - int hash = LangUtils.HASH_SEED; - hash = LangUtils.hashCode(hash, mAccessToken); - return hash; - } - - /** - * These credentials are assumed equal if accessToken is the same. - * - * @param o The other object to compare with. - * - * @return 'True' if the object is equivalent. - */ - public boolean equals(Object o) { - if (o == null) return false; - if (this == o) return true; - if (this.getClass().equals(o.getClass())) { - BearerCredentials that = (BearerCredentials) o; - if (LangUtils.equals(mAccessToken, that.mAccessToken)) { - return true; - } - } - return false; - } - -} - diff --git a/src/com/owncloud/android/network/CertificateCombinedException.java b/src/com/owncloud/android/network/CertificateCombinedException.java deleted file mode 100644 index e96d9dc6..00000000 --- a/src/com/owncloud/android/network/CertificateCombinedException.java +++ /dev/null @@ -1,130 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.network; - -import java.security.cert.CertPathValidatorException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateExpiredException; -import java.security.cert.CertificateNotYetValidException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLPeerUnverifiedException; - -/** - * Exception joining all the problems that {@link AdvancedX509TrustManager} can find in - * a certificate chain for a server. - * - * This was initially created as an extension of CertificateException, but some - * implementations of the SSL socket layer in existing devices are REPLACING the CertificateException - * instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)} - * with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it. - * - * Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException - * instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}. - * - * BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it - * in client methods. Be sure to use it only when you know exactly where it will go. - * - * @author David A. Velasco - */ -public class CertificateCombinedException extends RuntimeException { - - /** Generated - to refresh every time the class changes */ - private static final long serialVersionUID = -8875782030758554999L; - - private X509Certificate mServerCert = null; - private String mHostInUrl; - - private CertificateExpiredException mCertificateExpiredException = null; - private CertificateNotYetValidException mCertificateNotYetValidException = null; - private CertPathValidatorException mCertPathValidatorException = null; - private CertificateException mOtherCertificateException = null; - private SSLPeerUnverifiedException mSslPeerUnverifiedException = null; - - public CertificateCombinedException(X509Certificate x509Certificate) { - mServerCert = x509Certificate; - } - - public X509Certificate getServerCertificate() { - return mServerCert; - } - - public String getHostInUrl() { - return mHostInUrl; - } - - public void setHostInUrl(String host) { - mHostInUrl = host; - } - - public CertificateExpiredException getCertificateExpiredException() { - return mCertificateExpiredException; - } - - public void setCertificateExpiredException(CertificateExpiredException c) { - mCertificateExpiredException = c; - } - - public CertificateNotYetValidException getCertificateNotYetValidException() { - return mCertificateNotYetValidException; - } - - public void setCertificateNotYetException(CertificateNotYetValidException c) { - mCertificateNotYetValidException = c; - } - - public CertPathValidatorException getCertPathValidatorException() { - return mCertPathValidatorException; - } - - public void setCertPathValidatorException(CertPathValidatorException c) { - mCertPathValidatorException = c; - } - - public CertificateException getOtherCertificateException() { - return mOtherCertificateException; - } - - public void setOtherCertificateException(CertificateException c) { - mOtherCertificateException = c; - } - - public SSLPeerUnverifiedException getSslPeerUnverifiedException() { - return mSslPeerUnverifiedException ; - } - - public void setSslPeerUnverifiedException(SSLPeerUnverifiedException s) { - mSslPeerUnverifiedException = s; - } - - public boolean isException() { - return (mCertificateExpiredException != null || - mCertificateNotYetValidException != null || - mCertPathValidatorException != null || - mOtherCertificateException != null || - mSslPeerUnverifiedException != null); - } - - public boolean isRecoverable() { - return (mCertificateExpiredException != null || - mCertificateNotYetValidException != null || - mCertPathValidatorException != null || - mSslPeerUnverifiedException != null); - } - -} diff --git a/src/com/owncloud/android/network/OwnCloudClientUtils.java b/src/com/owncloud/android/network/OwnCloudClientUtils.java deleted file mode 100644 index bb299381..00000000 --- a/src/com/owncloud/android/network/OwnCloudClientUtils.java +++ /dev/null @@ -1,285 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ -package com.owncloud.android.network; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; - -import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; -import org.apache.commons.httpclient.protocol.Protocol; -import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier; -import org.apache.http.conn.ssl.X509HostnameVerifier; - -import com.owncloud.android.Log_OC; -import com.owncloud.android.MainApp; -import com.owncloud.android.authentication.AccountAuthenticator; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException; - - -import eu.alefzero.webdav.WebdavClient; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountManagerFuture; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; -import android.app.Activity; -import android.content.Context; -import android.net.Uri; -import android.os.Bundle; - -public class OwnCloudClientUtils { - - final private static String TAG = OwnCloudClientUtils.class.getSimpleName(); - - /** Default timeout for waiting data from the server */ - public static final int DEFAULT_DATA_TIMEOUT = 60000; - - /** Default timeout for establishing a connection */ - public static final int DEFAULT_CONNECTION_TIMEOUT = 60000; - - /** Connection manager for all the WebdavClients */ - private static MultiThreadedHttpConnectionManager mConnManager = null; - - private static Protocol mDefaultHttpsProtocol = null; - - private static AdvancedSslSocketFactory mAdvancedSslSocketFactory = null; - - private static X509HostnameVerifier mHostnameVerifier = null; - - - /** - * Creates a WebdavClient setup for an ownCloud account - * - * Do not call this method from the main thread. - * - * @param account The ownCloud account - * @param appContext Android application context - * @return A WebdavClient object ready to be used - * @throws AuthenticatorException If the authenticator failed to get the authorization token for the account. - * @throws OperationCanceledException If the authenticator operation was cancelled while getting the authorization token for the account. - * @throws IOException If there was some I/O error while getting the authorization token for the account. - * @throws AccountNotFoundException If 'account' is unknown for the AccountManager - */ - public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException { - //Log_OC.d(TAG, "Creating WebdavClient associated to " + account.name); - - Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account)); - AccountManager am = AccountManager.get(appContext); - boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here - boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null; - WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso); - if (isOauth2) { - String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), false); - client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token - - } else if (isSamlSso) { // TODO avoid a call to getUserData here - String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), false); - client.setSsoSessionCookie(accessToken); - - } else { - String username = account.name.substring(0, account.name.lastIndexOf('@')); - //String password = am.getPassword(account); - String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false); - client.setBasicCredentials(username, password); - } - - return client; - } - - - public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException { - Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account)); - AccountManager am = AccountManager.get(appContext); - boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here - boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null; - WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso); - - if (isOauth2) { // TODO avoid a call to getUserData here - AccountManagerFuture future = am.getAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), null, currentActivity, null, null); - Bundle result = future.getResult(); - String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN); - if (accessToken == null) throw new AuthenticatorException("WTF!"); - client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token - - } else if (isSamlSso) { // TODO avoid a call to getUserData here - AccountManagerFuture future = am.getAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), null, currentActivity, null, null); - Bundle result = future.getResult(); - String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN); - if (accessToken == null) throw new AuthenticatorException("WTF!"); - client.setSsoSessionCookie(accessToken); - - } else { - String username = account.name.substring(0, account.name.lastIndexOf('@')); - //String password = am.getPassword(account); - //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false); - AccountManagerFuture future = am.getAuthToken(account, MainApp.getAuthTokenTypePass(), null, currentActivity, null, null); - Bundle result = future.getResult(); - String password = result.getString(AccountManager.KEY_AUTHTOKEN); - client.setBasicCredentials(username, password); - } - - return client; - } - - /** - * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections. - * - * @param uri URL to the ownCloud server - * @param context Android context where the WebdavClient is being created. - * @return A WebdavClient object ready to be used - */ - public static WebdavClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) { - try { - registerAdvancedSslContext(true, context); - } catch (GeneralSecurityException e) { - Log_OC.e(TAG, "Advanced SSL Context could not be loaded. Default SSL management in the system will be used for HTTPS connections", e); - - } catch (IOException e) { - Log_OC.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e); - } - - WebdavClient client = new WebdavClient(getMultiThreadedConnManager()); - - client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); - client.setBaseUri(uri); - client.setFollowRedirects(followRedirects); - - return client; - } - - - /** - * Registers or unregisters the proper components for advanced SSL handling. - * @throws IOException - */ - private static void registerAdvancedSslContext(boolean register, Context context) throws GeneralSecurityException, IOException { - Protocol pr = null; - try { - pr = Protocol.getProtocol("https"); - if (pr != null && mDefaultHttpsProtocol == null) { - mDefaultHttpsProtocol = pr; - } - } catch (IllegalStateException e) { - // nothing to do here; really - } - boolean isRegistered = (pr != null && pr.getSocketFactory() instanceof AdvancedSslSocketFactory); - if (register && !isRegistered) { - Protocol.registerProtocol("https", new Protocol("https", getAdvancedSslSocketFactory(context), 443)); - - } else if (!register && isRegistered) { - if (mDefaultHttpsProtocol != null) { - Protocol.registerProtocol("https", mDefaultHttpsProtocol); - } - } - } - - public static AdvancedSslSocketFactory getAdvancedSslSocketFactory(Context context) throws GeneralSecurityException, IOException { - if (mAdvancedSslSocketFactory == null) { - KeyStore trustStore = getKnownServersStore(context); - AdvancedX509TrustManager trustMgr = new AdvancedX509TrustManager(trustStore); - TrustManager[] tms = new TrustManager[] { trustMgr }; - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, tms, null); - - mHostnameVerifier = new BrowserCompatHostnameVerifier(); - mAdvancedSslSocketFactory = new AdvancedSslSocketFactory(sslContext, trustMgr, mHostnameVerifier); - } - return mAdvancedSslSocketFactory; - } - - - private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks"; - - private static String LOCAL_TRUSTSTORE_PASSWORD = "password"; - - private static KeyStore mKnownServersStore = null; - - /** - * Returns the local store of reliable server certificates, explicitly accepted by the user. - * - * Returns a KeyStore instance with empty content if the local store was never created. - * - * Loads the store from the storage environment if needed. - * - * @param context Android context where the operation is being performed. - * @return KeyStore instance with explicitly-accepted server certificates. - * @throws KeyStoreException When the KeyStore instance could not be created. - * @throws IOException When an existing local trust store could not be loaded. - * @throws NoSuchAlgorithmException When the existing local trust store was saved with an unsupported algorithm. - * @throws CertificateException When an exception occurred while loading the certificates from the local trust store. - */ - private static KeyStore getKnownServersStore(Context context) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { - if (mKnownServersStore == null) { - //mKnownServersStore = KeyStore.getInstance("BKS"); - mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType()); - File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME); - Log_OC.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath()); - if (localTrustStoreFile.exists()) { - InputStream in = new FileInputStream(localTrustStoreFile); - try { - mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); - } finally { - in.close(); - } - } else { - mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); // necessary to initialize an empty KeyStore instance - } - } - return mKnownServersStore; - } - - - public static void addCertToKnownServersStore(Certificate cert, Context context) throws KeyStoreException, NoSuchAlgorithmException, - CertificateException, IOException { - KeyStore knownServers = getKnownServersStore(context); - knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert); - FileOutputStream fos = null; - try { - fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE); - knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); - } finally { - fos.close(); - } - } - - - static private MultiThreadedHttpConnectionManager getMultiThreadedConnManager() { - if (mConnManager == null) { - mConnManager = new MultiThreadedHttpConnectionManager(); - mConnManager.getParams().setDefaultMaxConnectionsPerHost(5); - mConnManager.getParams().setMaxTotalConnections(5); - } - return mConnManager; - } - - -} diff --git a/src/com/owncloud/android/network/ProgressiveDataTransferer.java b/src/com/owncloud/android/network/ProgressiveDataTransferer.java deleted file mode 100644 index c6fa545b..00000000 --- a/src/com/owncloud/android/network/ProgressiveDataTransferer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.network; - -import java.util.Collection; - -import eu.alefzero.webdav.OnDatatransferProgressListener; - -public interface ProgressiveDataTransferer { - - public void addDatatransferProgressListener (OnDatatransferProgressListener listener); - - public void addDatatransferProgressListeners(Collection listeners); - - public void removeDatatransferProgressListener(OnDatatransferProgressListener listener); - -} diff --git a/src/com/owncloud/android/operations/ChunkedUploadFileOperation.java b/src/com/owncloud/android/operations/ChunkedUploadFileOperation.java index 2649d197..fd8a5a6d 100644 --- a/src/com/owncloud/android/operations/ChunkedUploadFileOperation.java +++ b/src/com/owncloud/android/operations/ChunkedUploadFileOperation.java @@ -27,16 +27,16 @@ import java.util.Random; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.PutMethod; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.network.ProgressiveDataTransferer; +import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer; +import com.owncloud.android.oc_framework.network.webdav.ChunkFromFileChannelRequestEntity; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; -import eu.alefzero.webdav.ChunkFromFileChannelRequestEntity; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; public class ChunkedUploadFileOperation extends UploadFileOperation { diff --git a/src/com/owncloud/android/operations/CreateFolderOperation.java b/src/com/owncloud/android/operations/CreateFolderOperation.java index f28ad29e..1cf1f84d 100644 --- a/src/com/owncloud/android/operations/CreateFolderOperation.java +++ b/src/com/owncloud/android/operations/CreateFolderOperation.java @@ -17,30 +17,27 @@ package com.owncloud.android.operations; -import java.io.File; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.client.methods.MkColMethod; - -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.operations.remote.CreateRemoteFolderOperation; +import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; - /** - * Remote operation performing the creation of a new folder in the ownCloud server. + * Access to remote operation performing the creation of a new folder in the ownCloud server. + * Save the new folder in Database * * @author David A. Velasco + * @author masensio */ -public class CreateFolderOperation extends RemoteOperation { +public class CreateFolderOperation extends RemoteOperation implements OnRemoteOperationListener{ private static final String TAG = CreateFolderOperation.class.getSimpleName(); - - private static final int READ_TIMEOUT = 10000; - private static final int CONNECTION_TIMEOUT = 5000; protected String mRemotePath; protected boolean mCreateFullPath; @@ -49,7 +46,6 @@ public class CreateFolderOperation extends RemoteOperation { /** * Constructor * - * @param remotePath Full path to the new directory to create in the remote server. * @param createFullPath 'True' means that all the ancestor folders should be created if don't exist yet. * @param storageManager Reference to the local database corresponding to the account where the file is contained. */ @@ -57,62 +53,55 @@ public class CreateFolderOperation extends RemoteOperation { mRemotePath = remotePath; mCreateFullPath = createFullPath; mStorageManager = storageManager; + } - - - /** - * Performs the operation - * - * @param client Client object to communicate with the remote ownCloud server. - */ + + @Override protected RemoteOperationResult run(WebdavClient client) { - RemoteOperationResult result = null; - MkColMethod mkcol = null; - try { - mkcol = new MkColMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath)); - int status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT); - if (!mkcol.succeeded() && mkcol.getStatusCode() == HttpStatus.SC_CONFLICT && mCreateFullPath) { - result = createParentFolder(getParentPath(), client); - status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT); // second (and last) try - } - if (mkcol.succeeded()) { - // Save new directory in local database - OCFile newDir = new OCFile(mRemotePath); - newDir.setMimetype("DIR"); - long parentId = mStorageManager.getFileByPath(getParentPath()).getFileId(); - newDir.setParentId(parentId); - newDir.setModificationTimestamp(System.currentTimeMillis()); - mStorageManager.saveFile(newDir); - } - result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders()); - Log_OC.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage()); - client.exhaustResponse(mkcol.getResponseBodyAsStream()); - - } catch (Exception e) { - result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e); - - } finally { - if (mkcol != null) - mkcol.releaseConnection(); + CreateRemoteFolderOperation operation = new CreateRemoteFolderOperation(mRemotePath, mCreateFullPath); + RemoteOperationResult result = operation.execute(client); + + if (result.isSuccess()) { + saveFolderInDB(); + } else { + Log_OC.e(TAG, mRemotePath + "hasn't been created"); } + return result; } - - private String getParentPath() { - String parentPath = new File(mRemotePath).getParent(); - parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR; - return parentPath; + @Override + public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { + if (operation instanceof CreateRemoteFolderOperation) { + onCreateRemoteFolderOperationFinish((CreateRemoteFolderOperation)operation, result); + } + } + + + private void onCreateRemoteFolderOperationFinish(CreateRemoteFolderOperation operation, RemoteOperationResult result) { + if (result.isSuccess()) { + saveFolderInDB(); + } else { + Log_OC.e(TAG, mRemotePath + "hasn't been created"); + } } - private RemoteOperationResult createParentFolder(String parentPath, WebdavClient client) { - RemoteOperation operation = new CreateFolderOperation( parentPath, - mCreateFullPath, - mStorageManager ); - return operation.execute(client); + /** + * Save new directory in local database + */ + public void saveFolderInDB() { + OCFile newDir = new OCFile(mRemotePath); + newDir.setMimetype("DIR"); + long parentId = mStorageManager.getFileByPath(FileStorageUtils.getParentPath(mRemotePath)).getFileId(); + newDir.setParentId(parentId); + newDir.setModificationTimestamp(System.currentTimeMillis()); + mStorageManager.saveFile(newDir); + + Log_OC.d(TAG, "Create directory " + mRemotePath + " in Database"); + } + } diff --git a/src/com/owncloud/android/operations/DownloadFileOperation.java b/src/com/owncloud/android/operations/DownloadFileOperation.java index db986f47..eb45d4ba 100644 --- a/src/com/owncloud/android/operations/DownloadFileOperation.java +++ b/src/com/owncloud/android/operations/DownloadFileOperation.java @@ -32,16 +32,16 @@ import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.http.HttpStatus; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.OperationCancelledException; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; - -import eu.alefzero.webdav.OnDatatransferProgressListener; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; import android.accounts.Account; import android.webkit.MimeTypeMap; diff --git a/src/com/owncloud/android/operations/ExistenceCheckOperation.java b/src/com/owncloud/android/operations/ExistenceCheckOperation.java index 24336bd6..d92190c1 100644 --- a/src/com/owncloud/android/operations/ExistenceCheckOperation.java +++ b/src/com/owncloud/android/operations/ExistenceCheckOperation.java @@ -20,11 +20,12 @@ package com.owncloud.android.operations; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.HeadMethod; -import com.owncloud.android.Log_OC; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.utils.Log_OC; - -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; import android.content.Context; import android.net.ConnectivityManager; diff --git a/src/com/owncloud/android/operations/OAuth2GetAccessToken.java b/src/com/owncloud/android/operations/OAuth2GetAccessToken.java index 09a6e1e5..d3926453 100644 --- a/src/com/owncloud/android/operations/OAuth2GetAccessToken.java +++ b/src/com/owncloud/android/operations/OAuth2GetAccessToken.java @@ -8,13 +8,14 @@ import org.apache.commons.httpclient.NameValuePair; import org.json.JSONException; import org.json.JSONObject; -import com.owncloud.android.Log_OC; import com.owncloud.android.authentication.OAuth2Constants; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.WebdavClient; - public class OAuth2GetAccessToken extends RemoteOperation { private static final String TAG = OAuth2GetAccessToken.class.getSimpleName(); diff --git a/src/com/owncloud/android/operations/OnRemoteOperationListener.java b/src/com/owncloud/android/operations/OnRemoteOperationListener.java deleted file mode 100644 index e6a58e73..00000000 --- a/src/com/owncloud/android/operations/OnRemoteOperationListener.java +++ /dev/null @@ -1,25 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.operations; - -public interface OnRemoteOperationListener { - - void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result); - -} diff --git a/src/com/owncloud/android/operations/OperationCancelledException.java b/src/com/owncloud/android/operations/OperationCancelledException.java deleted file mode 100644 index 0b7878ce..00000000 --- a/src/com/owncloud/android/operations/OperationCancelledException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.operations; - -public class OperationCancelledException extends Exception { - - /** - * Generated serial version - to avoid Java warning - */ - private static final long serialVersionUID = -6350981497740424983L; - -} diff --git a/src/com/owncloud/android/operations/OwnCloudServerCheckOperation.java b/src/com/owncloud/android/operations/OwnCloudServerCheckOperation.java index 62342249..82096854 100644 --- a/src/com/owncloud/android/operations/OwnCloudServerCheckOperation.java +++ b/src/com/owncloud/android/operations/OwnCloudServerCheckOperation.java @@ -22,12 +22,13 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.json.JSONException; import org.json.JSONObject; -import com.owncloud.android.Log_OC; import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.utils.OwnCloudVersion; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.utils.OwnCloudVersion; +import com.owncloud.android.utils.Log_OC; - -import eu.alefzero.webdav.WebdavClient; import android.content.Context; import android.net.ConnectivityManager; import android.net.Uri; diff --git a/src/com/owncloud/android/operations/RemoteOperation.java b/src/com/owncloud/android/operations/RemoteOperation.java deleted file mode 100644 index 6e674c46..00000000 --- a/src/com/owncloud/android/operations/RemoteOperation.java +++ /dev/null @@ -1,287 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ -package com.owncloud.android.operations; - -import java.io.IOException; - -import org.apache.commons.httpclient.Credentials; - -import com.owncloud.android.Log_OC; -import com.owncloud.android.MainApp; -import com.owncloud.android.network.BearerCredentials; -import com.owncloud.android.network.OwnCloudClientUtils; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; - - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountsException; -import android.app.Activity; -import android.content.Context; -import android.os.Handler; - -import eu.alefzero.webdav.WebdavClient; - -/** - * Operation which execution involves one or several interactions with an ownCloud server. - * - * Provides methods to execute the operation both synchronously or asynchronously. - * - * @author David A. Velasco - */ -public abstract class RemoteOperation implements Runnable { - - private static final String TAG = RemoteOperation.class.getSimpleName(); - - /** ownCloud account in the remote ownCloud server to operate */ - private Account mAccount = null; - - /** Android Application context */ - private Context mContext = null; - - /** Object to interact with the remote server */ - private WebdavClient mClient = null; - - /** Callback object to notify about the execution of the remote operation */ - private OnRemoteOperationListener mListener = null; - - /** Handler to the thread where mListener methods will be called */ - private Handler mListenerHandler = null; - - /** Activity */ - private Activity mCallerActivity; - - - /** - * Abstract method to implement the operation in derived classes. - */ - protected abstract RemoteOperationResult run(WebdavClient client); - - - /** - * Synchronously executes the remote operation on the received ownCloud account. - * - * Do not call this method from the main thread. - * - * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}. - * - * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation. - * @param context Android context for the component calling the method. - * @return Result of the operation. - */ - public final RemoteOperationResult execute(Account account, Context context) { - if (account == null) - throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account"); - if (context == null) - throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context"); - mAccount = account; - mContext = context.getApplicationContext(); - try { - mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext); - } catch (Exception e) { - Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e); - return new RemoteOperationResult(e); - } - return run(mClient); - } - - - /** - * Synchronously executes the remote operation - * - * Do not call this method from the main thread. - * - * @param client Client object to reach an ownCloud server during the execution of the operation. - * @return Result of the operation. - */ - public final RemoteOperationResult execute(WebdavClient client) { - if (client == null) - throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient"); - mClient = client; - return run(client); - } - - - /** - * Asynchronously executes the remote operation - * - * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}. - * - * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation. - * @param context Android context for the component calling the method. - * @param listener Listener to be notified about the execution of the operation. - * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. - * @return Thread were the remote operation is executed. - */ - public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) { - if (account == null) - throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account"); - if (context == null) - throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context"); - mAccount = account; - mContext = context.getApplicationContext(); - mCallerActivity = callerActivity; - mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below - - mListener = listener; - - mListenerHandler = listenerHandler; - - Thread runnerThread = new Thread(this); - runnerThread.start(); - return runnerThread; - } - - - /** - * Asynchronously executes the remote operation - * - * @param client Client object to reach an ownCloud server during the execution of the operation. - * @param listener Listener to be notified about the execution of the operation. - * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. - * @return Thread were the remote operation is executed. - */ - public final Thread execute(WebdavClient client, OnRemoteOperationListener listener, Handler listenerHandler) { - if (client == null) { - throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient"); - } - mClient = client; - - if (listener == null) { - throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result"); - } - mListener = listener; - - if (listenerHandler == null) { - throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread"); - } - mListenerHandler = listenerHandler; - - Thread runnerThread = new Thread(this); - runnerThread.start(); - return runnerThread; - } - - /** - * Synchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient)} - * - * @param listener Listener to be notified about the execution of the operation. - * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. - * @return Thread were the remote operation is executed. - */ - public final RemoteOperationResult retry() { - return execute(mClient); - } - - /** - * Asynchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)} - * - * @param listener Listener to be notified about the execution of the operation. - * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called. - * @return Thread were the remote operation is executed. - */ - public final Thread retry(OnRemoteOperationListener listener, Handler listenerHandler) { - return execute(mClient, listener, listenerHandler); - } - - - /** - * Asynchronous execution of the operation - * started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}, - * and result posting. - * - * TODO refactor && clean the code; now it's a mess - */ - @Override - public final void run() { - RemoteOperationResult result = null; - boolean repeat = false; - do { - try{ - if (mClient == null) { - if (mAccount != null && mContext != null) { - if (mCallerActivity != null) { - mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext, mCallerActivity); - } else { - mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext); - } - } else { - throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account"); - } - } - - } catch (IOException e) { - Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e)); - result = new RemoteOperationResult(e); - - } catch (AccountsException e) { - Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e); - result = new RemoteOperationResult(e); - } - - if (result == null) - result = run(mClient); - - repeat = false; - if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() && -// (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) { - (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) { - /// possible fail due to lack of authorization in an operation performed in foreground - Credentials cred = mClient.getCredentials(); - String ssoSessionCookie = mClient.getSsoSessionCookie(); - if (cred != null || ssoSessionCookie != null) { - /// confirmed : unauthorized operation - AccountManager am = AccountManager.get(mContext); - boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials); - boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null); - if (bearerAuthorization) { - am.invalidateAuthToken(MainApp.getAccountType(), ((BearerCredentials)cred).getAccessToken()); - } else if (samlBasedSsoAuthorization ) { - am.invalidateAuthToken(MainApp.getAccountType(), ssoSessionCookie); - } else { - am.clearPassword(mAccount); - } - mClient = null; - repeat = true; // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity - result = null; - } - } - } while (repeat); - - final RemoteOperationResult resultToSend = result; - if (mListenerHandler != null && mListener != null) { - mListenerHandler.post(new Runnable() { - @Override - public void run() { - mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend); - } - }); - } - } - - - /** - * Returns the current client instance to access the remote server. - * - * @return Current client instance to access the remote server. - */ - public final WebdavClient getClient() { - return mClient; - } - - -} diff --git a/src/com/owncloud/android/operations/RemoteOperationResult.java b/src/com/owncloud/android/operations/RemoteOperationResult.java deleted file mode 100644 index 8d9b3ee2..00000000 --- a/src/com/owncloud/android/operations/RemoteOperationResult.java +++ /dev/null @@ -1,338 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.operations; - -import java.io.IOException; -import java.io.Serializable; -import java.net.MalformedURLException; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; - -import javax.net.ssl.SSLException; - -import org.apache.commons.httpclient.ConnectTimeoutException; -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.DavException; - -import com.owncloud.android.Log_OC; -import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException; -import com.owncloud.android.network.CertificateCombinedException; - -import android.accounts.Account; -import android.accounts.AccountsException; - - -/** - * The result of a remote operation required to an ownCloud server. - * - * Provides a common classification of remote operation results for all the - * application. - * - * @author David A. Velasco - */ -public class RemoteOperationResult implements Serializable { - - /** Generated - should be refreshed every time the class changes!! */ - private static final long serialVersionUID = -4415103901492836870L; - - - - private static final String TAG = "RemoteOperationResult"; - - public enum ResultCode { - OK, - OK_SSL, - OK_NO_SSL, - UNHANDLED_HTTP_CODE, - UNAUTHORIZED, - FILE_NOT_FOUND, - INSTANCE_NOT_CONFIGURED, - UNKNOWN_ERROR, - WRONG_CONNECTION, - TIMEOUT, - INCORRECT_ADDRESS, - HOST_NOT_AVAILABLE, - NO_NETWORK_CONNECTION, - SSL_ERROR, - SSL_RECOVERABLE_PEER_UNVERIFIED, - BAD_OC_VERSION, - CANCELLED, - INVALID_LOCAL_FILE_NAME, - INVALID_OVERWRITE, - CONFLICT, - OAUTH2_ERROR, - SYNC_CONFLICT, - LOCAL_STORAGE_FULL, - LOCAL_STORAGE_NOT_MOVED, - LOCAL_STORAGE_NOT_COPIED, - OAUTH2_ERROR_ACCESS_DENIED, - QUOTA_EXCEEDED, - ACCOUNT_NOT_FOUND, - ACCOUNT_EXCEPTION, - ACCOUNT_NOT_NEW, - ACCOUNT_NOT_THE_SAME - } - - private boolean mSuccess = false; - private int mHttpCode = -1; - private Exception mException = null; - private ResultCode mCode = ResultCode.UNKNOWN_ERROR; - private String mRedirectedLocation; - - public RemoteOperationResult(ResultCode code) { - mCode = code; - mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL); - } - - private RemoteOperationResult(boolean success, int httpCode) { - mSuccess = success; - mHttpCode = httpCode; - - if (success) { - mCode = ResultCode.OK; - - } else if (httpCode > 0) { - switch (httpCode) { - case HttpStatus.SC_UNAUTHORIZED: - mCode = ResultCode.UNAUTHORIZED; - break; - case HttpStatus.SC_NOT_FOUND: - mCode = ResultCode.FILE_NOT_FOUND; - break; - case HttpStatus.SC_INTERNAL_SERVER_ERROR: - mCode = ResultCode.INSTANCE_NOT_CONFIGURED; - break; - case HttpStatus.SC_CONFLICT: - mCode = ResultCode.CONFLICT; - break; - case HttpStatus.SC_INSUFFICIENT_STORAGE: - mCode = ResultCode.QUOTA_EXCEEDED; - break; - default: - mCode = ResultCode.UNHANDLED_HTTP_CODE; - Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode); - } - } - } - - public RemoteOperationResult(boolean success, int httpCode, Header[] headers) { - this(success, httpCode); - if (headers != null) { - Header current; - for (int i=0; i= HttpStatus.SC_INTERNAL_SERVER_ERROR); - } - - public boolean isException() { - return (mException != null); - } - - public boolean isTemporalRedirection() { - return (mHttpCode == 302 || mHttpCode == 307); - } - - public String getRedirectedLocation() { - return mRedirectedLocation; - } - - public boolean isIdPRedirection() { - return (mRedirectedLocation != null && - (mRedirectedLocation.toUpperCase().contains("SAML") || - mRedirectedLocation.toLowerCase().contains("wayf"))); - } - -} diff --git a/src/com/owncloud/android/operations/RemoveFileOperation.java b/src/com/owncloud/android/operations/RemoveFileOperation.java index 659037b6..34926afb 100644 --- a/src/com/owncloud/android/operations/RemoveFileOperation.java +++ b/src/com/owncloud/android/operations/RemoveFileOperation.java @@ -20,14 +20,15 @@ package com.owncloud.android.operations; import org.apache.commons.httpclient.HttpStatus; import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; - /** * Remote operation performing the removal of a remote file or folder in the ownCloud server. * diff --git a/src/com/owncloud/android/operations/RenameFileOperation.java b/src/com/owncloud/android/operations/RenameFileOperation.java index a2370f3b..2b352bdd 100644 --- a/src/com/owncloud/android/operations/RenameFileOperation.java +++ b/src/com/owncloud/android/operations/RenameFileOperation.java @@ -22,18 +22,20 @@ import java.io.IOException; import org.apache.jackrabbit.webdav.client.methods.DavMethodBase; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; //import org.apache.jackrabbit.webdav.client.methods.MoveMethod; import android.accounts.Account; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; /** * Remote operation performing the rename of a remote file (or folder?) in the ownCloud server. diff --git a/src/com/owncloud/android/operations/SynchronizeFileOperation.java b/src/com/owncloud/android/operations/SynchronizeFileOperation.java index 2e15c2fe..f087aa5e 100644 --- a/src/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -23,22 +23,23 @@ import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.MultiStatus; import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavEntry; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.content.Context; import android.content.Intent; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavEntry; -import eu.alefzero.webdav.WebdavUtils; - public class SynchronizeFileOperation extends RemoteOperation { private String TAG = SynchronizeFileOperation.class.getSimpleName(); diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index 09683d63..780fd42c 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -37,16 +37,18 @@ import android.accounts.Account; import android.content.Context; import android.content.Intent; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavEntry; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.syncadapter.FileSyncService; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavEntry; -import eu.alefzero.webdav.WebdavUtils; /** diff --git a/src/com/owncloud/android/operations/UpdateOCVersionOperation.java b/src/com/owncloud/android/operations/UpdateOCVersionOperation.java index 2e116ac6..7e330604 100644 --- a/src/com/owncloud/android/operations/UpdateOCVersionOperation.java +++ b/src/com/owncloud/android/operations/UpdateOCVersionOperation.java @@ -22,19 +22,20 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.json.JSONException; import org.json.JSONObject; -import com.owncloud.android.Log_OC; -import com.owncloud.android.authentication.AccountAuthenticator; import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; -import com.owncloud.android.utils.OwnCloudVersion; +import com.owncloud.android.oc_framework.accounts.OwnCloudAccount; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.utils.OwnCloudVersion; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; -import eu.alefzero.webdav.WebdavClient; - /** * Remote operation that checks the version of an ownCloud server and stores it locally * @@ -57,7 +58,7 @@ public class UpdateOCVersionOperation extends RemoteOperation { @Override protected RemoteOperationResult run(WebdavClient client) { AccountManager accountMngr = AccountManager.get(mContext); - String statUrl = accountMngr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL); + String statUrl = accountMngr.getUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_BASE_URL); statUrl += AccountUtils.STATUS_PATH; RemoteOperationResult result = null; GetMethod get = null; @@ -75,7 +76,7 @@ public class UpdateOCVersionOperation extends RemoteOperation { if (json != null && json.getString("version") != null) { OwnCloudVersion ocver = new OwnCloudVersion(json.getString("version")); if (ocver.isVersionValid()) { - accountMngr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, ocver.toString()); + accountMngr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_VERSION, ocver.toString()); Log_OC.d(TAG, "Got new OC version " + ocver.toString()); result = new RemoteOperationResult(ResultCode.OK); diff --git a/src/com/owncloud/android/operations/UploadFileOperation.java b/src/com/owncloud/android/operations/UploadFileOperation.java index 936ac01e..80a3463b 100644 --- a/src/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/com/owncloud/android/operations/UploadFileOperation.java @@ -32,23 +32,23 @@ import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.http.HttpStatus; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileUploader; -import com.owncloud.android.network.ProgressiveDataTransferer; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer; +import com.owncloud.android.oc_framework.network.webdav.FileRequestEntity; +import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.OperationCancelledException; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; -import eu.alefzero.webdav.FileRequestEntity; -import eu.alefzero.webdav.OnDatatransferProgressListener; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; - /** * Remote operation performing the upload of a file to an ownCloud server * diff --git a/src/com/owncloud/android/providers/FileContentProvider.java b/src/com/owncloud/android/providers/FileContentProvider.java index 41b643c3..9e7cd3fd 100644 --- a/src/com/owncloud/android/providers/FileContentProvider.java +++ b/src/com/owncloud/android/providers/FileContentProvider.java @@ -21,11 +21,11 @@ package com.owncloud.android.providers; import java.util.ArrayList; import java.util.HashMap; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; +import com.owncloud.android.utils.Log_OC; diff --git a/src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java b/src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java index 6e815579..74d26868 100644 --- a/src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/AbstractOwnCloudSyncAdapter.java @@ -24,10 +24,11 @@ import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException; import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.network.OwnCloudClientUtils; +import com.owncloud.android.oc_framework.accounts.AccountUtils; +import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException; +import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; import android.accounts.Account; @@ -37,7 +38,6 @@ import android.accounts.OperationCanceledException; import android.content.AbstractThreadedSyncAdapter; import android.content.ContentProviderClient; import android.content.Context; -import eu.alefzero.webdav.WebdavClient; /** * Base synchronization adapter for ownCloud designed to be subclassed for different @@ -102,7 +102,7 @@ public abstract class AbstractOwnCloudSyncAdapter extends protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException { AccountUtils.constructFullURLForAccount(getContext(), account); - mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext()); + mClient = OwnCloudClientFactory.createOwnCloudClient(account, getContext()); } protected WebdavClient getClient() { diff --git a/src/com/owncloud/android/syncadapter/ContactSyncAdapter.java b/src/com/owncloud/android/syncadapter/ContactSyncAdapter.java index 662c8a64..9bf5d835 100644 --- a/src/com/owncloud/android/syncadapter/ContactSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/ContactSyncAdapter.java @@ -26,6 +26,7 @@ import org.apache.http.entity.ByteArrayEntity; import com.owncloud.android.authentication.AccountAuthenticator; import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.oc_framework.accounts.OwnCloudAccount; import android.accounts.Account; @@ -92,7 +93,7 @@ public class ContactSyncAdapter extends AbstractOwnCloudSyncAdapter { AccountManager am = getAccountManager(); @SuppressWarnings("deprecation") String uri = am.getUserData(getAccount(), - AccountAuthenticator.KEY_OC_URL).replace( + OwnCloudAccount.Constants.KEY_OC_URL).replace( AccountUtils.WEBDAV_PATH_2_0, AccountUtils.CARDDAV_PATH_2_0); uri += "/addressbooks/" + getAccount().name.substring(0, diff --git a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java index 5b0376c3..f0bc8709 100644 --- a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java @@ -26,17 +26,17 @@ import java.util.Map; import org.apache.jackrabbit.webdav.DavException; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UpdateOCVersionOperation; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; @@ -279,8 +279,9 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { } else { // in failures, the statistics for the global result are updated if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED || - ( result.isIdPRedirection() && - MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) { + ( result.isIdPRedirection() && + getClient().getCredentials() == null )) { + //MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) { mSyncResult.stats.numAuthExceptions++; } else if (result.getException() instanceof DavException) { @@ -371,9 +372,9 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { notification.flags |= Notification.FLAG_AUTO_CANCEL; boolean needsToUpdateCredentials = (mLastFailedResult != null && ( mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED || - // (mLastFailedResult.isTemporalRedirection() && mLastFailedResult.isIdPRedirection() && ( mLastFailedResult.isIdPRedirection() && - MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType())) + getClient().getCredentials() == null ) + //MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType())) ) ); // TODO put something smart in the contentIntent below for all the possible errors diff --git a/src/com/owncloud/android/syncadapter/FileSyncService.java b/src/com/owncloud/android/syncadapter/FileSyncService.java index dafc771b..1e1e11fd 100644 --- a/src/com/owncloud/android/syncadapter/FileSyncService.java +++ b/src/com/owncloud/android/syncadapter/FileSyncService.java @@ -17,7 +17,7 @@ */ package com.owncloud.android.syncadapter; -import com.owncloud.android.Log_OC; +import com.owncloud.android.utils.Log_OC; import android.app.Service; import android.content.Intent; diff --git a/src/com/owncloud/android/ui/activity/AccountSelectActivity.java b/src/com/owncloud/android/ui/activity/AccountSelectActivity.java index 716d6dfd..b1ff8453 100644 --- a/src/com/owncloud/android/ui/activity/AccountSelectActivity.java +++ b/src/com/owncloud/android/ui/activity/AccountSelectActivity.java @@ -47,12 +47,12 @@ import com.actionbarsherlock.app.SherlockListActivity; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.Log_OC; +import com.owncloud.android.authentication.AuthenticatorActivity; +import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.oc_framework.accounts.OwnCloudAccount; +import com.owncloud.android.utils.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.R; -import com.owncloud.android.authentication.AccountAuthenticator; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.authentication.AuthenticatorActivity; public class AccountSelectActivity extends SherlockListActivity implements @@ -211,7 +211,7 @@ public class AccountSelectActivity extends SherlockListActivity implements h.put("VER", "ownCloud version: " + am.getUserData(a, - AccountAuthenticator.KEY_OC_VERSION)); + OwnCloudAccount.Constants.KEY_OC_VERSION)); ll.add(h); } diff --git a/src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java b/src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java index f0895bfb..2d13a66b 100644 --- a/src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java +++ b/src/com/owncloud/android/ui/activity/ConflictsResolveActivity.java @@ -18,13 +18,13 @@ package com.owncloud.android.ui.activity; -import com.owncloud.android.Log_OC; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.ui.dialog.ConflictsResolveDialog; import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision; import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener; +import com.owncloud.android.utils.Log_OC; import android.content.Intent; import android.os.Bundle; diff --git a/src/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java b/src/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java index 056ffd6c..cf69a48c 100644 --- a/src/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java +++ b/src/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java @@ -39,13 +39,13 @@ import android.widget.TextView; import android.widget.Toast; import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.ui.dialog.IndeterminateProgressDialog; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; diff --git a/src/com/owncloud/android/ui/activity/FileActivity.java b/src/com/owncloud/android/ui/activity/FileActivity.java index cddf9b66..59b44433 100644 --- a/src/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/com/owncloud/android/ui/activity/FileActivity.java @@ -29,15 +29,14 @@ import android.os.Bundle; import android.webkit.MimeTypeMap; import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.WebdavUtils; - /** * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s . * diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index 39b7bd8a..685cff24 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -31,7 +31,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.content.IntentFilter.AuthorityEntry; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.SyncRequest; @@ -59,7 +58,6 @@ import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; -import com.owncloud.android.Log_OC; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -70,10 +68,10 @@ import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.operations.CreateFolderOperation; -import com.owncloud.android.operations.OnRemoteOperationListener; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; @@ -90,6 +88,7 @@ import com.owncloud.android.ui.fragment.OCFileListFragment; import com.owncloud.android.ui.preview.PreviewImageActivity; import com.owncloud.android.ui.preview.PreviewMediaFragment; import com.owncloud.android.ui.preview.PreviewVideoActivity; +import com.owncloud.android.utils.Log_OC; /** @@ -1335,6 +1334,9 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa } else { dismissLoadingDialog(); + if (result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME) { + Toast.makeText(FileDisplayActivity.this, R.string.filename_forbidden_characters, Toast.LENGTH_LONG).show(); + } else { try { Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG); msg.show(); @@ -1342,6 +1344,7 @@ OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNa } catch (NotFoundException e) { Log_OC.e(TAG, "Error while trying to show fail message " , e); } + } } } diff --git a/src/com/owncloud/android/ui/activity/InstantUploadActivity.java b/src/com/owncloud/android/ui/activity/InstantUploadActivity.java index a851dd18..0d455bd2 100644 --- a/src/com/owncloud/android/ui/activity/InstantUploadActivity.java +++ b/src/com/owncloud/android/ui/activity/InstantUploadActivity.java @@ -19,13 +19,13 @@ package com.owncloud.android.ui.activity; import java.util.ArrayList; import java.util.List; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.db.DbHandler; import com.owncloud.android.files.InstantUploadBroadcastReceiver; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.app.Activity; diff --git a/src/com/owncloud/android/ui/activity/Preferences.java b/src/com/owncloud/android/ui/activity/Preferences.java index e94942e2..678dad6d 100644 --- a/src/com/owncloud/android/ui/activity/Preferences.java +++ b/src/com/owncloud/android/ui/activity/Preferences.java @@ -36,10 +36,10 @@ import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.SherlockPreferenceActivity; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.Log_OC; -import com.owncloud.android.OwnCloudSession; import com.owncloud.android.R; import com.owncloud.android.db.DbHandler; +import com.owncloud.android.utils.Log_OC; +import com.owncloud.android.utils.OwnCloudSession; /** diff --git a/src/com/owncloud/android/ui/activity/UploadFilesActivity.java b/src/com/owncloud/android/ui/activity/UploadFilesActivity.java index cc1f7de1..7c941e8e 100644 --- a/src/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/src/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -35,13 +35,13 @@ import android.widget.TextView; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar.OnNavigationListener; import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.ui.dialog.IndeterminateProgressDialog; import com.owncloud.android.ui.fragment.ConfirmationDialogFragment; import com.owncloud.android.ui.fragment.LocalFileListFragment; import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; import com.owncloud.android.utils.FileStorageUtils; +import com.owncloud.android.utils.Log_OC; /** diff --git a/src/com/owncloud/android/ui/activity/Uploader.java b/src/com/owncloud/android/ui/activity/Uploader.java new file mode 100644 index 00000000..08e2d215 --- /dev/null +++ b/src/com/owncloud/android/ui/activity/Uploader.java @@ -0,0 +1,432 @@ +/* ownCloud Android client application + * Copyright (C) 2012 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.ui.activity; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; +import java.util.Vector; + +import com.owncloud.android.MainApp; +import com.owncloud.android.R; +import com.owncloud.android.R.id; +import com.owncloud.android.R.layout; +import com.owncloud.android.R.string; +import com.owncloud.android.authentication.AccountAuthenticator; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.utils.Log_OC; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.Dialog; +import android.app.ListActivity; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcelable; +import android.provider.MediaStore.Audio; +import android.provider.MediaStore.Images; +import android.provider.MediaStore.Video; +import android.view.View; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.Button; +import android.widget.EditText; +import android.widget.SimpleAdapter; +import android.widget.Toast; + + +/** + * This can be used to upload things to an ownCloud instance. + * + * @author Bartek Przybylski + * + */ +public class Uploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener { + private static final String TAG = "ownCloudUploader"; + + private Account mAccount; + private AccountManager mAccountManager; + private Stack mParents; + private ArrayList mStreamsToUpload; + private boolean mCreateDir; + private String mUploadPath; + private FileDataStorageManager mStorageManager; + private OCFile mFile; + + private final static int DIALOG_NO_ACCOUNT = 0; + private final static int DIALOG_WAITING = 1; + private final static int DIALOG_NO_STREAM = 2; + private final static int DIALOG_MULTIPLE_ACCOUNT = 3; + + private final static int REQUEST_CODE_SETUP_ACCOUNT = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().requestFeature(Window.FEATURE_NO_TITLE); + mParents = new Stack(); + mParents.add(""); + if (prepareStreamsToUpload()) { + mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE); + Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType()); + if (accounts.length == 0) { + Log_OC.i(TAG, "No ownCloud account is available"); + showDialog(DIALOG_NO_ACCOUNT); + } else if (accounts.length > 1) { + Log_OC.i(TAG, "More then one ownCloud is available"); + showDialog(DIALOG_MULTIPLE_ACCOUNT); + } else { + mAccount = accounts[0]; + mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); + populateDirectoryList(); + } + } else { + showDialog(DIALOG_NO_STREAM); + } + } + + @Override + protected Dialog onCreateDialog(final int id) { + final AlertDialog.Builder builder = new Builder(this); + switch (id) { + case DIALOG_WAITING: + ProgressDialog pDialog = new ProgressDialog(this); + pDialog.setIndeterminate(false); + pDialog.setCancelable(false); + pDialog.setMessage(getResources().getString(R.string.uploader_info_uploading)); + return pDialog; + case DIALOG_NO_ACCOUNT: + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setTitle(R.string.uploader_wrn_no_account_title); + builder.setMessage(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name))); + builder.setCancelable(false); + builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) { + // using string value since in API7 this + // constatn is not defined + // in API7 < this constatant is defined in + // Settings.ADD_ACCOUNT_SETTINGS + // and Settings.EXTRA_AUTHORITIES + Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT); + intent.putExtra("authorities", new String[] { MainApp.getAuthTokenType() }); + startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT); + } else { + // since in API7 there is no direct call for + // account setup, so we need to + // show our own AccountSetupAcricity, get + // desired results and setup + // everything for ourself + Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class); + startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT); + } + } + }); + builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + return builder.create(); + case DIALOG_MULTIPLE_ACCOUNT: + CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(MainApp.getAccountType()).length]; + for (int i = 0; i < ac.length; ++i) { + ac[i] = mAccountManager.getAccountsByType(MainApp.getAccountType())[i].name; + } + builder.setTitle(R.string.common_choose_account); + builder.setItems(ac, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mAccount = mAccountManager.getAccountsByType(MainApp.getAccountType())[which]; + mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); + populateDirectoryList(); + } + }); + builder.setCancelable(true); + builder.setOnCancelListener(new OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + dialog.cancel(); + finish(); + } + }); + return builder.create(); + case DIALOG_NO_STREAM: + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setTitle(R.string.uploader_wrn_no_content_title); + builder.setMessage(R.string.uploader_wrn_no_content_text); + builder.setCancelable(false); + builder.setNegativeButton(R.string.common_cancel, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + return builder.create(); + default: + throw new IllegalArgumentException("Unknown dialog id: " + id); + } + } + + class a implements OnClickListener { + String mPath; + EditText mDirname; + + public a(String path, EditText dirname) { + mPath = path; + mDirname = dirname; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + Uploader.this.mUploadPath = mPath + mDirname.getText().toString(); + Uploader.this.mCreateDir = true; + uploadFiles(); + } + } + + @Override + public void onBackPressed() { + + if (mParents.size() <= 1) { + super.onBackPressed(); + return; + } else { + mParents.pop(); + populateDirectoryList(); + } + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + // click on folder in the list + Log_OC.d(TAG, "on item click"); + Vector tmpfiles = mStorageManager.getFolderContent(mFile); + if (tmpfiles.size() <= 0) return; + // filter on dirtype + Vector files = new Vector(); + for (OCFile f : tmpfiles) + if (f.isFolder()) + files.add(f); + if (files.size() < position) { + throw new IndexOutOfBoundsException("Incorrect item selected"); + } + mParents.push(files.get(position).getFileName()); + populateDirectoryList(); + } + + @Override + public void onClick(View v) { + // click on button + switch (v.getId()) { + case R.id.uploader_choose_folder: + mUploadPath = ""; // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix + for (String p : mParents) + mUploadPath += p + OCFile.PATH_SEPARATOR; + Log_OC.d(TAG, "Uploading file to dir " + mUploadPath); + + uploadFiles(); + + break; + default: + throw new IllegalArgumentException("Wrong element clicked"); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log_OC.i(TAG, "result received. req: " + requestCode + " res: " + resultCode); + if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) { + dismissDialog(DIALOG_NO_ACCOUNT); + if (resultCode == RESULT_CANCELED) { + finish(); + } + Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAuthTokenType()); + if (accounts.length == 0) { + showDialog(DIALOG_NO_ACCOUNT); + } else { + // there is no need for checking for is there more then one + // account at this point + // since account setup can set only one account at time + mAccount = accounts[0]; + populateDirectoryList(); + } + } + } + + private void populateDirectoryList() { + setContentView(R.layout.uploader_layout); + + String full_path = ""; + for (String a : mParents) + full_path += a + "/"; + + Log_OC.d(TAG, "Populating view with content of : " + full_path); + + mFile = mStorageManager.getFileByPath(full_path); + if (mFile != null) { + Vector files = mStorageManager.getFolderContent(mFile); + List> data = new LinkedList>(); + for (OCFile f : files) { + HashMap h = new HashMap(); + if (f.isFolder()) { + h.put("dirname", f.getFileName()); + data.add(h); + } + } + SimpleAdapter sa = new SimpleAdapter(this, + data, + R.layout.uploader_list_item_layout, + new String[] {"dirname"}, + new int[] {R.id.textView1}); + setListAdapter(sa); + Button btn = (Button) findViewById(R.id.uploader_choose_folder); + btn.setOnClickListener(this); + getListView().setOnItemClickListener(this); + } + } + + private boolean prepareStreamsToUpload() { + if (getIntent().getAction().equals(Intent.ACTION_SEND)) { + mStreamsToUpload = new ArrayList(); + mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM)); + } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) { + mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM); + } + return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null); + } + + public void uploadFiles() { + try { + //WebdavClient webdav = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext()); + + ArrayList local = new ArrayList(); + ArrayList remote = new ArrayList(); + + /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed + WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext()); + // create last directory in path if necessary + if (mCreateDir) { + wdc.createDirectory(mUploadPath); + } + */ + + // this checks the mimeType + for (Parcelable mStream : mStreamsToUpload) { + + Uri uri = (Uri) mStream; + if (uri !=null) { + if (uri.getScheme().equals("content")) { + + String mimeType = getContentResolver().getType(uri); + + if (mimeType.contains("image")) { + String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE}; + Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null); + c.moveToFirst(); + int index = c.getColumnIndex(Images.Media.DATA); + String data = c.getString(index); + local.add(data); + remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME))); + + } + else if (mimeType.contains("video")) { + String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE, Video.Media.DATE_MODIFIED }; + Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null); + c.moveToFirst(); + int index = c.getColumnIndex(Video.Media.DATA); + String data = c.getString(index); + local.add(data); + remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME))); + + } + else if (mimeType.contains("audio")) { + String[] CONTENT_PROJECTION = { Audio.Media.DATA, Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE, Audio.Media.SIZE }; + Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null); + c.moveToFirst(); + int index = c.getColumnIndex(Audio.Media.DATA); + String data = c.getString(index); + local.add(data); + remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME))); + + } + else { + String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", ""); + // cut everything whats before mnt. It occured to me that sometimes apps send their name into the URI + if (filePath.contains("mnt")) { + String splitedFilePath[] = filePath.split("/mnt"); + filePath = splitedFilePath[1]; + } + final File file = new File(filePath); + local.add(file.getAbsolutePath()); + remote.add(mUploadPath + file.getName()); + } + + } else if (uri.getScheme().equals("file")) { + String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", ""); + if (filePath.contains("mnt")) { + String splitedFilePath[] = filePath.split("/mnt"); + filePath = splitedFilePath[1]; + } + final File file = new File(filePath); + local.add(file.getAbsolutePath()); + remote.add(mUploadPath + file.getName()); + } + else { + throw new SecurityException(); + } + } + else { + throw new SecurityException(); + } + + Intent intent = new Intent(getApplicationContext(), FileUploader.class); + intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES); + intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()])); + intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote.toArray(new String[remote.size()])); + intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount); + startService(intent); + finish(); + } + + } catch (SecurityException e) { + String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name)); + Toast.makeText(this, message, Toast.LENGTH_LONG).show(); + } + } + +} diff --git a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java index a864f7a5..841a5d04 100644 --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -31,7 +31,6 @@ import android.widget.TextView; import java.util.Vector; -import com.owncloud.android.DisplayUtils; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -39,6 +38,7 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.ui.activity.TransferServiceGetter; +import com.owncloud.android.utils.DisplayUtils; /** diff --git a/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index ff883c56..7cd2c2a4 100644 --- a/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -21,8 +21,8 @@ import java.io.File; import java.util.Arrays; import java.util.Comparator; -import com.owncloud.android.DisplayUtils; import com.owncloud.android.R; +import com.owncloud.android.utils.DisplayUtils; import android.content.Context; diff --git a/src/com/owncloud/android/ui/dialog/EditNameDialog.java b/src/com/owncloud/android/ui/dialog/EditNameDialog.java index 4d6243af..862715fd 100644 --- a/src/com/owncloud/android/ui/dialog/EditNameDialog.java +++ b/src/com/owncloud/android/ui/dialog/EditNameDialog.java @@ -27,10 +27,11 @@ import android.view.View; import android.view.WindowManager.LayoutParams; import android.widget.EditText; import android.widget.TextView; +import android.widget.Toast; import com.actionbarsherlock.app.SherlockDialogFragment; import com.owncloud.android.R; - +import com.owncloud.android.oc_framework.utils.FileUtils; /** @@ -129,6 +130,10 @@ public class EditNameDialog extends SherlockDialogFragment implements DialogInte switch (which) { case AlertDialog.BUTTON_POSITIVE: { mNewFilename = ((TextView)(getDialog().findViewById(R.id.user_input))).getText().toString(); + if (!FileUtils.isValidName(mNewFilename)) { + Toast.makeText(getSherlockActivity(), R.string.filename_forbidden_characters, Toast.LENGTH_LONG).show(); + return; + } mResult = true; } case AlertDialog.BUTTON_NEGATIVE: { // fall through diff --git a/src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java b/src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java index 516fce10..91c607e4 100644 --- a/src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java +++ b/src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java @@ -35,14 +35,13 @@ import android.webkit.WebSettings; import android.webkit.WebView; import com.actionbarsherlock.app.SherlockDialogFragment; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.authentication.SsoWebViewClient; import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener; +import com.owncloud.android.oc_framework.network.webdav.WebdavClient; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.WebdavClient; - /** * Dialog to show the WebView for SAML Authentication * diff --git a/src/com/owncloud/android/ui/dialog/SslValidatorDialog.java b/src/com/owncloud/android/ui/dialog/SslValidatorDialog.java index 1ccc8fa8..a50ecc95 100644 --- a/src/com/owncloud/android/ui/dialog/SslValidatorDialog.java +++ b/src/com/owncloud/android/ui/dialog/SslValidatorDialog.java @@ -29,11 +29,7 @@ import java.util.Map; import javax.security.auth.x500.X500Principal; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; -import com.owncloud.android.network.CertificateCombinedException; -import com.owncloud.android.network.OwnCloudClientUtils; -import com.owncloud.android.operations.RemoteOperationResult; import android.app.Dialog; import android.content.Context; @@ -43,6 +39,10 @@ import android.view.Window; import android.widget.Button; import android.widget.TextView; +import com.owncloud.android.oc_framework.network.CertificateCombinedException; +import com.owncloud.android.oc_framework.network.NetworkUtils; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.utils.Log_OC; /** * Dialog to request the user about a certificate that could not be validated with the certificates store in the system. @@ -343,7 +343,7 @@ public class SslValidatorDialog extends Dialog { private void saveServerCert() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException { if (mException.getServerCertificate() != null) { // TODO make this asynchronously, it can take some time - OwnCloudClientUtils.addCertToKnownServersStore(mException.getServerCertificate(), getContext()); + NetworkUtils.addCertToKnownServersStore(mException.getServerCertificate(), getContext()); } } diff --git a/src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java b/src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java index 869cb266..bb9adbe2 100644 --- a/src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java +++ b/src/com/owncloud/android/ui/fragment/ConfirmationDialogFragment.java @@ -24,7 +24,7 @@ import android.content.DialogInterface; import android.os.Bundle; import com.actionbarsherlock.app.SherlockDialogFragment; -import com.owncloud.android.Log_OC; +import com.owncloud.android.utils.Log_OC; public class ConfirmationDialogFragment extends SherlockDialogFragment { diff --git a/src/com/owncloud/android/ui/fragment/ExtendedListFragment.java b/src/com/owncloud/android/ui/fragment/ExtendedListFragment.java index a57fb095..409c5e61 100644 --- a/src/com/owncloud/android/ui/fragment/ExtendedListFragment.java +++ b/src/com/owncloud/android/ui/fragment/ExtendedListFragment.java @@ -19,9 +19,9 @@ package com.owncloud.android.ui.fragment; import com.actionbarsherlock.app.SherlockFragment; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.ui.ExtendedListView; +import com.owncloud.android.utils.Log_OC; import android.os.Bundle; diff --git a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java index af737662..01ef8d07 100644 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -43,8 +43,6 @@ import android.widget.Toast; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.DisplayUtils; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -52,23 +50,24 @@ import com.owncloud.android.files.services.FileObserverService; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.operations.OnRemoteOperationListener; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener; +import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; -import com.owncloud.android.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.ui.activity.ConflictsResolveActivity; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.dialog.EditNameDialog; import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; import com.owncloud.android.ui.preview.PreviewImageFragment; +import com.owncloud.android.utils.DisplayUtils; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.OnDatatransferProgressListener; - /** * This Fragment is used to display the details about a file. * diff --git a/src/com/owncloud/android/ui/fragment/LocalFileListFragment.java b/src/com/owncloud/android/ui/fragment/LocalFileListFragment.java index 40c03a46..270a8d5c 100644 --- a/src/com/owncloud/android/ui/fragment/LocalFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/LocalFileListFragment.java @@ -19,9 +19,9 @@ package com.owncloud.android.ui.fragment; import java.io.File; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.ui.adapter.LocalFileListAdapter; +import com.owncloud.android.utils.Log_OC; import android.app.Activity; diff --git a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java index a3c6d3be..c75eedb4 100644 --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -21,7 +21,6 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -29,8 +28,8 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.FileHandler; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.operations.OnRemoteOperationListener; -import com.owncloud.android.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener; +import com.owncloud.android.oc_framework.operations.RemoteOperation; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; import com.owncloud.android.operations.SynchronizeFileOperation; @@ -42,6 +41,7 @@ import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.ui.preview.PreviewMediaFragment; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; diff --git a/src/com/owncloud/android/ui/preview/FileDownloadFragment.java b/src/com/owncloud/android/ui/preview/FileDownloadFragment.java index 306df1f7..c18f84be 100644 --- a/src/com/owncloud/android/ui/preview/FileDownloadFragment.java +++ b/src/com/owncloud/android/ui/preview/FileDownloadFragment.java @@ -19,11 +19,11 @@ package com.owncloud.android.ui.preview; import java.lang.ref.WeakReference; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.ui.fragment.FileFragment; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.app.Activity; @@ -37,10 +37,9 @@ import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.TextView; +import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener; -import eu.alefzero.webdav.OnDatatransferProgressListener; - /** * This Fragment is used to monitor the progress of a file downloading. * diff --git a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java index ef48d123..fde70df0 100644 --- a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -35,7 +35,6 @@ import android.view.View.OnTouchListener; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -48,6 +47,7 @@ import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.dialog.LoadingDialog; import com.owncloud.android.ui.fragment.FileFragment; +import com.owncloud.android.utils.Log_OC; /** diff --git a/src/com/owncloud/android/ui/preview/PreviewImageFragment.java b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java index 9ef4db4b..ebc17c61 100644 --- a/src/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -50,18 +50,17 @@ import android.widget.Toast; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.operations.OnRemoteOperationListener; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.ui.fragment.ConfirmationDialogFragment; import com.owncloud.android.ui.fragment.FileFragment; - -import eu.alefzero.webdav.WebdavUtils; +import com.owncloud.android.utils.Log_OC; /** diff --git a/src/com/owncloud/android/ui/preview/PreviewMediaFragment.java b/src/com/owncloud/android/ui/preview/PreviewMediaFragment.java index 815dbbde..0cce76ae 100644 --- a/src/com/owncloud/android/ui/preview/PreviewMediaFragment.java +++ b/src/com/owncloud/android/ui/preview/PreviewMediaFragment.java @@ -52,23 +52,23 @@ import android.widget.VideoView; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.media.MediaControlView; import com.owncloud.android.media.MediaService; import com.owncloud.android.media.MediaServiceBinder; -import com.owncloud.android.operations.OnRemoteOperationListener; -import com.owncloud.android.operations.RemoteOperation; -import com.owncloud.android.operations.RemoteOperationResult; +import com.owncloud.android.oc_framework.network.webdav.WebdavUtils; +import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener; +import com.owncloud.android.oc_framework.operations.RemoteOperation; +import com.owncloud.android.oc_framework.operations.RemoteOperationResult; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.fragment.ConfirmationDialogFragment; import com.owncloud.android.ui.fragment.FileFragment; +import com.owncloud.android.utils.Log_OC; -import eu.alefzero.webdav.WebdavUtils; /** * This fragment shows a preview of a downloaded media file (audio or video). diff --git a/src/com/owncloud/android/ui/preview/PreviewVideoActivity.java b/src/com/owncloud/android/ui/preview/PreviewVideoActivity.java index 7db07b35..59d96555 100644 --- a/src/com/owncloud/android/ui/preview/PreviewVideoActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewVideoActivity.java @@ -17,14 +17,12 @@ package com.owncloud.android.ui.preview; -import com.owncloud.android.Log_OC; import com.owncloud.android.R; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.media.MediaService; import com.owncloud.android.ui.activity.FileActivity; +import com.owncloud.android.utils.Log_OC; import android.accounts.Account; import android.app.AlertDialog; @@ -39,6 +37,9 @@ import android.os.Bundle; import android.widget.MediaController; import android.widget.VideoView; +import com.owncloud.android.oc_framework.accounts.AccountUtils; +import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException; + /** * Activity implementing a basic video player. * diff --git a/src/com/owncloud/android/utils/DisplayUtils.java b/src/com/owncloud/android/utils/DisplayUtils.java new file mode 100644 index 00000000..e4bdd999 --- /dev/null +++ b/src/com/owncloud/android/utils/DisplayUtils.java @@ -0,0 +1,193 @@ +/* ownCloud Android client application + * Copyright (C) 2011 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ + +package com.owncloud.android.utils; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import com.owncloud.android.R; +import com.owncloud.android.R.drawable; + +/** + * A helper class for some string operations. + * + * @author Bartek Przybylski + * @author David A. Velasco + */ +public class DisplayUtils { + + //private static String TAG = DisplayUtils.class.getSimpleName(); + + private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; + + private static HashMap mimeType2HUmanReadable; + static { + mimeType2HUmanReadable = new HashMap(); + // images + mimeType2HUmanReadable.put("image/jpeg", "JPEG image"); + mimeType2HUmanReadable.put("image/jpg", "JPEG image"); + mimeType2HUmanReadable.put("image/png", "PNG image"); + mimeType2HUmanReadable.put("image/bmp", "Bitmap image"); + mimeType2HUmanReadable.put("image/gif", "GIF image"); + mimeType2HUmanReadable.put("image/svg+xml", "JPEG image"); + mimeType2HUmanReadable.put("image/tiff", "TIFF image"); + // music + mimeType2HUmanReadable.put("audio/mpeg", "MP3 music file"); + mimeType2HUmanReadable.put("application/ogg", "OGG music file"); + + } + + private static final String TYPE_APPLICATION = "application"; + private static final String TYPE_AUDIO = "audio"; + private static final String TYPE_IMAGE = "image"; + private static final String TYPE_TXT = "text"; + private static final String TYPE_VIDEO = "video"; + + private static final String SUBTYPE_PDF = "pdf"; + private static final String[] SUBTYPES_DOCUMENT = { "msword", "mspowerpoint", "msexcel", + "vnd.oasis.opendocument.presentation", + "vnd.oasis.opendocument.spreadsheet", + "vnd.oasis.opendocument.text" + }; + private static Set SUBTYPES_DOCUMENT_SET = new HashSet(Arrays.asList(SUBTYPES_DOCUMENT)); + private static final String[] SUBTYPES_COMPRESSED = {"x-tar", "x-gzip", "zip"}; + private static final Set SUBTYPES_COMPRESSED_SET = new HashSet(Arrays.asList(SUBTYPES_COMPRESSED)); + + /** + * Converts the file size in bytes to human readable output. + * + * @param bytes Input file size + * @return Like something readable like "12 MB" + */ + public static String bytesToHumanReadable(long bytes) { + double result = bytes; + int attachedsuff = 0; + while (result > 1024 && attachedsuff < sizeSuffixes.length) { + result /= 1024.; + attachedsuff++; + } + result = ((int) (result * 100)) / 100.; + return result + " " + sizeSuffixes[attachedsuff]; + } + + /** + * Removes special HTML entities from a string + * + * @param s Input string + * @return A cleaned version of the string + */ + public static String HtmlDecode(String s) { + /* + * TODO: Perhaps we should use something more proven like: + * http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#unescapeHtml%28java.lang.String%29 + */ + + String ret = ""; + for (int i = 0; i < s.length(); ++i) { + if (s.charAt(i) == '%') { + ret += (char) Integer.parseInt(s.substring(i + 1, i + 3), 16); + i += 2; + } else { + ret += s.charAt(i); + } + } + return ret; + } + + /** + * Converts MIME types like "image/jpg" to more end user friendly output + * like "JPG image". + * + * @param mimetype MIME type to convert + * @return A human friendly version of the MIME type + */ + public static String convertMIMEtoPrettyPrint(String mimetype) { + if (mimeType2HUmanReadable.containsKey(mimetype)) { + return mimeType2HUmanReadable.get(mimetype); + } + if (mimetype.split("/").length >= 2) + return mimetype.split("/")[1].toUpperCase() + " file"; + return "Unknown type"; + } + + + /** + * Returns the resource identifier of an image resource to use as icon associated to a + * known MIME type. + * + * @param mimetype MIME type string. + * @return Resource identifier of an image resource. + */ + public static int getResourceId(String mimetype) { + + if (mimetype == null || "DIR".equals(mimetype)) { + return R.drawable.ic_menu_archive; + + } else { + String [] parts = mimetype.split("/"); + String type = parts[0]; + String subtype = (parts.length > 1) ? parts[1] : ""; + + if(TYPE_TXT.equals(type)) { + return R.drawable.file_doc; + + } else if(TYPE_IMAGE.equals(type)) { + return R.drawable.file_image; + + } else if(TYPE_VIDEO.equals(type)) { + return R.drawable.file_movie; + + } else if(TYPE_AUDIO.equals(type)) { + return R.drawable.file_sound; + + } else if(TYPE_APPLICATION.equals(type)) { + + if (SUBTYPE_PDF.equals(subtype)) { + return R.drawable.file_pdf; + + } else if (SUBTYPES_DOCUMENT_SET.contains(subtype)) { + return R.drawable.file_doc; + + } else if (SUBTYPES_COMPRESSED_SET.contains(subtype)) { + return R.drawable.file_zip; + } + + } + // problems: RAR, RTF, 3GP are send as application/octet-stream from the server ; extension in the filename should be explicitly reviewed + } + + // default icon + return R.drawable.file; + } + + + + /** + * Converts Unix time to human readable format + * @param miliseconds that have passed since 01/01/1970 + * @return The human readable time for the users locale + */ + public static String unixTimeToHumanReadable(long milliseconds) { + Date date = new Date(milliseconds); + return date.toLocaleString(); + } +} diff --git a/src/com/owncloud/android/utils/FileStorageUtils.java b/src/com/owncloud/android/utils/FileStorageUtils.java index 0c7e9911..25206443 100644 --- a/src/com/owncloud/android/utils/FileStorageUtils.java +++ b/src/com/owncloud/android/utils/FileStorageUtils.java @@ -76,5 +76,11 @@ public class FileStorageUtils { String value = uploadPath + OCFile.PATH_SEPARATOR + (fileName == null ? "" : fileName); return value; } + + public static String getParentPath(String remotePath) { + String parentPath = new File(remotePath).getParent(); + parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR; + return parentPath; + } } \ No newline at end of file diff --git a/src/com/owncloud/android/utils/Log_OC.java b/src/com/owncloud/android/utils/Log_OC.java new file mode 100644 index 00000000..098625ad --- /dev/null +++ b/src/com/owncloud/android/utils/Log_OC.java @@ -0,0 +1,129 @@ +package com.owncloud.android.utils; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import com.owncloud.android.MainApp; + +import android.util.Log; + + + +public class Log_OC { + + + private static boolean isEnabled = false; + private static File logFile; + private static File folder; + private static BufferedWriter buf; + + public static void i(String TAG, String message){ + // Printing the message to LogCat console + int a = Log.i(TAG, message); + // Write the log message to the file + appendLog(TAG+" : "+message); + } + + public static void d(String TAG, String message){ + Log.d(TAG, message); + appendLog(TAG + " : " + message); + } + public static void d(String TAG, String message, Exception e) { + Log.d(TAG, message, e); + appendLog(TAG + " : " + message + " Exception : "+ e.getStackTrace()); + } + public static void e(String TAG, String message){ + Log.e(TAG, message); + appendLog(TAG + " : " + message); + } + + public static void e(String TAG, String message, Throwable e) { + Log.e(TAG, message, e); + appendLog(TAG+" : " + message +" Exception : " + e.getStackTrace()); + } + + public static void v(String TAG, String message){ + Log.v(TAG, message); + appendLog(TAG+" : "+ message); + } + + public static void w(String TAG, String message) { + Log.w(TAG,message); + appendLog(TAG+" : "+ message); + } + + public static void wtf(String TAG, String message) { + Log.wtf(TAG,message); + appendLog(TAG+" : "+ message); + } + + public static void startLogging(String logPath) { + folder = new File(logPath); + logFile = new File(folder + File.separator + "log.txt"); + + if (!folder.exists()) { + folder.mkdirs(); + } + if (logFile.exists()) { + logFile.delete(); + } + try { + logFile.createNewFile(); + buf = new BufferedWriter(new FileWriter(logFile, true)); + isEnabled = true; + appendPhoneInfo(); + }catch (IOException e){ + e.printStackTrace(); + } + } + + public static void stopLogging() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()); + String currentDateandTime = sdf.format(new Date()); + if (logFile != null) { + logFile.renameTo(new File(folder + File.separator + MainApp.getLogName() + currentDateandTime+".log")); + + isEnabled = false; + try { + buf.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + } + + private static void appendPhoneInfo() { + appendLog("Model : " + android.os.Build.MODEL); + appendLog("Brand : " + android.os.Build.BRAND); + appendLog("Product : " + android.os.Build.PRODUCT); + appendLog("Device : " + android.os.Build.DEVICE); + appendLog("Version-Codename : " + android.os.Build.VERSION.CODENAME); + appendLog("Version-Release : " + android.os.Build.VERSION.RELEASE); + } + + private static void appendLog(String text) { + if (isEnabled) { + try { + buf.append(text); + buf.newLine(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} + + + + + + + + +} diff --git a/src/com/owncloud/android/utils/OwnCloudSession.java b/src/com/owncloud/android/utils/OwnCloudSession.java new file mode 100644 index 00000000..13ead88b --- /dev/null +++ b/src/com/owncloud/android/utils/OwnCloudSession.java @@ -0,0 +1,56 @@ +/* ownCloud Android client application + * Copyright (C) 2011 Bartek Przybylski + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 . + * + */ +package com.owncloud.android.utils; + +/** + * Represents a session to an ownCloud instance + * + * @author Bartek Przybylski + * + */ +public class OwnCloudSession { + private String mSessionName; + private String mSessionUrl; + private int mEntryId; + + public OwnCloudSession(String name, String url, int entryId) { + mSessionName = name; + mSessionUrl = url; + mEntryId = entryId; + } + + public void setName(String name) { + mSessionName = name; + } + + public String getName() { + return mSessionName; + } + + public void setUrl(String url) { + mSessionUrl = url; + } + + public String getUrl() { + return mSessionUrl; + } + + public int getEntryId() { + return mEntryId; + } +} diff --git a/src/com/owncloud/android/utils/OwnCloudVersion.java b/src/com/owncloud/android/utils/OwnCloudVersion.java deleted file mode 100644 index 630e7acd..00000000 --- a/src/com/owncloud/android/utils/OwnCloudVersion.java +++ /dev/null @@ -1,85 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package com.owncloud.android.utils; - -public class OwnCloudVersion implements Comparable { - public static final OwnCloudVersion owncloud_v1 = new OwnCloudVersion( - 0x010000); - public static final OwnCloudVersion owncloud_v2 = new OwnCloudVersion( - 0x020000); - public static final OwnCloudVersion owncloud_v3 = new OwnCloudVersion( - 0x030000); - public static final OwnCloudVersion owncloud_v4 = new OwnCloudVersion( - 0x040000); - public static final OwnCloudVersion owncloud_v4_5 = new OwnCloudVersion( - 0x040500); - - // format is in version - // 0xAABBCC - // for version AA.BB.CC - // ie version 2.0.3 will be stored as 0x030003 - private int mVersion; - private boolean mIsValid; - - public OwnCloudVersion(int version) { - mVersion = version; - mIsValid = true; - } - - public OwnCloudVersion(String version) { - mVersion = 0; - mIsValid = false; - parseVersionString(version); - } - - public String toString() { - return ((mVersion >> 16) % 256) + "." + ((mVersion >> 8) % 256) + "." - + ((mVersion) % 256); - } - - public boolean isVersionValid() { - return mIsValid; - } - - @Override - public int compareTo(OwnCloudVersion another) { - return another.mVersion == mVersion ? 0 - : another.mVersion < mVersion ? 1 : -1; - } - - private void parseVersionString(String version) { - try { - String[] nums = version.split("\\."); - if (nums.length > 0) { - mVersion += Integer.parseInt(nums[0]); - } - mVersion = mVersion << 8; - if (nums.length > 1) { - mVersion += Integer.parseInt(nums[1]); - } - mVersion = mVersion << 8; - if (nums.length > 2) { - mVersion += Integer.parseInt(nums[2]); - } - mIsValid = true; - } catch (Exception e) { - mIsValid = false; - } - } -} diff --git a/src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java b/src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java deleted file mode 100644 index a91b64ea..00000000 --- a/src/eu/alefzero/webdav/ChunkFromFileChannelRequestEntity.java +++ /dev/null @@ -1,147 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package eu.alefzero.webdav; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import org.apache.commons.httpclient.methods.RequestEntity; - -import com.owncloud.android.Log_OC; -import com.owncloud.android.network.ProgressiveDataTransferer; - - -import eu.alefzero.webdav.OnDatatransferProgressListener; - - -/** - * A RequestEntity that represents a PIECE of a file. - * - * @author David A. Velasco - */ -public class ChunkFromFileChannelRequestEntity implements RequestEntity, ProgressiveDataTransferer { - - private static final String TAG = ChunkFromFileChannelRequestEntity.class.getSimpleName(); - - //private final File mFile; - private final FileChannel mChannel; - private final String mContentType; - private final long mChunkSize; - private final File mFile; - private long mOffset; - private long mTransferred; - Set mDataTransferListeners = new HashSet(); - private ByteBuffer mBuffer = ByteBuffer.allocate(4096); - - public ChunkFromFileChannelRequestEntity(final FileChannel channel, final String contentType, long chunkSize, final File file) { - super(); - if (channel == null) { - throw new IllegalArgumentException("File may not be null"); - } - if (chunkSize <= 0) { - throw new IllegalArgumentException("Chunk size must be greater than zero"); - } - mChannel = channel; - mContentType = contentType; - mChunkSize = chunkSize; - mFile = file; - mOffset = 0; - mTransferred = 0; - } - - public void setOffset(long offset) { - mOffset = offset; - } - - public long getContentLength() { - try { - return Math.min(mChunkSize, mChannel.size() - mChannel.position()); - } catch (IOException e) { - return mChunkSize; - } - } - - public String getContentType() { - return mContentType; - } - - public boolean isRepeatable() { - return true; - } - - @Override - public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.add(listener); - } - } - - @Override - public void addDatatransferProgressListeners(Collection listeners) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.addAll(listeners); - } - } - - @Override - public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.remove(listener); - } - } - - - public void writeRequest(final OutputStream out) throws IOException { - int readCount = 0; - Iterator it = null; - - try { - mChannel.position(mOffset); - long size = mFile.length(); - if (size == 0) size = -1; - long maxCount = Math.min(mOffset + mChunkSize, mChannel.size()); - while (mChannel.position() < maxCount) { - readCount = mChannel.read(mBuffer); - out.write(mBuffer.array(), 0, readCount); - mBuffer.clear(); - if (mTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks - mTransferred += readCount; - } - synchronized (mDataTransferListeners) { - it = mDataTransferListeners.iterator(); - while (it.hasNext()) { - it.next().onTransferProgress(readCount, mTransferred, size, mFile.getName()); - } - } - } - - } catch (IOException io) { - Log_OC.e(TAG, io.getMessage()); - throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io); - - } - } - -} \ No newline at end of file diff --git a/src/eu/alefzero/webdav/FileRequestEntity.java b/src/eu/alefzero/webdav/FileRequestEntity.java deleted file mode 100644 index 1d525c43..00000000 --- a/src/eu/alefzero/webdav/FileRequestEntity.java +++ /dev/null @@ -1,134 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package eu.alefzero.webdav; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import org.apache.commons.httpclient.methods.RequestEntity; - -import com.owncloud.android.Log_OC; -import com.owncloud.android.network.ProgressiveDataTransferer; - - -import eu.alefzero.webdav.OnDatatransferProgressListener; - - -/** - * A RequestEntity that represents a File. - * - */ -public class FileRequestEntity implements RequestEntity, ProgressiveDataTransferer { - - final File mFile; - final String mContentType; - Set mDataTransferListeners = new HashSet(); - - public FileRequestEntity(final File file, final String contentType) { - super(); - this.mFile = file; - this.mContentType = contentType; - if (file == null) { - throw new IllegalArgumentException("File may not be null"); - } - } - - @Override - public long getContentLength() { - return mFile.length(); - } - - @Override - public String getContentType() { - return mContentType; - } - - @Override - public boolean isRepeatable() { - return true; - } - - @Override - public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.add(listener); - } - } - - @Override - public void addDatatransferProgressListeners(Collection listeners) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.addAll(listeners); - } - } - - @Override - public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.remove(listener); - } - } - - - @Override - public void writeRequest(final OutputStream out) throws IOException { - //byte[] tmp = new byte[4096]; - ByteBuffer tmp = ByteBuffer.allocate(4096); - int readResult = 0; - - // TODO(bprzybylski): each mem allocation can throw OutOfMemoryError we need to handle it - // globally in some fashionable manner - RandomAccessFile raf = new RandomAccessFile(mFile, "r"); - FileChannel channel = raf.getChannel(); - Iterator it = null; - long transferred = 0; - long size = mFile.length(); - if (size == 0) size = -1; - try { - while ((readResult = channel.read(tmp)) >= 0) { - out.write(tmp.array(), 0, readResult); - tmp.clear(); - transferred += readResult; - synchronized (mDataTransferListeners) { - it = mDataTransferListeners.iterator(); - while (it.hasNext()) { - it.next().onTransferProgress(readResult, transferred, size, mFile.getName()); - } - } - } - - } catch (IOException io) { - Log_OC.e("FileRequestException", io.getMessage()); - throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io); - - } finally { - channel.close(); - raf.close(); - } - } - -} diff --git a/src/eu/alefzero/webdav/OnDatatransferProgressListener.java b/src/eu/alefzero/webdav/OnDatatransferProgressListener.java deleted file mode 100644 index 5c4783a1..00000000 --- a/src/eu/alefzero/webdav/OnDatatransferProgressListener.java +++ /dev/null @@ -1,24 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package eu.alefzero.webdav; - -public interface OnDatatransferProgressListener { - public void onTransferProgress(long progressRate); - public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName); -} diff --git a/src/eu/alefzero/webdav/WebdavClient.java b/src/eu/alefzero/webdav/WebdavClient.java deleted file mode 100644 index 51209280..00000000 --- a/src/eu/alefzero/webdav/WebdavClient.java +++ /dev/null @@ -1,257 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2011 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package eu.alefzero.webdav; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.httpclient.Credentials; -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpConnectionManager; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.HttpMethodBase; -import org.apache.commons.httpclient.HttpVersion; -import org.apache.commons.httpclient.URI; -import org.apache.commons.httpclient.UsernamePasswordCredentials; -import org.apache.commons.httpclient.auth.AuthPolicy; -import org.apache.commons.httpclient.auth.AuthScope; -import org.apache.commons.httpclient.cookie.CookiePolicy; -import org.apache.commons.httpclient.methods.HeadMethod; -import org.apache.commons.httpclient.params.HttpMethodParams; -import org.apache.http.HttpStatus; -import org.apache.http.params.CoreProtocolPNames; - -import com.owncloud.android.Log_OC; -import com.owncloud.android.MainApp; -import com.owncloud.android.network.BearerAuthScheme; -import com.owncloud.android.network.BearerCredentials; - - - -import android.net.Uri; - -public class WebdavClient extends HttpClient { - private static final int MAX_REDIRECTIONS_COUNT = 3; - - private Uri mUri; - private Credentials mCredentials; - private boolean mFollowRedirects; - private String mSsoSessionCookie; - private String mAuthTokenType; - final private static String TAG = "WebdavClient"; - public static final String USER_AGENT = "Android-ownCloud"; - - static private byte[] sExhaustBuffer = new byte[1024]; - - /** - * Constructor - */ - public WebdavClient(HttpConnectionManager connectionMgr) { - super(connectionMgr); - Log_OC.d(TAG, "Creating WebdavClient"); - getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT); - getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1); - mFollowRedirects = true; - mSsoSessionCookie = null; - mAuthTokenType = MainApp.getAuthTokenTypePass(); - } - - public void setBearerCredentials(String accessToken) { - AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class); - - List authPrefs = new ArrayList(1); - authPrefs.add(BearerAuthScheme.AUTH_POLICY); - getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); - - mCredentials = new BearerCredentials(accessToken); - getState().setCredentials(AuthScope.ANY, mCredentials); - mSsoSessionCookie = null; - mAuthTokenType = MainApp.getAuthTokenTypeAccessToken(); - } - - public void setBasicCredentials(String username, String password) { - List authPrefs = new ArrayList(1); - authPrefs.add(AuthPolicy.BASIC); - getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); - - getParams().setAuthenticationPreemptive(true); - mCredentials = new UsernamePasswordCredentials(username, password); - getState().setCredentials(AuthScope.ANY, mCredentials); - mSsoSessionCookie = null; - mAuthTokenType = MainApp.getAuthTokenTypePass(); - } - - public void setSsoSessionCookie(String accessToken) { - getParams().setAuthenticationPreemptive(false); - getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES); - mSsoSessionCookie = accessToken; - mCredentials = null; - mAuthTokenType = MainApp.getAuthTokenTypeSamlSessionCookie(); - } - - - /** - * Check if a file exists in the OC server - * - * TODO replace with ExistenceOperation - * - * @return 'true' if the file exists; 'false' it doesn't exist - * @throws Exception When the existence could not be determined - */ - public boolean existsFile(String path) throws IOException, HttpException { - HeadMethod head = new HeadMethod(mUri.toString() + WebdavUtils.encodePath(path)); - try { - int status = executeMethod(head); - Log_OC.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":"")); - exhaustResponse(head.getResponseBodyAsStream()); - return (status == HttpStatus.SC_OK); - - } finally { - head.releaseConnection(); // let the connection available for other methods - } - } - - /** - * Requests the received method with the received timeout (milliseconds). - * - * Executes the method through the inherited HttpClient.executedMethod(method). - * - * Sets the socket and connection timeouts only for the method received. - * - * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default' - * - * @param method HTTP method request. - * @param readTimeout Timeout to set for data reception - * @param conntionTimout Timeout to set for connection establishment - */ - public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException { - int oldSoTimeout = getParams().getSoTimeout(); - int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout(); - try { - if (readTimeout >= 0) { - method.getParams().setSoTimeout(readTimeout); // this should be enough... - getParams().setSoTimeout(readTimeout); // ... but this looks like necessary for HTTPS - } - if (connectionTimeout >= 0) { - getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout); - } - return executeMethod(method); - } finally { - getParams().setSoTimeout(oldSoTimeout); - getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout); - } - } - - - @Override - public int executeMethod(HttpMethod method) throws IOException, HttpException { - boolean customRedirectionNeeded = false; - try { - method.setFollowRedirects(mFollowRedirects); - } catch (Exception e) { - //if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used if needed"); - customRedirectionNeeded = mFollowRedirects; - } - if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) { - method.setRequestHeader("Cookie", mSsoSessionCookie); - } - int status = super.executeMethod(method); - int redirectionsCount = 0; - while (customRedirectionNeeded && - redirectionsCount < MAX_REDIRECTIONS_COUNT && - ( status == HttpStatus.SC_MOVED_PERMANENTLY || - status == HttpStatus.SC_MOVED_TEMPORARILY || - status == HttpStatus.SC_TEMPORARY_REDIRECT) - ) { - - Header location = method.getResponseHeader("Location"); - if (location != null) { - Log_OC.d(TAG, "Location to redirect: " + location.getValue()); - method.setURI(new URI(location.getValue(), true)); - status = super.executeMethod(method); - redirectionsCount++; - - } else { - Log_OC.d(TAG, "No location to redirect!"); - status = HttpStatus.SC_NOT_FOUND; - } - } - - return status; - } - - - /** - * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation. - * - * @param responseBodyAsStream InputStream with the HTTP response to exhaust. - */ - public void exhaustResponse(InputStream responseBodyAsStream) { - if (responseBodyAsStream != null) { - try { - while (responseBodyAsStream.read(sExhaustBuffer) >= 0); - responseBodyAsStream.close(); - - } catch (IOException io) { - Log_OC.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io); - } - } - } - - /** - * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client. - */ - public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) { - getParams().setSoTimeout(defaultDataTimeout); - getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout); - } - - /** - * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs - * @param uri - */ - public void setBaseUri(Uri uri) { - mUri = uri; - } - - public Uri getBaseUri() { - return mUri; - } - - public final Credentials getCredentials() { - return mCredentials; - } - - public final String getSsoSessionCookie() { - return mSsoSessionCookie; - } - - public void setFollowRedirects(boolean followRedirects) { - mFollowRedirects = followRedirects; - } - - public String getAuthTokenType() { - return mAuthTokenType; - } - -} diff --git a/src/eu/alefzero/webdav/WebdavEntry.java b/src/eu/alefzero/webdav/WebdavEntry.java deleted file mode 100644 index 1c6ba695..00000000 --- a/src/eu/alefzero/webdav/WebdavEntry.java +++ /dev/null @@ -1,150 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 ownCloud - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ -package eu.alefzero.webdav; - -import java.util.Date; - -import org.apache.jackrabbit.webdav.MultiStatusResponse; -import org.apache.jackrabbit.webdav.property.DavProperty; -import org.apache.jackrabbit.webdav.property.DavPropertyName; -import org.apache.jackrabbit.webdav.property.DavPropertySet; - -import com.owncloud.android.Log_OC; - - -import android.net.Uri; - -public class WebdavEntry { - private String mName, mPath, mUri, mContentType, mEtag; - private long mContentLength, mCreateTimestamp, mModifiedTimestamp; - - public WebdavEntry(MultiStatusResponse ms, String splitElement) { - resetData(); - if (ms.getStatus().length != 0) { - mUri = ms.getHref(); - - mPath = mUri.split(splitElement, 2)[1]; - - int status = ms.getStatus()[0].getStatusCode(); - DavPropertySet propSet = ms.getProperties(status); - @SuppressWarnings("rawtypes") - DavProperty prop = propSet.get(DavPropertyName.DISPLAYNAME); - if (prop != null) { - mName = (String) prop.getName().toString(); - mName = mName.substring(1, mName.length()-1); - } - else { - String[] tmp = mPath.split("/"); - if (tmp.length > 0) - mName = tmp[tmp.length - 1]; - } - - // use unknown mimetype as default behavior - mContentType = "application/octet-stream"; - prop = propSet.get(DavPropertyName.GETCONTENTTYPE); - if (prop != null) { - mContentType = (String) prop.getValue(); - // dvelasco: some builds of ownCloud server 4.0.x added a trailing ';' to the MIME type ; if looks fixed, but let's be cautious - if (mContentType.indexOf(";") >= 0) { - mContentType = mContentType.substring(0, mContentType.indexOf(";")); - } - } - - // check if it's a folder in the standard way: see RFC2518 12.2 . RFC4918 14.3 - prop = propSet.get(DavPropertyName.RESOURCETYPE); - if (prop!= null) { - Object value = prop.getValue(); - if (value != null) { - mContentType = "DIR"; // a specific attribute would be better, but this is enough; unless while we have no reason to distinguish MIME types for folders - } - } - - prop = propSet.get(DavPropertyName.GETCONTENTLENGTH); - if (prop != null) - mContentLength = Long.parseLong((String) prop.getValue()); - - prop = propSet.get(DavPropertyName.GETLASTMODIFIED); - if (prop != null) { - Date d = WebdavUtils - .parseResponseDate((String) prop.getValue()); - mModifiedTimestamp = (d != null) ? d.getTime() : 0; - } - - prop = propSet.get(DavPropertyName.CREATIONDATE); - if (prop != null) { - Date d = WebdavUtils - .parseResponseDate((String) prop.getValue()); - mCreateTimestamp = (d != null) ? d.getTime() : 0; - } - - prop = propSet.get(DavPropertyName.GETETAG); - if (prop != null) { - mEtag = (String) prop.getValue(); - mEtag = mEtag.substring(1, mEtag.length()-1); - } - - } else { - Log_OC.e("WebdavEntry", - "General fuckup, no status for webdav response"); - } - } - - public String path() { - return mPath; - } - - public String decodedPath() { - return Uri.decode(mPath); - } - - public String name() { - return mName; - } - - public boolean isDirectory() { - return mContentType.equals("DIR"); - } - - public String contentType() { - return mContentType; - } - - public String uri() { - return mUri; - } - - public long contentLength() { - return mContentLength; - } - - public long createTimestamp() { - return mCreateTimestamp; - } - - public long modifiedTimestamp() { - return mModifiedTimestamp; - } - - public String etag() { - return mEtag; - } - - private void resetData() { - mName = mUri = mContentType = null; - mContentLength = mCreateTimestamp = mModifiedTimestamp = 0; - } -} diff --git a/src/eu/alefzero/webdav/WebdavUtils.java b/src/eu/alefzero/webdav/WebdavUtils.java deleted file mode 100644 index 132a1f92..00000000 --- a/src/eu/alefzero/webdav/WebdavUtils.java +++ /dev/null @@ -1,76 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * 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 . - * - */ - -package eu.alefzero.webdav; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -import android.net.Uri; - -public class WebdavUtils { - public static final SimpleDateFormat DISPLAY_DATE_FORMAT = new SimpleDateFormat( - "dd.MM.yyyy hh:mm"); - private static final SimpleDateFormat DATETIME_FORMATS[] = { - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US), - new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US), - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US), - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US), - new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US), - new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), - new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) }; - - public static String prepareXmlForPropFind() { - String ret = ""; - return ret; - } - - public static String prepareXmlForPatch() { - return ""; - } - - public static Date parseResponseDate(String date) { - Date returnDate = null; - for (int i = 0; i < DATETIME_FORMATS.length; ++i) { - try { - returnDate = DATETIME_FORMATS[i].parse(date); - return returnDate; - } catch (ParseException e) { - } - } - return null; - } - - /** - * Encodes a path according to URI RFC 2396. - * - * If the received path doesn't start with "/", the method adds it. - * - * @param remoteFilePath Path - * @return Encoded path according to RFC 2396, always starting with "/" - */ - public static String encodePath(String remoteFilePath) { - String encodedPath = Uri.encode(remoteFilePath, "/"); - if (!encodedPath.startsWith("/")) - encodedPath = "/" + encodedPath; - return encodedPath; - } - -} diff --git a/tests/.classpath b/tests/.classpath index 66527241..9b141f6f 100644 --- a/tests/.classpath +++ b/tests/.classpath @@ -1,10 +1,10 @@ + + + - - - diff --git a/tests/ant.properties b/tests/ant.properties new file mode 100644 index 00000000..16244024 --- /dev/null +++ b/tests/ant.properties @@ -0,0 +1,18 @@ +# This file is used to override default values used by the Ant build system. +# +# This file must be checked into Version Control Systems, as it is +# integral to the build system of your project. + +# This file is only used by the Ant script. + +# You can use this to override default values such as +# 'source.dir' for the location of your java source folder and +# 'out.dir' for the location of your output folder. + +# You can also use it define how the release builds are signed by declaring +# the following properties: +# 'key.store' for the location of your keystore and +# 'key.alias' for the name of the key to use. +# The password will be asked during the build when you use the 'release' target. + +tested.project.dir=.. diff --git a/tests/build.xml b/tests/build.xml new file mode 100644 index 00000000..3e821961 --- /dev/null +++ b/tests/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/project.properties b/tests/project.properties index a3ee5ab6..4ab12569 100644 --- a/tests/project.properties +++ b/tests/project.properties @@ -11,4 +11,4 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-17 +target=android-19 diff --git a/tests/src/com/owncloud/android/test/AccountUtilsTest.java b/tests/src/com/owncloud/android/test/AccountUtilsTest.java index 529776b7..1524d3b2 100644 --- a/tests/src/com/owncloud/android/test/AccountUtilsTest.java +++ b/tests/src/com/owncloud/android/test/AccountUtilsTest.java @@ -20,8 +20,8 @@ package com.owncloud.android.test; import android.test.AndroidTestCase; -import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.utils.OwnCloudVersion; +import com.owncloud.android.oc_framework.accounts.AccountUtils; +import com.owncloud.android.oc_framework.utils.OwnCloudVersion; public class AccountUtilsTest extends AndroidTestCase {