diff --git a/src/com/owncloud/android/lib/common/OwnCloudClient.java b/src/com/owncloud/android/lib/common/OwnCloudClient.java index 7364f345..a02e9ee3 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/src/com/owncloud/android/lib/common/OwnCloudClient.java @@ -52,6 +52,7 @@ import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.network.RedirectionPath; import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; public class OwnCloudClient extends HttpClient { @@ -68,6 +69,8 @@ public class OwnCloudClient extends HttpClient { private int mInstanceNumber = 0; private Uri mBaseUri; + + private OwnCloudVersion mVersion = null; /** * Constructor @@ -441,4 +444,12 @@ public class OwnCloudClient extends HttpClient { } + public void setOwnCloudVersion(String version){ + OwnCloudVersion ver = new OwnCloudVersion(version); + mVersion = ver; + } + + public OwnCloudVersion getOwnCloudVersion(){ + return mVersion; + } } diff --git a/src/com/owncloud/android/lib/common/operations/InvalidCharacterExceptionParser.java b/src/com/owncloud/android/lib/common/operations/InvalidCharacterExceptionParser.java new file mode 100644 index 00000000..63d28319 --- /dev/null +++ b/src/com/owncloud/android/lib/common/operations/InvalidCharacterExceptionParser.java @@ -0,0 +1,146 @@ + +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2015 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.common.operations; + +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Parser for Invalid Character server exception + * @author masensio + */ +public class InvalidCharacterExceptionParser { + + private static final String EXCEPTION_STRING = "OC\\Connector\\Sabre\\Exception\\InvalidPath"; + private static final String EXCEPTION_UPLOAD_STRING = "OCP\\Files\\InvalidPathException"; + + // No namespaces + private static final String ns = null; + + // Nodes for XML Parser + private static final String NODE_ERROR = "d:error"; + private static final String NODE_EXCEPTION = "s:exception"; + /** + * Parse is as an Invalid Path Exception + * @param is + * @return if The exception is an Invalid Char Exception + * @throws XmlPullParserException + * @throws IOException + */ + public boolean parseXMLResponse(InputStream is) throws XmlPullParserException, + IOException { + boolean result = false; + + try { + // XMLPullParser + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(is, null); + parser.nextTag(); + result = readError(parser); + + } finally { + is.close(); + } + return result; + } + + /** + * Parse OCS node + * @param parser + * @return List of ShareRemoteFiles + * @throws XmlPullParserException + * @throws IOException + */ + private boolean readError (XmlPullParser parser) throws XmlPullParserException, IOException { + String exception = ""; + parser.require(XmlPullParser.START_TAG, ns , NODE_ERROR); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + // read NODE_EXCEPTION + if (name.equalsIgnoreCase(NODE_EXCEPTION)) { + exception = readText(parser); + } else { + skip(parser); + } + + } + return exception.equalsIgnoreCase(EXCEPTION_STRING) || + exception.equalsIgnoreCase(EXCEPTION_UPLOAD_STRING); + + + } + + /** + * Skip tags in parser procedure + * @param parser + * @throws XmlPullParserException + * @throws IOException + */ + private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } + + /** + * Read the text from a node + * @param parser + * @return Text of the node + * @throws IOException + * @throws XmlPullParserException + */ + private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { + String result = ""; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + return result; + } +} diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index bde719ae..a54ca113 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -24,7 +24,9 @@ package com.owncloud.android.lib.common.operations; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.Serializable; import java.net.MalformedURLException; import java.net.SocketException; @@ -60,7 +62,7 @@ import com.owncloud.android.lib.common.utils.Log_OC; public class RemoteOperationResult implements Serializable { /** Generated - should be refreshed every time the class changes!! */; - private static final long serialVersionUID = 25745846447996048L; + private static final long serialVersionUID = -1909603208238358633L; private static final String TAG = RemoteOperationResult.class.getSimpleName(); @@ -103,7 +105,8 @@ public class RemoteOperationResult implements Serializable { SHARE_FORBIDDEN, OK_REDIRECT_TO_NON_SECURE_CONNECTION, INVALID_MOVE_INTO_DESCENDANT, - PARTIAL_MOVE_DONE + PARTIAL_MOVE_DONE, + INVALID_CHARACTER_DETECT_IN_SERVER } private boolean mSuccess = false; @@ -118,7 +121,9 @@ public class RemoteOperationResult implements Serializable { public RemoteOperationResult(ResultCode code) { mCode = code; - mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL || code == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION); + mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || + code == ResultCode.OK_NO_SSL || + code == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION); mData = null; } @@ -148,10 +153,11 @@ public class RemoteOperationResult implements Serializable { break; case HttpStatus.SC_FORBIDDEN: mCode = ResultCode.FORBIDDEN; - break; + break; default: mCode = ResultCode.UNHANDLED_HTTP_CODE; - Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode); + Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + + httpCode); } } } @@ -172,7 +178,38 @@ public class RemoteOperationResult implements Serializable { } } } - } + } + + public RemoteOperationResult(boolean success, String bodyResponse, int httpCode) { + mSuccess = success; + mHttpCode = httpCode; + + if (success) { + mCode = ResultCode.OK; + + } else if (httpCode > 0) { + switch (httpCode) { + case HttpStatus.SC_BAD_REQUEST: + + InputStream is = new ByteArrayInputStream(bodyResponse.getBytes()); + InvalidCharacterExceptionParser xmlParser = new InvalidCharacterExceptionParser(); + try { + if (xmlParser.parseXMLResponse(is)) + mCode = ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER; + + } catch (Exception e) { + mCode = ResultCode.UNHANDLED_HTTP_CODE; + Log_OC.e(TAG, "Exception reading exception from server", e); + } + break; + default: + mCode = ResultCode.UNHANDLED_HTTP_CODE; + Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + + httpCode); + } + } + + } public RemoteOperationResult(Exception e) { mException = e; @@ -265,7 +302,8 @@ public class RemoteOperationResult implements Serializable { } Throwable cause = mException.getCause(); Throwable previousCause = null; - while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) { + while (cause != null && cause != previousCause && + !(cause instanceof CertificateCombinedException)) { previousCause = cause; cause = cause.getCause(); } @@ -315,8 +353,10 @@ public class RemoteOperationResult implements Serializable { return "Unrecovered transport exception"; } else if (mException instanceof AccountNotFoundException) { - Account failedAccount = ((AccountNotFoundException)mException).getFailedAccount(); - return mException.getMessage() + " (" + (failedAccount != null ? failedAccount.name : "NULL") + ")"; + Account failedAccount = + ((AccountNotFoundException)mException).getFailedAccount(); + return mException.getMessage() + " (" + + (failedAccount != null ? failedAccount.name : "NULL") + ")"; } else if (mException instanceof AccountsException) { return "Exception while using account"; @@ -353,7 +393,8 @@ public class RemoteOperationResult implements Serializable { return "The file name contains an forbidden character"; } - return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")"; + return "Operation finished with HTTP status code " + mHttpCode + " (" + + (isSuccess() ? "success" : "fail") + ")"; } @@ -385,7 +426,8 @@ public class RemoteOperationResult implements Serializable { * @return boolean true/false */ public boolean isNonSecureRedirection() { - return (mRedirectedLocation != null && !(mRedirectedLocation.toLowerCase().startsWith("https://"))); + return (mRedirectedLocation != null && + !(mRedirectedLocation.toLowerCase().startsWith("https://"))); } public String getAuthenticateHeader() { diff --git a/src/com/owncloud/android/lib/resources/files/ChunkedUploadRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/ChunkedUploadRemoteFileOperation.java index 15284adb..1fa9d5fd 100644 --- a/src/com/owncloud/android/lib/resources/files/ChunkedUploadRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/ChunkedUploadRemoteFileOperation.java @@ -24,8 +24,10 @@ package com.owncloud.android.lib.resources.files; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.util.Random; @@ -37,6 +39,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 com.owncloud.android.lib.common.operations.InvalidCharacterExceptionParser; import com.owncloud.android.lib.common.utils.Log_OC; @@ -46,7 +49,7 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation private static final String OC_CHUNKED_HEADER = "OC-Chunked"; private static final String TAG = ChunkedUploadRemoteFileOperation.class.getSimpleName(); - public ChunkedUploadRemoteFileOperation(String storagePath, String remotePath, String mimeType) { + public ChunkedUploadRemoteFileOperation(String storagePath, String remotePath, String mimeType){ super(storagePath, remotePath, mimeType); } @@ -61,17 +64,21 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation raf = new RandomAccessFile(file, "r"); channel = raf.getChannel(); mEntity = new ChunkFromFileChannelRequestEntity(channel, mMimeType, CHUNK_SIZE, file); - //((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(getDataTransferListeners()); + //((ProgressiveDataTransferer)mEntity). + // addDatatransferProgressListeners(getDataTransferListeners()); synchronized (mDataTransferListeners) { - ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(mDataTransferListeners); + ((ProgressiveDataTransferer)mEntity) + .addDatatransferProgressListeners(mDataTransferListeners); } long offset = 0; - String uriPrefix = client.getWebdavUri() + WebdavUtils.encodePath(mRemotePath) + "-chunking-" + Math.abs((new Random()).nextInt(9000)+1000) + "-" ; + String uriPrefix = client.getWebdavUri() + WebdavUtils.encodePath(mRemotePath) + + "-chunking-" + Math.abs((new Random()).nextInt(9000)+1000) + "-" ; long chunkCount = (long) Math.ceil((double)file.length() / CHUNK_SIZE); for (int chunkIndex = 0; chunkIndex < chunkCount ; chunkIndex++, offset += CHUNK_SIZE) { if (mPutMethod != null) { - mPutMethod.releaseConnection(); // let the connection available for other methods + mPutMethod.releaseConnection(); // let the connection available + // for other methods } mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex); mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER); @@ -79,8 +86,26 @@ public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation ((ChunkFromFileChannelRequestEntity) mEntity).setOffset(offset); mPutMethod.setRequestEntity(mEntity); status = client.executeMethod(mPutMethod); + + if (status == 400) { + InvalidCharacterExceptionParser xmlParser = + new InvalidCharacterExceptionParser(); + InputStream is = new ByteArrayInputStream( + mPutMethod.getResponseBodyAsString().getBytes()); + try { + mForbiddenCharsInServer = xmlParser.parseXMLResponse(is); + + } catch (Exception e) { + mForbiddenCharsInServer = false; + Log_OC.e(TAG, "Exception reading exception from server", e); + } + } + client.exhaustResponse(mPutMethod.getResponseBodyAsStream()); - Log_OC.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 0d248e6f..79a7de2f 100644 --- a/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java +++ b/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java @@ -24,7 +24,6 @@ package com.owncloud.android.lib.resources.files; -import org.apache.commons.httpclient.HttpStatus; import org.apache.jackrabbit.webdav.client.methods.MkColMethod; import com.owncloud.android.lib.common.OwnCloudClient; @@ -33,7 +32,7 @@ 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; - +import com.owncloud.android.lib.resources.status.OwnCloudVersion; /** @@ -58,7 +57,8 @@ public class CreateRemoteFolderOperation 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 createFullPath 'True' means that all the ancestor folders should be created + * if don't exist yet. */ public CreateRemoteFolderOperation(String remotePath, boolean createFullPath) { mRemotePath = remotePath; @@ -73,7 +73,10 @@ public class CreateRemoteFolderOperation extends RemoteOperation { @Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result = null; - boolean noInvalidChars = FileUtils.isValidPath(mRemotePath); + OwnCloudVersion version = client.getOwnCloudVersion(); + boolean versionWithForbiddenChars = + (version != null && version.isVersionWithForbiddenCharacters()); + boolean noInvalidChars = FileUtils.isValidPath(mRemotePath, versionWithForbiddenChars); if (noInvalidChars) { result = createFolder(client); if (!result.isSuccess() && mCreateFullPath && @@ -98,9 +101,17 @@ public class CreateRemoteFolderOperation extends RemoteOperation { try { mkcol = new MkColMethod(client.getWebdavUri() + WebdavUtils.encodePath(mRemotePath)); int status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT); - result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders()); - Log_OC.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage()); - client.exhaustResponse(mkcol.getResponseBodyAsStream()); + if ( status == 400 ) { + result = new RemoteOperationResult(mkcol.succeeded(), + mkcol.getResponseBodyAsString(), status); + Log_OC.d(TAG, mkcol.getResponseBodyAsString()); + + } else { + 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); diff --git a/src/com/owncloud/android/lib/resources/files/FileUtils.java b/src/com/owncloud/android/lib/resources/files/FileUtils.java index a6fd29f6..e04e3bbf 100644 --- a/src/com/owncloud/android/lib/resources/files/FileUtils.java +++ b/src/com/owncloud/android/lib/resources/files/FileUtils.java @@ -27,9 +27,12 @@ package com.owncloud.android.lib.resources.files; import java.io.File; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; public class FileUtils { + private static final String TAG = FileUtils.class.getSimpleName(); + public static final String PATH_SEPARATOR = "/"; @@ -40,39 +43,44 @@ public class FileUtils { } /** - * Validate the fileName to detect if contains any forbidden character: / , \ , < , > , : , " , | , ? , * + * Validate the fileName to detect if contains any forbidden character: / , \ , < , > , + * : , " , | , ? , * * @param fileName + * @param versionSupportsForbiddenChars * @return */ - public static boolean isValidName(String fileName) { + public static boolean isValidName(String fileName, boolean versionSupportsForbiddenChars) { boolean result = true; - Log_OC.d("FileUtils", "fileName =======" + fileName); - if (fileName.contains(PATH_SEPARATOR) || + Log_OC.d(TAG, "fileName =======" + fileName); + if ( (versionSupportsForbiddenChars && fileName.contains(PATH_SEPARATOR)) || + (!versionSupportsForbiddenChars && ( fileName.contains(PATH_SEPARATOR) || fileName.contains("\\") || fileName.contains("<") || fileName.contains(">") || - fileName.contains(":") || fileName.contains("\"") || 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: \ , < , > , : , " , | , ? , * + * Validate the path to detect if contains any forbidden character: \ , < , > , : , " , | , + * ? , * * @param path * @return */ - public static boolean isValidPath(String path) { + public static boolean isValidPath(String path, boolean versionSupportsForbidenChars) { boolean result = true; - Log_OC.d("FileUtils", "path ....... " + path); - if (path.contains("\\") || path.contains("<") || path.contains(">") || + Log_OC.d(TAG, "path ....... " + path); + if (!versionSupportsForbidenChars && + (path.contains("\\") || path.contains("<") || path.contains(">") || path.contains(":") || path.contains("\"") || path.contains("|") || - path.contains("?") || path.contains("*")) { + path.contains("?") || path.contains("*") ) ){ result = false; } return result; } - } diff --git a/src/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java index 1eb88765..ed68535d 100644 --- a/src/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java @@ -39,6 +39,8 @@ 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; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; /** @@ -87,9 +89,13 @@ public class MoveRemoteFileOperation extends RemoteOperation { */ @Override protected RemoteOperationResult run(OwnCloudClient client) { - + + OwnCloudVersion version = client.getOwnCloudVersion(); + boolean versionWithForbiddenChars = + (version != null && version.isVersionWithForbiddenCharacters()); + /// check parameters - if (!FileUtils.isValidPath(mTargetRemotePath)) { + if (!FileUtils.isValidPath(mTargetRemotePath, versionWithForbiddenChars)) { return new RemoteOperationResult(ResultCode.INVALID_CHARACTER_IN_NAME); } @@ -128,15 +134,18 @@ public class MoveRemoteFileOperation extends RemoteOperation { /// 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()); - } + } else if (status == 400) { + result = new RemoteOperationResult(move.succeeded(), + move.getResponseBodyAsString(), status); + Log_OC.d(TAG, move.getResponseBodyAsString()); + } 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()); diff --git a/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java index d3af55f5..0ceaf227 100644 --- a/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java @@ -34,6 +34,7 @@ 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; +import com.owncloud.android.lib.resources.status.OwnCloudVersion; /** @@ -88,42 +89,52 @@ public class RenameRemoteFileOperation extends RemoteOperation { RemoteOperationResult result = null; LocalMoveMethod move = null; - - boolean noInvalidChars = FileUtils.isValidPath(mNewRemotePath); + + OwnCloudVersion version = client.getOwnCloudVersion(); + boolean versionWithForbiddenChars = + (version != null && version.isVersionWithForbiddenCharacters()); + boolean noInvalidChars = FileUtils.isValidPath(mNewRemotePath, versionWithForbiddenChars); if (noInvalidChars) { - try { + try { + if (mNewName.equals(mOldName)) { + return new RemoteOperationResult(ResultCode.OK); + } - if (mNewName.equals(mOldName)) { - return new RemoteOperationResult(ResultCode.OK); - } - - - // check if a file with the new name already exists - if (client.existsFile(mNewRemotePath)) { - return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE); + // check if a file with the new name already exists + if (client.existsFile(mNewRemotePath)) { + return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE); + } + + move = new LocalMoveMethod( client.getWebdavUri() + + WebdavUtils.encodePath(mOldRemotePath), + client.getWebdavUri() + WebdavUtils.encodePath(mNewRemotePath)); + int status = client.executeMethod(move, RENAME_READ_TIMEOUT, + RENAME_CONNECTION_TIMEOUT); + + if (status == 400) { + result = new RemoteOperationResult(move.succeeded(), + move.getResponseBodyAsString(), status); + Log_OC.d(TAG, move.getResponseBodyAsString()); + } else { + client.exhaustResponse(move.getResponseBodyAsStream());//exhaust response, + // although not interesting + result = new RemoteOperationResult(move.succeeded(), status, + move.getResponseHeaders()); + Log_OC.i(TAG, "Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " + + result.getLogMessage()); + } + } catch (Exception e) { + result = new RemoteOperationResult(e); + Log_OC.e(TAG, "Rename " + mOldRemotePath + " to " + + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + + result.getLogMessage(), e); + + } finally { + if (move != null) + move.releaseConnection(); } - move = new LocalMoveMethod( client.getWebdavUri() + - WebdavUtils.encodePath(mOldRemotePath), - client.getWebdavUri() + WebdavUtils.encodePath(mNewRemotePath)); - int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT); - - move.getResponseBodyAsString(); // exhaust response, although not interesting - result = new RemoteOperationResult(move.succeeded(), status, move.getResponseHeaders()); - Log_OC.i(TAG, "Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " + - result.getLogMessage()); - - } catch (Exception e) { - result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Rename " + mOldRemotePath + " to " + - ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + - result.getLogMessage(), e); - - } finally { - if (move != null) - move.releaseConnection(); - } } else { result = new RemoteOperationResult(ResultCode.INVALID_CHARACTER_IN_NAME); } diff --git a/src/com/owncloud/android/lib/resources/files/UploadRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/UploadRemoteFileOperation.java index 9043fe36..25dc87f5 100644 --- a/src/com/owncloud/android/lib/resources/files/UploadRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/UploadRemoteFileOperation.java @@ -24,8 +24,10 @@ package com.owncloud.android.lib.resources.files; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -40,9 +42,11 @@ import com.owncloud.android.lib.common.network.FileRequestEntity; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.network.ProgressiveDataTransferer; import com.owncloud.android.lib.common.network.WebdavUtils; +import com.owncloud.android.lib.common.operations.InvalidCharacterExceptionParser; 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 upload of a remote file to the ownCloud server. @@ -53,12 +57,15 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; public class UploadRemoteFileOperation extends RemoteOperation { + private static final String TAG = UploadRemoteFileOperation.class.getSimpleName(); + protected static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length"; protected String mLocalPath; protected String mRemotePath; protected String mMimeType; protected PutMethod mPutMethod = null; + protected boolean mForbiddenCharsInServer = false; private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); protected Set mDataTransferListeners = new HashSet(); @@ -87,10 +94,13 @@ public class UploadRemoteFileOperation extends RemoteOperation { } int status = uploadFile(client); - - result = new RemoteOperationResult(isSuccess(status), status, - (mPutMethod != null ? mPutMethod.getResponseHeaders() : null)); - + if (mForbiddenCharsInServer){ + result = new RemoteOperationResult( + RemoteOperationResult.ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER); + } else { + result = new RemoteOperationResult(isSuccess(status), status, + (mPutMethod != null ? mPutMethod.getResponseHeaders() : null)); + } } catch (Exception e) { // TODO something cleaner with cancellations if (mCancellationRequested.get()) { @@ -120,6 +130,20 @@ public class UploadRemoteFileOperation extends RemoteOperation { mPutMethod.addRequestHeader(OC_TOTAL_LENGTH_HEADER, String.valueOf(f.length())); mPutMethod.setRequestEntity(mEntity); status = client.executeMethod(mPutMethod); + + if (status == 400) { + InvalidCharacterExceptionParser xmlParser = new InvalidCharacterExceptionParser(); + InputStream is = new ByteArrayInputStream( + mPutMethod.getResponseBodyAsString().getBytes()); + try { + mForbiddenCharsInServer = xmlParser.parseXMLResponse(is); + + } catch (Exception e) { + mForbiddenCharsInServer = false; + Log_OC.e(TAG, "Exception reading exception from server", e); + } + } + client.exhaustResponse(mPutMethod.getResponseBodyAsStream()); } finally { diff --git a/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java b/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java index eb56f2d2..aa89cf3d 100644 --- a/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java +++ b/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java @@ -38,6 +38,8 @@ public class OwnCloudVersion implements Comparable { 0x04050000); public static final int MINIMUM_VERSION_FOR_SHARING_API = 0x05001B00; // 5.0.27 + + public static final int MINIMUM_VERSION_WITH_FORBIDDEN_CHARS = 0x08010000; // 8.1 private static final int MAX_DOTS = 3; @@ -120,6 +122,10 @@ public class OwnCloudVersion implements Comparable { public boolean isSharedSupported() { return (mVersion >= MINIMUM_VERSION_FOR_SHARING_API); } + + public boolean isVersionWithForbiddenCharacters() { + return (mVersion >= MINIMUM_VERSION_WITH_FORBIDDEN_CHARS); + } }