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..7f72efe9 --- /dev/null +++ b/src/com/owncloud/android/lib/common/operations/InvalidCharacterExceptionParser.java @@ -0,0 +1,144 @@ + +/* 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 on 02/06/2015. + */ +public class InvalidCharacterExceptionParser { + + private static final String EXCEPTION_STRING = "OC\\Connector\\Sabre\\Exception\\InvalidPath"; + + // 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 response of Share API + * @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); + + + } + + /** + * 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 56492955..89848ac9 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; @@ -40,6 +42,7 @@ import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpStatus; import org.apache.jackrabbit.webdav.DavException; import org.json.JSONException; +import org.xmlpull.v1.XmlPullParserException; import android.accounts.Account; import android.accounts.AccountsException; @@ -47,6 +50,7 @@ import android.accounts.AccountsException; 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; +import com.owncloud.android.lib.resources.shares.ShareXMLParser; /** @@ -103,7 +107,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; @@ -117,7 +122,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; } @@ -147,10 +154,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); } } } @@ -171,7 +179,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; @@ -264,7 +303,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(); } @@ -314,8 +354,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"; @@ -352,7 +394,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") + ")"; } @@ -384,7 +427,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/CreateRemoteFolderOperation.java b/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java index 0bfbb453..89078ba3 100644 --- a/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java +++ b/src/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java @@ -28,6 +28,7 @@ import org.apache.jackrabbit.webdav.client.methods.MkColMethod; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavUtils; +import com.owncloud.android.lib.common.operations.InvalidCharacterExceptionParser; 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; @@ -98,9 +99,16 @@ 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);