mirror of
				https://github.com/owncloud/android-library.git
				synced 2025-11-04 04:17:50 +00:00 
			
		
		
		
	
						commit
						c36096d08c
					
				@ -1,6 +1,6 @@
 | 
			
		||||
buildscript {
 | 
			
		||||
    ext {
 | 
			
		||||
        kotlinVersion = '1.3.72'
 | 
			
		||||
        kotlinVersion = '1.4.20'
 | 
			
		||||
        moshiVersion = "1.9.2"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -9,9 +9,8 @@ buildscript {
 | 
			
		||||
        jcenter()
 | 
			
		||||
    }
 | 
			
		||||
    dependencies {
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:4.0.1'
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:4.1.1'
 | 
			
		||||
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
 | 
			
		||||
        classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,25 +1,20 @@
 | 
			
		||||
apply plugin: 'com.android.library'
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
apply plugin: 'kotlin-kapt'
 | 
			
		||||
apply plugin: 'kotlin-allopen'
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    api 'com.squareup.okhttp3:okhttp:4.6.0'
 | 
			
		||||
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
 | 
			
		||||
    api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5'
 | 
			
		||||
    api 'com.github.AppDevNext.Logcat:LogcatCore:2.1.1'
 | 
			
		||||
    api 'net.openid:appauth:0.7.1'
 | 
			
		||||
    api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2'
 | 
			
		||||
 | 
			
		||||
    // Moshi
 | 
			
		||||
    implementation ("com.squareup.moshi:moshi-kotlin:$moshiVersion") {
 | 
			
		||||
        exclude module: "kotlin-reflect"
 | 
			
		||||
    }
 | 
			
		||||
    kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
allOpen {
 | 
			
		||||
    // allows mocking for classes w/o directly opening them for release builds
 | 
			
		||||
    annotation 'com.owncloud.android.lib.testing.OpenClass'
 | 
			
		||||
    testImplementation 'junit:junit:4.13.1'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
@ -29,12 +24,8 @@ android {
 | 
			
		||||
        minSdkVersion 21
 | 
			
		||||
        targetSdkVersion 29
 | 
			
		||||
 | 
			
		||||
        versionCode = 10000800
 | 
			
		||||
        versionName = "1.0.8"
 | 
			
		||||
 | 
			
		||||
        // This is pretty ugly but manifest placeholders don't seem to work very well when using different modules
 | 
			
		||||
        // See https://github.com/openid/AppAuth-Android/issues/325
 | 
			
		||||
        manifestPlaceholders = [appAuthRedirectScheme: '']
 | 
			
		||||
        versionCode = 10000900
 | 
			
		||||
        versionName = "1.0.9"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lintOptions {
 | 
			
		||||
 | 
			
		||||
@ -57,6 +57,7 @@ public class HttpClient {
 | 
			
		||||
    private static OkHttpClient sOkHttpClient;
 | 
			
		||||
    private static Context sContext;
 | 
			
		||||
    private static HashMap<String, List<Cookie>> sCookieStore = new HashMap<>();
 | 
			
		||||
    private static LogInterceptor sLogInterceptor;
 | 
			
		||||
 | 
			
		||||
    public static OkHttpClient getOkHttpClient() {
 | 
			
		||||
        if (sOkHttpClient == null) {
 | 
			
		||||
@ -110,6 +111,7 @@ public class HttpClient {
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
 | 
			
		||||
                        .addNetworkInterceptor(getLogInterceptor())
 | 
			
		||||
                        .protocols(Arrays.asList(Protocol.HTTP_1_1))
 | 
			
		||||
                        .readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
 | 
			
		||||
                        .writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
 | 
			
		||||
@ -120,6 +122,7 @@ public class HttpClient {
 | 
			
		||||
                        .cookieJar(cookieJar);
 | 
			
		||||
                // TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok.
 | 
			
		||||
                //.hostnameVerifier(new BrowserCompatHostnameVerifier());
 | 
			
		||||
 | 
			
		||||
                sOkHttpClient = clientBuilder.build();
 | 
			
		||||
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
@ -137,6 +140,13 @@ public class HttpClient {
 | 
			
		||||
        sContext = context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static LogInterceptor getLogInterceptor() {
 | 
			
		||||
        if (sLogInterceptor == null) {
 | 
			
		||||
            sLogInterceptor = new LogInterceptor();
 | 
			
		||||
        }
 | 
			
		||||
        return sLogInterceptor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
 | 
			
		||||
        return sCookieStore.get(httpUrl.host());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,14 @@ public class HttpConstants {
 | 
			
		||||
    public static final String ACCEPT_ENCODING_IDENTITY = "identity";
 | 
			
		||||
    public static final String OC_FILE_REMOTE_ID = "OC-FileId";
 | 
			
		||||
 | 
			
		||||
    /***********************************************************************************************************
 | 
			
		||||
     ************************************************ CONTENT TYPES ********************************************
 | 
			
		||||
     ***********************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
    public static final String CONTENT_TYPE_XML = "application/xml";
 | 
			
		||||
    public static final String CONTENT_TYPE_JSON = "application/json";
 | 
			
		||||
    public static final String CONTENT_TYPE_WWW_FORM = "application/x-www-form-urlencoded";
 | 
			
		||||
 | 
			
		||||
    /***********************************************************************************************************
 | 
			
		||||
     ************************************************ STATUS CODES *********************************************
 | 
			
		||||
     ***********************************************************************************************************/
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,65 @@
 | 
			
		||||
/* ownCloud Android Library is available under MIT license
 | 
			
		||||
 *   Copyright (C) 2020 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.http
 | 
			
		||||
 | 
			
		||||
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_JSON
 | 
			
		||||
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_WWW_FORM
 | 
			
		||||
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_XML
 | 
			
		||||
import okhttp3.MediaType
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
 | 
			
		||||
object LogBuilder {
 | 
			
		||||
    fun logHttp(
 | 
			
		||||
        networkPetition: NetworkPetition,
 | 
			
		||||
        networkNode: NetworkNode,
 | 
			
		||||
        requestId: String? = "",
 | 
			
		||||
        description: String
 | 
			
		||||
    ) = Timber.d("[Network, $networkPetition] [$networkNode] [$requestId] $description")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum class NetworkPetition {
 | 
			
		||||
    REQUEST, RESPONSE;
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = super.toString().toLowerCase(Locale.ROOT)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum class NetworkNode {
 | 
			
		||||
    INFO, HEADER, BODY;
 | 
			
		||||
 | 
			
		||||
    override fun toString(): String = super.toString().toLowerCase(Locale.ROOT)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check whether a media type is loggable.
 | 
			
		||||
 *
 | 
			
		||||
 * @return true if its type is text, xml, json, or x-www-form-urlencoded.
 | 
			
		||||
 */
 | 
			
		||||
fun MediaType?.isLoggable(): Boolean =
 | 
			
		||||
    this?.let { mediaType ->
 | 
			
		||||
        val mediaTypeString = mediaType.toString()
 | 
			
		||||
        (mediaType.type == "text" ||
 | 
			
		||||
                mediaTypeString.contains(CONTENT_TYPE_XML) ||
 | 
			
		||||
                mediaTypeString.contains(CONTENT_TYPE_JSON) ||
 | 
			
		||||
                mediaTypeString.contains(CONTENT_TYPE_WWW_FORM))
 | 
			
		||||
    } ?: false
 | 
			
		||||
@ -0,0 +1,179 @@
 | 
			
		||||
/* ownCloud Android Library is available under MIT license
 | 
			
		||||
 *   Copyright (C) 2020 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.http
 | 
			
		||||
 | 
			
		||||
import com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER
 | 
			
		||||
import com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID
 | 
			
		||||
import com.owncloud.android.lib.common.http.LogBuilder.logHttp
 | 
			
		||||
import com.owncloud.android.lib.common.http.NetworkNode.BODY
 | 
			
		||||
import com.owncloud.android.lib.common.http.NetworkNode.HEADER
 | 
			
		||||
import com.owncloud.android.lib.common.http.NetworkNode.INFO
 | 
			
		||||
import com.owncloud.android.lib.common.http.NetworkPetition.REQUEST
 | 
			
		||||
import com.owncloud.android.lib.common.http.NetworkPetition.RESPONSE
 | 
			
		||||
import okhttp3.Headers
 | 
			
		||||
import okhttp3.Interceptor
 | 
			
		||||
import okhttp3.RequestBody
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import okhttp3.ResponseBody
 | 
			
		||||
import okio.Buffer
 | 
			
		||||
import java.nio.charset.Charset
 | 
			
		||||
import java.nio.charset.StandardCharsets
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
 | 
			
		||||
class LogInterceptor : Interceptor {
 | 
			
		||||
 | 
			
		||||
    override fun intercept(chain: Interceptor.Chain): Response {
 | 
			
		||||
 | 
			
		||||
        if (!httpLogsEnabled) {
 | 
			
		||||
            return chain.proceed(chain.request())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val request = chain.request().also {
 | 
			
		||||
            val requestId = it.headers[OC_X_REQUEST_ID]
 | 
			
		||||
            logHttp(REQUEST, INFO, requestId, "Type: ${it.method} URL: ${it.url}")
 | 
			
		||||
            logHeaders(requestId, it.headers, REQUEST)
 | 
			
		||||
            logRequestBody(requestId, it.body)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val response = chain.proceed(request)
 | 
			
		||||
 | 
			
		||||
        return response.also {
 | 
			
		||||
            val requestId = it.request.headers[OC_X_REQUEST_ID]
 | 
			
		||||
            logHttp(
 | 
			
		||||
                RESPONSE,
 | 
			
		||||
                INFO,
 | 
			
		||||
                requestId,
 | 
			
		||||
                "Code: ${it.code}  Message: ${it.message} IsSuccessful: ${it.isSuccessful}"
 | 
			
		||||
            )
 | 
			
		||||
            logHeaders(requestId, it.headers, RESPONSE)
 | 
			
		||||
            logResponseBody(requestId, it.body)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun logHeaders(requestId: String?, headers: Headers, networkPetition: NetworkPetition) {
 | 
			
		||||
        headers.forEach { header ->
 | 
			
		||||
            val headerValue: String = if (header.first.equals(AUTHORIZATION_HEADER, true)) {
 | 
			
		||||
                "[redacted]"
 | 
			
		||||
            } else {
 | 
			
		||||
                header.second
 | 
			
		||||
            }
 | 
			
		||||
            logHttp(networkPetition, HEADER, requestId, "${header.first}: $headerValue")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun logRequestBody(requestId: String?, requestBodyParam: RequestBody?) {
 | 
			
		||||
        requestBodyParam?.let { requestBody ->
 | 
			
		||||
 | 
			
		||||
            if (requestBody.isOneShot()) {
 | 
			
		||||
                logHttp(REQUEST, BODY, requestId, "One shot body -- Omitted")
 | 
			
		||||
                return@let
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (requestBody.isDuplex()) {
 | 
			
		||||
                logHttp(REQUEST, BODY, requestId, "Duplex body -- Omitted")
 | 
			
		||||
                return@let
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val buffer = Buffer()
 | 
			
		||||
            requestBody.writeTo(buffer)
 | 
			
		||||
 | 
			
		||||
            val contentType = requestBody.contentType()
 | 
			
		||||
            val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8
 | 
			
		||||
 | 
			
		||||
            logHttp(REQUEST, BODY, requestId, "Length: ${requestBody.contentLength()} byte body")
 | 
			
		||||
            logHttp(REQUEST, BODY, requestId, "Type: ${requestBody.contentType()}")
 | 
			
		||||
            logHttp(REQUEST, BODY, requestId, "--> Body start for request")
 | 
			
		||||
 | 
			
		||||
            if (contentType.isLoggable()) {
 | 
			
		||||
                if (requestBody.contentLength() < LIMIT_BODY_LOG) {
 | 
			
		||||
                    logHttp(REQUEST, BODY, requestId, buffer.readString(charset))
 | 
			
		||||
                } else {
 | 
			
		||||
                    logHttp(REQUEST, BODY, requestId, buffer.readString(LIMIT_BODY_LOG, charset))
 | 
			
		||||
                }
 | 
			
		||||
                logHttp(
 | 
			
		||||
                    REQUEST,
 | 
			
		||||
                    BODY,
 | 
			
		||||
                    requestId,
 | 
			
		||||
                    "<-- Body end for request -- Omitted: ${max(0, requestBody.contentLength() - LIMIT_BODY_LOG)} bytes"
 | 
			
		||||
                )
 | 
			
		||||
            } else {
 | 
			
		||||
                logHttp(
 | 
			
		||||
                    REQUEST,
 | 
			
		||||
                    BODY,
 | 
			
		||||
                    requestId,
 | 
			
		||||
                    "<-- Body end for request -- Binary -- Omitted: ${requestBody.contentLength()} bytes"
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } ?: logHttp(REQUEST, BODY, requestId, "Empty body")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun logResponseBody(requestId: String?, responseBodyParam: ResponseBody?) {
 | 
			
		||||
        responseBodyParam?.let { responseBody ->
 | 
			
		||||
 | 
			
		||||
            val contentType = responseBody.contentType()
 | 
			
		||||
            val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8
 | 
			
		||||
 | 
			
		||||
            logHttp(RESPONSE, BODY, requestId, "Length: ${responseBody.contentLength()} byte body")
 | 
			
		||||
            logHttp(RESPONSE, BODY, requestId, "Type: ${responseBody.contentType()}")
 | 
			
		||||
            logHttp(RESPONSE, BODY, requestId, "--> Body start for response")
 | 
			
		||||
 | 
			
		||||
            val source = responseBody.source()
 | 
			
		||||
            source.request(LIMIT_BODY_LOG)
 | 
			
		||||
            val buffer = source.buffer
 | 
			
		||||
 | 
			
		||||
            if (contentType.isLoggable()) {
 | 
			
		||||
 | 
			
		||||
                if (responseBody.contentLength() < LIMIT_BODY_LOG) {
 | 
			
		||||
                    logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(charset))
 | 
			
		||||
                } else {
 | 
			
		||||
                    logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(LIMIT_BODY_LOG, charset))
 | 
			
		||||
                }
 | 
			
		||||
                logHttp(
 | 
			
		||||
                    RESPONSE,
 | 
			
		||||
                    BODY,
 | 
			
		||||
                    requestId,
 | 
			
		||||
                    "<-- Body end for response -- Omitted: ${
 | 
			
		||||
                        max(
 | 
			
		||||
                            0,
 | 
			
		||||
                            responseBody.contentLength() - LIMIT_BODY_LOG
 | 
			
		||||
                        )
 | 
			
		||||
                    } bytes"
 | 
			
		||||
                )
 | 
			
		||||
            } else {
 | 
			
		||||
                logHttp(
 | 
			
		||||
                    RESPONSE,
 | 
			
		||||
                    BODY,
 | 
			
		||||
                    requestId,
 | 
			
		||||
                    "<-- Body end for response -- Binary -- Omitted: ${responseBody.contentLength()} bytes"
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        } ?: logHttp(RESPONSE, BODY, requestId, "Empty body")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        var httpLogsEnabled: Boolean = false
 | 
			
		||||
        private const val LIMIT_BODY_LOG: Long = 1024
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -84,18 +84,12 @@ public class ChunkFromFileRequestBody extends FileRequestBody {
 | 
			
		||||
            long maxCount = Math.min(mOffset + mChunkSize, mChannel.size());
 | 
			
		||||
            while (mChannel.position() < maxCount) {
 | 
			
		||||
 | 
			
		||||
                Timber.v("Sink buffer size: %s", sink.buffer().size());
 | 
			
		||||
 | 
			
		||||
                readCount = mChannel.read(mBuffer);
 | 
			
		||||
 | 
			
		||||
                Timber.v("Read " + readCount + " bytes from file channel to " + mBuffer.toString());
 | 
			
		||||
 | 
			
		||||
                sink.buffer().write(mBuffer.array(), 0, readCount);
 | 
			
		||||
                sink.getBuffer().write(mBuffer.array(), 0, readCount);
 | 
			
		||||
 | 
			
		||||
                sink.flush();
 | 
			
		||||
 | 
			
		||||
                Timber.v("Write " + readCount + " bytes to sink buffer with size " + sink.buffer().size());
 | 
			
		||||
 | 
			
		||||
                mBuffer.clear();
 | 
			
		||||
                if (mTransferred < maxCount) {  // condition to avoid accumulate progress for repeated chunks
 | 
			
		||||
                    mTransferred += readCount;
 | 
			
		||||
 | 
			
		||||
@ -53,6 +53,11 @@ public class FileRequestBody extends RequestBody implements ProgressiveDataTrans
 | 
			
		||||
        mContentType = contentType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean isOneShot() {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MediaType contentType() {
 | 
			
		||||
        return mContentType;
 | 
			
		||||
 | 
			
		||||
@ -21,8 +21,9 @@
 | 
			
		||||
 *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 *   THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
package com.owncloud.android.lib.resources.response
 | 
			
		||||
package com.owncloud.android.lib.resources
 | 
			
		||||
 | 
			
		||||
import com.squareup.moshi.Json
 | 
			
		||||
import com.squareup.moshi.JsonClass
 | 
			
		||||
 | 
			
		||||
// Response retrieved by OCS Rest API, used to obtain capabilities, shares and user info among others.
 | 
			
		||||
@ -41,6 +42,11 @@ data class OCSResponse<T>(
 | 
			
		||||
@JsonClass(generateAdapter = true)
 | 
			
		||||
data class MetaData(
 | 
			
		||||
    val status: String,
 | 
			
		||||
    val statuscode: Int,
 | 
			
		||||
    val message: String?
 | 
			
		||||
    @Json(name = "statuscode")
 | 
			
		||||
    val statusCode: Int,
 | 
			
		||||
    val message: String?,
 | 
			
		||||
    @Json(name = "itemsperpage")
 | 
			
		||||
    val itemsPerPage: String?,
 | 
			
		||||
    @Json(name = "totalitems")
 | 
			
		||||
    val totalItems: String?
 | 
			
		||||
)
 | 
			
		||||
@ -30,6 +30,8 @@ import java.io.File;
 | 
			
		||||
 | 
			
		||||
public class FileUtils {
 | 
			
		||||
    public static final String FINAL_CHUNKS_FILE = ".file";
 | 
			
		||||
    public static final String MIME_DIR = "DIR";
 | 
			
		||||
    public static final String MIME_DIR_UNIX = "httpd/unix-directory";
 | 
			
		||||
 | 
			
		||||
    static String getParentPath(String remotePath) {
 | 
			
		||||
        String parentPath = new File(remotePath).getParent();
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,17 @@ import android.os.Parcelable;
 | 
			
		||||
 | 
			
		||||
import at.bitfire.dav4jvm.Property;
 | 
			
		||||
import at.bitfire.dav4jvm.Response;
 | 
			
		||||
import at.bitfire.dav4jvm.property.*;
 | 
			
		||||
import at.bitfire.dav4jvm.property.CreationDate;
 | 
			
		||||
import at.bitfire.dav4jvm.property.GetContentLength;
 | 
			
		||||
import at.bitfire.dav4jvm.property.GetContentType;
 | 
			
		||||
import at.bitfire.dav4jvm.property.GetETag;
 | 
			
		||||
import at.bitfire.dav4jvm.property.GetLastModified;
 | 
			
		||||
import at.bitfire.dav4jvm.property.OCId;
 | 
			
		||||
import at.bitfire.dav4jvm.property.OCPermissions;
 | 
			
		||||
import at.bitfire.dav4jvm.property.OCPrivatelink;
 | 
			
		||||
import at.bitfire.dav4jvm.property.OCSize;
 | 
			
		||||
import at.bitfire.dav4jvm.property.QuotaAvailableBytes;
 | 
			
		||||
import at.bitfire.dav4jvm.property.QuotaUsedBytes;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
@ -83,7 +93,8 @@ public class RemoteFile implements Parcelable, Serializable {
 | 
			
		||||
    /**
 | 
			
		||||
     * Create new {@link RemoteFile} with given path.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'.
 | 
			
		||||
     * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first
 | 
			
		||||
     * character in 'path'.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path The remote path of the file.
 | 
			
		||||
     */
 | 
			
		||||
@ -95,7 +106,7 @@ public class RemoteFile implements Parcelable, Serializable {
 | 
			
		||||
        mRemotePath = path;
 | 
			
		||||
        mCreationTimestamp = 0;
 | 
			
		||||
        mLength = 0;
 | 
			
		||||
        mMimeType = "DIR";
 | 
			
		||||
        mMimeType = FileUtils.MIME_DIR;
 | 
			
		||||
        mQuotaUsedBytes = BigDecimal.ZERO;
 | 
			
		||||
        mQuotaAvailableBytes = BigDecimal.ZERO;
 | 
			
		||||
        mPrivateLink = null;
 | 
			
		||||
@ -154,6 +165,14 @@ public class RemoteFile implements Parcelable, Serializable {
 | 
			
		||||
        readFromParcel(source);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Use this to find out if this file is a folder.
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if it is a folder
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isFolder() {
 | 
			
		||||
        return mMimeType != null && (mMimeType.equals(FileUtils.MIME_DIR) || mMimeType.equals(FileUtils.MIME_DIR_UNIX));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Getters and Setters
 | 
			
		||||
 | 
			
		||||
@ -46,9 +46,9 @@ import java.net.URL;
 | 
			
		||||
 | 
			
		||||
public class GetRemoteShareOperation extends RemoteOperation<ShareParserResult> {
 | 
			
		||||
 | 
			
		||||
    private long mRemoteId;
 | 
			
		||||
    private String mRemoteId;
 | 
			
		||||
 | 
			
		||||
    public GetRemoteShareOperation(long remoteId) {
 | 
			
		||||
    public GetRemoteShareOperation(String remoteId) {
 | 
			
		||||
        mRemoteId = remoteId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@ public class GetRemoteShareOperation extends RemoteOperation<ShareParserResult>
 | 
			
		||||
            Uri requestUri = client.getBaseUri();
 | 
			
		||||
            Uri.Builder uriBuilder = requestUri.buildUpon();
 | 
			
		||||
            uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH);
 | 
			
		||||
            uriBuilder.appendEncodedPath(Long.toString(mRemoteId));
 | 
			
		||||
            uriBuilder.appendEncodedPath(mRemoteId);
 | 
			
		||||
 | 
			
		||||
            GetMethod getMethod = new GetMethod(new URL(uriBuilder.build().toString()));
 | 
			
		||||
            getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE);
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
/* ownCloud Android Library is available under MIT license
 | 
			
		||||
 *
 | 
			
		||||
 *   @author Christian Schabesberger
 | 
			
		||||
 *   @author masensio
 | 
			
		||||
 *   @author David A. Velasco
 | 
			
		||||
 *   @author David González Verdugo
 | 
			
		||||
@ -28,16 +29,21 @@
 | 
			
		||||
 | 
			
		||||
package com.owncloud.android.lib.resources.shares
 | 
			
		||||
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
			
		||||
import com.owncloud.android.lib.common.http.HttpConstants
 | 
			
		||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
 | 
			
		||||
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.OK
 | 
			
		||||
import org.json.JSONObject
 | 
			
		||||
import com.owncloud.android.lib.resources.CommonOcsResponse
 | 
			
		||||
import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse
 | 
			
		||||
import com.squareup.moshi.JsonAdapter
 | 
			
		||||
import com.squareup.moshi.Moshi
 | 
			
		||||
import com.squareup.moshi.Types
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import java.lang.reflect.Type
 | 
			
		||||
import java.net.URL
 | 
			
		||||
import java.util.ArrayList
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by masensio on 08/10/2015.
 | 
			
		||||
@ -65,6 +71,7 @@ import java.util.ArrayList
 | 
			
		||||
 * Status codes:
 | 
			
		||||
 * 100 - successful
 | 
			
		||||
 *
 | 
			
		||||
 * @author Christian Schabesberger
 | 
			
		||||
 * @author masensio
 | 
			
		||||
 * @author David A. Velasco
 | 
			
		||||
 * @author David González Verdugo
 | 
			
		||||
@ -78,82 +85,68 @@ class GetRemoteShareesOperation
 | 
			
		||||
 * @param perPage      maximum number of results in a single page
 | 
			
		||||
 */
 | 
			
		||||
    (private val searchString: String, private val page: Int, private val perPage: Int) :
 | 
			
		||||
    RemoteOperation<ArrayList<JSONObject>>() {
 | 
			
		||||
    RemoteOperation<ShareeOcsResponse>() {
 | 
			
		||||
 | 
			
		||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<ArrayList<JSONObject>> {
 | 
			
		||||
        var result: RemoteOperationResult<ArrayList<JSONObject>>
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            val requestUri = client.baseUri
 | 
			
		||||
            val uriBuilder = requestUri.buildUpon()
 | 
			
		||||
    private fun buildRequestUri(baseUri: Uri) =
 | 
			
		||||
        baseUri.buildUpon()
 | 
			
		||||
            .appendEncodedPath(OCS_ROUTE)
 | 
			
		||||
            .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT)
 | 
			
		||||
            .appendQueryParameter(PARAM_ITEM_TYPE, VALUE_ITEM_TYPE)
 | 
			
		||||
            .appendQueryParameter(PARAM_SEARCH, searchString)
 | 
			
		||||
            .appendQueryParameter(PARAM_PAGE, page.toString())
 | 
			
		||||
            .appendQueryParameter(PARAM_PER_PAGE, perPage.toString())
 | 
			
		||||
            .build()
 | 
			
		||||
 | 
			
		||||
            val getMethod = GetMethod(URL(uriBuilder.build().toString()))
 | 
			
		||||
 | 
			
		||||
            getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
 | 
			
		||||
 | 
			
		||||
            val status = client.executeHttpMethod(getMethod)
 | 
			
		||||
            val response = getMethod.getResponseBodyAsString()
 | 
			
		||||
 | 
			
		||||
            if (isSuccess(status)) {
 | 
			
		||||
                Timber.d("Successful response: $response")
 | 
			
		||||
 | 
			
		||||
                // Parse the response
 | 
			
		||||
                val respJSON = JSONObject(response)
 | 
			
		||||
                val respOCS = respJSON.getJSONObject(NODE_OCS)
 | 
			
		||||
                val respData = respOCS.getJSONObject(NODE_DATA)
 | 
			
		||||
                val respExact = respData.getJSONObject(NODE_EXACT)
 | 
			
		||||
                val respExactUsers = respExact.getJSONArray(NODE_USERS)
 | 
			
		||||
                val respExactGroups = respExact.getJSONArray(NODE_GROUPS)
 | 
			
		||||
                val respExactRemotes = respExact.getJSONArray(NODE_REMOTES)
 | 
			
		||||
                val respPartialUsers = respData.getJSONArray(NODE_USERS)
 | 
			
		||||
                val respPartialGroups = respData.getJSONArray(NODE_GROUPS)
 | 
			
		||||
                val respPartialRemotes = respData.getJSONArray(NODE_REMOTES)
 | 
			
		||||
                val jsonResults = arrayOf(
 | 
			
		||||
                    respExactUsers,
 | 
			
		||||
                    respExactGroups,
 | 
			
		||||
                    respExactRemotes,
 | 
			
		||||
                    respPartialUsers,
 | 
			
		||||
                    respPartialGroups,
 | 
			
		||||
                    respPartialRemotes
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                val data = ArrayList<JSONObject>() // For result data
 | 
			
		||||
                for (i in 0..5) {
 | 
			
		||||
                    for (j in 0 until jsonResults[i].length()) {
 | 
			
		||||
                        val jsonResult = jsonResults[i].getJSONObject(j)
 | 
			
		||||
                        data.add(jsonResult)
 | 
			
		||||
                        Timber.d("*** Added item: ${jsonResult.getString(PROPERTY_LABEL)}")
 | 
			
		||||
                    }
 | 
			
		||||
    private fun parseResponse(response: String): ShareeOcsResponse? {
 | 
			
		||||
        val moshi = Moshi.Builder().build()
 | 
			
		||||
        val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java)
 | 
			
		||||
        val adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>> = moshi.adapter(type)
 | 
			
		||||
        return adapter.fromJson(response)!!.ocs.data
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
                result = RemoteOperationResult(OK)
 | 
			
		||||
                result.data = data
 | 
			
		||||
 | 
			
		||||
                Timber.d("*** Get Users or groups completed ")
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
                result = RemoteOperationResult(getMethod)
 | 
			
		||||
    private fun onResultUnsuccessful(
 | 
			
		||||
        method: GetMethod,
 | 
			
		||||
        response: String?,
 | 
			
		||||
        status: Int
 | 
			
		||||
    ): RemoteOperationResult<ShareeOcsResponse> {
 | 
			
		||||
        Timber.e("Failed response while getting users/groups from the server ")
 | 
			
		||||
        if (response != null) {
 | 
			
		||||
            Timber.e("*** status code: $status; response message: $response")
 | 
			
		||||
        } else {
 | 
			
		||||
            Timber.e("*** status code: $status")
 | 
			
		||||
        }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            result = RemoteOperationResult(e)
 | 
			
		||||
            Timber.e(e, "Exception while getting users/groups")
 | 
			
		||||
        return RemoteOperationResult(method)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareeOcsResponse> {
 | 
			
		||||
        val result = RemoteOperationResult<ShareeOcsResponse>(OK)
 | 
			
		||||
        Timber.d("Successful response: $response")
 | 
			
		||||
        result.data = parseResponse(response!!)
 | 
			
		||||
        Timber.d("*** Get Users or groups completed ")
 | 
			
		||||
        return result
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<ShareeOcsResponse> {
 | 
			
		||||
        val requestUri = buildRequestUri(client.baseUri)
 | 
			
		||||
 | 
			
		||||
        val getMethod = GetMethod(URL(requestUri.toString()))
 | 
			
		||||
        getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE)
 | 
			
		||||
 | 
			
		||||
        return try {
 | 
			
		||||
            val status = client.executeHttpMethod(getMethod)
 | 
			
		||||
            val response = getMethod.getResponseBodyAsString()
 | 
			
		||||
 | 
			
		||||
            if (!isSuccess(status)) {
 | 
			
		||||
                onResultUnsuccessful(getMethod, response, status)
 | 
			
		||||
            } else {
 | 
			
		||||
                onRequestSuccessful(response)
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            Timber.e(e, "Exception while getting users/groups")
 | 
			
		||||
            RemoteOperationResult(e)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
@ -171,18 +164,5 @@ class GetRemoteShareesOperation
 | 
			
		||||
        // Arguments - constant values
 | 
			
		||||
        private const val VALUE_FORMAT = "json"
 | 
			
		||||
        private const val VALUE_ITEM_TYPE = "file"         //  to get the server search for users / groups
 | 
			
		||||
 | 
			
		||||
        // JSON Node names
 | 
			
		||||
        private const val NODE_OCS = "ocs"
 | 
			
		||||
        private const val NODE_DATA = "data"
 | 
			
		||||
        private const val NODE_EXACT = "exact"
 | 
			
		||||
        private const val NODE_USERS = "users"
 | 
			
		||||
        private const val NODE_GROUPS = "groups"
 | 
			
		||||
        private const val NODE_REMOTES = "remotes"
 | 
			
		||||
        const val NODE_VALUE = "value"
 | 
			
		||||
        const val PROPERTY_LABEL = "label"
 | 
			
		||||
        const val PROPERTY_SHARE_TYPE = "shareType"
 | 
			
		||||
        const val PROPERTY_SHARE_WITH = "shareWith"
 | 
			
		||||
        const val PROPERTY_SHARE_WITH_ADDITIONAL_INFO = "shareWithAdditionalInfo"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@
 | 
			
		||||
 | 
			
		||||
package com.owncloud.android.lib.resources.shares
 | 
			
		||||
 | 
			
		||||
import com.owncloud.android.lib.resources.files.FileUtils
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -35,7 +34,7 @@ import java.io.File
 | 
			
		||||
 * @author David González Verdugo
 | 
			
		||||
 */
 | 
			
		||||
data class RemoteShare(
 | 
			
		||||
    var id: Long = 0,
 | 
			
		||||
    var id: String = "0",
 | 
			
		||||
    var shareWith: String = "",
 | 
			
		||||
    var path: String = "",
 | 
			
		||||
    var token: String = "",
 | 
			
		||||
@ -43,15 +42,11 @@ data class RemoteShare(
 | 
			
		||||
    var sharedWithAdditionalInfo: String = "",
 | 
			
		||||
    var name: String = "",
 | 
			
		||||
    var shareLink: String = "",
 | 
			
		||||
    var fileSource: String = "0",
 | 
			
		||||
    var itemSource: String = "0",
 | 
			
		||||
    var shareType: ShareType? = ShareType.UNKNOWN,
 | 
			
		||||
    var permissions: Int = DEFAULT_PERMISSION,
 | 
			
		||||
    var sharedDate: Long = INIT_SHARED_DATE,
 | 
			
		||||
    var expirationDate: Long = INIT_EXPIRATION_DATE_IN_MILLIS,
 | 
			
		||||
    var isFolder: Boolean = path.endsWith(File.separator),
 | 
			
		||||
    var userId: Long = 0,
 | 
			
		||||
    val isValid: Boolean = id > -1
 | 
			
		||||
    var isFolder: Boolean = path.endsWith(File.separator)
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
@ -106,17 +101,6 @@ enum class ShareType constructor(val value: Int) {
 | 
			
		||||
    FEDERATED(6);
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun fromValue(value: Int): ShareType? {
 | 
			
		||||
            return when (value) {
 | 
			
		||||
                -1 -> UNKNOWN
 | 
			
		||||
                0 -> USER
 | 
			
		||||
                1 -> GROUP
 | 
			
		||||
                3 -> PUBLIC_LINK
 | 
			
		||||
                4 -> EMAIL
 | 
			
		||||
                5 -> CONTACT
 | 
			
		||||
                6 -> FEDERATED
 | 
			
		||||
                else -> null
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fun fromValue(value: Int) = values().firstOrNull { it.value == value }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ import java.net.URL
 | 
			
		||||
 *
 | 
			
		||||
 * @param remoteShareId Share ID
 | 
			
		||||
 */
 | 
			
		||||
class RemoveRemoteShareOperation(private val remoteShareId: Long) : RemoteOperation<ShareParserResult>() {
 | 
			
		||||
class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation<ShareParserResult>() {
 | 
			
		||||
 | 
			
		||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<ShareParserResult> {
 | 
			
		||||
        var result: RemoteOperationResult<ShareParserResult>
 | 
			
		||||
@ -57,7 +57,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: Long) : RemoteOperat
 | 
			
		||||
            val requestUri = client.baseUri
 | 
			
		||||
            val uriBuilder = requestUri.buildUpon()
 | 
			
		||||
            uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH)
 | 
			
		||||
            uriBuilder.appendEncodedPath(remoteShareId.toString())
 | 
			
		||||
            uriBuilder.appendEncodedPath(remoteShareId)
 | 
			
		||||
 | 
			
		||||
            val deleteMethod = DeleteMethod(
 | 
			
		||||
                URL(uriBuilder.build().toString())
 | 
			
		||||
 | 
			
		||||
@ -179,7 +179,7 @@ class ShareXMLParser {
 | 
			
		||||
                name.equals(NODE_ID, ignoreCase = true) -> {// Parse Create XML Response
 | 
			
		||||
                    share = RemoteShare()
 | 
			
		||||
                    val value = readNode(parser, NODE_ID)
 | 
			
		||||
                    share.id = Integer.parseInt(value).toLong()
 | 
			
		||||
                    share.id = value
 | 
			
		||||
                }
 | 
			
		||||
                name.equals(NODE_URL, ignoreCase = true) -> {
 | 
			
		||||
                    // NOTE: this field is received in all the public shares from OC 9.0.0
 | 
			
		||||
@ -236,7 +236,7 @@ class ShareXMLParser {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                name.equals(NODE_ID, ignoreCase = true) -> {
 | 
			
		||||
                    remoteShare.id = Integer.parseInt(readNode(parser, NODE_ID)).toLong()
 | 
			
		||||
                    remoteShare.id = readNode(parser, NODE_ID)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                name.equals(NODE_ITEM_TYPE, ignoreCase = true) -> {
 | 
			
		||||
@ -244,10 +244,6 @@ class ShareXMLParser {
 | 
			
		||||
                    fixPathForFolder(remoteShare)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                name.equals(NODE_ITEM_SOURCE, ignoreCase = true) -> {
 | 
			
		||||
                    remoteShare.itemSource = readNode(parser, NODE_ITEM_SOURCE)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                name.equals(NODE_PARENT, ignoreCase = true) -> {
 | 
			
		||||
                    readNode(parser, NODE_PARENT)
 | 
			
		||||
                }
 | 
			
		||||
@ -261,10 +257,6 @@ class ShareXMLParser {
 | 
			
		||||
                    remoteShare.shareWith = readNode(parser, NODE_SHARE_WITH)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                name.equals(NODE_FILE_SOURCE, ignoreCase = true) -> {
 | 
			
		||||
                    remoteShare.fileSource = readNode(parser, NODE_FILE_SOURCE)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                name.equals(NODE_PATH, ignoreCase = true) -> {
 | 
			
		||||
                    remoteShare.path = readNode(parser, NODE_PATH)
 | 
			
		||||
                    fixPathForFolder(remoteShare)
 | 
			
		||||
@ -320,10 +312,8 @@ class ShareXMLParser {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (remoteShare.isValid) {
 | 
			
		||||
        shares.add(remoteShare)
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun fixPathForFolder(share: RemoteShare) {
 | 
			
		||||
        if (share.isFolder && share.path.isNotEmpty() &&
 | 
			
		||||
@ -403,11 +393,9 @@ class ShareXMLParser {
 | 
			
		||||
        private const val NODE_ELEMENT = "element"
 | 
			
		||||
        private const val NODE_ID = "id"
 | 
			
		||||
        private const val NODE_ITEM_TYPE = "item_type"
 | 
			
		||||
        private const val NODE_ITEM_SOURCE = "item_source"
 | 
			
		||||
        private const val NODE_PARENT = "parent"
 | 
			
		||||
        private const val NODE_SHARE_TYPE = "share_type"
 | 
			
		||||
        private const val NODE_SHARE_WITH = "share_with"
 | 
			
		||||
        private const val NODE_FILE_SOURCE = "file_source"
 | 
			
		||||
        private const val NODE_PATH = "path"
 | 
			
		||||
        private const val NODE_PERMISSIONS = "permissions"
 | 
			
		||||
        private const val NODE_STIME = "stime"
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@ class UpdateRemoteShareOperation
 | 
			
		||||
    /**
 | 
			
		||||
     * @param remoteId Identifier of the share to update.
 | 
			
		||||
     */
 | 
			
		||||
    private val remoteId: Long
 | 
			
		||||
    private val remoteId: String
 | 
			
		||||
 | 
			
		||||
) : RemoteOperation<ShareParserResult>() {
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,72 @@
 | 
			
		||||
/* ownCloud Android Library is available under MIT license
 | 
			
		||||
 *
 | 
			
		||||
 *   Copyright (C) 2020 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.resources.shares.responses
 | 
			
		||||
 | 
			
		||||
import com.squareup.moshi.Json
 | 
			
		||||
import com.squareup.moshi.JsonClass
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This was modeled according to the documentation:
 | 
			
		||||
 * https://doc.owncloud.com/server/developer_manual/core/apis/ocs-recipient-api.html#get-shares-recipients
 | 
			
		||||
 */
 | 
			
		||||
@JsonClass(generateAdapter = true)
 | 
			
		||||
data class ShareeOcsResponse(
 | 
			
		||||
    val exact: ExactSharees?,
 | 
			
		||||
    val groups: List<ShareeItem>,
 | 
			
		||||
    val remotes: List<ShareeItem>,
 | 
			
		||||
    val users: List<ShareeItem>
 | 
			
		||||
) {
 | 
			
		||||
    fun getFlatRepresentationWithoutExact() = ArrayList<ShareeItem>().apply {
 | 
			
		||||
        addAll(users)
 | 
			
		||||
        addAll(remotes)
 | 
			
		||||
        addAll(groups)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonClass(generateAdapter = true)
 | 
			
		||||
data class ExactSharees(
 | 
			
		||||
    val groups: List<ShareeItem>,
 | 
			
		||||
    val remotes: List<ShareeItem>,
 | 
			
		||||
    val users: List<ShareeItem>
 | 
			
		||||
) {
 | 
			
		||||
    fun getFlatRepresentation() = ArrayList<ShareeItem>().apply {
 | 
			
		||||
        addAll(users)
 | 
			
		||||
        addAll(remotes)
 | 
			
		||||
        addAll(groups)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@JsonClass(generateAdapter = true)
 | 
			
		||||
data class ShareeItem(
 | 
			
		||||
    val label: String,
 | 
			
		||||
    val value: ShareeValue
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@JsonClass(generateAdapter = true)
 | 
			
		||||
data class ShareeValue(
 | 
			
		||||
    val shareType: Int,
 | 
			
		||||
    val shareWith: String,
 | 
			
		||||
    @Json(name = "shareWithAdditionalInfo")
 | 
			
		||||
    val additionalInfo: String?
 | 
			
		||||
)
 | 
			
		||||
@ -44,7 +44,7 @@ interface ShareService : Service {
 | 
			
		||||
    ): RemoteOperationResult<ShareParserResult>
 | 
			
		||||
 | 
			
		||||
    fun updateShare(
 | 
			
		||||
        remoteId: Long,
 | 
			
		||||
        remoteId: String,
 | 
			
		||||
        name: String,
 | 
			
		||||
        password: String?,
 | 
			
		||||
        expirationDate: Long,
 | 
			
		||||
@ -52,5 +52,5 @@ interface ShareService : Service {
 | 
			
		||||
        publicUpload: Boolean
 | 
			
		||||
    ): RemoteOperationResult<ShareParserResult>
 | 
			
		||||
 | 
			
		||||
    fun deleteShare(remoteId: Long): RemoteOperationResult<ShareParserResult>
 | 
			
		||||
    fun deleteShare(remoteId: String): RemoteOperationResult<ShareParserResult>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
/**
 | 
			
		||||
 * ownCloud Android client application
 | 
			
		||||
 *
 | 
			
		||||
 * @author Christian Schabesberger
 | 
			
		||||
 * @author David González Verdugo
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 ownCloud GmbH.
 | 
			
		||||
@ -22,13 +23,12 @@ package com.owncloud.android.lib.resources.shares.services
 | 
			
		||||
 | 
			
		||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
			
		||||
import com.owncloud.android.lib.resources.Service
 | 
			
		||||
import org.json.JSONObject
 | 
			
		||||
import java.util.ArrayList
 | 
			
		||||
import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse
 | 
			
		||||
 | 
			
		||||
interface ShareeService : Service {
 | 
			
		||||
    fun getSharees(
 | 
			
		||||
        searchString: String,
 | 
			
		||||
        page: Int,
 | 
			
		||||
        perPage: Int
 | 
			
		||||
    ): RemoteOperationResult<ArrayList<JSONObject>>
 | 
			
		||||
    ): RemoteOperationResult<ShareeOcsResponse>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ class OCShareService(override val client: OwnCloudClient) :
 | 
			
		||||
        }.execute(client)
 | 
			
		||||
 | 
			
		||||
    override fun updateShare(
 | 
			
		||||
        remoteId: Long,
 | 
			
		||||
        remoteId: String,
 | 
			
		||||
        name: String,
 | 
			
		||||
        password: String?,
 | 
			
		||||
        expirationDate: Long,
 | 
			
		||||
@ -84,7 +84,7 @@ class OCShareService(override val client: OwnCloudClient) :
 | 
			
		||||
            this.retrieveShareDetails = true
 | 
			
		||||
        }.execute(client)
 | 
			
		||||
 | 
			
		||||
    override fun deleteShare(remoteId: Long): RemoteOperationResult<ShareParserResult> =
 | 
			
		||||
    override fun deleteShare(remoteId: String): RemoteOperationResult<ShareParserResult> =
 | 
			
		||||
        RemoveRemoteShareOperation(
 | 
			
		||||
            remoteId
 | 
			
		||||
        ).execute(client)
 | 
			
		||||
 | 
			
		||||
@ -23,9 +23,8 @@ package com.owncloud.android.lib.resources.shares.services.implementation
 | 
			
		||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
			
		||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
			
		||||
import com.owncloud.android.lib.resources.shares.GetRemoteShareesOperation
 | 
			
		||||
import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse
 | 
			
		||||
import com.owncloud.android.lib.resources.shares.services.ShareeService
 | 
			
		||||
import org.json.JSONObject
 | 
			
		||||
import java.util.ArrayList
 | 
			
		||||
 | 
			
		||||
class OCShareeService(override val client: OwnCloudClient) :
 | 
			
		||||
    ShareeService {
 | 
			
		||||
@ -33,7 +32,7 @@ class OCShareeService(override val client: OwnCloudClient) :
 | 
			
		||||
        searchString: String,
 | 
			
		||||
        page: Int,
 | 
			
		||||
        perPage: Int
 | 
			
		||||
    ): RemoteOperationResult<ArrayList<JSONObject>> =
 | 
			
		||||
    ): RemoteOperationResult<ShareeOcsResponse> =
 | 
			
		||||
        GetRemoteShareesOperation(
 | 
			
		||||
            searchString,
 | 
			
		||||
            page,
 | 
			
		||||
 | 
			
		||||
@ -34,8 +34,8 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
 | 
			
		||||
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.OK
 | 
			
		||||
import com.owncloud.android.lib.resources.response.CapabilityResponse
 | 
			
		||||
import com.owncloud.android.lib.resources.response.CommonOcsResponse
 | 
			
		||||
import com.owncloud.android.lib.resources.status.responses.CapabilityResponse
 | 
			
		||||
import com.owncloud.android.lib.resources.CommonOcsResponse
 | 
			
		||||
import com.squareup.moshi.JsonAdapter
 | 
			
		||||
import com.squareup.moshi.Moshi
 | 
			
		||||
import com.squareup.moshi.Types
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@
 | 
			
		||||
 *   THE SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
package com.owncloud.android.lib.resources.response
 | 
			
		||||
package com.owncloud.android.lib.resources.status.responses
 | 
			
		||||
 | 
			
		||||
import com.owncloud.android.lib.resources.status.RemoteCapability
 | 
			
		||||
import com.owncloud.android.lib.resources.status.RemoteCapability.CapabilityBooleanType
 | 
			
		||||
@ -29,8 +29,8 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
 | 
			
		||||
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.resources.response.CommonOcsResponse
 | 
			
		||||
import com.owncloud.android.lib.resources.response.UserInfoResponse
 | 
			
		||||
import com.owncloud.android.lib.resources.CommonOcsResponse
 | 
			
		||||
import com.owncloud.android.lib.resources.users.responses.UserInfoResponse
 | 
			
		||||
import com.squareup.moshi.JsonAdapter
 | 
			
		||||
import com.squareup.moshi.Moshi
 | 
			
		||||
import com.squareup.moshi.Types
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@
 | 
			
		||||
 *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 *   THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
package com.owncloud.android.lib.resources.response
 | 
			
		||||
package com.owncloud.android.lib.resources.users.responses
 | 
			
		||||
 | 
			
		||||
import com.owncloud.android.lib.resources.users.RemoteUserInfo
 | 
			
		||||
import com.squareup.moshi.Json
 | 
			
		||||
@ -0,0 +1,93 @@
 | 
			
		||||
/* ownCloud Android Library is available under MIT license
 | 
			
		||||
 *   Copyright (C) 2020 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.resources.shares.responses
 | 
			
		||||
 | 
			
		||||
import com.owncloud.android.lib.resources.CommonOcsResponse
 | 
			
		||||
import com.squareup.moshi.JsonAdapter
 | 
			
		||||
import com.squareup.moshi.Moshi
 | 
			
		||||
import com.squareup.moshi.Types
 | 
			
		||||
import org.junit.Assert.assertNull
 | 
			
		||||
import org.junit.Assert.assertEquals
 | 
			
		||||
import org.junit.Assert.assertTrue
 | 
			
		||||
import org.junit.Before
 | 
			
		||||
import org.junit.Test
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.lang.reflect.Type
 | 
			
		||||
 | 
			
		||||
class ShareeResponseTest {
 | 
			
		||||
 | 
			
		||||
    lateinit var adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>>
 | 
			
		||||
 | 
			
		||||
    private fun loadResponses(fileName: String) =
 | 
			
		||||
        adapter.fromJson(File(fileName).readText())
 | 
			
		||||
 | 
			
		||||
    @Before
 | 
			
		||||
    fun prepare() {
 | 
			
		||||
        val moshi = Moshi.Builder().build()
 | 
			
		||||
        val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java)
 | 
			
		||||
        adapter = moshi.adapter(type)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun `check structure - ok - contains meta`() {
 | 
			
		||||
        val response = loadResponses(EXAMPLE_RESPONSE_JSON)!!
 | 
			
		||||
        assertEquals("OK", response.ocs.meta.message!!)
 | 
			
		||||
        assertEquals(200, response.ocs.meta.statusCode)
 | 
			
		||||
        assertEquals("ok", response.ocs.meta.status)
 | 
			
		||||
        assertTrue(response.ocs.meta.itemsPerPage?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.meta.totalItems?.isEmpty()!!)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun `example response - ok - correct sturcture`() {
 | 
			
		||||
        val response = loadResponses(EXAMPLE_RESPONSE_JSON)!!
 | 
			
		||||
        assertEquals(2, response.ocs.data.groups.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data.remotes.size)
 | 
			
		||||
        assertEquals(2, response.ocs.data.users.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data.exact?.groups?.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data.exact?.remotes?.size)
 | 
			
		||||
        assertEquals(1, response.ocs.data.exact?.users?.size)
 | 
			
		||||
        assertEquals("user1@user1.com", response.ocs.data.users.get(0).value.additionalInfo)
 | 
			
		||||
        assertNull(response.ocs.data.users[1].value.additionalInfo)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun `check empty response - ok - parsing ok`() {
 | 
			
		||||
        val response = loadResponses(EMPTY_RESPONSE_JSON)!!
 | 
			
		||||
        assertTrue(response.ocs.data.exact?.groups?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data.exact?.remotes?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data.exact?.users?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data.groups.isEmpty())
 | 
			
		||||
        assertTrue(response.ocs.data.remotes.isEmpty())
 | 
			
		||||
        assertTrue(response.ocs.data.users.isEmpty())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val RESOURCES_PATH =
 | 
			
		||||
            "src/test/responses/com.owncloud.android.lib.resources.sharees.responses"
 | 
			
		||||
        val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json"
 | 
			
		||||
        val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
{
 | 
			
		||||
  "ocs": {
 | 
			
		||||
    "meta": {
 | 
			
		||||
      "status": "ok",
 | 
			
		||||
      "statuscode": 100,
 | 
			
		||||
      "message": "OK",
 | 
			
		||||
      "totalitems": "",
 | 
			
		||||
      "itemsperpage": ""
 | 
			
		||||
    },
 | 
			
		||||
    "data": {
 | 
			
		||||
      "exact": {
 | 
			
		||||
        "users": [],
 | 
			
		||||
        "groups": [],
 | 
			
		||||
        "remotes": []
 | 
			
		||||
      },
 | 
			
		||||
      "users": [],
 | 
			
		||||
      "groups": [],
 | 
			
		||||
      "remotes": []
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,60 @@
 | 
			
		||||
{
 | 
			
		||||
  "ocs": {
 | 
			
		||||
    "data": {
 | 
			
		||||
      "exact": {
 | 
			
		||||
        "groups": [],
 | 
			
		||||
        "remotes": [],
 | 
			
		||||
        "users": [
 | 
			
		||||
          {
 | 
			
		||||
            "label": "admin",
 | 
			
		||||
            "value": {
 | 
			
		||||
              "shareType": 0,
 | 
			
		||||
              "shareWith": "admin"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "groups": [
 | 
			
		||||
        {
 | 
			
		||||
          "label": "group1",
 | 
			
		||||
          "value": {
 | 
			
		||||
            "shareType": 1,
 | 
			
		||||
            "shareWith": "group1"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "label": "group2",
 | 
			
		||||
          "value": {
 | 
			
		||||
            "shareType": 1,
 | 
			
		||||
            "shareWith": "group2"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "remotes": [],
 | 
			
		||||
      "users": [
 | 
			
		||||
        {
 | 
			
		||||
          "label": "user1",
 | 
			
		||||
          "value": {
 | 
			
		||||
            "shareType": 0,
 | 
			
		||||
            "shareWith": "user1",
 | 
			
		||||
            "shareWithAdditionalInfo": "user1@user1.com"
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "label": "user2",
 | 
			
		||||
          "value": {
 | 
			
		||||
            "shareType": 0,
 | 
			
		||||
            "shareWith": "user2"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "meta": {
 | 
			
		||||
      "itemsperpage": "",
 | 
			
		||||
      "message": "OK",
 | 
			
		||||
      "status": "ok",
 | 
			
		||||
      "statuscode": 200,
 | 
			
		||||
      "totalitems": ""
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user