diff --git a/src/com/owncloud/android/lib/common/operations/ForbiddenExceptionParser.java b/src/com/owncloud/android/lib/common/operations/ForbiddenExceptionParser.java new file mode 100644 index 00000000..c6d9124f --- /dev/null +++ b/src/com/owncloud/android/lib/common/operations/ForbiddenExceptionParser.java @@ -0,0 +1,143 @@ + +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2016 ownCloud GmbH. + * + * 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 forbidden server exception + * @author masensio + */ +public class ForbiddenExceptionParser { + + private static final String EXCEPTION_STRING = "OCA\\DAV\\Connector\\Sabre\\Exception\\Forbidden"; + + // 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_MESSAGE = "s:message"; + + /** + * Parse is as an forbidden exception + * @param is + * @return reason for forbidden exception + * @throws XmlPullParserException + * @throws IOException + */ + public String parseXMLResponse(InputStream is) throws XmlPullParserException, + IOException { + String errorMessage = ""; + + 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(); + errorMessage = readError(parser); + + } finally { + is.close(); + } + return errorMessage; + } + + /** + * Parse OCS node + * @param parser + * @return reason for forbidden exception + * @throws XmlPullParserException + * @throws IOException + */ + private String readError (XmlPullParser parser) throws XmlPullParserException, IOException { + String message = ""; + 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_MESSAGE + if (name.equalsIgnoreCase(NODE_MESSAGE)) { + message = readText(parser); + } else { + skip(parser); + } + + } + return message; + } + + /** + * 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/InvalidCharacterExceptionParser.java b/src/com/owncloud/android/lib/common/operations/InvalidCharacterExceptionParser.java index 4629a6a1..a7cc9cc6 100644 --- a/src/com/owncloud/android/lib/common/operations/InvalidCharacterExceptionParser.java +++ b/src/com/owncloud/android/lib/common/operations/InvalidCharacterExceptionParser.java @@ -101,8 +101,6 @@ public class InvalidCharacterExceptionParser { } return exception.equalsIgnoreCase(EXCEPTION_STRING) || exception.equalsIgnoreCase(EXCEPTION_UPLOAD_STRING); - - } /** diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index 6c9c6ffd..7008cc07 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -63,7 +63,9 @@ import javax.net.ssl.SSLException; */ public class RemoteOperationResult implements Serializable { - /** Generated - should be refreshed every time the class changes!! */ + /** + * Generated - should be refreshed every time the class changes!! + */ private static final long serialVersionUID = 4968939884332372230L; private static final String TAG = RemoteOperationResult.class.getSimpleName(); @@ -131,10 +133,10 @@ public class RemoteOperationResult implements Serializable { /** * Public constructor from result code. - * + *
* To be used when the caller takes the responsibility of interpreting the result of a {@link RemoteOperation} * - * @param code {@link ResultCode} decided by the caller. + * @param code {@link ResultCode} decided by the caller. */ public RemoteOperationResult(ResultCode code) { mCode = code; @@ -146,12 +148,12 @@ public class RemoteOperationResult implements Serializable { /** * Public constructor from exception. - * + *
* To be used when an exception prevented the end of the {@link RemoteOperation}. - * + *
* Determines a {@link ResultCode} depending on the type of the exception. * - * @param e Exception that interrupted the {@link RemoteOperation} + * @param e Exception that interrupted the {@link RemoteOperation} */ public RemoteOperationResult(Exception e) { mException = e; @@ -205,28 +207,28 @@ public class RemoteOperationResult implements Serializable { /** * Public constructor from separate elements of an HTTP or DAV response. - * + *
* To be used when the result needs to be interpreted from the response of an HTTP/DAV method. - * + *
* Determines a {@link ResultCode} from the already executed method received as a parameter. Generally, * will depend on the HTTP code and HTTP response headers received. In some cases will inspect also the * response body. * - * @param success The operation was considered successful or not. - * @param httpMethod HTTP/DAV method already executed which response will be examined to interpret the - * result. + * @param success The operation was considered successful or not. + * @param httpMethod HTTP/DAV method already executed which response will be examined to interpret the + * result. */ public RemoteOperationResult(boolean success, HttpMethod httpMethod) throws IOException { this( - success, - httpMethod.getStatusCode(), - httpMethod.getStatusText(), - httpMethod.getResponseHeaders() + success, + httpMethod.getStatusCode(), + httpMethod.getStatusText(), + httpMethod.getResponseHeaders() ); if (mHttpCode == HttpStatus.SC_BAD_REQUEST) { // 400 String bodyResponse = httpMethod.getResponseBodyAsString(); - // do not get for other HTTP codes!; could not be available + // do not get for other HTTP codes!; could not be available if (bodyResponse != null && bodyResponse.length() > 0) { InputStream is = new ByteArrayInputStream(bodyResponse.getBytes()); @@ -242,23 +244,38 @@ public class RemoteOperationResult implements Serializable { } } } + + if (mHttpCode == HttpStatus.SC_FORBIDDEN) { + String bodyResponse = httpMethod.getResponseBodyAsString(); + + if (bodyResponse != null && bodyResponse.length() > 0) { + InputStream is = new ByteArrayInputStream(bodyResponse.getBytes()); + ForbiddenExceptionParser xmlParser = new ForbiddenExceptionParser(); + try { + mHttpPhrase = xmlParser.parseXMLResponse(is); + } catch (Exception e) { + Log_OC.w(TAG, "Error reading exception from server: " + e.getMessage()); + // mCode stays as set in this(success, httpCode, headers) + } + } + } } /** * Public constructor from separate elements of an HTTP or DAV response. - * + *
* To be used when the result needs to be interpreted from HTTP response elements that could come from * different requests (WARNING: black magic, try to avoid). - * + *
* If all the fields come from the same HTTP/DAV response, {@link #RemoteOperationResult(boolean, HttpMethod)} * should be used instead. - * + *
* Determines a {@link ResultCode} depending on the HTTP code and HTTP response headers received. * - * @param success The operation was considered successful or not. - * @param httpCode HTTP status code returned by an HTTP/DAV method. - * @param httpPhrase HTTP status line phrase returned by an HTTP/DAV method - * @param httpHeaders HTTP response header returned by an HTTP/DAV method + * @param success The operation was considered successful or not. + * @param httpCode HTTP status code returned by an HTTP/DAV method. + * @param httpPhrase HTTP status line phrase returned by an HTTP/DAV method + * @param httpHeaders HTTP response header returned by an HTTP/DAV method */ public RemoteOperationResult(boolean success, int httpCode, String httpPhrase, Header[] httpHeaders) { this(success, httpCode, httpPhrase); @@ -283,12 +300,12 @@ public class RemoteOperationResult implements Serializable { /** * Private constructor for results built interpreting a HTTP or DAV response. - * + *
* Determines a {@link ResultCode} depending of the type of the exception. * - * @param success Operation was successful or not. - * @param httpCode HTTP status code returned by the HTTP/DAV method. - * @param httpPhrase HTTP status line phrase returned by the HTTP/DAV method + * @param success Operation was successful or not. + * @param httpCode HTTP status code returned by the HTTP/DAV method. + * @param httpPhrase HTTP status line phrase returned by the HTTP/DAV method */ private RemoteOperationResult(boolean success, int httpCode, String httpPhrase) { mSuccess = success; @@ -324,8 +341,8 @@ public class RemoteOperationResult implements Serializable { default: mCode = ResultCode.UNHANDLED_HTTP_CODE; // UNKNOWN ERROR Log_OC.d(TAG, - "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + - mHttpCode + " " + mHttpPhrase + "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + + mHttpCode + " " + mHttpPhrase ); } }