diff --git a/.travis.yml b/.travis.yml index 2920d050..3619bd9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,16 @@ -language: java +language: android +android: + components: + - build-tools-20.0.0 + - android-19 + - android-17 + - android-14 + - extra-android-support + licenses: + - 'android-sdk-license-5be876d5' + - 'android-sdk-license-598b93a6' jdk: oraclejdk7 before_install: -- sudo apt-get update -qq -- sudo apt-get install -qq libstdc++6:i386 lib32z1 expect -- export LICENSES="android-sdk-license-5be876d5|android-sdk-license-598b93a6" -- curl -3L https://raw.github.com/embarkmobile/android-sdk-installer/version-2/android-sdk-installer - | bash /dev/stdin --install=$COMPONENTS --accept=$LICENSES -- source ~/.android-sdk-installer/env - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI -c 20M - emulator -avd test -no-skin -no-audio -no-window & - rm pom.xml @@ -25,4 +29,4 @@ env: - secure: aF4U20Xlu/rfrbxCmoJAiGh1doYTAZ10UEDmajuinT+ZGSJLivuqD7DDY/00sI6IXWg+J1vL+7jJm4JSYusHPg38UHZ4q92k6RmZycW2ATUzZnGT54O5FRnY67MfVwgVpIMK9UOL/6NEciBHEjlIOL0wbKQiJB++1YtBZOQLGL4= - secure: N+ECSwNg8v2GsAFJ2y/tCiffauHDpN76zuFI2pDqf0fjmCtJZHu4BH5ArXBHjyHKmgn20a/8eZXcwJaH1HsJ80bo7vDJ2miShjGIQ90hPcdmUiB2XVJcew4f04CtvMDH5o7DRt4ykWArlbPL2rhVag0jotlSidolHBwRFnbDhDY= matrix: - - COMPONENTS=build-tools-20.0.0,android-19,sys-img-armeabi-v7a-android-19 ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a + - ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a diff --git a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java index 8e24c15e..06210073 100644 --- a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java +++ b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClient; @@ -79,7 +78,7 @@ public class MainActivity extends Activity implements OnRemoteOperationListener, mHandler = new Handler(); - Uri serverUri = Uri.parse(getString(R.string.server_base_url) + AccountUtils.WEBDAV_PATH_4_0); + Uri serverUri = Uri.parse(getString(R.string.server_base_url)); mClient = OwnCloudClientFactory.createOwnCloudClient(serverUri, this, true); mClient.setCredentials( OwnCloudCredentialsFactory.newBasicCredentials( diff --git a/src/com/owncloud/android/lib/common/OwnCloudClient.java b/src/com/owncloud/android/lib/common/OwnCloudClient.java index 7b48bbc0..c4c4ee38 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/src/com/owncloud/android/lib/common/OwnCloudClient.java @@ -44,11 +44,11 @@ import org.apache.http.HttpStatus; import org.apache.http.params.CoreProtocolPNames; import android.net.Uri; -import android.util.Log; import com.owncloud.android.lib.common.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.network.WebdavUtils; +import com.owncloud.android.lib.common.utils.Log_OC; public class OwnCloudClient extends HttpClient { @@ -79,7 +79,7 @@ public class OwnCloudClient extends HttpClient { mBaseUri = baseUri; mInstanceNumber = sIntanceCounter++; - Log.d(TAG + " #" + mInstanceNumber, "Creating OwnCloudClient"); + Log_OC.d(TAG + " #" + mInstanceNumber, "Creating OwnCloudClient"); getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT); getParams().setParameter( @@ -125,7 +125,7 @@ public class OwnCloudClient extends HttpClient { HeadMethod head = new HeadMethod(getWebdavUri() + WebdavUtils.encodePath(path)); try { int status = executeMethod(head); - Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + + 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); @@ -185,7 +185,7 @@ public class OwnCloudClient extends HttpClient { customRedirectionNeeded = mFollowRedirects; } - Log.d(TAG + " #" + mInstanceNumber, "REQUEST " + + Log_OC.d(TAG + " #" + mInstanceNumber, "REQUEST " + method.getName() + " " + method.getPath()); // logCookiesAtRequest(method.getRequestHeaders(), "before"); @@ -204,7 +204,7 @@ public class OwnCloudClient extends HttpClient { return status; } catch (IOException e) { - Log.d(TAG + " #" + mInstanceNumber, "Exception occured", e); + Log_OC.d(TAG + " #" + mInstanceNumber, "Exception occured", e); throw e; } } @@ -222,14 +222,34 @@ public class OwnCloudClient extends HttpClient { location = method.getResponseHeader("location"); } if (location != null) { - Log.d(TAG + " #" + mInstanceNumber, + Log_OC.d(TAG + " #" + mInstanceNumber, "Location to redirect: " + location.getValue()); method.setURI(new URI(location.getValue(), true)); + Header destination = method.getRequestHeader("Destination"); + if (destination == null) { + destination = method.getRequestHeader("destination"); + } + if (destination != null) { + String locationStr = location.getValue(); + int suffixIndex = locationStr.lastIndexOf( + (mCredentials instanceof OwnCloudBearerCredentials) ? + AccountUtils.ODAV_PATH : + AccountUtils.WEBDAV_PATH_4_0 + ); + String redirectionBase = locationStr.substring(0, suffixIndex); + + String destinationStr = destination.getValue(); + String destinationPath = destinationStr.substring(mBaseUri.toString().length()); + String redirectedDestination = redirectionBase + destinationPath; + + destination.setValue(redirectedDestination); + method.setRequestHeader(destination); + } status = super.executeMethod(method); redirectionsCount++; } else { - Log.d(TAG + " #" + mInstanceNumber, "No location to redirect!"); + Log_OC.d(TAG + " #" + mInstanceNumber, "No location to redirect!"); status = HttpStatus.SC_NOT_FOUND; } } @@ -248,7 +268,7 @@ public class OwnCloudClient extends HttpClient { responseBodyAsStream.close(); } catch (IOException io) { - Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response;" + + Log_OC.e(TAG, "Unexpected exception while exhausting not interesting HTTP response;" + " will be IGNORED", io); } } @@ -306,24 +326,24 @@ public class OwnCloudClient extends HttpClient { int counter = 0; for (int i=0; i Build.VERSION_CODES.FROYO) { - Log.v(TAG, "SUPPORTED SSL PARAMETERS"); + Log_OC.v(TAG, "SUPPORTED SSL PARAMETERS"); logSslParameters(mSslContext.getSupportedSSLParameters()); - Log.v(TAG, "DEFAULT SSL PARAMETERS"); + Log_OC.v(TAG, "DEFAULT SSL PARAMETERS"); logSslParameters(mSslContext.getDefaultSSLParameters()); - Log.i(TAG, "CURRENT PARAMETERS"); - Log.i(TAG, "Protocol: " + mSslContext.getProtocol()); + Log_OC.i(TAG, "CURRENT PARAMETERS"); + Log_OC.i(TAG, "Protocol: " + mSslContext.getProtocol()); } - Log.i(TAG, "PROVIDER"); + Log_OC.i(TAG, "PROVIDER"); logSecurityProvider(mSslContext.getProvider()); } private void logSecurityProvider(Provider provider) { - Log.i(TAG, "name: " + provider.getName()); - Log.i(TAG, "version: " + provider.getVersion()); - Log.i(TAG, "info: " + provider.getInfo()); + Log_OC.i(TAG, "name: " + provider.getName()); + Log_OC.i(TAG, "version: " + provider.getVersion()); + Log_OC.i(TAG, "info: " + provider.getInfo()); Enumeration keys = provider.propertyNames(); String key; while (keys.hasMoreElements()) { key = (String) keys.nextElement(); - Log.i(TAG, " property " + key + " : " + provider.getProperty(key)); + Log_OC.i(TAG, " property " + key + " : " + provider.getProperty(key)); } } private void logSslParameters(SSLParameters params) { - Log.v(TAG, "Cipher suites: "); + Log_OC.v(TAG, "Cipher suites: "); String [] elements = params.getCipherSuites(); for (int i=0; i sslSocketClass) { - Log.i(TAG, "SSLSocket implementation: " + sslSocketClass.getCanonicalName()); + Log_OC.i(TAG, "SSLSocket implementation: " + sslSocketClass.getCanonicalName()); Method setHostnameMethod = null; try { setHostnameMethod = sslSocketClass.getMethod(METHOD_NAME, String.class); } catch (SecurityException e) { - Log.e(TAG, "Could not access to SSLSocket#setHostname(String) method ", e); + Log_OC.e(TAG, "Could not access to SSLSocket#setHostname(String) method ", e); } catch (NoSuchMethodException e) { - Log.i(TAG, "Could not find SSLSocket#setHostname(String) method - SNI not supported"); + Log_OC.i(TAG, "Could not find SSLSocket#setHostname(String) method - SNI not supported"); } mSingleInstance.set(new ServerNameIndicator(sslSocketClass, setHostnameMethod)); return setHostnameMethod; diff --git a/src/com/owncloud/android/lib/common/network/WebdavEntry.java b/src/com/owncloud/android/lib/common/network/WebdavEntry.java index d6fe7a54..d8b88fa8 100644 --- a/src/com/owncloud/android/lib/common/network/WebdavEntry.java +++ b/src/com/owncloud/android/lib/common/network/WebdavEntry.java @@ -33,7 +33,8 @@ import org.apache.jackrabbit.webdav.property.DavPropertySet; import org.apache.jackrabbit.webdav.xml.Namespace; import android.net.Uri; -import android.util.Log; + +import com.owncloud.android.lib.common.utils.Log_OC; public class WebdavEntry { private static final String NAMESPACE_OC = "http://owncloud.org/ns"; @@ -125,7 +126,7 @@ public class WebdavEntry { } } else { - Log.e("WebdavEntry", + Log_OC.e("WebdavEntry", "General fuckup, no status for webdav response"); } } diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java index bef4b86c..d3eea6ee 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java @@ -26,6 +26,13 @@ package com.owncloud.android.lib.common.operations; import java.io.IOException; +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 com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; @@ -33,16 +40,7 @@ import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.OwnCloudCredentials; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.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; +import com.owncloud.android.lib.common.utils.Log_OC; /** @@ -110,7 +108,7 @@ public abstract class RemoteOperation implements Runnable { mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). getClientFor(ocAccount, mContext); } catch (Exception e) { - Log.e(TAG, "Error while trying to access to " + mAccount.name, e); + Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e); return new RemoteOperationResult(e); } return run(mClient); @@ -266,11 +264,11 @@ public abstract class RemoteOperation implements Runnable { } } 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)); + 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.e(TAG, "Error while trying to access to " + mAccount.name, e); + Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e); result = new RemoteOperationResult(e); } diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index e8b0be88..861db55e 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -41,12 +41,12 @@ import org.apache.commons.httpclient.HttpStatus; import org.apache.jackrabbit.webdav.DavException; import org.json.JSONException; -import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; -import com.owncloud.android.lib.common.network.CertificateCombinedException; - import android.accounts.Account; import android.accounts.AccountsException; -import android.util.Log; + +import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; +import com.owncloud.android.lib.common.network.CertificateCombinedException; +import com.owncloud.android.lib.common.utils.Log_OC; /** @@ -98,7 +98,12 @@ public class RemoteOperationResult implements Serializable { ACCOUNT_NOT_THE_SAME, INVALID_CHARACTER_IN_NAME, SHARE_NOT_FOUND, - LOCAL_STORAGE_NOT_REMOVED + LOCAL_STORAGE_NOT_REMOVED, + FORBIDDEN, + SHARE_FORBIDDEN, + OK_REDIRECT_TO_NON_SECURE_CONNECTION, + INVALID_MOVE_INTO_DESCENDANT, + PARTIAL_MOVE_DONE } private boolean mSuccess = false; @@ -112,7 +117,7 @@ public class RemoteOperationResult implements Serializable { public RemoteOperationResult(ResultCode code) { mCode = code; - mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL); + mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL || code == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION); mData = null; } @@ -140,9 +145,12 @@ public class RemoteOperationResult implements Serializable { case HttpStatus.SC_INSUFFICIENT_STORAGE: mCode = ResultCode.QUOTA_EXCEEDED; break; + case HttpStatus.SC_FORBIDDEN: + mCode = ResultCode.FORBIDDEN; + break; default: mCode = ResultCode.UNHANDLED_HTTP_CODE; - Log.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode); + Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode); } } } @@ -245,6 +253,10 @@ public class RemoteOperationResult implements Serializable { return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED; } + public boolean isRedirectToNonSecureConnection() { + return mCode == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION; + } + private CertificateCombinedException getCertificateCombinedException(Exception e) { CertificateCombinedException result = null; if (e instanceof CertificateCombinedException) { @@ -366,6 +378,15 @@ public class RemoteOperationResult implements Serializable { mRedirectedLocation.toLowerCase().contains("wayf"))); } + /** + * Checks if is a non https connection + * + * @return boolean true/false + */ + public boolean isNonSecureRedirection() { + return (mRedirectedLocation != null && !(mRedirectedLocation.toLowerCase().startsWith("https://"))); + } + public String getAuthenticateHeader() { return mAuthenticate; } diff --git a/src/com/owncloud/android/lib/common/utils/Log_OC.java b/src/com/owncloud/android/lib/common/utils/Log_OC.java new file mode 100644 index 00000000..8ed8c52c --- /dev/null +++ b/src/com/owncloud/android/lib/common/utils/Log_OC.java @@ -0,0 +1,178 @@ +package com.owncloud.android.lib.common.utils; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +import android.os.Environment; +import android.util.Log; + + +public class Log_OC { + private static final String SIMPLE_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss"; + private static final String LOG_FOLDER_NAME = "log"; + private static final long MAX_FILE_SIZE = 1000000; // 1MB + + private static String mOwncloudDataFolderLog = "owncloud_log"; + + private static File mLogFile; + private static File mFolder; + private static BufferedWriter mBuf; + + private static String[] mLogFileNames = {"currentLog.txt", "olderLog.txt"}; + + private static boolean isMaxFileSizeReached = false; + private static boolean isEnabled = false; + + public static void setLogDataFolder(String logFolder){ + mOwncloudDataFolderLog = logFolder; + } + + public static void i(String TAG, String 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); + } + + /** + * Start doing logging + * @param logPath : path of log file + */ + public static void startLogging() { + String logPath = Environment.getExternalStorageDirectory() + File.separator + + mOwncloudDataFolderLog + File.separator + LOG_FOLDER_NAME; + mFolder = new File(logPath); + mLogFile = new File(mFolder + File.separator + mLogFileNames[0]); + + boolean isFileCreated = false; + + if (!mFolder.exists()) { + mFolder.mkdirs(); + isFileCreated = true; + Log.d("LOG_OC", "Log file created"); + } + + try { + + // Create the current log file if does not exist + mLogFile.createNewFile(); + mBuf = new BufferedWriter(new FileWriter(mLogFile, true)); + isEnabled = true; + + if (isFileCreated) { + appendPhoneInfo(); + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Delete history logging + */ + public static void deleteHistoryLogging() { + File folderLogs = new File(mFolder + File.separator); + if(folderLogs.isDirectory()){ + String[] myFiles = folderLogs.list(); + for (int i=0; i MAX_FILE_SIZE) { + isMaxFileSizeReached = true; + } + } + } + + public static String[] getLogFileNames() { + return mLogFileNames; + } +} diff --git a/src/com/owncloud/android/lib/resources/files/ChunkedUploadRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/ChunkedUploadRemoteFileOperation.java index 28c31307..439fe121 100644 --- a/src/com/owncloud/android/lib/resources/files/ChunkedUploadRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/ChunkedUploadRemoteFileOperation.java @@ -37,9 +37,7 @@ import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.ChunkFromFileChannelRequestEntity; import com.owncloud.android.lib.common.network.ProgressiveDataTransferer; import com.owncloud.android.lib.common.network.WebdavUtils; - - -import android.util.Log; +import com.owncloud.android.lib.common.utils.Log_OC; public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation { @@ -81,7 +79,7 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation mPutMethod.setRequestEntity(mEntity); status = client.executeMethod(mPutMethod); client.exhaustResponse(mPutMethod.getResponseBodyAsStream()); - Log.d(TAG, "Upload of " + mLocalPath + " to " + mRemotePath + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status); + Log_OC.d(TAG, "Upload of " + mLocalPath + " to " + mRemotePath + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status); if (!isSuccess(status)) break; } diff --git a/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java b/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java index e8797826..f0ba3492 100644 --- a/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java +++ b/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java @@ -27,13 +27,12 @@ package com.owncloud.android.lib.resources.files; import org.apache.commons.httpclient.HttpStatus; import org.apache.jackrabbit.webdav.client.methods.MkColMethod; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.common.utils.Log_OC; @@ -87,12 +86,12 @@ public class CreateRemoteFolderOperation extends RemoteOperation { } result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders()); - Log.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage()); + Log_OC.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); + Log_OC.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e); } finally { if (mkcol != null) diff --git a/src/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.java index 7500c121..d642fb9a 100644 --- a/src/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.java @@ -39,14 +39,13 @@ import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.http.HttpStatus; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.operations.OperationCancelledException; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; /** * Remote operation performing the download of a remote file in the ownCloud server. @@ -84,11 +83,11 @@ public class DownloadRemoteFileOperation extends RemoteOperation { tmpFile.getParentFile().mkdirs(); int status = downloadFile(client, tmpFile); result = new RemoteOperationResult(isSuccess(status), status, (mGet != null ? mGet.getResponseHeaders() : null)); - Log.i(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage()); + Log_OC.i(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage()); } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage(), e); + Log_OC.e(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage(), e); } return result; diff --git a/src/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java b/src/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java index 1eda4480..0c610714 100644 --- a/src/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java +++ b/src/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java @@ -27,14 +27,14 @@ package com.owncloud.android.lib.resources.files; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.HeadMethod; +import android.content.Context; +import android.net.ConnectivityManager; + import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.util.Log; +import com.owncloud.android.lib.common.utils.Log_OC; /** * Operation to check the existence or absence of a path in a remote server. @@ -80,11 +80,11 @@ public class ExistenceCheckRemoteOperation extends RemoteOperation { client.exhaustResponse(head.getResponseBodyAsStream()); boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) || (status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent); result = new RemoteOperationResult(success, status, head.getResponseHeaders()); - Log.d(TAG, "Existence check for " + client.getWebdavUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":"")); + Log_OC.d(TAG, "Existence check for " + client.getWebdavUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":"")); } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Existence check for " + client.getWebdavUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException()); + Log_OC.e(TAG, "Existence check for " + client.getWebdavUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException()); } finally { if (head != null) diff --git a/src/com/owncloud/android/lib/resources/files/FileUtils.java b/src/com/owncloud/android/lib/resources/files/FileUtils.java index d9399819..796e0e82 100644 --- a/src/com/owncloud/android/lib/resources/files/FileUtils.java +++ b/src/com/owncloud/android/lib/resources/files/FileUtils.java @@ -26,7 +26,7 @@ package com.owncloud.android.lib.resources.files; import java.io.File; -import android.util.Log; +import com.owncloud.android.lib.common.utils.Log_OC; public class FileUtils { @@ -47,7 +47,7 @@ public class FileUtils { public static boolean isValidName(String fileName) { boolean result = true; - Log.d("FileUtils", "fileName =======" + fileName); + Log_OC.d("FileUtils", "fileName =======" + fileName); if (fileName.contains(PATH_SEPARATOR) || fileName.contains("\\") || fileName.contains("<") || fileName.contains(">") || fileName.contains(":") || fileName.contains("\"") || fileName.contains("|") || @@ -65,7 +65,7 @@ public class FileUtils { public static boolean isValidPath(String path) { boolean result = true; - Log.d("FileUtils", "path ....... " + path); + Log_OC.d("FileUtils", "path ....... " + path); if (path.contains("\\") || path.contains("<") || path.contains(">") || path.contains(":") || path.contains("\"") || path.contains("|") || path.contains("?") || path.contains("*")) { diff --git a/src/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java new file mode 100644 index 00000000..844c727e --- /dev/null +++ b/src/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java @@ -0,0 +1,213 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.resources.files; + +import java.io.IOException; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.MultiStatusResponse; +import org.apache.jackrabbit.webdav.Status; +import org.apache.jackrabbit.webdav.client.methods.MoveMethod; + +import android.util.Log; + +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.network.WebdavUtils; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; + + +/** + * Remote operation moving a remote file or folder in the ownCloud server to a different folder + * in the same account. + * + * Allows renaming the moving file/folder at the same time. + * + * @author David A. Velasco + */ +public class MoveRemoteFileOperation extends RemoteOperation { + + private static final String TAG = MoveRemoteFileOperation.class.getSimpleName(); + + private static final int MOVE_READ_TIMEOUT = 600000; + private static final int MOVE_CONNECTION_TIMEOUT = 5000; + + private String mSrcRemotePath; + private String mTargetRemotePath; + + private boolean mOverwrite; + + + /** + * Constructor. + * + * TODO Paths should finish in "/" in the case of folders. ? + * + * @param srcRemotePath Remote path of the file/folder to move. + * @param targetRemotePath Remove path desired for the file/folder after moving it. + */ + public MoveRemoteFileOperation( + String srcRemotePath, String targetRemotePath, boolean overwrite + ) { + + mSrcRemotePath = srcRemotePath; + mTargetRemotePath = targetRemotePath; + mOverwrite = overwrite; + } + + + /** + * Performs the rename operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + + /// check parameters + if (!FileUtils.isValidPath(mTargetRemotePath)) { + return new RemoteOperationResult(ResultCode.INVALID_CHARACTER_IN_NAME); + } + + if (mTargetRemotePath.equals(mSrcRemotePath)) { + // nothing to do! + return new RemoteOperationResult(ResultCode.OK); + } + + if (mTargetRemotePath.startsWith(mSrcRemotePath)) { + return new RemoteOperationResult(ResultCode.INVALID_MOVE_INTO_DESCENDANT); + } + + + /// perform remote operation + //LocalMoveMethod move = null; + MoveMethod move = null; + RemoteOperationResult result = null; + try { + move = new MoveMethod( + client.getWebdavUri() + WebdavUtils.encodePath(mSrcRemotePath), + client.getWebdavUri() + WebdavUtils.encodePath(mTargetRemotePath), + mOverwrite + ); + int status = client.executeMethod(move, MOVE_READ_TIMEOUT, MOVE_CONNECTION_TIMEOUT); + + /// process response + if (status == HttpStatus.SC_MULTI_STATUS) { + result = processPartialError(move); + + } else if (status == HttpStatus.SC_PRECONDITION_FAILED && !mOverwrite) { + + result = new RemoteOperationResult(ResultCode.INVALID_OVERWRITE); + client.exhaustResponse(move.getResponseBodyAsStream()); + + + /// for other errors that could be explicitly handled, check first: + /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4 + + } else { + + result = new RemoteOperationResult( + isSuccess(status), // move.succeeded()? trustful? + status, + move.getResponseHeaders() + ); + client.exhaustResponse(move.getResponseBodyAsStream()); + } + + Log.i(TAG, "Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + + result.getLogMessage()); + + } catch (Exception e) { + result = new RemoteOperationResult(e); + Log.e(TAG, "Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + + result.getLogMessage(), e); + + } finally { + if (move != null) + move.releaseConnection(); + } + + return result; + } + + + /** + * Analyzes a multistatus response from the OC server to generate an appropriate result. + * + * In WebDAV, a MOVE request on collections (folders) can be PARTIALLY successful: some + * children are moved, some other aren't. + * + * According to the WebDAV specification, a multistatus response SHOULD NOT include partial + * successes (201, 204) nor for descendants of already failed children (424) in the response + * entity. But SHOULD NOT != MUST NOT, so take carefully. + * + * @param move Move operation just finished with a multistatus response + * @return A result for the {@link MoveRemoteFileOperation} caller + * + * @throws IOException If the response body could not be parsed + * @throws DavException If the status code is other than MultiStatus or if obtaining + * the response XML document fails + */ + private RemoteOperationResult processPartialError(MoveMethod move) + throws IOException, DavException { + // Adding a list of failed descendants to the result could be interesting; or maybe not. + // For the moment, let's take the easy way. + + /// check that some error really occurred + MultiStatusResponse[] responses = move.getResponseBodyAsMultiStatus().getResponses(); + Status[] status = null; + boolean failFound = false; + for (int i = 0; i < responses.length && !failFound; i++ ) { + status = responses[i].getStatus(); + failFound = ( + status != null && + status.length > 0 && + status[0].getStatusCode() > 299 + ); + } + + RemoteOperationResult result; + if (failFound) { + result = new RemoteOperationResult(ResultCode.PARTIAL_MOVE_DONE); + } else { + result = new RemoteOperationResult( + true, + HttpStatus.SC_MULTI_STATUS, + move.getResponseHeaders() + ); + } + + return result; + + } + + + protected boolean isSuccess(int status) { + return status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT; + } + +} diff --git a/src/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java index 347e7395..3fb695a4 100644 --- a/src/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java @@ -30,13 +30,12 @@ import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.MultiStatus; import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavEntry; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; /** @@ -49,7 +48,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; public class ReadRemoteFileOperation extends RemoteOperation { private static final String TAG = ReadRemoteFileOperation.class.getSimpleName(); - private static final int SYNC_READ_TIMEOUT = 10000; + private static final int SYNC_READ_TIMEOUT = 40000; private static final int SYNC_CONNECTION_TIMEOUT = 5000; private String mRemotePath; @@ -82,8 +81,11 @@ public class ReadRemoteFileOperation extends RemoteOperation { int status; status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT); - boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS; - if (isMultiStatus) { + boolean isSuccess = ( + status == HttpStatus.SC_MULTI_STATUS || + status == HttpStatus.SC_OK + ); + if (isSuccess) { // Parse response MultiStatus resp = propfind.getResponseBodyAsMultiStatus(); WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getWebdavUri().getPath()); @@ -103,7 +105,7 @@ public class ReadRemoteFileOperation extends RemoteOperation { } catch (Exception e) { result = new RemoteOperationResult(e); e.printStackTrace(); - Log.e(TAG, "Synchronizing file " + mRemotePath + ": " + result.getLogMessage(), result.getException()); + Log_OC.e(TAG, "Synchronizing file " + mRemotePath + ": " + result.getLogMessage(), result.getException()); } finally { if (propfind != null) propfind.releaseConnection(); diff --git a/src/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java b/src/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java index 922b7c9f..af92d8fe 100644 --- a/src/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java +++ b/src/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java @@ -31,13 +31,12 @@ import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.MultiStatus; import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavEntry; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; /** * Remote operation performing the read of remote file or folder in the ownCloud server. @@ -80,7 +79,11 @@ public class ReadRemoteFolderOperation extends RemoteOperation { int status = client.executeMethod(query); // check and process response - if (isMultiStatus(status)) { + boolean isSuccess = ( + status == HttpStatus.SC_MULTI_STATUS || + status == HttpStatus.SC_OK + ); + if (isSuccess) { // get data from remote folder MultiStatus dataInServer = query.getResponseBodyAsMultiStatus(); readData(dataInServer, client); @@ -105,12 +108,12 @@ public class ReadRemoteFolderOperation extends RemoteOperation { if (query != null) query.releaseConnection(); // let the connection available for other methods if (result.isSuccess()) { - Log.i(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage()); + Log_OC.i(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage()); } else { if (result.isException()) { - Log.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage(), result.getException()); + Log_OC.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage(), result.getException()); } else { - Log.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage()); + Log_OC.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage()); } } diff --git a/src/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.java index ed8158a0..acd524b9 100644 --- a/src/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.java @@ -27,12 +27,11 @@ package com.owncloud.android.lib.resources.files; import org.apache.commons.httpclient.HttpStatus; import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; /** * Remote operation performing the removal of a remote file or folder in the ownCloud server. @@ -73,11 +72,11 @@ public class RemoveRemoteFileOperation extends RemoteOperation { delete.getResponseBodyAsString(); // exhaust the response, although not interesting result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders()); - Log.i(TAG, "Remove " + mRemotePath + ": " + result.getLogMessage()); + Log_OC.i(TAG, "Remove " + mRemotePath + ": " + result.getLogMessage()); } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Remove " + mRemotePath + ": " + result.getLogMessage(), e); + Log_OC.e(TAG, "Remove " + mRemotePath + ": " + result.getLogMessage(), e); } finally { if (delete != null) diff --git a/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java index a42543b0..360e5b26 100644 --- a/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java @@ -28,13 +28,12 @@ import java.io.File; import org.apache.jackrabbit.webdav.client.methods.DavMethodBase; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.common.utils.Log_OC; /** @@ -47,7 +46,7 @@ public class RenameRemoteFileOperation extends RemoteOperation { private static final String TAG = RenameRemoteFileOperation.class.getSimpleName(); - private static final int RENAME_READ_TIMEOUT = 10000; + private static final int RENAME_READ_TIMEOUT = 600000; private static final int RENAME_CONNECTION_TIMEOUT = 5000; private String mOldName; @@ -109,11 +108,11 @@ public class RenameRemoteFileOperation extends RemoteOperation { move.getResponseBodyAsString(); // exhaust response, although not interesting result = new RemoteOperationResult(move.succeeded(), status, move.getResponseHeaders()); - Log.i(TAG, "Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " + result.getLogMessage()); + Log_OC.i(TAG, "Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " + result.getLogMessage()); } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Rename " + mOldRemotePath + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e); + Log_OC.e(TAG, "Rename " + mOldRemotePath + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e); } finally { if (move != null) diff --git a/src/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.java b/src/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.java index 67a1ba51..9bb06bc2 100644 --- a/src/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.java +++ b/src/com/owncloud/android/lib/resources/shares/CreateRemoteShareOperation.java @@ -31,12 +31,11 @@ import java.util.ArrayList; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.http.HttpStatus; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.common.utils.Log_OC; /** * Creates a new share. This allows sharing with a user or group or as a link. @@ -103,7 +102,7 @@ public class CreateRemoteShareOperation extends RemoteOperation { try { // Post Method post = new PostMethod(client.getBaseUri() + ShareUtils.SHARING_API_PATH); - //Log.d(TAG, "URL ------> " + client.getBaseUri() + ShareUtils.SHARING_API_PATH); + //Log_OC.d(TAG, "URL ------> " + client.getBaseUri() + ShareUtils.SHARING_API_PATH); post.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); // necessary for special characters post.addParameter(PARAM_PATH, mRemoteFilePath); @@ -131,7 +130,7 @@ public class CreateRemoteShareOperation extends RemoteOperation { mShares = xmlParser.parseXMLResponse(is); if (xmlParser.isSuccess()) { if (mShares != null) { - Log.d(TAG, "Created " + mShares.size() + " share(s)"); + Log_OC.d(TAG, "Created " + mShares.size() + " share(s)"); result = new RemoteOperationResult(ResultCode.OK); ArrayList sharesObjects = new ArrayList(); for (OCShare share: mShares) { @@ -142,6 +141,9 @@ public class CreateRemoteShareOperation extends RemoteOperation { } else if (xmlParser.isFileNotFound()){ result = new RemoteOperationResult(ResultCode.SHARE_NOT_FOUND); + } else if (xmlParser.isFailure()) { + result = new RemoteOperationResult(ResultCode.SHARE_FORBIDDEN); + } else { result = new RemoteOperationResult(false, status, post.getResponseHeaders()); } @@ -152,7 +154,7 @@ public class CreateRemoteShareOperation extends RemoteOperation { } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Exception while Creating New Share", e); + Log_OC.e(TAG, "Exception while Creating New Share", e); } finally { if (post != null) { diff --git a/src/com/owncloud/android/lib/resources/shares/GetRemoteSharesForFileOperation.java b/src/com/owncloud/android/lib/resources/shares/GetRemoteSharesForFileOperation.java index bca3e257..21ef0ec7 100644 --- a/src/com/owncloud/android/lib/resources/shares/GetRemoteSharesForFileOperation.java +++ b/src/com/owncloud/android/lib/resources/shares/GetRemoteSharesForFileOperation.java @@ -32,12 +32,11 @@ import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.http.HttpStatus; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.common.utils.Log_OC; /** * Provide a list shares for a specific file. @@ -110,7 +109,7 @@ public class GetRemoteSharesForFileOperation extends RemoteOperation { ShareXMLParser xmlParser = new ShareXMLParser(); mShares = xmlParser.parseXMLResponse(is); if (mShares != null) { - Log.d(TAG, "Got " + mShares.size() + " shares"); + Log_OC.d(TAG, "Got " + mShares.size() + " shares"); result = new RemoteOperationResult(ResultCode.OK); ArrayList sharesObjects = new ArrayList(); for (OCShare share: mShares) { @@ -129,7 +128,7 @@ public class GetRemoteSharesForFileOperation extends RemoteOperation { } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Exception while getting shares", e); + Log_OC.e(TAG, "Exception while getting shares", e); } finally { if (get != null) { diff --git a/src/com/owncloud/android/lib/resources/shares/GetRemoteSharesOperation.java b/src/com/owncloud/android/lib/resources/shares/GetRemoteSharesOperation.java index 05ceb710..5a97136f 100644 --- a/src/com/owncloud/android/lib/resources/shares/GetRemoteSharesOperation.java +++ b/src/com/owncloud/android/lib/resources/shares/GetRemoteSharesOperation.java @@ -35,8 +35,7 @@ import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; - -import android.util.Log; +import com.owncloud.android.lib.common.utils.Log_OC; /** @@ -78,7 +77,7 @@ public class GetRemoteSharesOperation extends RemoteOperation { ShareXMLParser xmlParser = new ShareXMLParser(); mShares = xmlParser.parseXMLResponse(is); if (mShares != null) { - Log.d(TAG, "Got " + mShares.size() + " shares"); + Log_OC.d(TAG, "Got " + mShares.size() + " shares"); result = new RemoteOperationResult(ResultCode.OK); ArrayList sharesObjects = new ArrayList(); for (OCShare share: mShares) { @@ -92,7 +91,7 @@ public class GetRemoteSharesOperation extends RemoteOperation { } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Exception while getting remote shares ", e); + Log_OC.e(TAG, "Exception while getting remote shares ", e); } finally { if (get != null) { diff --git a/src/com/owncloud/android/lib/resources/shares/OCShare.java b/src/com/owncloud/android/lib/resources/shares/OCShare.java index 1bf6cc1a..638fd1b9 100644 --- a/src/com/owncloud/android/lib/resources/shares/OCShare.java +++ b/src/com/owncloud/android/lib/resources/shares/OCShare.java @@ -26,11 +26,11 @@ package com.owncloud.android.lib.resources.shares; import java.io.Serializable; -import com.owncloud.android.lib.resources.files.FileUtils; - import android.os.Parcel; import android.os.Parcelable; -import android.util.Log; + +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.files.FileUtils; /** @@ -70,7 +70,7 @@ public class OCShare implements Parcelable, Serializable { public OCShare(String path) { resetData(); if (path == null || path.length() <= 0 || !path.startsWith(FileUtils.PATH_SEPARATOR)) { - Log.e(TAG, "Trying to create a OCShare with a non valid path"); + Log_OC.e(TAG, "Trying to create a OCShare with a non valid path"); throw new IllegalArgumentException("Trying to create a OCShare with a non valid path: " + path); } mPath = path; diff --git a/src/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.java b/src/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.java index db7a6d60..9b8c8b03 100644 --- a/src/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.java +++ b/src/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.java @@ -30,12 +30,11 @@ import java.io.InputStream; import org.apache.commons.httpclient.HttpStatus; import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.common.utils.Log_OC; /** * Remove a share @@ -94,13 +93,13 @@ public class RemoveRemoteShareOperation extends RemoteOperation { result = new RemoteOperationResult(false, status, delete.getResponseHeaders()); } - Log.d(TAG, "Unshare " + id + ": " + result.getLogMessage()); + Log_OC.d(TAG, "Unshare " + id + ": " + result.getLogMessage()); } else { result = new RemoteOperationResult(false, status, delete.getResponseHeaders()); } } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Unshare Link Exception " + result.getLogMessage(), e); + Log_OC.e(TAG, "Unshare Link Exception " + result.getLogMessage(), e); } finally { if (delete != null) diff --git a/src/com/owncloud/android/lib/resources/shares/ShareXMLParser.java b/src/com/owncloud/android/lib/resources/shares/ShareXMLParser.java index 48a8358e..e45f206b 100644 --- a/src/com/owncloud/android/lib/resources/shares/ShareXMLParser.java +++ b/src/com/owncloud/android/lib/resources/shares/ShareXMLParser.java @@ -182,7 +182,7 @@ public class ShareXMLParser { */ private void readMeta(XmlPullParser parser) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, ns, NODE_META); - //Log.d(TAG, "---- NODE META ---"); + //Log_OC.d(TAG, "---- NODE META ---"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; @@ -214,7 +214,7 @@ public class ShareXMLParser { OCShare share = null; parser.require(XmlPullParser.START_TAG, ns, NODE_DATA); - //Log.d(TAG, "---- NODE DATA ---"); + //Log_OC.d(TAG, "---- NODE DATA ---"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; @@ -264,7 +264,7 @@ public class ShareXMLParser { OCShare share = new OCShare(); - //Log.d(TAG, "---- NODE ELEMENT ---"); + //Log_OC.d(TAG, "---- NODE ELEMENT ---"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; @@ -360,7 +360,7 @@ public class ShareXMLParser { private String readNode (XmlPullParser parser, String node) throws XmlPullParserException, IOException{ parser.require(XmlPullParser.START_TAG, ns, node); String value = readText(parser); - //Log.d(TAG, "node= " + node + ", value= " + value); + //Log_OC.d(TAG, "node= " + node + ", value= " + value); parser.require(XmlPullParser.END_TAG, ns, node); return value; } diff --git a/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java b/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java index 458a91d3..a32f9161 100644 --- a/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java +++ b/src/com/owncloud/android/lib/resources/status/GetRemoteStatusOperation.java @@ -31,15 +31,15 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.json.JSONException; import org.json.JSONObject; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Uri; + import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.Uri; -import android.util.Log; +import com.owncloud.android.lib.common.utils.Log_OC; /** * Checks if the server is valid and if the server supports the Share API @@ -51,7 +51,10 @@ import android.util.Log; public class GetRemoteStatusOperation extends RemoteOperation { - /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs. */ + /** + * Maximum time to wait for a response from the server when the connection is being tested, + * in MILLISECONDs. + */ public static final int TRY_CONNECTION_TIMEOUT = 5000; private static final String TAG = GetRemoteStatusOperation.class.getSimpleName(); @@ -72,7 +75,37 @@ public class GetRemoteStatusOperation extends RemoteOperation { String baseUrlSt = client.getBaseUri().toString(); try { get = new GetMethod(baseUrlSt + AccountUtils.STATUS_PATH); + + client.setFollowRedirects(false); + boolean isRedirectToNonSecureConnection = false; int status = client.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT); + mLatestResult = new RemoteOperationResult( + (status == HttpStatus.SC_OK), + status, + get.getResponseHeaders() + ); + + String redirectedLocation = mLatestResult.getRedirectedLocation(); + while (redirectedLocation != null && redirectedLocation.length() > 0 + && !mLatestResult.isSuccess()) { + + isRedirectToNonSecureConnection |= ( + baseUrlSt.startsWith("https://") && + redirectedLocation.startsWith("http://") + ); + get.releaseConnection(); + get = new GetMethod(redirectedLocation); + status = client.executeMethod( + get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT + ); + mLatestResult = new RemoteOperationResult( + (status == HttpStatus.SC_OK), + status, + get.getResponseHeaders() + ); + redirectedLocation = mLatestResult.getRedirectedLocation(); + } + String response = get.getResponseBodyAsString(); if (status == HttpStatus.SC_OK) { JSONObject json = new JSONObject(response); @@ -87,16 +120,24 @@ public class GetRemoteStatusOperation extends RemoteOperation { RemoteOperationResult.ResultCode.BAD_OC_VERSION); } else { - mLatestResult = new RemoteOperationResult( - baseUrlSt.startsWith("https://") ? - RemoteOperationResult.ResultCode.OK_SSL : - RemoteOperationResult.ResultCode.OK_NO_SSL - ); + // success + if (isRedirectToNonSecureConnection) { + mLatestResult = new RemoteOperationResult( + RemoteOperationResult.ResultCode. + OK_REDIRECT_TO_NON_SECURE_CONNECTION + ); + } else { + mLatestResult = new RemoteOperationResult( + baseUrlSt.startsWith("https://") ? + RemoteOperationResult.ResultCode.OK_SSL : + RemoteOperationResult.ResultCode.OK_NO_SSL + ); + } - ArrayList data = new ArrayList(); - data.add(ocVersion); - mLatestResult.setData(data); - retval = true; + ArrayList data = new ArrayList(); + data.add(ocVersion); + mLatestResult.setData(data); + retval = true; } } @@ -117,13 +158,14 @@ public class GetRemoteStatusOperation extends RemoteOperation { } if (mLatestResult.isSuccess()) { - Log.i(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage()); + Log_OC.i(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage()); } else if (mLatestResult.getException() != null) { - Log.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException()); + Log_OC.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage(), + mLatestResult.getException()); } else { - Log.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage()); + Log_OC.e(TAG, "Connection check at " + baseUrlSt + ": " + mLatestResult.getLogMessage()); } return retval; @@ -148,8 +190,8 @@ public class GetRemoteStatusOperation extends RemoteOperation { } else { client.setBaseUri(Uri.parse("https://" + baseUriStr)); boolean httpsSuccess = tryConnection(client); - if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) { - Log.d(TAG, "establishing secure connection failed, trying non secure connection"); + if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) { + Log_OC.d(TAG, "establishing secure connection failed, trying non secure connection"); client.setBaseUri(Uri.parse("http://" + baseUriStr)); tryConnection(client); } diff --git a/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java b/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java index f153be44..36ce7ffc 100644 --- a/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java +++ b/src/com/owncloud/android/lib/resources/users/GetRemoteUserNameOperation.java @@ -30,11 +30,10 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.apache.http.HttpStatus; import org.json.JSONObject; -import android.util.Log; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; /** @@ -83,7 +82,7 @@ public class GetRemoteUserNameOperation extends RemoteOperation { status = client.executeMethod(get); if(isSuccess(status)) { String response = get.getResponseBodyAsString(); - Log.d(TAG, "Successful response: " + response); + Log_OC.d(TAG, "Successful response: " + response); // Parse the response JSONObject respJSON = new JSONObject(response); @@ -101,21 +100,21 @@ public class GetRemoteUserNameOperation extends RemoteOperation { result.setData(data); mUserName = displayName; - Log.d(TAG, "*** Parsed user information: " + id + " - " + displayName + " - " + email); + Log_OC.d(TAG, "*** Parsed user information: " + id + " - " + displayName + " - " + email); } else { result = new RemoteOperationResult(false, status, get.getResponseHeaders()); String response = get.getResponseBodyAsString(); - Log.e(TAG, "Failed response while getting user information "); + Log_OC.e(TAG, "Failed response while getting user information "); if (response != null) { - Log.e(TAG, "*** status code: " + status + " ; response message: " + response); + Log_OC.e(TAG, "*** status code: " + status + " ; response message: " + response); } else { - Log.e(TAG, "*** status code: " + status); + Log_OC.e(TAG, "*** status code: " + status); } } } catch (Exception e) { result = new RemoteOperationResult(e); - Log.e(TAG, "Exception while getting OC user information", e); + Log_OC.e(TAG, "Exception while getting OC user information", e); } finally { get.releaseConnection(); diff --git a/test_client/res/values/setup.xml b/test_client/res/values/setup.xml index aeb08218..ac5cedde 100644 --- a/test_client/res/values/setup.xml +++ b/test_client/res/values/setup.xml @@ -24,9 +24,7 @@ --> - - /remote.php/webdav + - true diff --git a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java index 66ff274e..905fa511 100644 --- a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java +++ b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java @@ -33,16 +33,24 @@ import java.security.GeneralSecurityException; import org.apache.commons.httpclient.protocol.Protocol; import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; -import com.owncloud.android.lib.common.OwnCloudClientFactory; +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.Menu; + import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; -import com.owncloud.android.lib.resources.files.RemoteFile; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.ChunkedUploadRemoteFileOperation; import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation; import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation; import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation; +import com.owncloud.android.lib.resources.files.RemoteFile; import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation; import com.owncloud.android.lib.resources.files.RenameRemoteFileOperation; import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation; @@ -50,14 +58,6 @@ import com.owncloud.android.lib.resources.shares.CreateRemoteShareOperation; import com.owncloud.android.lib.resources.shares.GetRemoteSharesOperation; import com.owncloud.android.lib.resources.shares.RemoveRemoteShareOperation; import com.owncloud.android.lib.resources.shares.ShareType; -import com.owncloud.android.lib.test_project.R; - -import android.net.Uri; -import android.os.Bundle; -import android.os.Environment; -import android.app.Activity; -import android.util.Log; -import android.view.Menu; /** * Activity to test OC framework @@ -72,7 +72,6 @@ public class TestActivity extends Activity { private String mServerUri; private String mUser; private String mPass; - private boolean mChunked; private static final int BUFFER_SIZE = 1024; @@ -91,7 +90,6 @@ public class TestActivity extends Activity { mServerUri = getString(R.string.server_base_url); mUser = getString(R.string.username); mPass = getString(R.string.password); - mChunked = getResources().getBoolean(R.bool.chunked); Protocol pr = Protocol.getProtocol("https"); if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) { @@ -133,14 +131,32 @@ public class TestActivity extends Activity { /** * Access to the library method to Create a Folder * @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 createFullPath 'True' means that all the ancestor folders should be created if + * don't exist yet. * * @return */ public RemoteOperationResult createFolder(String remotePath, boolean createFullPath) { - CreateRemoteFolderOperation createOperation = new CreateRemoteFolderOperation(remotePath, createFullPath); - RemoteOperationResult result = createOperation.execute(mClient); + return TestActivity.createFolder(remotePath, createFullPath, mClient); + } + + /** + * Access to the library method to Create a Folder + * @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 client Client instance configured to access the target OC server. + * + * @return Result of the operation + */ + public static RemoteOperationResult createFolder( + String remotePath, boolean createFullPath, OwnCloudClient client + ) { + + CreateRemoteFolderOperation createOperation = + new CreateRemoteFolderOperation(remotePath, createFullPath); + RemoteOperationResult result = createOperation.execute(client); return result; } @@ -170,13 +186,24 @@ public class TestActivity extends Activity { * @return */ public RemoteOperationResult removeFile(String remotePath) { - RemoveRemoteFileOperation removeOperation = new RemoveRemoteFileOperation(remotePath); RemoteOperationResult result = removeOperation.execute(mClient); - + return TestActivity.removeFile(remotePath, mClient); + } + + /** + * Access to the library method to Remove a File or Folder + * + * @param remotePath Remote path of the file or folder in the server. + * @return + */ + public static RemoteOperationResult removeFile(String remotePath, OwnCloudClient client) { + RemoveRemoteFileOperation removeOperation = new RemoveRemoteFileOperation(remotePath); + RemoteOperationResult result = removeOperation.execute(client); return result; } + /** * Access to the library method to Read a Folder (PROPFIND DEPTH 1) * @param remotePath @@ -217,21 +244,39 @@ public class TestActivity extends Activity { * * @return */ - public RemoteOperationResult uploadFile(String storagePath, String remotePath, String mimeType) { - - UploadRemoteFileOperation uploadOperation; - if ( mChunked && (new File(storagePath)).length() > ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) { - uploadOperation = new ChunkedUploadRemoteFileOperation(storagePath, remotePath, mimeType); - } else { - uploadOperation = new UploadRemoteFileOperation(storagePath, remotePath, mimeType); - } - - RemoteOperationResult result = uploadOperation.execute(mClient); - - return result; + public RemoteOperationResult uploadFile( + String storagePath, String remotePath, String mimeType + ) { + return TestActivity.uploadFile(storagePath, remotePath, mimeType, mClient); } + /** Access to the library method to Upload a File + * @param storagePath + * @param remotePath + * @param mimeType + * @param client Client instance configured to access the target OC server. + * + * @return + */ + public static RemoteOperationResult uploadFile( + String storagePath, String remotePath, String mimeType, OwnCloudClient client + ) { + UploadRemoteFileOperation uploadOperation; + if ((new File(storagePath)).length() > ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) { + uploadOperation = new ChunkedUploadRemoteFileOperation( + storagePath, remotePath, mimeType + ); + } else { + uploadOperation = new UploadRemoteFileOperation( + storagePath, remotePath, mimeType + ); + } + + RemoteOperationResult result = uploadOperation.execute(client); + return result; + } + /** Access to the library method to Get Shares * * @return @@ -295,11 +340,22 @@ public class TestActivity extends Activity { * @return File instance of the extracted file. */ public File extractAsset(String fileName) throws IOException { - File extractedFile = new File(getCacheDir() + File.separator + fileName); + return TestActivity.extractAsset(fileName, this); + } + + /** + * Extracts file from AssetManager to cache folder. + * + * @param fileName Name of the asset file to extract. + * @param context Android context to access assets and file system. + * @return File instance of the extracted file. + */ + public static File extractAsset(String fileName, Context context) throws IOException { + File extractedFile = new File(context.getCacheDir() + File.separator + fileName); if (!extractedFile.exists()) { InputStream in = null; FileOutputStream out = null; - in = getAssets().open(fileName); + in = context.getAssets().open(fileName); out = new FileOutputStream(extractedFile); byte[] buffer = new byte[BUFFER_SIZE]; int readCount; @@ -312,7 +368,6 @@ public class TestActivity extends Activity { } return extractedFile; } - } diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/MoveFileTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/MoveFileTest.java new file mode 100644 index 00000000..e5ec8a37 --- /dev/null +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/MoveFileTest.java @@ -0,0 +1,459 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.test_project.test; + +import java.io.File; +import java.security.GeneralSecurityException; + +import junit.framework.AssertionFailedError; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; + +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientFactory; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.network.NetworkUtils; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation; +import com.owncloud.android.lib.test_project.R; +import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory; +import com.owncloud.android.lib.test_project.TestActivity; + +import android.content.Context; +import android.net.Uri; +import android.test.ActivityInstrumentationTestCase2; +//import android.test.AndroidTestCase; +import android.util.Log; + +/** + * Class to test MoveRemoteFileOperation + * + * With this TestCase we are experimenting a bit to improve the test suite design, in two aspects: + * + * - Reduce the dependency from the set of test cases on the "test project" needed to + * have an instrumented APK to install in the device, as required by the testing framework + * provided by Android. To get there, this class avoids calling TestActivity methods in the test + * method. + * + * - Reduce the impact of creating a remote fixture over the Internet, while the structure of the + * TestCase is kept easy to maintain. To get this, all the tests are done in a single test method, + * granting this way that setUp and tearDown are run only once. + * + * + * @author David A. Velasco + */ + +//public class MoveFileTest extends AndroidTestCase { +public class MoveFileTest extends ActivityInstrumentationTestCase2 { + + private static final String LOG_TAG = MoveFileTest.class.getCanonicalName(); + + + /// Paths to files and folders in fixture + + private static final String SRC_BASE_FOLDER = "/src/"; + private static final String TARGET_BASE_FOLDER = "/target/"; + private static final String NO_FILE = "nofile.txt"; + private static final String FILE1 = "file1.txt"; + private static final String FILE2 = "file2.txt"; + private static final String FILE3 = "file3.txt"; + private static final String FILE4 = "file4.txt"; + private static final String FILE5 = "file5.txt"; + private static final String FILE6 = "file6.txt"; + private static final String FILE7 = "file7.txt"; + private static final String EMPTY = "empty/"; + private static final String NO_FOLDER = "nofolder/"; + private static final String FOLDER1 = "folder1/"; + private static final String FOLDER2 = "folder2/"; + private static final String FOLDER3 = "folder3/"; + private static final String FOLDER4 = "folder4/"; + + private static final String SRC_PATH_TO_FILE_1 = SRC_BASE_FOLDER + FILE1; + private static final String TARGET_PATH_TO_FILE_1 = TARGET_BASE_FOLDER + FILE1; + + private static final String SRC_PATH_TO_FILE_2 = SRC_BASE_FOLDER + FILE2; + private static final String TARGET_PATH_TO_FILE_2_RENAMED = + TARGET_BASE_FOLDER + "renamed_" + FILE2; + + private static final String SRC_PATH_TO_FILE_3 = SRC_BASE_FOLDER + FILE3; + private static final String SRC_PATH_TO_FILE_3_RENAMED = SRC_BASE_FOLDER + "renamed_" + FILE3; + + private static final String SRC_PATH_TO_FILE_4 = SRC_BASE_FOLDER + FILE4; + + private static final String SRC_PATH_TO_FILE_5 = SRC_BASE_FOLDER + FILE5; + + private static final String SRC_PATH_TO_FILE_6 = SRC_BASE_FOLDER + FILE6; + + private static final String SRC_PATH_TO_FILE_7 = SRC_BASE_FOLDER + FILE7; + + private static final String SRC_PATH_TO_NON_EXISTENT_FILE = SRC_BASE_FOLDER + NO_FILE; + + private static final String SRC_PATH_TO_EMPTY_FOLDER = SRC_BASE_FOLDER + EMPTY; + private static final String TARGET_PATH_TO_EMPTY_FOLDER = TARGET_BASE_FOLDER + EMPTY; + + private static final String SRC_PATH_TO_FULL_FOLDER_1 = SRC_BASE_FOLDER + FOLDER1; + private static final String TARGET_PATH_TO_FULL_FOLDER_1 = TARGET_BASE_FOLDER + FOLDER1; + + private static final String SRC_PATH_TO_FULL_FOLDER_2 = SRC_BASE_FOLDER + FOLDER2; + + private static final String TARGET_PATH_TO_FULL_FOLDER_2_RENAMED = + TARGET_BASE_FOLDER + "renamed_" + FOLDER2; + + private static final String SRC_PATH_TO_FULL_FOLDER_3 = SRC_BASE_FOLDER + FOLDER3; + private static final String SRC_PATH_TO_FULL_FOLDER_4 = SRC_BASE_FOLDER + FOLDER4; + + private static final String SRC_PATH_TO_FULL_FOLDER_3_RENAMED = + SRC_BASE_FOLDER + "renamed_" + FOLDER3; + + private static final String TARGET_PATH_RENAMED_WITH_INVALID_CHARS = + SRC_BASE_FOLDER + "renamed:??_" + FILE6; + + private static final String TARGET_PATH_TO_ALREADY_EXISTENT_EMPTY_FOLDER_4 = TARGET_BASE_FOLDER + + FOLDER4; + + private static final String TARGET_PATH_TO_NON_EXISTENT_FILE = TARGET_BASE_FOLDER + NO_FILE; + + private static final String TARGET_PATH_TO_FILE_5_INTO_NON_EXISTENT_FOLDER = + TARGET_BASE_FOLDER + NO_FOLDER + FILE5; + + private static final String TARGET_PATH_TO_ALREADY_EXISTENT_FILE_7 = TARGET_BASE_FOLDER + FILE7; + + private static final String[] FOLDERS_IN_FIXTURE = { + SRC_PATH_TO_EMPTY_FOLDER, + + SRC_PATH_TO_FULL_FOLDER_1, + SRC_PATH_TO_FULL_FOLDER_1 + FOLDER1, + SRC_PATH_TO_FULL_FOLDER_1 + FOLDER2, + SRC_PATH_TO_FULL_FOLDER_1 + FOLDER2 + FOLDER1, + SRC_PATH_TO_FULL_FOLDER_1 + FOLDER2 + FOLDER2, + + SRC_PATH_TO_FULL_FOLDER_2, + SRC_PATH_TO_FULL_FOLDER_2 + FOLDER1, + SRC_PATH_TO_FULL_FOLDER_2 + FOLDER2, + SRC_PATH_TO_FULL_FOLDER_2 + FOLDER2 + FOLDER1, + SRC_PATH_TO_FULL_FOLDER_2 + FOLDER2 + FOLDER2, + + SRC_PATH_TO_FULL_FOLDER_3, + SRC_PATH_TO_FULL_FOLDER_3 + FOLDER1, + SRC_PATH_TO_FULL_FOLDER_3 + FOLDER2, + SRC_PATH_TO_FULL_FOLDER_3 + FOLDER2 + FOLDER1, + SRC_PATH_TO_FULL_FOLDER_3 + FOLDER2 + FOLDER2, + + SRC_PATH_TO_FULL_FOLDER_4, + SRC_PATH_TO_FULL_FOLDER_4 + FOLDER1, + SRC_PATH_TO_FULL_FOLDER_4 + FOLDER2, + SRC_PATH_TO_FULL_FOLDER_4 + FOLDER2 + FOLDER1, + SRC_PATH_TO_FULL_FOLDER_4 + FOLDER2 + FOLDER2, + + TARGET_BASE_FOLDER, + TARGET_PATH_TO_ALREADY_EXISTENT_EMPTY_FOLDER_4 + }; + + private static final String[] FILES_IN_FIXTURE = { + SRC_PATH_TO_FILE_1, + SRC_PATH_TO_FILE_2, + SRC_PATH_TO_FILE_3, + SRC_PATH_TO_FILE_4, + SRC_PATH_TO_FILE_5, + + SRC_PATH_TO_FULL_FOLDER_1 + FILE1, + SRC_PATH_TO_FULL_FOLDER_1 + FOLDER2 + FILE1, + SRC_PATH_TO_FULL_FOLDER_1 + FOLDER2 + FILE2, + SRC_PATH_TO_FULL_FOLDER_1 + FOLDER2 + FOLDER2 + FILE2, + + SRC_PATH_TO_FULL_FOLDER_2 + FILE1, + SRC_PATH_TO_FULL_FOLDER_2 + FOLDER2 + FILE1, + SRC_PATH_TO_FULL_FOLDER_2 + FOLDER2 + FILE2, + SRC_PATH_TO_FULL_FOLDER_2 + FOLDER2 + FOLDER2 + FILE2, + + SRC_PATH_TO_FULL_FOLDER_3 + FILE1, + SRC_PATH_TO_FULL_FOLDER_3 + FOLDER2 + FILE1, + SRC_PATH_TO_FULL_FOLDER_3 + FOLDER2 + FILE2, + SRC_PATH_TO_FULL_FOLDER_3 + FOLDER2 + FOLDER2 + FILE2, + + SRC_PATH_TO_FULL_FOLDER_4 + FILE1, + SRC_PATH_TO_FULL_FOLDER_4 + FOLDER2 + FILE1, + SRC_PATH_TO_FULL_FOLDER_4 + FOLDER2 + FILE2, + SRC_PATH_TO_FULL_FOLDER_4 + FOLDER2 + FOLDER2 + FILE2, + + TARGET_PATH_TO_ALREADY_EXISTENT_FILE_7 + }; + + + String mServerUri, mUser, mPass; + OwnCloudClient mClient = null; + + public MoveFileTest() { + super(TestActivity.class); + + Protocol pr = Protocol.getProtocol("https"); + if (pr == null || !(pr.getSocketFactory() instanceof SelfSignedConfidentSslSocketFactory)) { + try { + ProtocolSocketFactory psf = new SelfSignedConfidentSslSocketFactory(); + Protocol.registerProtocol( + "https", + new Protocol("https", psf, 443)); + + } catch (GeneralSecurityException e) { + throw new AssertionFailedError( + "Self-signed confident SSL context could not be loaded"); + } + } + + } + + + protected Context getContext() { + return getActivity(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // Next initialization cannot be done in the constructor because getContext() is not + // ready yet, returns NULL. + initAccessToServer(getContext()); + + Log.v(LOG_TAG, "Setting up the remote fixture..."); + + RemoteOperationResult result = null; + for (String folderPath : FOLDERS_IN_FIXTURE) { + result = TestActivity.createFolder(folderPath, true, mClient); + if (!result.isSuccess()) { + Utils.logAndThrow(LOG_TAG, result); + } + } + + File txtFile = TestActivity.extractAsset( + TestActivity.ASSETS__TEXT_FILE_NAME, getContext() + ); + for (String filePath : FILES_IN_FIXTURE) { + result = TestActivity.uploadFile( + txtFile.getAbsolutePath(), filePath, "txt/plain", mClient + ); + if (!result.isSuccess()) { + Utils.logAndThrow(LOG_TAG, result); + } + } + + Log.v(LOG_TAG, "Remote fixture created."); + + } + + + /** + * Test move folder + */ + public void testMoveRemoteFileOperation() { + Log.v(LOG_TAG, "testMoveFolder in"); + + /// successful cases + + // move file + MoveRemoteFileOperation moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FILE_1, + TARGET_PATH_TO_FILE_1, + false + ); + RemoteOperationResult result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + // move & rename file, different location + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FILE_2, + TARGET_PATH_TO_FILE_2_RENAMED, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + // move & rename file, same location (rename file) + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FILE_3, + SRC_PATH_TO_FILE_3_RENAMED, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + // move empty folder + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_EMPTY_FOLDER, + TARGET_PATH_TO_EMPTY_FOLDER, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + // move non-empty folder + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FULL_FOLDER_1, + TARGET_PATH_TO_FULL_FOLDER_1, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + // move & rename folder, different location + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FULL_FOLDER_2, + TARGET_PATH_TO_FULL_FOLDER_2_RENAMED, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + // move & rename folder, same location (rename folder) + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FULL_FOLDER_3, + SRC_PATH_TO_FULL_FOLDER_3_RENAMED, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + // move for nothing (success, but no interaction with network) + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FILE_4, + SRC_PATH_TO_FILE_4, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + // move overwriting + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FULL_FOLDER_4, + TARGET_PATH_TO_ALREADY_EXISTENT_EMPTY_FOLDER_4, + true + ); + result = moveOperation.execute(mClient); + assertTrue(result.isSuccess()); + + + /// Failed cases + + // file to move does not exist + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_NON_EXISTENT_FILE, + TARGET_PATH_TO_NON_EXISTENT_FILE, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.getCode() == ResultCode.FILE_NOT_FOUND); + + // folder to move into does no exist + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FILE_5, + TARGET_PATH_TO_FILE_5_INTO_NON_EXISTENT_FOLDER, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.getHttpCode() == HttpStatus.SC_CONFLICT); + + // target location (renaming) has invalid characters + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FILE_6, + TARGET_PATH_RENAMED_WITH_INVALID_CHARS, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME); + + // name collision + moveOperation = new MoveRemoteFileOperation( + SRC_PATH_TO_FILE_7, + TARGET_PATH_TO_ALREADY_EXISTENT_FILE_7, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.getCode() == ResultCode.INVALID_OVERWRITE); + + // move a folder into a descendant + moveOperation = new MoveRemoteFileOperation( + SRC_BASE_FOLDER, + SRC_PATH_TO_EMPTY_FOLDER, + false + ); + result = moveOperation.execute(mClient); + assertTrue(result.getCode() == ResultCode.INVALID_MOVE_INTO_DESCENDANT); + + } + + @Override + protected void tearDown() throws Exception { + Log.v(LOG_TAG, "Deleting remote fixture..."); + + String[] mPathsToCleanUp = { + SRC_BASE_FOLDER, + TARGET_BASE_FOLDER + }; + + for (String path : mPathsToCleanUp) { + RemoteOperationResult removeResult = + TestActivity.removeFile(path, mClient); + if (!removeResult.isSuccess() && removeResult.getCode() != ResultCode.TIMEOUT ) { + Utils.logAndThrow(LOG_TAG, removeResult); + } + } + + super.tearDown(); + + Log.v(LOG_TAG, "Remote fixture delete."); + } + + + private void initAccessToServer(Context context) { + Log.v(LOG_TAG, "Setting up client instance to access OC server..."); + + mServerUri = context.getString(R.string.server_base_url); + mUser = context.getString(R.string.username); + mPass = context.getString(R.string.password); + + mClient = new OwnCloudClient( + Uri.parse(mServerUri), + NetworkUtils.getMultiThreadedConnManager() + ); + mClient.setDefaultTimeouts( + OwnCloudClientFactory.DEFAULT_DATA_TIMEOUT, + OwnCloudClientFactory.DEFAULT_CONNECTION_TIMEOUT); + mClient.setFollowRedirects(true); + mClient.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials( + mUser, + mPass + ) + ); + + Log.v(LOG_TAG, "Client instance set up."); + + } + + +}