mirror of
				https://github.com/owncloud/android-library.git
				synced 2025-11-04 04:17:50 +00:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "359e5912752abbdbea18fd6627212928e51a86bb" and "c2f7426a3e7f92abf570442b685958fd7fe7ee88" have entirely different histories.
		
	
	
		
			359e591275
			...
			c2f7426a3e
		
	
		
@ -1,7 +1,6 @@
 | 
				
			|||||||
apply plugin: 'com.android.library'
 | 
					apply plugin: 'com.android.library'
 | 
				
			||||||
apply plugin: 'kotlin-android'
 | 
					apply plugin: 'kotlin-android'
 | 
				
			||||||
apply plugin: 'kotlin-kapt'
 | 
					apply plugin: 'kotlin-kapt'
 | 
				
			||||||
apply plugin: 'kotlin-parcelize'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
    api 'com.squareup.okhttp3:okhttp:4.6.0'
 | 
					    api 'com.squareup.okhttp3:okhttp:4.6.0'
 | 
				
			||||||
 | 
				
			|||||||
@ -36,6 +36,7 @@ import com.owncloud.android.lib.common.http.HttpClient;
 | 
				
			|||||||
import com.owncloud.android.lib.common.http.HttpConstants;
 | 
					import com.owncloud.android.lib.common.http.HttpConstants;
 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
 | 
					import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
 | 
				
			||||||
import com.owncloud.android.lib.common.utils.RandomUtils;
 | 
					import com.owncloud.android.lib.common.utils.RandomUtils;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 | 
				
			||||||
import okhttp3.Cookie;
 | 
					import okhttp3.Cookie;
 | 
				
			||||||
import okhttp3.HttpUrl;
 | 
					import okhttp3.HttpUrl;
 | 
				
			||||||
import timber.log.Timber;
 | 
					import timber.log.Timber;
 | 
				
			||||||
@ -58,6 +59,7 @@ public class OwnCloudClient extends HttpClient {
 | 
				
			|||||||
    private OwnCloudCredentials mCredentials = null;
 | 
					    private OwnCloudCredentials mCredentials = null;
 | 
				
			||||||
    private int mInstanceNumber;
 | 
					    private int mInstanceNumber;
 | 
				
			||||||
    private Uri mBaseUri;
 | 
					    private Uri mBaseUri;
 | 
				
			||||||
 | 
					    private OwnCloudVersion mVersion = null;
 | 
				
			||||||
    private OwnCloudAccount mAccount;
 | 
					    private OwnCloudAccount mAccount;
 | 
				
			||||||
    private final ConnectionValidator mConnectionValidator;
 | 
					    private final ConnectionValidator mConnectionValidator;
 | 
				
			||||||
    private Object mRequestMutex = new Object();
 | 
					    private Object mRequestMutex = new Object();
 | 
				
			||||||
@ -183,7 +185,7 @@ public class OwnCloudClient extends HttpClient {
 | 
				
			|||||||
        return (mCredentials instanceof OwnCloudAnonymousCredentials || mAccount == null)
 | 
					        return (mCredentials instanceof OwnCloudAnonymousCredentials || mAccount == null)
 | 
				
			||||||
                ? Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0)
 | 
					                ? Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0)
 | 
				
			||||||
                : Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0 + AccountUtils.getUserId(
 | 
					                : Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0 + AccountUtils.getUserId(
 | 
				
			||||||
                        mAccount.getSavedAccount(), getContext()
 | 
					                mAccount.getSavedAccount(), getContext()
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -192,7 +194,7 @@ public class OwnCloudClient extends HttpClient {
 | 
				
			|||||||
        return mCredentials instanceof OwnCloudAnonymousCredentials
 | 
					        return mCredentials instanceof OwnCloudAnonymousCredentials
 | 
				
			||||||
                ? Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0)
 | 
					                ? Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0)
 | 
				
			||||||
                : Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0 + AccountUtils.getUserId(
 | 
					                : Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0 + AccountUtils.getUserId(
 | 
				
			||||||
                        mAccount.getSavedAccount(), getContext()
 | 
					                mAccount.getSavedAccount(), getContext()
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -239,6 +241,14 @@ public class OwnCloudClient extends HttpClient {
 | 
				
			|||||||
                HttpUrl.parse(mBaseUri.toString()));
 | 
					                HttpUrl.parse(mBaseUri.toString()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public OwnCloudVersion getOwnCloudVersion() {
 | 
				
			||||||
 | 
					        return mVersion;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setOwnCloudVersion(OwnCloudVersion version) {
 | 
				
			||||||
 | 
					        mVersion = version;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public OwnCloudAccount getAccount() {
 | 
					    public OwnCloudAccount getAccount() {
 | 
				
			||||||
        return mAccount;
 | 
					        return mAccount;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -250,4 +260,4 @@ public class OwnCloudClient extends HttpClient {
 | 
				
			|||||||
    public void setFollowRedirects(boolean followRedirects) {
 | 
					    public void setFollowRedirects(boolean followRedirects) {
 | 
				
			||||||
        this.mFollowRedirects = followRedirects;
 | 
					        this.mFollowRedirects = followRedirects;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -94,6 +94,26 @@ public class AccountUtils {
 | 
				
			|||||||
        return username;
 | 
					        return username;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the stored server version corresponding to an OC account.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param account An OC account
 | 
				
			||||||
 | 
					     * @param context Application context
 | 
				
			||||||
 | 
					     * @return Version of the OC server, according to last check
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static OwnCloudVersion getServerVersionForAccount(Account account, Context context) {
 | 
				
			||||||
 | 
					        AccountManager ama = AccountManager.get(context);
 | 
				
			||||||
 | 
					        OwnCloudVersion version = null;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            String versionString = ama.getUserData(account, Constants.KEY_OC_VERSION);
 | 
				
			||||||
 | 
					            version = new OwnCloudVersion(versionString);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Timber.e(e, "Couldn't get a the server version for an account");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return version;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @return
 | 
					     * @return
 | 
				
			||||||
     * @throws IOException
 | 
					     * @throws IOException
 | 
				
			||||||
@ -189,6 +209,11 @@ public class AccountUtils {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static class Constants {
 | 
					    public static class Constants {
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Version should be 3 numbers separated by dot so it can be parsed by
 | 
				
			||||||
 | 
					         * {@link OwnCloudVersion}
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public static final String KEY_OC_VERSION = "oc_version";
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * Base url should point to owncloud installation without trailing / ie:
 | 
					         * Base url should point to owncloud installation without trailing / ie:
 | 
				
			||||||
         * http://server/path or https://owncloud.server
 | 
					         * http://server/path or https://owncloud.server
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ import java.net.URL
 | 
				
			|||||||
class CopyMethod(
 | 
					class CopyMethod(
 | 
				
			||||||
    val url: URL,
 | 
					    val url: URL,
 | 
				
			||||||
    private val destinationUrl: String,
 | 
					    private val destinationUrl: String,
 | 
				
			||||||
    private val forceOverride: Boolean = false
 | 
					    private val forceOverride: Boolean
 | 
				
			||||||
) : DavMethod(url) {
 | 
					) : DavMethod(url) {
 | 
				
			||||||
    @Throws(Exception::class)
 | 
					    @Throws(Exception::class)
 | 
				
			||||||
    public override fun onDavExecute(davResource: DavOCResource): Int {
 | 
					    public override fun onDavExecute(davResource: DavOCResource): Int {
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ import java.net.URL
 | 
				
			|||||||
class MoveMethod(
 | 
					class MoveMethod(
 | 
				
			||||||
    url: URL,
 | 
					    url: URL,
 | 
				
			||||||
    private val destinationUrl: String,
 | 
					    private val destinationUrl: String,
 | 
				
			||||||
    private val forceOverride: Boolean = false
 | 
					    private val forceOverride: Boolean
 | 
				
			||||||
) : DavMethod(url) {
 | 
					) : DavMethod(url) {
 | 
				
			||||||
    @Throws(Exception::class)
 | 
					    @Throws(Exception::class)
 | 
				
			||||||
    override fun onDavExecute(davResource: DavOCResource): Int {
 | 
					    override fun onDavExecute(davResource: DavOCResource): Int {
 | 
				
			||||||
 | 
				
			|||||||
@ -53,7 +53,7 @@ class PropfindMethod(
 | 
				
			|||||||
            depth = depth,
 | 
					            depth = depth,
 | 
				
			||||||
            reqProp = propertiesToRequest,
 | 
					            reqProp = propertiesToRequest,
 | 
				
			||||||
            listOfHeaders = super.getRequestHeadersAsHashMap(),
 | 
					            listOfHeaders = super.getRequestHeadersAsHashMap(),
 | 
				
			||||||
            callback = { response: Response, hrefRelation: HrefRelation ->
 | 
					            callback = { response: Response, hrefRelation: HrefRelation? ->
 | 
				
			||||||
                when (hrefRelation) {
 | 
					                when (hrefRelation) {
 | 
				
			||||||
                    HrefRelation.MEMBER -> members.add(response)
 | 
					                    HrefRelation.MEMBER -> members.add(response)
 | 
				
			||||||
                    HrefRelation.SELF -> this.root = response
 | 
					                    HrefRelation.SELF -> this.root = response
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,114 @@
 | 
				
			|||||||
 | 
					/* 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.network;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import okhttp3.MediaType;
 | 
				
			||||||
 | 
					import okio.BufferedSink;
 | 
				
			||||||
 | 
					import timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.nio.ByteBuffer;
 | 
				
			||||||
 | 
					import java.nio.channels.FileChannel;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A Request body that represents a file chunk and include information about the progress when uploading it
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ChunkFromFileRequestBody extends FileRequestBody {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final FileChannel mChannel;
 | 
				
			||||||
 | 
					    private final long mChunkSize;
 | 
				
			||||||
 | 
					    private long mOffset;
 | 
				
			||||||
 | 
					    private long mTransferred;
 | 
				
			||||||
 | 
					    private ByteBuffer mBuffer = ByteBuffer.allocate(4096);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ChunkFromFileRequestBody(File file, MediaType contentType, FileChannel channel, long chunkSize) {
 | 
				
			||||||
 | 
					        super(file, contentType);
 | 
				
			||||||
 | 
					        if (channel == null) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("File may not be null");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (chunkSize <= 0) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Chunk size must be greater than zero");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.mChannel = channel;
 | 
				
			||||||
 | 
					        this.mChunkSize = chunkSize;
 | 
				
			||||||
 | 
					        mOffset = 0;
 | 
				
			||||||
 | 
					        mTransferred = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long contentLength() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return Math.min(mChunkSize, mChannel.size() - mChannel.position());
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            return mChunkSize;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeTo(BufferedSink sink) {
 | 
				
			||||||
 | 
					        int readCount;
 | 
				
			||||||
 | 
					        Iterator<OnDatatransferProgressListener> it;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mChannel.position(mOffset);
 | 
				
			||||||
 | 
					            long size = mFile.length();
 | 
				
			||||||
 | 
					            if (size == 0) {
 | 
				
			||||||
 | 
					                size = -1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            long maxCount = Math.min(mOffset + mChunkSize, mChannel.size());
 | 
				
			||||||
 | 
					            while (mChannel.position() < maxCount) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                readCount = mChannel.read(mBuffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                int bytesToWriteInBuffer = (int) Math.min(readCount, mFile.length() - mTransferred);
 | 
				
			||||||
 | 
					                sink.getBuffer().write(mBuffer.array(), 0, bytesToWriteInBuffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                sink.flush();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                mBuffer.clear();
 | 
				
			||||||
 | 
					                if (mTransferred < maxCount) {  // condition to avoid accumulate progress for repeated chunks
 | 
				
			||||||
 | 
					                    mTransferred += readCount;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					                    it = mDataTransferListeners.iterator();
 | 
				
			||||||
 | 
					                    while (it.hasNext()) {
 | 
				
			||||||
 | 
					                        it.next().onTransferProgress(readCount, mTransferred, size, mFile.getAbsolutePath());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception exception) {
 | 
				
			||||||
 | 
					            Timber.e(exception, "Transferred " + mTransferred + " bytes from a total of " + mFile.length());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setOffset(long offset) {
 | 
				
			||||||
 | 
					        this.mOffset = offset;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,92 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2022 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.network
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.chunks.ChunkedUploadFromFileSystemOperation.Companion.CHUNK_SIZE
 | 
					 | 
				
			||||||
import okhttp3.MediaType
 | 
					 | 
				
			||||||
import okio.BufferedSink
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.nio.ByteBuffer
 | 
					 | 
				
			||||||
import java.nio.channels.FileChannel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A Request body that represents a file chunk and include information about the progress when uploading it
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David González Verdugo
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class ChunkFromFileRequestBody(
 | 
					 | 
				
			||||||
    file: File,
 | 
					 | 
				
			||||||
    contentType: MediaType?,
 | 
					 | 
				
			||||||
    private val channel: FileChannel,
 | 
					 | 
				
			||||||
    private val chunkSize: Long = CHUNK_SIZE
 | 
					 | 
				
			||||||
) : FileRequestBody(file, contentType) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private var offset: Long = 0
 | 
					 | 
				
			||||||
    private var alreadyTransferred: Long = 0
 | 
					 | 
				
			||||||
    private val buffer = ByteBuffer.allocate(4_096)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    init {
 | 
					 | 
				
			||||||
        require(chunkSize > 0) { "Chunk size must be greater than zero" }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun contentLength(): Long {
 | 
					 | 
				
			||||||
        return chunkSize.coerceAtMost(channel.size() - channel.position())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun writeTo(sink: BufferedSink) {
 | 
					 | 
				
			||||||
        var readCount: Int
 | 
					 | 
				
			||||||
        var iterator: Iterator<OnDatatransferProgressListener>
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            channel.position(offset)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val maxCount = (offset + chunkSize).coerceAtMost(channel.size())
 | 
					 | 
				
			||||||
            while (channel.position() < maxCount) {
 | 
					 | 
				
			||||||
                readCount = channel.read(buffer)
 | 
					 | 
				
			||||||
                val bytesToWriteInBuffer = readCount.toLong().coerceAtMost(file.length() - alreadyTransferred).toInt()
 | 
					 | 
				
			||||||
                sink.buffer.write(buffer.array(), 0, bytesToWriteInBuffer)
 | 
					 | 
				
			||||||
                sink.flush()
 | 
					 | 
				
			||||||
                buffer.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (alreadyTransferred < maxCount) {  // condition to avoid accumulate progress for repeated chunks
 | 
					 | 
				
			||||||
                    alreadyTransferred += readCount.toLong()
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
                    iterator = dataTransferListeners.iterator()
 | 
					 | 
				
			||||||
                    while (iterator.hasNext()) {
 | 
					 | 
				
			||||||
                        iterator.next().onTransferProgress(readCount.toLong(), alreadyTransferred, file.length(), file.absolutePath)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (exception: Exception) {
 | 
					 | 
				
			||||||
            Timber.e(exception, "Transferred " + alreadyTransferred + " bytes from a total of " + file.length())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun setOffset(newOffset: Long) {
 | 
					 | 
				
			||||||
        offset = newOffset
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,117 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2022 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.network
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.content.ContentResolver
 | 
					 | 
				
			||||||
import android.net.Uri
 | 
					 | 
				
			||||||
import android.provider.OpenableColumns
 | 
					 | 
				
			||||||
import okhttp3.MediaType
 | 
					 | 
				
			||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
 | 
					 | 
				
			||||||
import okhttp3.RequestBody
 | 
					 | 
				
			||||||
import okio.BufferedSink
 | 
					 | 
				
			||||||
import okio.Source
 | 
					 | 
				
			||||||
import okio.source
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.io.IOException
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ContentUriRequestBody(
 | 
					 | 
				
			||||||
    private val contentResolver: ContentResolver,
 | 
					 | 
				
			||||||
    private val contentUri: Uri
 | 
					 | 
				
			||||||
) : RequestBody(), ProgressiveDataTransferer {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val fileSize: Long = contentResolver.query(contentUri, null, null, null, null)?.use { cursor ->
 | 
					 | 
				
			||||||
        val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
 | 
					 | 
				
			||||||
        cursor.moveToFirst()
 | 
					 | 
				
			||||||
        cursor.getLong(sizeIndex)
 | 
					 | 
				
			||||||
    } ?: -1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun contentType(): MediaType? {
 | 
					 | 
				
			||||||
        val contentType = contentResolver.getType(contentUri) ?: return null
 | 
					 | 
				
			||||||
        return contentType.toMediaTypeOrNull()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun contentLength(): Long {
 | 
					 | 
				
			||||||
        return fileSize
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun writeTo(sink: BufferedSink) {
 | 
					 | 
				
			||||||
        val inputStream = contentResolver.openInputStream(contentUri)
 | 
					 | 
				
			||||||
            ?: throw IOException("Couldn't open content URI for reading: $contentUri")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        val previousTime = System.currentTimeMillis()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sink.writeAndUpdateProgress(inputStream.source())
 | 
					 | 
				
			||||||
        inputStream.source().close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        val laterTime = System.currentTimeMillis()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Timber.d("Difference - ${laterTime - previousTime} milliseconds")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun BufferedSink.writeAndUpdateProgress(source: Source) {
 | 
					 | 
				
			||||||
        var iterator: Iterator<OnDatatransferProgressListener>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            var totalBytesRead = 0L
 | 
					 | 
				
			||||||
            var read: Long
 | 
					 | 
				
			||||||
            while (source.read(this.buffer, BYTES_TO_READ).also { read = it } != -1L) {
 | 
					 | 
				
			||||||
                totalBytesRead += read
 | 
					 | 
				
			||||||
                this.flush()
 | 
					 | 
				
			||||||
                synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
                    iterator = dataTransferListeners.iterator()
 | 
					 | 
				
			||||||
                    while (iterator.hasNext()) {
 | 
					 | 
				
			||||||
                        iterator.next().onTransferProgress(read, totalBytesRead, fileSize, contentUri.toString())
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            Timber.e(e)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
            dataTransferListeners.add(listener)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun addDatatransferProgressListeners(listeners: MutableCollection<OnDatatransferProgressListener>) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
            dataTransferListeners.addAll(listeners)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
            dataTransferListeners.remove(listener)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        private const val BYTES_TO_READ = 4_096L
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					/* 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.network;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import okhttp3.MediaType;
 | 
				
			||||||
 | 
					import okhttp3.RequestBody;
 | 
				
			||||||
 | 
					import okio.BufferedSink;
 | 
				
			||||||
 | 
					import okio.Okio;
 | 
				
			||||||
 | 
					import okio.Source;
 | 
				
			||||||
 | 
					import timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A Request body that represents a file and include information about the progress when uploading it
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class FileRequestBody extends RequestBody implements ProgressiveDataTransferer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
 | 
				
			||||||
 | 
					    protected File mFile;
 | 
				
			||||||
 | 
					    private MediaType mContentType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public FileRequestBody(File file, MediaType contentType) {
 | 
				
			||||||
 | 
					        mFile = file;
 | 
				
			||||||
 | 
					        mContentType = contentType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean isOneShot() {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public MediaType contentType() {
 | 
				
			||||||
 | 
					        return mContentType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public long contentLength() {
 | 
				
			||||||
 | 
					        return mFile.length();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeTo(BufferedSink sink) {
 | 
				
			||||||
 | 
					        Source source;
 | 
				
			||||||
 | 
					        Iterator<OnDatatransferProgressListener> it;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            source = Okio.source(mFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            long transferred = 0;
 | 
				
			||||||
 | 
					            long read;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            while ((read = source.read(sink.buffer(), 4096)) != -1) {
 | 
				
			||||||
 | 
					                transferred += read;
 | 
				
			||||||
 | 
					                sink.flush();
 | 
				
			||||||
 | 
					                synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					                    it = mDataTransferListeners.iterator();
 | 
				
			||||||
 | 
					                    while (it.hasNext()) {
 | 
				
			||||||
 | 
					                        it.next().onTransferProgress(read, transferred, mFile.length(), mFile.getAbsolutePath());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Timber.d("File with name " + mFile.getName() + " and size " + mFile.length() + " written in request body");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Timber.e(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mDataTransferListeners.add(listener);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mDataTransferListeners.addAll(listeners);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mDataTransferListeners.remove(listener);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,97 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2022 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.network
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import okhttp3.MediaType
 | 
					 | 
				
			||||||
import okhttp3.RequestBody
 | 
					 | 
				
			||||||
import okio.BufferedSink
 | 
					 | 
				
			||||||
import okio.Source
 | 
					 | 
				
			||||||
import okio.source
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.util.HashSet
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A Request body that represents a file and include information about the progress when uploading it
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David González Verdugo
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
open class FileRequestBody(
 | 
					 | 
				
			||||||
    val file: File,
 | 
					 | 
				
			||||||
    private val contentType: MediaType?,
 | 
					 | 
				
			||||||
) : RequestBody(), ProgressiveDataTransferer {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun isOneShot(): Boolean = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun contentType(): MediaType? = contentType
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun contentLength(): Long = file.length()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun writeTo(sink: BufferedSink) {
 | 
					 | 
				
			||||||
        val source: Source
 | 
					 | 
				
			||||||
        var it: Iterator<OnDatatransferProgressListener>
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            source = file.source()
 | 
					 | 
				
			||||||
            var transferred: Long = 0
 | 
					 | 
				
			||||||
            var read: Long
 | 
					 | 
				
			||||||
            while (source.read(sink.buffer, BYTES_TO_READ).also { read = it } != -1L) {
 | 
					 | 
				
			||||||
                transferred += read
 | 
					 | 
				
			||||||
                sink.flush()
 | 
					 | 
				
			||||||
                synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
                    it = dataTransferListeners.iterator()
 | 
					 | 
				
			||||||
                    while (it.hasNext()) {
 | 
					 | 
				
			||||||
                        it.next().onTransferProgress(read, transferred, file.length(), file.absolutePath)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Timber.d("File with name ${file.name} and size ${file.length()} written in request body")
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            Timber.e(e)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
            dataTransferListeners.add(listener)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun addDatatransferProgressListeners(listeners: Collection<OnDatatransferProgressListener>) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
            dataTransferListeners.addAll(listeners)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
            dataTransferListeners.remove(listener)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        private const val BYTES_TO_READ = 4_096L
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -36,7 +36,7 @@ data class CommonOcsResponse<T>(
 | 
				
			|||||||
@JsonClass(generateAdapter = true)
 | 
					@JsonClass(generateAdapter = true)
 | 
				
			||||||
data class OCSResponse<T>(
 | 
					data class OCSResponse<T>(
 | 
				
			||||||
    val meta: MetaData,
 | 
					    val meta: MetaData,
 | 
				
			||||||
    val data: T?
 | 
					    val data: T
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@JsonClass(generateAdapter = true)
 | 
					@JsonClass(generateAdapter = true)
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					/* 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.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.OwnCloudClient;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.HttpConstants;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.CopyMethod;
 | 
				
			||||||
 | 
					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 timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation moving a remote file or folder in the ownCloud server to a different folder
 | 
				
			||||||
 | 
					 * in the same account.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Allows renaming the moving file/folder at the same time.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author Christian Schabesberger
 | 
				
			||||||
 | 
					 * @author David González V.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class CopyRemoteFileOperation extends RemoteOperation<String> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int COPY_READ_TIMEOUT = 600000;
 | 
				
			||||||
 | 
					    private static final int COPY_CONNECTION_TIMEOUT = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String mSrcRemotePath;
 | 
				
			||||||
 | 
					    private String mTargetRemotePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean mOverwrite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructor.
 | 
				
			||||||
 | 
					     * <p/>
 | 
				
			||||||
 | 
					     * TODO Paths should finish in "/" in the case of folders. ?
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param srcRemotePath    Remote path of the file/folder to move.
 | 
				
			||||||
 | 
					     * @param targetRemotePath Remove path desired for the file/folder after moving it.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public CopyRemoteFileOperation(String srcRemotePath, String targetRemotePath, boolean overwrite
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        mSrcRemotePath = srcRemotePath;
 | 
				
			||||||
 | 
					        mTargetRemotePath = targetRemotePath;
 | 
				
			||||||
 | 
					        mOverwrite = overwrite;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the rename operation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client Client object to communicate with the remote ownCloud server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult<String> run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (mTargetRemotePath.equals(mSrcRemotePath)) {
 | 
				
			||||||
 | 
					            // nothing to do!
 | 
				
			||||||
 | 
					            return new RemoteOperationResult<>(ResultCode.OK);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (mTargetRemotePath.startsWith(mSrcRemotePath)) {
 | 
				
			||||||
 | 
					            return new RemoteOperationResult<>(ResultCode.INVALID_COPY_INTO_DESCENDANT);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// perform remote operation
 | 
				
			||||||
 | 
					        RemoteOperationResult result;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            CopyMethod copyMethod =
 | 
				
			||||||
 | 
					                    new CopyMethod(
 | 
				
			||||||
 | 
					                            new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mSrcRemotePath)),
 | 
				
			||||||
 | 
					                    client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath),
 | 
				
			||||||
 | 
					                    mOverwrite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            copyMethod.setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					            copyMethod.setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final int status = client.executeHttpMethod(copyMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT) {
 | 
				
			||||||
 | 
					                String fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID);
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(ResultCode.OK);
 | 
				
			||||||
 | 
					                result.setData(fileRemoteId);
 | 
				
			||||||
 | 
					            } else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !mOverwrite) {
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
 | 
				
			||||||
 | 
					                client.exhaustResponse(copyMethod.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                /// for other errors that could be explicitly handled, check first:
 | 
				
			||||||
 | 
					                /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(copyMethod);
 | 
				
			||||||
 | 
					                client.exhaustResponse(copyMethod.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Timber.i("Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					            Timber.e(e, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,114 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2022 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.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.CopyMethod
 | 
					 | 
				
			||||||
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.isOneOf
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
import java.util.concurrent.TimeUnit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation copying a remote file or folder in the ownCloud server to a different folder
 | 
					 | 
				
			||||||
 * in the same account.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Allows renaming the copying file/folder at the same time.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author Christian Schabesberger
 | 
					 | 
				
			||||||
 * @author David González V.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param srcRemotePath    Remote path of the file/folder to copy.
 | 
					 | 
				
			||||||
 * @param targetRemotePath Remote path desired for the file/folder to copy it.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class CopyRemoteFileOperation(
 | 
					 | 
				
			||||||
    private val srcRemotePath: String,
 | 
					 | 
				
			||||||
    private val targetRemotePath: String,
 | 
					 | 
				
			||||||
) : RemoteOperation<String>() {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Performs the rename operation.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param client Client object to communicate with the remote ownCloud server.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
 | 
					 | 
				
			||||||
        if (targetRemotePath == srcRemotePath) {
 | 
					 | 
				
			||||||
            // nothing to do!
 | 
					 | 
				
			||||||
            return RemoteOperationResult(ResultCode.OK)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (targetRemotePath.startsWith(srcRemotePath)) {
 | 
					 | 
				
			||||||
            return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// perform remote operation
 | 
					 | 
				
			||||||
        var result: RemoteOperationResult<String>
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            val copyMethod = CopyMethod(
 | 
					 | 
				
			||||||
                URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(srcRemotePath)),
 | 
					 | 
				
			||||||
                client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath),
 | 
					 | 
				
			||||||
            ).apply {
 | 
					 | 
				
			||||||
                setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS)
 | 
					 | 
				
			||||||
                setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            val status = client.executeHttpMethod(copyMethod)
 | 
					 | 
				
			||||||
            when {
 | 
					 | 
				
			||||||
                isSuccess(status) -> {
 | 
					 | 
				
			||||||
                    val fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID)
 | 
					 | 
				
			||||||
                    result = RemoteOperationResult(ResultCode.OK)
 | 
					 | 
				
			||||||
                    result.setData(fileRemoteId)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                isPreconditionFailed(status) -> {
 | 
					 | 
				
			||||||
                    result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE)
 | 
					 | 
				
			||||||
                    client.exhaustResponse(copyMethod.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /// for other errors that could be explicitly handled, check first:
 | 
					 | 
				
			||||||
                    /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else -> {
 | 
					 | 
				
			||||||
                    result = RemoteOperationResult(copyMethod)
 | 
					 | 
				
			||||||
                    client.exhaustResponse(copyMethod.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Timber.i("Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            result = RemoteOperationResult(e)
 | 
					 | 
				
			||||||
            Timber.e(e, "Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        private const val COPY_READ_TIMEOUT = 10L
 | 
					 | 
				
			||||||
        private const val COPY_CONNECTION_TIMEOUT = 6L
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,114 @@
 | 
				
			|||||||
 | 
					/* ownCloud Android Library is available under MIT license
 | 
				
			||||||
 | 
					 *   Copyright (C) 2019 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.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.webdav.MkColMethod;
 | 
				
			||||||
 | 
					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 timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the creation of a new folder in the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author masensio
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class CreateRemoteFolderOperation extends RemoteOperation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int READ_TIMEOUT = 30000;
 | 
				
			||||||
 | 
					    private static final int CONNECTION_TIMEOUT = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String mRemotePath;
 | 
				
			||||||
 | 
					    private boolean mCreateFullPath;
 | 
				
			||||||
 | 
					    protected boolean createChunksFolder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public CreateRemoteFolderOperation(String remotePath, boolean createFullPath) {
 | 
				
			||||||
 | 
					        mRemotePath = remotePath;
 | 
				
			||||||
 | 
					        mCreateFullPath = createFullPath;
 | 
				
			||||||
 | 
					        createChunksFolder = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the operation
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client Client object to communicate with the remote ownCloud server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        RemoteOperationResult result = createFolder(client);
 | 
				
			||||||
 | 
					        if (!result.isSuccess() && mCreateFullPath &&
 | 
				
			||||||
 | 
					                RemoteOperationResult.ResultCode.CONFLICT == result.getCode()) {
 | 
				
			||||||
 | 
					            result = createParentFolder(FileUtils.getParentPath(mRemotePath), client);
 | 
				
			||||||
 | 
					            if (result.isSuccess()) {
 | 
				
			||||||
 | 
					                result = createFolder(client);    // second (and last) try
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private RemoteOperationResult createFolder(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        RemoteOperationResult result;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            Uri webDavUri = createChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
 | 
				
			||||||
 | 
					            final MkColMethod mkcol = new MkColMethod(
 | 
				
			||||||
 | 
					                    new URL(webDavUri + WebdavUtils.encodePath(mRemotePath)));
 | 
				
			||||||
 | 
					            mkcol.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					            mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					            final int status = client.executeHttpMethod(mkcol);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result = (status == HttpConstants.HTTP_CREATED)
 | 
				
			||||||
 | 
					                    ? new RemoteOperationResult<>(ResultCode.OK)
 | 
				
			||||||
 | 
					                    : new RemoteOperationResult<>(mkcol);
 | 
				
			||||||
 | 
					            Timber.d("Create directory " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					            client.exhaustResponse(mkcol.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					            Timber.e(e, "Create directory " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private RemoteOperationResult createParentFolder(String parentPath, OwnCloudClient client) {
 | 
				
			||||||
 | 
					        RemoteOperation operation = new CreateRemoteFolderOperation(parentPath, mCreateFullPath);
 | 
				
			||||||
 | 
					        return operation.execute(client);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,109 +0,0 @@
 | 
				
			|||||||
/* 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.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.MkColMethod
 | 
					 | 
				
			||||||
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 timber.log.Timber
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
import java.util.concurrent.TimeUnit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation performing the creation of a new folder in the ownCloud server.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author masensio
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @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.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class CreateRemoteFolderOperation(
 | 
					 | 
				
			||||||
    val remotePath: String,
 | 
					 | 
				
			||||||
    private val createFullPath: Boolean,
 | 
					 | 
				
			||||||
    private val isChunksFolder: Boolean = false
 | 
					 | 
				
			||||||
) : RemoteOperation<Unit>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var result = createFolder(client)
 | 
					 | 
				
			||||||
        if (!result.isSuccess && createFullPath && result.code == ResultCode.CONFLICT) {
 | 
					 | 
				
			||||||
            result = createParentFolder(FileUtils.getParentPath(remotePath), client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (result.isSuccess) {
 | 
					 | 
				
			||||||
                // Second and last try
 | 
					 | 
				
			||||||
                result = createFolder(client)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun createFolder(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        var result: RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            val webDavUri = if (isChunksFolder) {
 | 
					 | 
				
			||||||
                client.uploadsWebDavUri
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                client.userFilesWebDavUri
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val mkCol = MkColMethod(
 | 
					 | 
				
			||||||
                URL(webDavUri.toString() + WebdavUtils.encodePath(remotePath))
 | 
					 | 
				
			||||||
            ).apply {
 | 
					 | 
				
			||||||
                setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
 | 
					 | 
				
			||||||
                setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val status = client.executeHttpMethod(mkCol)
 | 
					 | 
				
			||||||
            result =
 | 
					 | 
				
			||||||
                if (status == HttpConstants.HTTP_CREATED) {
 | 
					 | 
				
			||||||
                    RemoteOperationResult(ResultCode.OK)
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    RemoteOperationResult(mkCol)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Timber.d("Create directory $remotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
            client.exhaustResponse(mkCol.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            result = RemoteOperationResult(e)
 | 
					 | 
				
			||||||
            Timber.e(e, "Create directory $remotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun createParentFolder(parentPath: String, client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        val operation: RemoteOperation<Unit> = CreateRemoteFolderOperation(parentPath, createFullPath)
 | 
					 | 
				
			||||||
        return operation.execute(client)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        private const val READ_TIMEOUT: Long = 30_000
 | 
					 | 
				
			||||||
        private const val CONNECTION_TIMEOUT: Long = 5_000
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,221 @@
 | 
				
			|||||||
 | 
					/* 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.resources.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.network.OnDatatransferProgressListener;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.network.WebdavUtils;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.operations.OperationCancelledException;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperation;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 | 
				
			||||||
 | 
					import timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.BufferedInputStream;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.FileOutputStream;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicBoolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the download of a remote file in the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author masensio
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DownloadRemoteFileOperation extends RemoteOperation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int FORBIDDEN_ERROR = 403;
 | 
				
			||||||
 | 
					    private static final int SERVICE_UNAVAILABLE_ERROR = 503;
 | 
				
			||||||
 | 
					    private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
 | 
				
			||||||
 | 
					    private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
 | 
				
			||||||
 | 
					    private long mModificationTimestamp = 0;
 | 
				
			||||||
 | 
					    private String mEtag = "";
 | 
				
			||||||
 | 
					    private GetMethod mGet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String mRemotePath;
 | 
				
			||||||
 | 
					    private String mLocalFolderPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DownloadRemoteFileOperation(String remotePath, String localFolderPath) {
 | 
				
			||||||
 | 
					        mRemotePath = remotePath;
 | 
				
			||||||
 | 
					        mLocalFolderPath = localFolderPath;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        RemoteOperationResult result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// download will be performed to a temporal file, then moved to the final location
 | 
				
			||||||
 | 
					        File tmpFile = new File(getTmpPath());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// perform the download
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            tmpFile.getParentFile().mkdirs();
 | 
				
			||||||
 | 
					            result = downloadFile(client, tmpFile);
 | 
				
			||||||
 | 
					            Timber.i("Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					            Timber.e(e, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private RemoteOperationResult downloadFile(OwnCloudClient client, File targetFile) throws
 | 
				
			||||||
 | 
					            Exception {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        RemoteOperationResult result;
 | 
				
			||||||
 | 
					        int status;
 | 
				
			||||||
 | 
					        boolean savedFile = false;
 | 
				
			||||||
 | 
					        mGet = new GetMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)));
 | 
				
			||||||
 | 
					        Iterator<OnDatatransferProgressListener> it;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        FileOutputStream fos = null;
 | 
				
			||||||
 | 
					        BufferedInputStream bis = null;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            status = client.executeHttpMethod(mGet);
 | 
				
			||||||
 | 
					            if (isSuccess(status)) {
 | 
				
			||||||
 | 
					                targetFile.createNewFile();
 | 
				
			||||||
 | 
					                bis = new BufferedInputStream(mGet.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					                fos = new FileOutputStream(targetFile);
 | 
				
			||||||
 | 
					                long transferred = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String contentLength = mGet.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER);
 | 
				
			||||||
 | 
					                long totalToTransfer =
 | 
				
			||||||
 | 
					                        (contentLength != null
 | 
				
			||||||
 | 
					                                && contentLength.length() > 0)
 | 
				
			||||||
 | 
					                                ? Long.parseLong(contentLength)
 | 
				
			||||||
 | 
					                                : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                byte[] bytes = new byte[4096];
 | 
				
			||||||
 | 
					                int readResult;
 | 
				
			||||||
 | 
					                while ((readResult = bis.read(bytes)) != -1) {
 | 
				
			||||||
 | 
					                    synchronized (mCancellationRequested) {
 | 
				
			||||||
 | 
					                        if (mCancellationRequested.get()) {
 | 
				
			||||||
 | 
					                            mGet.abort();
 | 
				
			||||||
 | 
					                            throw new OperationCancelledException();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    fos.write(bytes, 0, readResult);
 | 
				
			||||||
 | 
					                    transferred += readResult;
 | 
				
			||||||
 | 
					                    synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					                        it = mDataTransferListeners.iterator();
 | 
				
			||||||
 | 
					                        while (it.hasNext()) {
 | 
				
			||||||
 | 
					                            it.next().onTransferProgress(readResult, transferred, totalToTransfer,
 | 
				
			||||||
 | 
					                                    targetFile.getName());
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (transferred == totalToTransfer) {  // Check if the file is completed
 | 
				
			||||||
 | 
					                    savedFile = true;
 | 
				
			||||||
 | 
					                    final String modificationTime =
 | 
				
			||||||
 | 
					                            mGet.getResponseHeaders().get("Last-Modified") != null
 | 
				
			||||||
 | 
					                                    ? mGet.getResponseHeaders().get("Last-Modified")
 | 
				
			||||||
 | 
					                                    : mGet.getResponseHeader("last-modified");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (modificationTime != null) {
 | 
				
			||||||
 | 
					                        final Date d = WebdavUtils.parseResponseDate(modificationTime);
 | 
				
			||||||
 | 
					                        mModificationTimestamp = (d != null) ? d.getTime() : 0;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        Timber.e("Could not read modification time from response downloading %s", mRemotePath);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    mEtag = WebdavUtils.getEtagFromResponse(mGet);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Get rid of extra quotas
 | 
				
			||||||
 | 
					                    mEtag = mEtag.replace("\"", "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (mEtag.length() == 0) {
 | 
				
			||||||
 | 
					                        Timber.e("Could not read eTag from response downloading %s", mRemotePath);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Timber.e("Content-Length not equal to transferred bytes.");
 | 
				
			||||||
 | 
					                    Timber.d("totalToTransfer = %d, transferred = %d", totalToTransfer, transferred);
 | 
				
			||||||
 | 
					                    client.exhaustResponse(mGet.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					                    // TODO some kind of error control!
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else if (status != FORBIDDEN_ERROR && status != SERVICE_UNAVAILABLE_ERROR) {
 | 
				
			||||||
 | 
					                client.exhaustResponse(mGet.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } // else, body read by RemoteOperationResult constructor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result = isSuccess(status)
 | 
				
			||||||
 | 
					                    ? new RemoteOperationResult<>(RemoteOperationResult.ResultCode.OK)
 | 
				
			||||||
 | 
					                    : new RemoteOperationResult<>(mGet);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (fos != null) {
 | 
				
			||||||
 | 
					                fos.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (bis != null) {
 | 
				
			||||||
 | 
					                bis.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!savedFile && targetFile.exists()) {
 | 
				
			||||||
 | 
					                targetFile.delete();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean isSuccess(int status) {
 | 
				
			||||||
 | 
					        return (status == HttpConstants.HTTP_OK);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String getTmpPath() {
 | 
				
			||||||
 | 
					        return mLocalFolderPath + mRemotePath;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mDataTransferListeners.add(listener);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mDataTransferListeners.remove(listener);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void cancel() {
 | 
				
			||||||
 | 
					        mCancellationRequested.set(true);   // atomic set; there is no need of synchronizing it
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getModificationTimestamp() {
 | 
				
			||||||
 | 
					        return mModificationTimestamp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getEtag() {
 | 
				
			||||||
 | 
					        return mEtag;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,184 +0,0 @@
 | 
				
			|||||||
/* 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.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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.network.OnDatatransferProgressListener
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.network.WebdavUtils
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.OperationCancelledException
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.io.BufferedInputStream
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.io.FileOutputStream
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
import java.util.HashSet
 | 
					 | 
				
			||||||
import java.util.concurrent.atomic.AtomicBoolean
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation performing the download of a remote file in the ownCloud server.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author masensio
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class DownloadRemoteFileOperation(
 | 
					 | 
				
			||||||
    private val remotePath: String,
 | 
					 | 
				
			||||||
    localFolderPath: String
 | 
					 | 
				
			||||||
) : RemoteOperation<Unit>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private val cancellationRequested = AtomicBoolean(false)
 | 
					 | 
				
			||||||
    private val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var modificationTimestamp: Long = 0
 | 
					 | 
				
			||||||
        private set
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var etag: String = ""
 | 
					 | 
				
			||||||
        private set
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        // download will be performed to a temporal file, then moved to the final location
 | 
					 | 
				
			||||||
        val tmpFile = File(tmpPath)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // perform the download
 | 
					 | 
				
			||||||
        return try {
 | 
					 | 
				
			||||||
            tmpFile.parentFile?.mkdirs()
 | 
					 | 
				
			||||||
            downloadFile(client, tmpFile).also { result ->
 | 
					 | 
				
			||||||
                Timber.i("Download of $remotePath to $tmpPath: ${result.logMessage}")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            RemoteOperationResult<Unit>(e).also { result ->
 | 
					 | 
				
			||||||
                Timber.e(e, "Download of $remotePath to $tmpPath: ${result.logMessage}")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Throws(Exception::class)
 | 
					 | 
				
			||||||
    private fun downloadFile(client: OwnCloudClient, targetFile: File): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        val result: RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
        var it: Iterator<OnDatatransferProgressListener>
 | 
					 | 
				
			||||||
        var fos: FileOutputStream? = null
 | 
					 | 
				
			||||||
        var bis: BufferedInputStream? = null
 | 
					 | 
				
			||||||
        var savedFile = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        val getMethod = GetMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            val status = client.executeHttpMethod(getMethod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (isSuccess(status)) {
 | 
					 | 
				
			||||||
                targetFile.createNewFile()
 | 
					 | 
				
			||||||
                bis = BufferedInputStream(getMethod.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
                fos = FileOutputStream(targetFile)
 | 
					 | 
				
			||||||
                var transferred: Long = 0
 | 
					 | 
				
			||||||
                val contentLength = getMethod.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER)
 | 
					 | 
				
			||||||
                val totalToTransfer = if (!contentLength.isNullOrEmpty()) {
 | 
					 | 
				
			||||||
                    contentLength.toLong()
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    0
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                val bytes = ByteArray(4096)
 | 
					 | 
				
			||||||
                var readResult: Int
 | 
					 | 
				
			||||||
                while (bis.read(bytes).also { readResult = it } != -1) {
 | 
					 | 
				
			||||||
                    synchronized(cancellationRequested) {
 | 
					 | 
				
			||||||
                        if (cancellationRequested.get()) {
 | 
					 | 
				
			||||||
                            getMethod.abort()
 | 
					 | 
				
			||||||
                            throw OperationCancelledException()
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    fos.write(bytes, 0, readResult)
 | 
					 | 
				
			||||||
                    transferred += readResult.toLong()
 | 
					 | 
				
			||||||
                    synchronized(dataTransferListeners) {
 | 
					 | 
				
			||||||
                        it = dataTransferListeners.iterator()
 | 
					 | 
				
			||||||
                        while (it.hasNext()) {
 | 
					 | 
				
			||||||
                            it.next()
 | 
					 | 
				
			||||||
                                .onTransferProgress(readResult.toLong(), transferred, totalToTransfer, targetFile.name)
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (transferred == totalToTransfer) {  // Check if the file is completed
 | 
					 | 
				
			||||||
                    savedFile = true
 | 
					 | 
				
			||||||
                    val modificationTime =
 | 
					 | 
				
			||||||
                        getMethod.getResponseHeaders()?.get("Last-Modified")
 | 
					 | 
				
			||||||
                            ?: getMethod.getResponseHeader("last-modified")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (modificationTime != null) {
 | 
					 | 
				
			||||||
                        val modificationDate = WebdavUtils.parseResponseDate(modificationTime)
 | 
					 | 
				
			||||||
                        modificationTimestamp = modificationDate?.time ?: 0
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        Timber.e("Could not read modification time from response downloading %s", remotePath)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    etag = WebdavUtils.getEtagFromResponse(getMethod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    // Get rid of extra quotas
 | 
					 | 
				
			||||||
                    etag = etag.replace("\"", "")
 | 
					 | 
				
			||||||
                    if (etag.isEmpty()) {
 | 
					 | 
				
			||||||
                        Timber.e("Could not read eTag from response downloading %s", remotePath)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    Timber.e("Content-Length not equal to transferred bytes.")
 | 
					 | 
				
			||||||
                    Timber.d("totalToTransfer = $totalToTransfer, transferred = $transferred")
 | 
					 | 
				
			||||||
                    client.exhaustResponse(getMethod.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
                    // TODO some kind of error control!
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            } else if (status != HttpConstants.HTTP_FORBIDDEN && status != HttpConstants.HTTP_SERVICE_UNAVAILABLE) {
 | 
					 | 
				
			||||||
                client.exhaustResponse(getMethod.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
            } // else, body read by RemoteOperationResult constructor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            result =
 | 
					 | 
				
			||||||
                if (isSuccess(status)) {
 | 
					 | 
				
			||||||
                    RemoteOperationResult(RemoteOperationResult.ResultCode.OK)
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    RemoteOperationResult(getMethod)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
        } finally {
 | 
					 | 
				
			||||||
            fos?.close()
 | 
					 | 
				
			||||||
            bis?.close()
 | 
					 | 
				
			||||||
            if (!savedFile && targetFile.exists()) {
 | 
					 | 
				
			||||||
                targetFile.delete()
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private val tmpPath: String = localFolderPath + remotePath
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListeners) { dataTransferListeners.add(listener) }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener?) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListeners) { dataTransferListeners.remove(listener) }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun cancel() {
 | 
					 | 
				
			||||||
        cancellationRequested.set(true) // atomic set; there is no need of synchronizing it
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -32,7 +32,6 @@ public class FileUtils {
 | 
				
			|||||||
    public static final String FINAL_CHUNKS_FILE = ".file";
 | 
					    public static final String FINAL_CHUNKS_FILE = ".file";
 | 
				
			||||||
    public static final String MIME_DIR = "DIR";
 | 
					    public static final String MIME_DIR = "DIR";
 | 
				
			||||||
    public static final String MIME_DIR_UNIX = "httpd/unix-directory";
 | 
					    public static final String MIME_DIR_UNIX = "httpd/unix-directory";
 | 
				
			||||||
    public static final String MODE_READ_ONLY = "r";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static String getParentPath(String remotePath) {
 | 
					    static String getParentPath(String remotePath) {
 | 
				
			||||||
        String parentPath = new File(remotePath).getParent();
 | 
					        String parentPath = new File(remotePath).getParent();
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					/* 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.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.webdav.MoveMethod;
 | 
				
			||||||
 | 
					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 timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation moving a remote file or folder in the ownCloud server to a different folder
 | 
				
			||||||
 | 
					 * in the same account.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Allows renaming the moving file/folder at the same time.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MoveRemoteFileOperation extends RemoteOperation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int MOVE_READ_TIMEOUT = 600000;
 | 
				
			||||||
 | 
					    private static final int MOVE_CONNECTION_TIMEOUT = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String mSrcRemotePath;
 | 
				
			||||||
 | 
					    private String mTargetRemotePath;
 | 
				
			||||||
 | 
					    private boolean mOverwrite;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected boolean moveChunkedFile = false;
 | 
				
			||||||
 | 
					    protected String mFileLastModifTimestamp;
 | 
				
			||||||
 | 
					    protected long mFileLength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructor.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * TODO Paths should finish in "/" in the case of folders. ?
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param srcRemotePath    Remote path of the file/folder to move.
 | 
				
			||||||
 | 
					     * @param targetRemotePath Remote path desired for the file/folder after moving it.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public MoveRemoteFileOperation(String srcRemotePath,
 | 
				
			||||||
 | 
					                                   String targetRemotePath,
 | 
				
			||||||
 | 
					                                   boolean overwrite) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mSrcRemotePath = srcRemotePath;
 | 
				
			||||||
 | 
					        mTargetRemotePath = targetRemotePath;
 | 
				
			||||||
 | 
					        mOverwrite = overwrite;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the rename operation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client Client object to communicate with the remote ownCloud server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        if (mTargetRemotePath.equals(mSrcRemotePath)) {
 | 
				
			||||||
 | 
					            // nothing to do!
 | 
				
			||||||
 | 
					            return new RemoteOperationResult<>(ResultCode.OK);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (mTargetRemotePath.startsWith(mSrcRemotePath)) {
 | 
				
			||||||
 | 
					            return new RemoteOperationResult<>(ResultCode.INVALID_MOVE_INTO_DESCENDANT);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// perform remote operation
 | 
				
			||||||
 | 
					        RemoteOperationResult result;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // After finishing a chunked upload, we have to move the resulting file from uploads folder to files one,
 | 
				
			||||||
 | 
					            // so this uri has to be customizable
 | 
				
			||||||
 | 
					            Uri srcWebDavUri = moveChunkedFile ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final MoveMethod move = new MoveMethod(
 | 
				
			||||||
 | 
					                    new URL(srcWebDavUri + WebdavUtils.encodePath(mSrcRemotePath)),
 | 
				
			||||||
 | 
					                    client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath),
 | 
				
			||||||
 | 
					                    mOverwrite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (moveChunkedFile) {
 | 
				
			||||||
 | 
					                move.addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
 | 
				
			||||||
 | 
					                move.addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, String.valueOf(mFileLength));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            move.setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					            move.setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final int status = client.executeHttpMethod(move);
 | 
				
			||||||
 | 
					            /// process response
 | 
				
			||||||
 | 
					            if (isSuccess(status)) {
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(ResultCode.OK);
 | 
				
			||||||
 | 
					            } else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !mOverwrite) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
 | 
				
			||||||
 | 
					                client.exhaustResponse(move.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                /// for other errors that could be explicitly handled, check first:
 | 
				
			||||||
 | 
					                /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(move);
 | 
				
			||||||
 | 
					                client.exhaustResponse(move.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Timber.i("Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					            Timber.e(e, "Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected boolean isSuccess(int status) {
 | 
				
			||||||
 | 
					        return status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,133 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2021 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.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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.webdav.MoveMethod
 | 
					 | 
				
			||||||
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.isOneOf
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
import java.util.concurrent.TimeUnit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation moving a remote file or folder in the ownCloud server to a different folder
 | 
					 | 
				
			||||||
 * in the same account.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Allows renaming the moving file/folder at the same time.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author David González Verdugo
 | 
					 | 
				
			||||||
 * @author Abel García de Prada
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
open class MoveRemoteFileOperation(
 | 
					 | 
				
			||||||
    private val sourceRemotePath: String,
 | 
					 | 
				
			||||||
    private val targetRemotePath: String,
 | 
					 | 
				
			||||||
) : RemoteOperation<Unit>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Performs the rename operation.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param client Client object to communicate with the remote ownCloud server.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        if (targetRemotePath == sourceRemotePath) {
 | 
					 | 
				
			||||||
            // nothing to do!
 | 
					 | 
				
			||||||
            return RemoteOperationResult(ResultCode.OK)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (targetRemotePath.startsWith(sourceRemotePath)) {
 | 
					 | 
				
			||||||
            return RemoteOperationResult(ResultCode.INVALID_MOVE_INTO_DESCENDANT)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// perform remote operation
 | 
					 | 
				
			||||||
        var result: RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            // After finishing a chunked upload, we have to move the resulting file from uploads folder to files one,
 | 
					 | 
				
			||||||
            // so this uri has to be customizable
 | 
					 | 
				
			||||||
            val srcWebDavUri = getSrcWebDavUriForClient(client)
 | 
					 | 
				
			||||||
            val moveMethod = MoveMethod(
 | 
					 | 
				
			||||||
                url = URL(srcWebDavUri.toString() + WebdavUtils.encodePath(sourceRemotePath)),
 | 
					 | 
				
			||||||
                destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath),
 | 
					 | 
				
			||||||
            ).apply {
 | 
					 | 
				
			||||||
                addRequestHeaders(this)
 | 
					 | 
				
			||||||
                setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS)
 | 
					 | 
				
			||||||
                setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val status = client.executeHttpMethod(moveMethod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            when {
 | 
					 | 
				
			||||||
                isSuccess(status) -> {
 | 
					 | 
				
			||||||
                    result = RemoteOperationResult<Unit>(ResultCode.OK)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                isPreconditionFailed(status) -> {
 | 
					 | 
				
			||||||
                    result = RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
 | 
					 | 
				
			||||||
                    client.exhaustResponse(moveMethod.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    /// for other errors that could be explicitly handled, check first:
 | 
					 | 
				
			||||||
                    /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else -> {
 | 
					 | 
				
			||||||
                    result = RemoteOperationResult<Unit>(moveMethod)
 | 
					 | 
				
			||||||
                    client.exhaustResponse(moveMethod.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Timber.i("Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            result = RemoteOperationResult<Unit>(e)
 | 
					 | 
				
			||||||
            Timber.e(e, "Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * For standard moves, we will use [OwnCloudClient.getUserFilesWebDavUri].
 | 
					 | 
				
			||||||
     * In case we need a different source Uri, override this method.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * For standard moves, we won't need any special headers.
 | 
					 | 
				
			||||||
     * In case new headers are needed, override this method
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    open fun addRequestHeaders(moveMethod: MoveMethod) {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        private const val MOVE_READ_TIMEOUT = 10L
 | 
					 | 
				
			||||||
        private const val MOVE_CONNECTION_TIMEOUT = 6L
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					/* 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.resources.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.OwnCloudClient;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.accounts.AccountUtils;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.HttpConstants;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.DavUtils;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod;
 | 
				
			||||||
 | 
					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 timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0;
 | 
				
			||||||
 | 
					import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the read a file from the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author masensio
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ReadRemoteFileOperation extends RemoteOperation<RemoteFile> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int SYNC_READ_TIMEOUT = 40000;
 | 
				
			||||||
 | 
					    private static final int SYNC_CONNECTION_TIMEOUT = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String mRemotePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructor
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param remotePath Remote path of the file.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadRemoteFileOperation(String remotePath) {
 | 
				
			||||||
 | 
					        mRemotePath = remotePath;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the read operation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client Client object to communicate with the remote ownCloud server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult<RemoteFile> run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        PropfindMethod propfind;
 | 
				
			||||||
 | 
					        RemoteOperationResult<RemoteFile> result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// take the duty of check the server for the current state of the file there
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // remote request
 | 
				
			||||||
 | 
					            propfind = new PropfindMethod(
 | 
				
			||||||
 | 
					                    new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
 | 
				
			||||||
 | 
					                    DEPTH_0,
 | 
				
			||||||
 | 
					                    DavUtils.getAllPropset());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            propfind.setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					            propfind.setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					            final int status = client.executeHttpMethod(propfind);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (status == HttpConstants.HTTP_MULTI_STATUS
 | 
				
			||||||
 | 
					                    || status == HttpConstants.HTTP_OK) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                final RemoteFile file = new RemoteFile(propfind.getRoot(), AccountUtils.getUserId(mAccount, mContext));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(OK);
 | 
				
			||||||
 | 
					                result.setData(file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(propfind);
 | 
				
			||||||
 | 
					                client.exhaustResponse(propfind.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					            Timber.e(e, "Synchronizing  file %s", mRemotePath);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,97 +0,0 @@
 | 
				
			|||||||
/* 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.resources.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.accounts.AccountUtils
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.network.WebdavUtils
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.utils.isOneOf
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
import java.util.concurrent.TimeUnit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation performing the read a file from the ownCloud server.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author masensio
 | 
					 | 
				
			||||||
 * @author David González Verdugo
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation<RemoteFile>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Performs the read operation.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param client Client object to communicate with the remote ownCloud server.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteFile> {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            val propFind = PropfindMethod(
 | 
					 | 
				
			||||||
                url = URL("${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)}"),
 | 
					 | 
				
			||||||
                depth = DEPTH_0,
 | 
					 | 
				
			||||||
                propertiesToRequest = DavUtils.allPropset
 | 
					 | 
				
			||||||
            ).apply {
 | 
					 | 
				
			||||||
                setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS)
 | 
					 | 
				
			||||||
                setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val status = client.executeHttpMethod(propFind)
 | 
					 | 
				
			||||||
            Timber.i("Read remote file $remotePath with status ${propFind.statusCode}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return if (isSuccess(status)) {
 | 
					 | 
				
			||||||
                // TODO: Remove that !!
 | 
					 | 
				
			||||||
                val remoteFile = RemoteFile.getRemoteFileFromDav(
 | 
					 | 
				
			||||||
                    propFind.root!!,
 | 
					 | 
				
			||||||
                    AccountUtils.getUserId(mAccount, mContext), mAccount.name
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                RemoteOperationResult<RemoteFile>(RemoteOperationResult.ResultCode.OK).apply {
 | 
					 | 
				
			||||||
                    data = remoteFile
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                RemoteOperationResult<RemoteFile>(propFind).also {
 | 
					 | 
				
			||||||
                    client.exhaustResponse(propFind.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (exception: Exception) {
 | 
					 | 
				
			||||||
            return RemoteOperationResult(exception)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isSuccess(status: Int) = status.isOneOf(HTTP_MULTI_STATUS, HTTP_OK)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        private const val SYNC_READ_TIMEOUT = 40_000L
 | 
					 | 
				
			||||||
        private const val SYNC_CONNECTION_TIMEOUT = 5_000L
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					/* 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.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import at.bitfire.dav4jvm.PropertyRegistry;
 | 
				
			||||||
 | 
					import at.bitfire.dav4jvm.Response;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.OwnCloudClient;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.accounts.AccountUtils;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.HttpConstants;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.DavConstants;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.DavUtils;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes;
 | 
				
			||||||
 | 
					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 timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the read of remote file or folder in the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author masensio
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ReadRemoteFolderOperation extends RemoteOperation<ArrayList<RemoteFile>> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String mRemotePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructor
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param remotePath Remote path of the file.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadRemoteFolderOperation(String remotePath) {
 | 
				
			||||||
 | 
					        mRemotePath = remotePath;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the read operation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client Client object to communicate with the remote ownCloud server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult<ArrayList<RemoteFile>> run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        RemoteOperationResult<ArrayList<RemoteFile>> result = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            PropertyRegistry.INSTANCE.register(OCShareTypes.Factory.class.newInstance());
 | 
				
			||||||
 | 
					            PropfindMethod propfindMethod = new PropfindMethod(
 | 
				
			||||||
 | 
					                    new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)),
 | 
				
			||||||
 | 
					                    DavConstants.DEPTH_1,
 | 
				
			||||||
 | 
					                    DavUtils.getAllPropset());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int status = client.executeHttpMethod(propfindMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isSuccess(status)) {
 | 
				
			||||||
 | 
					                ArrayList<RemoteFile> mFolderAndFiles = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // parse data from remote folder
 | 
				
			||||||
 | 
					                mFolderAndFiles.add(
 | 
				
			||||||
 | 
					                        new RemoteFile(propfindMethod.getRoot(), AccountUtils.getUserId(mAccount, mContext))
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // loop to update every child
 | 
				
			||||||
 | 
					                for (Response resource : propfindMethod.getMembers()) {
 | 
				
			||||||
 | 
					                    RemoteFile file = new RemoteFile(resource, AccountUtils.getUserId(mAccount, mContext));
 | 
				
			||||||
 | 
					                    mFolderAndFiles.add(file);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Result of the operation
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(OK);
 | 
				
			||||||
 | 
					                result.setData(mFolderAndFiles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else { // synchronization failed
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(propfindMethod);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            if (result == null) {
 | 
				
			||||||
 | 
					                Timber.e("Synchronized " + mRemotePath + ": result is null");
 | 
				
			||||||
 | 
					            } else if (result.isSuccess()) {
 | 
				
			||||||
 | 
					                Timber.i("Synchronized " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if (result.isException()) {
 | 
				
			||||||
 | 
					                    Timber.e(result.getException(), "Synchronized " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Timber.e("Synchronized " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean isSuccess(int status) {
 | 
				
			||||||
 | 
					        return status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,111 +0,0 @@
 | 
				
			|||||||
/* 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.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import at.bitfire.dav4jvm.PropertyRegistry
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.accounts.AccountUtils
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
 | 
					 | 
				
			||||||
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.isOneOf
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation performing the read of remote file or folder in the ownCloud server.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author masensio
 | 
					 | 
				
			||||||
 * @author David González Verdugo
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class ReadRemoteFolderOperation(
 | 
					 | 
				
			||||||
    val remotePath: String
 | 
					 | 
				
			||||||
) : RemoteOperation<ArrayList<RemoteFile>>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Performs the read operation.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param client Client object to communicate with the remote ownCloud server.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<ArrayList<RemoteFile>> {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            PropertyRegistry.register(OCShareTypes.Factory())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val propfindMethod = PropfindMethod(
 | 
					 | 
				
			||||||
                URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)),
 | 
					 | 
				
			||||||
                DavConstants.DEPTH_1,
 | 
					 | 
				
			||||||
                DavUtils.allPropset
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val status = client.executeHttpMethod(propfindMethod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (isSuccess(status)) {
 | 
					 | 
				
			||||||
                val mFolderAndFiles = ArrayList<RemoteFile>()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // parse data from remote folder
 | 
					 | 
				
			||||||
                // TODO: Remove that !!
 | 
					 | 
				
			||||||
                val remoteFolder = RemoteFile.getRemoteFileFromDav(
 | 
					 | 
				
			||||||
                    davResource = propfindMethod.root!!,
 | 
					 | 
				
			||||||
                    userId = AccountUtils.getUserId(mAccount, mContext),
 | 
					 | 
				
			||||||
                    userName = mAccount.name
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                mFolderAndFiles.add(remoteFolder)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // loop to update every child
 | 
					 | 
				
			||||||
                propfindMethod.members.forEach { resource ->
 | 
					 | 
				
			||||||
                    val remoteFile = RemoteFile.getRemoteFileFromDav(
 | 
					 | 
				
			||||||
                        davResource = resource,
 | 
					 | 
				
			||||||
                        userId = AccountUtils.getUserId(mAccount, mContext),
 | 
					 | 
				
			||||||
                        userName = mAccount.name
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                    mFolderAndFiles.add(remoteFile)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Result of the operation
 | 
					 | 
				
			||||||
                return RemoteOperationResult<ArrayList<RemoteFile>>(ResultCode.OK).apply {
 | 
					 | 
				
			||||||
                    data = mFolderAndFiles
 | 
					 | 
				
			||||||
                    Timber.i("Synchronized $remotePath with ${mFolderAndFiles.size} files. ${this.logMessage}")
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else { // synchronization failed
 | 
					 | 
				
			||||||
                return RemoteOperationResult<ArrayList<RemoteFile>>(propfindMethod).also {
 | 
					 | 
				
			||||||
                    Timber.w("Synchronized $remotePath ${it.logMessage}")
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            return RemoteOperationResult<ArrayList<RemoteFile>>(e).also {
 | 
					 | 
				
			||||||
                Timber.e(it.exception, "Synchronized $remotePath")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isSuccess(status: Int): Boolean = status.isOneOf(HTTP_OK, HTTP_MULTI_STATUS)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,363 @@
 | 
				
			|||||||
 | 
					/* 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.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.os.Parcel;
 | 
				
			||||||
 | 
					import android.os.Parcelable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import at.bitfire.dav4jvm.Property;
 | 
				
			||||||
 | 
					import at.bitfire.dav4jvm.Response;
 | 
				
			||||||
 | 
					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 com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.resources.shares.ShareType;
 | 
				
			||||||
 | 
					import timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import java.math.BigDecimal;
 | 
				
			||||||
 | 
					import java.util.LinkedList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Contains the data of a Remote File from a WebDavEntry
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author masensio
 | 
				
			||||||
 | 
					 * @author Christian Schabesberger
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RemoteFile implements Parcelable, Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Parcelable Methods
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final Parcelable.Creator<RemoteFile> CREATOR = new Parcelable.Creator<RemoteFile>() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public RemoteFile createFromParcel(Parcel source) {
 | 
				
			||||||
 | 
					            return new RemoteFile(source);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public RemoteFile[] newArray(int size) {
 | 
				
			||||||
 | 
					            return new RemoteFile[size];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Generated - should be refreshed every time the class changes!!
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = -8965995357413958539L;
 | 
				
			||||||
 | 
					    private String mRemotePath;
 | 
				
			||||||
 | 
					    private String mMimeType;
 | 
				
			||||||
 | 
					    private long mLength;
 | 
				
			||||||
 | 
					    private long mCreationTimestamp;
 | 
				
			||||||
 | 
					    private long mModifiedTimestamp;
 | 
				
			||||||
 | 
					    private String mEtag;
 | 
				
			||||||
 | 
					    private String mPermissions;
 | 
				
			||||||
 | 
					    private String mRemoteId;
 | 
				
			||||||
 | 
					    private long mSize;
 | 
				
			||||||
 | 
					    private BigDecimal mQuotaUsedBytes;
 | 
				
			||||||
 | 
					    private BigDecimal mQuotaAvailableBytes;
 | 
				
			||||||
 | 
					    private String mPrivateLink;
 | 
				
			||||||
 | 
					    private boolean mSharedByLink;
 | 
				
			||||||
 | 
					    private boolean mSharedWithSharee;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public RemoteFile() {
 | 
				
			||||||
 | 
					        resetData();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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'.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param path The remote path of the file.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public RemoteFile(String path) {
 | 
				
			||||||
 | 
					        resetData();
 | 
				
			||||||
 | 
					        if (path == null || path.length() <= 0 || !path.startsWith(File.separator)) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        mRemotePath = path;
 | 
				
			||||||
 | 
					        mCreationTimestamp = 0;
 | 
				
			||||||
 | 
					        mLength = 0;
 | 
				
			||||||
 | 
					        mMimeType = FileUtils.MIME_DIR;
 | 
				
			||||||
 | 
					        mQuotaUsedBytes = BigDecimal.ZERO;
 | 
				
			||||||
 | 
					        mQuotaAvailableBytes = BigDecimal.ZERO;
 | 
				
			||||||
 | 
					        mPrivateLink = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public RemoteFile(final Response davResource, String userId) {
 | 
				
			||||||
 | 
					        this(RemoteFileUtil.Companion.getRemotePathFromUrl(davResource.getHref(), userId));
 | 
				
			||||||
 | 
					        final List<Property> properties = RemoteFileUtil.Companion.getProperties(davResource);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (Property property : properties) {
 | 
				
			||||||
 | 
					            if (property instanceof CreationDate) {
 | 
				
			||||||
 | 
					                this.setCreationTimestamp(
 | 
				
			||||||
 | 
					                        Long.parseLong(((CreationDate) property).getCreationDate()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof GetContentLength) {
 | 
				
			||||||
 | 
					                this.setLength(((GetContentLength) property).getContentLength());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof GetContentType) {
 | 
				
			||||||
 | 
					                this.setMimeType(((GetContentType) property).getType());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof GetLastModified) {
 | 
				
			||||||
 | 
					                this.setModifiedTimestamp(((GetLastModified) property).getLastModified());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof GetETag) {
 | 
				
			||||||
 | 
					                this.setEtag(((GetETag) property).getETag());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof OCPermissions) {
 | 
				
			||||||
 | 
					                this.setPermissions(((OCPermissions) property).getPermission());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof OCId) {
 | 
				
			||||||
 | 
					                this.setRemoteId(((OCId) property).getId());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof OCSize) {
 | 
				
			||||||
 | 
					                this.setSize(((OCSize) property).getSize());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof QuotaUsedBytes) {
 | 
				
			||||||
 | 
					                this.setQuotaUsedBytes(
 | 
				
			||||||
 | 
					                        BigDecimal.valueOf(((QuotaUsedBytes) property).getQuotaUsedBytes()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof QuotaAvailableBytes) {
 | 
				
			||||||
 | 
					                this.setQuotaAvailableBytes(
 | 
				
			||||||
 | 
					                        BigDecimal.valueOf(((QuotaAvailableBytes) property).getQuotaAvailableBytes()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof OCPrivatelink) {
 | 
				
			||||||
 | 
					                this.setPrivateLink(((OCPrivatelink) property).getLink());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (property instanceof OCShareTypes) {
 | 
				
			||||||
 | 
					                LinkedList<String> list = ((OCShareTypes) property).getShareTypes();
 | 
				
			||||||
 | 
					                for (int i = 0; i < list.size(); i++) {
 | 
				
			||||||
 | 
					                    ShareType shareType = ShareType.Companion.fromValue(Integer.parseInt(list.get(i)));
 | 
				
			||||||
 | 
					                    if (shareType == null) {
 | 
				
			||||||
 | 
					                        Timber.d("Illegal share type value: " + list.get(i));
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (shareType.equals(ShareType.PUBLIC_LINK)) {
 | 
				
			||||||
 | 
					                        this.setSharedViaLink(true);
 | 
				
			||||||
 | 
					                    } else if (shareType.equals(ShareType.USER) ||
 | 
				
			||||||
 | 
					                            shareType.equals(ShareType.FEDERATED) ||
 | 
				
			||||||
 | 
					                            shareType.equals(ShareType.GROUP)) {
 | 
				
			||||||
 | 
					                        this.setSharedWithSharee(true);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reconstruct from parcel
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param source The source parcel
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected RemoteFile(Parcel source) {
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getRemotePath() {
 | 
				
			||||||
 | 
					        return mRemotePath;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setRemotePath(String remotePath) {
 | 
				
			||||||
 | 
					        this.mRemotePath = remotePath;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMimeType() {
 | 
				
			||||||
 | 
					        return mMimeType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setMimeType(String mimeType) {
 | 
				
			||||||
 | 
					        this.mMimeType = mimeType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getLength() {
 | 
				
			||||||
 | 
					        return mLength;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setLength(long length) {
 | 
				
			||||||
 | 
					        this.mLength = length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getCreationTimestamp() {
 | 
				
			||||||
 | 
					        return mCreationTimestamp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setCreationTimestamp(long creationTimestamp) {
 | 
				
			||||||
 | 
					        this.mCreationTimestamp = creationTimestamp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getModifiedTimestamp() {
 | 
				
			||||||
 | 
					        return mModifiedTimestamp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setModifiedTimestamp(long modifiedTimestamp) {
 | 
				
			||||||
 | 
					        this.mModifiedTimestamp = modifiedTimestamp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getEtag() {
 | 
				
			||||||
 | 
					        return mEtag;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setEtag(String etag) {
 | 
				
			||||||
 | 
					        this.mEtag = etag;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getPermissions() {
 | 
				
			||||||
 | 
					        return mPermissions;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setPermissions(String permissions) {
 | 
				
			||||||
 | 
					        this.mPermissions = permissions;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getRemoteId() {
 | 
				
			||||||
 | 
					        return mRemoteId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setRemoteId(String remoteId) {
 | 
				
			||||||
 | 
					        this.mRemoteId = remoteId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long getSize() {
 | 
				
			||||||
 | 
					        return mSize;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setSize(long size) {
 | 
				
			||||||
 | 
					        mSize = size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setQuotaUsedBytes(BigDecimal quotaUsedBytes) {
 | 
				
			||||||
 | 
					        mQuotaUsedBytes = quotaUsedBytes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setQuotaAvailableBytes(BigDecimal quotaAvailableBytes) {
 | 
				
			||||||
 | 
					        mQuotaAvailableBytes = quotaAvailableBytes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getPrivateLink() {
 | 
				
			||||||
 | 
					        return mPrivateLink;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setPrivateLink(String privateLink) {
 | 
				
			||||||
 | 
					        mPrivateLink = privateLink;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setSharedWithSharee(boolean shareWithSharee) {
 | 
				
			||||||
 | 
					        mSharedWithSharee = shareWithSharee;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isSharedWithSharee() {
 | 
				
			||||||
 | 
					        return mSharedWithSharee;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setSharedViaLink(boolean sharedViaLink) {
 | 
				
			||||||
 | 
					        mSharedByLink = sharedViaLink;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isSharedByLink() {
 | 
				
			||||||
 | 
					        return mSharedByLink;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Used internally. Reset all file properties
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void resetData() {
 | 
				
			||||||
 | 
					        mRemotePath = null;
 | 
				
			||||||
 | 
					        mMimeType = null;
 | 
				
			||||||
 | 
					        mLength = 0;
 | 
				
			||||||
 | 
					        mCreationTimestamp = 0;
 | 
				
			||||||
 | 
					        mModifiedTimestamp = 0;
 | 
				
			||||||
 | 
					        mEtag = null;
 | 
				
			||||||
 | 
					        mPermissions = null;
 | 
				
			||||||
 | 
					        mRemoteId = null;
 | 
				
			||||||
 | 
					        mSize = 0;
 | 
				
			||||||
 | 
					        mQuotaUsedBytes = null;
 | 
				
			||||||
 | 
					        mQuotaAvailableBytes = null;
 | 
				
			||||||
 | 
					        mPrivateLink = null;
 | 
				
			||||||
 | 
					        mSharedWithSharee = false;
 | 
				
			||||||
 | 
					        mSharedByLink = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readFromParcel(Parcel source) {
 | 
				
			||||||
 | 
					        mRemotePath = source.readString();
 | 
				
			||||||
 | 
					        mMimeType = source.readString();
 | 
				
			||||||
 | 
					        mLength = source.readLong();
 | 
				
			||||||
 | 
					        mCreationTimestamp = source.readLong();
 | 
				
			||||||
 | 
					        mModifiedTimestamp = source.readLong();
 | 
				
			||||||
 | 
					        mEtag = source.readString();
 | 
				
			||||||
 | 
					        mPermissions = source.readString();
 | 
				
			||||||
 | 
					        mRemoteId = source.readString();
 | 
				
			||||||
 | 
					        mSize = source.readLong();
 | 
				
			||||||
 | 
					        mQuotaUsedBytes = (BigDecimal) source.readSerializable();
 | 
				
			||||||
 | 
					        mQuotaAvailableBytes = (BigDecimal) source.readSerializable();
 | 
				
			||||||
 | 
					        mPrivateLink = source.readString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int describeContents() {
 | 
				
			||||||
 | 
					        return this.hashCode();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeToParcel(Parcel dest, int flags) {
 | 
				
			||||||
 | 
					        dest.writeString(mRemotePath);
 | 
				
			||||||
 | 
					        dest.writeString(mMimeType);
 | 
				
			||||||
 | 
					        dest.writeLong(mLength);
 | 
				
			||||||
 | 
					        dest.writeLong(mCreationTimestamp);
 | 
				
			||||||
 | 
					        dest.writeLong(mModifiedTimestamp);
 | 
				
			||||||
 | 
					        dest.writeString(mEtag);
 | 
				
			||||||
 | 
					        dest.writeString(mPermissions);
 | 
				
			||||||
 | 
					        dest.writeString(mRemoteId);
 | 
				
			||||||
 | 
					        dest.writeLong(mSize);
 | 
				
			||||||
 | 
					        dest.writeSerializable(mQuotaUsedBytes);
 | 
				
			||||||
 | 
					        dest.writeSerializable(mQuotaAvailableBytes);
 | 
				
			||||||
 | 
					        dest.writeString(mPrivateLink);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,189 +0,0 @@
 | 
				
			|||||||
/* 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.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.net.Uri
 | 
					 | 
				
			||||||
import android.os.Parcelable
 | 
					 | 
				
			||||||
import at.bitfire.dav4jvm.PropStat
 | 
					 | 
				
			||||||
import at.bitfire.dav4jvm.Property
 | 
					 | 
				
			||||||
import at.bitfire.dav4jvm.Response
 | 
					 | 
				
			||||||
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 com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.utils.isOneOf
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.shares.ShareType
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.shares.ShareType.Companion.fromValue
 | 
					 | 
				
			||||||
import kotlinx.parcelize.Parcelize
 | 
					 | 
				
			||||||
import okhttp3.HttpUrl
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.math.BigDecimal
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Contains the data of a Remote File from a WebDavEntry
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author masensio
 | 
					 | 
				
			||||||
 * @author Christian Schabesberger
 | 
					 | 
				
			||||||
 * @author Abel García de Prada
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@Parcelize
 | 
					 | 
				
			||||||
data class RemoteFile(
 | 
					 | 
				
			||||||
    var remotePath: String,
 | 
					 | 
				
			||||||
    var mimeType: String = "DIR",
 | 
					 | 
				
			||||||
    var length: Long = 0,
 | 
					 | 
				
			||||||
    var creationTimestamp: Long = 0,
 | 
					 | 
				
			||||||
    var modifiedTimestamp: Long = 0,
 | 
					 | 
				
			||||||
    var etag: String? = null,
 | 
					 | 
				
			||||||
    var permissions: String? = null,
 | 
					 | 
				
			||||||
    var remoteId: String? = null,
 | 
					 | 
				
			||||||
    var size: Long = 0,
 | 
					 | 
				
			||||||
    var quotaUsedBytes: BigDecimal? = null,
 | 
					 | 
				
			||||||
    var quotaAvailableBytes: BigDecimal? = null,
 | 
					 | 
				
			||||||
    var privateLink: String? = null,
 | 
					 | 
				
			||||||
    var owner: String,
 | 
					 | 
				
			||||||
    var sharedByLink: Boolean = false,
 | 
					 | 
				
			||||||
    var sharedWithSharee: Boolean = false,
 | 
					 | 
				
			||||||
) : Parcelable {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO: Quotas not used. Use or remove them.
 | 
					 | 
				
			||||||
    init {
 | 
					 | 
				
			||||||
        require(
 | 
					 | 
				
			||||||
            !(remotePath.isEmpty() || !remotePath.startsWith(File.separator))
 | 
					 | 
				
			||||||
        ) { "Trying to create a OCFile with a non valid remote path: $remotePath" }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Use this to find out if this file is a folder.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return true if it is a folder
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    val isFolder
 | 
					 | 
				
			||||||
        get() = mimeType.isOneOf(MIME_DIR, MIME_DIR_UNIX)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const val MIME_DIR = "DIR"
 | 
					 | 
				
			||||||
        const val MIME_DIR_UNIX = "httpd/unix-directory"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fun getRemoteFileFromDav(davResource: Response, userId: String, userName: String): RemoteFile {
 | 
					 | 
				
			||||||
            val remotePath = getRemotePathFromUrl(davResource.href, userId)
 | 
					 | 
				
			||||||
            val remoteFile = RemoteFile(remotePath = remotePath, owner = userName)
 | 
					 | 
				
			||||||
            val properties = getPropertiesEvenIfPostProcessing(davResource)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (property in properties) {
 | 
					 | 
				
			||||||
                when (property) {
 | 
					 | 
				
			||||||
                    is CreationDate -> {
 | 
					 | 
				
			||||||
                        remoteFile.creationTimestamp = property.creationDate.toLong()
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is GetContentLength -> {
 | 
					 | 
				
			||||||
                        remoteFile.length = property.contentLength
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is GetContentType -> {
 | 
					 | 
				
			||||||
                        property.type?.let { remoteFile.mimeType = it }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is GetLastModified -> {
 | 
					 | 
				
			||||||
                        remoteFile.modifiedTimestamp = property.lastModified
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is GetETag -> {
 | 
					 | 
				
			||||||
                        remoteFile.etag = property.eTag
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is OCPermissions -> {
 | 
					 | 
				
			||||||
                        remoteFile.permissions = property.permission
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is OCId -> {
 | 
					 | 
				
			||||||
                        remoteFile.remoteId = property.id
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is OCSize -> {
 | 
					 | 
				
			||||||
                        remoteFile.size = property.size
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is QuotaUsedBytes -> {
 | 
					 | 
				
			||||||
                        remoteFile.quotaUsedBytes = BigDecimal.valueOf(property.quotaUsedBytes)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is QuotaAvailableBytes -> {
 | 
					 | 
				
			||||||
                        remoteFile.quotaAvailableBytes = BigDecimal.valueOf(property.quotaAvailableBytes)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is OCPrivatelink -> {
 | 
					 | 
				
			||||||
                        remoteFile.privateLink = property.link
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    is OCShareTypes -> {
 | 
					 | 
				
			||||||
                        val list = property.shareTypes
 | 
					 | 
				
			||||||
                        for (i in list.indices) {
 | 
					 | 
				
			||||||
                            val shareType = fromValue(list[i].toInt())
 | 
					 | 
				
			||||||
                            if (shareType == null) {
 | 
					 | 
				
			||||||
                                Timber.d("Illegal share type value: " + list[i])
 | 
					 | 
				
			||||||
                                continue
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            if (shareType == ShareType.PUBLIC_LINK) {
 | 
					 | 
				
			||||||
                                remoteFile.sharedByLink = true
 | 
					 | 
				
			||||||
                            } else if (shareType == ShareType.USER || shareType == ShareType.FEDERATED || shareType == ShareType.GROUP) {
 | 
					 | 
				
			||||||
                                remoteFile.sharedWithSharee = true
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return remoteFile
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Retrieves a relative path from a remote file url
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * Example: url:port/remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * @param url    remote file url
 | 
					 | 
				
			||||||
         * @param userId file owner
 | 
					 | 
				
			||||||
         * @return remote relative path of the file
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        private fun getRemotePathFromUrl(url: HttpUrl, userId: String): String {
 | 
					 | 
				
			||||||
            val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId
 | 
					 | 
				
			||||||
            val absoluteDavPath = Uri.decode(url.encodedPath)
 | 
					 | 
				
			||||||
            val pathToOc = absoluteDavPath.split(davFilesPath).first()
 | 
					 | 
				
			||||||
            return absoluteDavPath.replace(pathToOc + davFilesPath, "")
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private fun getPropertiesEvenIfPostProcessing(response: Response): List<Property> {
 | 
					 | 
				
			||||||
            return if (response.isSuccess())
 | 
					 | 
				
			||||||
                response.propstat.filter { propStat -> propStat.isSuccessOrPostProcessing() }.map { it.properties }.flatten()
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                emptyList()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private fun PropStat.isSuccessOrPostProcessing() = (status.code / 100 == 2 || status.code == HttpConstants.HTTP_TOO_EARLY)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					/* 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.files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.net.Uri
 | 
				
			||||||
 | 
					import at.bitfire.dav4jvm.PropStat
 | 
				
			||||||
 | 
					import at.bitfire.dav4jvm.Property
 | 
				
			||||||
 | 
					import at.bitfire.dav4jvm.Response
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.OwnCloudClient
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.HttpConstants
 | 
				
			||||||
 | 
					import okhttp3.HttpUrl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RemoteFileUtil {
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Retrieves a relative path from a remote file url
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * Example: url:port/remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param url    remote file url
 | 
				
			||||||
 | 
					         * @param userId file owner
 | 
				
			||||||
 | 
					         * @return remote relative path of the file
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        fun getRemotePathFromUrl(url: HttpUrl, userId: String): String? {
 | 
				
			||||||
 | 
					            val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId
 | 
				
			||||||
 | 
					            val absoluteDavPath = Uri.decode(url.encodedPath)
 | 
				
			||||||
 | 
					            val pathToOc = absoluteDavPath.split(davFilesPath)[0]
 | 
				
			||||||
 | 
					            return absoluteDavPath.replace(pathToOc + davFilesPath, "")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun getProperties(response: Response): List<Property> {
 | 
				
			||||||
 | 
					            return if (response.isSuccess())
 | 
				
			||||||
 | 
					                response.propstat.filter { propStat -> propStat.isSuccessOrPostProcessing() }.map { it.properties }.flatten()
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                emptyList()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private fun PropStat.isSuccessOrPostProcessing() = (status.code / 100 == 2 || status.code == HttpConstants.HTTP_TOO_EARLY)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					/* 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.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.DeleteMethod;
 | 
				
			||||||
 | 
					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 timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the removal of a remote file or folder in the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author masensio
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RemoveRemoteFileOperation extends RemoteOperation {
 | 
				
			||||||
 | 
					    private String mRemotePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected boolean removeChunksFolder = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructor
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param remotePath RemotePath of the remote file or folder to remove from the server
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public RemoveRemoteFileOperation(String remotePath) {
 | 
				
			||||||
 | 
					        mRemotePath = remotePath;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the rename operation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client Client object to communicate with the remote ownCloud server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        RemoteOperationResult result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            Uri srcWebDavUri = removeChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            DeleteMethod deleteMethod = new DeleteMethod(
 | 
				
			||||||
 | 
					                    new URL(srcWebDavUri + WebdavUtils.encodePath(mRemotePath)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int status = client.executeHttpMethod(deleteMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result = isSuccess(status) ?
 | 
				
			||||||
 | 
					                    new RemoteOperationResult<>(OK) :
 | 
				
			||||||
 | 
					                    new RemoteOperationResult<>(deleteMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Timber.i("Remove " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					            Timber.e(e, "Remove " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean isSuccess(int status) {
 | 
				
			||||||
 | 
					        return status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_NO_CONTENT;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,81 +0,0 @@
 | 
				
			|||||||
/* 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.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.net.Uri
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_NO_CONTENT
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod
 | 
					 | 
				
			||||||
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.isOneOf
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation performing the removal of a remote file or folder in the ownCloud server.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author masensio
 | 
					 | 
				
			||||||
 * @author David González Verdugo
 | 
					 | 
				
			||||||
 * @author Abel García de Prada
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
open class RemoveRemoteFileOperation(
 | 
					 | 
				
			||||||
    private val remotePath: String
 | 
					 | 
				
			||||||
) : RemoteOperation<Unit>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        var result: RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            val srcWebDavUri = getSrcWebDavUriForClient(client)
 | 
					 | 
				
			||||||
            val deleteMethod = DeleteMethod(
 | 
					 | 
				
			||||||
                URL(srcWebDavUri.toString() + WebdavUtils.encodePath(remotePath))
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            val status = client.executeHttpMethod(deleteMethod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            result = if (isSuccess(status)) {
 | 
					 | 
				
			||||||
                RemoteOperationResult<Unit>(ResultCode.OK)
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                RemoteOperationResult<Unit>(deleteMethod)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Timber.i("Remove $remotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            result = RemoteOperationResult<Unit>(e)
 | 
					 | 
				
			||||||
            Timber.e(e, "Remove $remotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri].
 | 
					 | 
				
			||||||
     * In case we need a different source Uri, override this method.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isSuccess(status: Int) = status.isOneOf(HTTP_OK, HTTP_NO_CONTENT)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					/* ownCloud Android Library is available under MIT license
 | 
				
			||||||
 | 
					 *   Copyright (C) 2019 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.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.OwnCloudClient;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.HttpConstants;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod;
 | 
				
			||||||
 | 
					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 timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the rename of a remote file or folder in the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author masensio
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RenameRemoteFileOperation extends RemoteOperation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int RENAME_READ_TIMEOUT = 600000;
 | 
				
			||||||
 | 
					    private static final int RENAME_CONNECTION_TIMEOUT = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String mOldName;
 | 
				
			||||||
 | 
					    private String mOldRemotePath;
 | 
				
			||||||
 | 
					    private String mNewName;
 | 
				
			||||||
 | 
					    private String mNewRemotePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructor
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param oldName       Old name of the file.
 | 
				
			||||||
 | 
					     * @param oldRemotePath Old remote path of the file.
 | 
				
			||||||
 | 
					     * @param newName       New name to set as the name of file.
 | 
				
			||||||
 | 
					     * @param isFolder      'true' for folder and 'false' for files
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public RenameRemoteFileOperation(String oldName, String oldRemotePath, String newName,
 | 
				
			||||||
 | 
					                                     boolean isFolder) {
 | 
				
			||||||
 | 
					        mOldName = oldName;
 | 
				
			||||||
 | 
					        mOldRemotePath = oldRemotePath;
 | 
				
			||||||
 | 
					        mNewName = newName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String parent = (new File(mOldRemotePath)).getParent();
 | 
				
			||||||
 | 
					        parent = (parent.endsWith(File.separator)) ? parent : parent + File.separator;
 | 
				
			||||||
 | 
					        mNewRemotePath = parent + mNewName;
 | 
				
			||||||
 | 
					        if (isFolder) {
 | 
				
			||||||
 | 
					            mNewRemotePath += File.separator;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the rename operation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client Client object to communicate with the remote ownCloud server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (mNewName.equals(mOldName)) {
 | 
				
			||||||
 | 
					                return new RemoteOperationResult<>(ResultCode.OK);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (targetPathIsUsed(client)) {
 | 
				
			||||||
 | 
					                return new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final MoveMethod move = new MoveMethod(
 | 
				
			||||||
 | 
					                    new URL(client.getUserFilesWebDavUri() +
 | 
				
			||||||
 | 
					                    WebdavUtils.encodePath(mOldRemotePath)),
 | 
				
			||||||
 | 
					                    client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mNewRemotePath), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            move.setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					            move.setConnectionTimeout(RENAME_READ_TIMEOUT, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final int status = client.executeHttpMethod(move);
 | 
				
			||||||
 | 
					            final RemoteOperationResult result =
 | 
				
			||||||
 | 
					                    (status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT)
 | 
				
			||||||
 | 
					                            ? new RemoteOperationResult<>(ResultCode.OK)
 | 
				
			||||||
 | 
					                            : new RemoteOperationResult<>(move);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Timber.i("Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					            client.exhaustResponse(move.getResponseBodyAsStream());
 | 
				
			||||||
 | 
					            return result;
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            final RemoteOperationResult result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					            Timber.e(e,
 | 
				
			||||||
 | 
					                    "Rename " + mOldRemotePath + " to " + ((mNewRemotePath == null) ? mNewName : mNewRemotePath) + ":" +
 | 
				
			||||||
 | 
					                            " " + result.getLogMessage());
 | 
				
			||||||
 | 
					            return result;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks if a file with the new name already exists.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 'True' if the target path is already used by an existing file.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private boolean targetPathIsUsed(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        CheckPathExistenceRemoteOperation checkPathExistenceRemoteOperation =
 | 
				
			||||||
 | 
					                new CheckPathExistenceRemoteOperation(mNewRemotePath, false);
 | 
				
			||||||
 | 
					        RemoteOperationResult exists = checkPathExistenceRemoteOperation.execute(client);
 | 
				
			||||||
 | 
					        return exists.isSuccess();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,119 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2021 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.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
 | 
					 | 
				
			||||||
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.isOneOf
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
import java.util.concurrent.TimeUnit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation performing the rename of a remote file or folder in the ownCloud server.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author masensio
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class RenameRemoteFileOperation(
 | 
					 | 
				
			||||||
    private val oldName: String,
 | 
					 | 
				
			||||||
    private val oldRemotePath: String,
 | 
					 | 
				
			||||||
    private val newName: String,
 | 
					 | 
				
			||||||
    isFolder: Boolean,
 | 
					 | 
				
			||||||
) : RemoteOperation<Unit>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private var newRemotePath: String
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    init {
 | 
					 | 
				
			||||||
        var parent = (File(oldRemotePath)).parent ?: throw IllegalArgumentException()
 | 
					 | 
				
			||||||
        if (!parent.endsWith(File.separator)) {
 | 
					 | 
				
			||||||
            parent = parent.plus(File.separator)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        newRemotePath = parent.plus(newName)
 | 
					 | 
				
			||||||
        if (isFolder) {
 | 
					 | 
				
			||||||
            newRemotePath.plus(File.separator)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        var result: RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            if (newName == oldName) {
 | 
					 | 
				
			||||||
                return RemoteOperationResult<Unit>(ResultCode.OK)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (targetPathIsUsed(client)) {
 | 
					 | 
				
			||||||
                return RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            val moveMethod: MoveMethod = MoveMethod(
 | 
					 | 
				
			||||||
                url = URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(oldRemotePath)),
 | 
					 | 
				
			||||||
                destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(newRemotePath),
 | 
					 | 
				
			||||||
            ).apply {
 | 
					 | 
				
			||||||
                setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS)
 | 
					 | 
				
			||||||
                setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            val status = client.executeHttpMethod(moveMethod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            result = if (isSuccess(status)) {
 | 
					 | 
				
			||||||
                RemoteOperationResult<Unit>(ResultCode.OK)
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                RemoteOperationResult<Unit>(moveMethod)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Timber.i("Rename $oldRemotePath to $newRemotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
            client.exhaustResponse(moveMethod.getResponseBodyAsStream())
 | 
					 | 
				
			||||||
            return result
 | 
					 | 
				
			||||||
        } catch (exception: Exception) {
 | 
					 | 
				
			||||||
            result = RemoteOperationResult<Unit>(exception)
 | 
					 | 
				
			||||||
            Timber.e(exception, "Rename $oldRemotePath to $newName: ${result.logMessage}")
 | 
					 | 
				
			||||||
            return result
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Checks if a file with the new name already exists.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return 'True' if the target path is already used by an existing file.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private fun targetPathIsUsed(client: OwnCloudClient): Boolean {
 | 
					 | 
				
			||||||
        val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation(newRemotePath, false)
 | 
					 | 
				
			||||||
        val exists = checkPathExistenceRemoteOperation.execute(client)
 | 
					 | 
				
			||||||
        return exists.isSuccess
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        private const val RENAME_READ_TIMEOUT = 10_000L
 | 
					 | 
				
			||||||
        private const val RENAME_CONNECTION_TIMEOUT = 5_000L
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					/* ownCloud Android Library is available under MIT license
 | 
				
			||||||
 *   Copyright (C) 2022 ownCloud GmbH.
 | 
					 *   Copyright (C) 2021 ownCloud GmbH.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 *   of this software and associated documentation files (the "Software"), to deal
 | 
					 *   of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
@ -24,15 +24,22 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.owncloud.android.lib.resources.files
 | 
					package com.owncloud.android.lib.resources.files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.ContentResolver
 | 
				
			||||||
 | 
					import android.net.Uri
 | 
				
			||||||
 | 
					import android.provider.OpenableColumns
 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					import com.owncloud.android.lib.common.OwnCloudClient
 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants
 | 
					import com.owncloud.android.lib.common.http.HttpConstants
 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
 | 
				
			||||||
import com.owncloud.android.lib.common.network.ContentUriRequestBody
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.network.WebdavUtils
 | 
					import com.owncloud.android.lib.common.network.WebdavUtils
 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperation
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperation
 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
				
			||||||
import com.owncloud.android.lib.common.utils.isOneOf
 | 
					import okhttp3.MediaType
 | 
				
			||||||
 | 
					import okhttp3.MediaType.Companion.toMediaTypeOrNull
 | 
				
			||||||
 | 
					import okhttp3.RequestBody
 | 
				
			||||||
 | 
					import okio.BufferedSink
 | 
				
			||||||
 | 
					import okio.source
 | 
				
			||||||
import timber.log.Timber
 | 
					import timber.log.Timber
 | 
				
			||||||
 | 
					import java.io.IOException
 | 
				
			||||||
import java.net.URL
 | 
					import java.net.URL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UploadFileFromContentUriOperation(
 | 
					class UploadFileFromContentUriOperation(
 | 
				
			||||||
@ -63,6 +70,34 @@ class UploadFileFromContentUriOperation(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun isSuccess(status: Int): Boolean {
 | 
					    fun isSuccess(status: Int): Boolean {
 | 
				
			||||||
        return status.isOneOf(HttpConstants.HTTP_OK, HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
 | 
					        return status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ContentUriRequestBody(
 | 
				
			||||||
 | 
					    private val contentResolver: ContentResolver,
 | 
				
			||||||
 | 
					    private val contentUri: Uri
 | 
				
			||||||
 | 
					) : RequestBody() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun contentType(): MediaType? {
 | 
				
			||||||
 | 
					        val contentType = contentResolver.getType(contentUri) ?: return null
 | 
				
			||||||
 | 
					        return contentType.toMediaTypeOrNull()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun contentLength(): Long {
 | 
				
			||||||
 | 
					        contentResolver.query(contentUri, null, null, null, null)?.use { cursor ->
 | 
				
			||||||
 | 
					            val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
 | 
				
			||||||
 | 
					            cursor.moveToFirst()
 | 
				
			||||||
 | 
					            return cursor.getLong(sizeIndex)
 | 
				
			||||||
 | 
					        } ?: return -1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun writeTo(sink: BufferedSink) {
 | 
				
			||||||
 | 
					        val inputStream = contentResolver.openInputStream(contentUri)
 | 
				
			||||||
 | 
					            ?: throw IOException("Couldn't open content URI for reading: $contentUri")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inputStream.source().use { source ->
 | 
				
			||||||
 | 
					            sink.writeAll(source)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,145 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2022 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
 | 
					 | 
				
			||||||
 *   NONINFINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 | 
					 | 
				
			||||||
 *   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | 
					 | 
				
			||||||
 *   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
					 | 
				
			||||||
 *   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
					 | 
				
			||||||
 *   THE SOFTWARE.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
package com.owncloud.android.lib.resources.files
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.network.FileRequestBody
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.network.WebdavUtils
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.OperationCancelledException
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.utils.isOneOf
 | 
					 | 
				
			||||||
import okhttp3.MediaType
 | 
					 | 
				
			||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
import java.util.concurrent.atomic.AtomicBoolean
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation performing the upload of a remote file to the ownCloud server.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author masensio
 | 
					 | 
				
			||||||
 * @author David González Verdugo
 | 
					 | 
				
			||||||
 * @author Abel García de Prada
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
open class UploadFileFromFileSystemOperation(
 | 
					 | 
				
			||||||
    val localPath: String,
 | 
					 | 
				
			||||||
    val remotePath: String,
 | 
					 | 
				
			||||||
    val mimeType: String,
 | 
					 | 
				
			||||||
    val lastModifiedTimestamp: String,
 | 
					 | 
				
			||||||
    val requiredEtag: String?,
 | 
					 | 
				
			||||||
) : RemoteOperation<Unit>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected val cancellationRequested = AtomicBoolean(false)
 | 
					 | 
				
			||||||
    protected var putMethod: PutMethod? = null
 | 
					 | 
				
			||||||
    protected val dataTransferListener: MutableSet<OnDatatransferProgressListener> = HashSet()
 | 
					 | 
				
			||||||
    protected var fileRequestBody: FileRequestBody? = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var etag: String = ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        var result: RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            if (cancellationRequested.get()) {
 | 
					 | 
				
			||||||
                // the operation was cancelled before getting it's turn to be executed in the queue of uploads
 | 
					 | 
				
			||||||
                result = RemoteOperationResult<Unit>(OperationCancelledException())
 | 
					 | 
				
			||||||
                Timber.i("Upload of $localPath to $remotePath has been cancelled")
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                // perform the upload
 | 
					 | 
				
			||||||
                result = uploadFile(client)
 | 
					 | 
				
			||||||
                Timber.i("Upload of $localPath to $remotePath: ${result.logMessage}")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (e: Exception) {
 | 
					 | 
				
			||||||
            if (putMethod?.isAborted == true) {
 | 
					 | 
				
			||||||
                result = RemoteOperationResult<Unit>(OperationCancelledException())
 | 
					 | 
				
			||||||
                Timber.e(result.exception, "Upload of $localPath to $remotePath has been aborted with this message: ${result.logMessage}")
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                result = RemoteOperationResult<Unit>(e)
 | 
					 | 
				
			||||||
                Timber.e(result.exception, "Upload of $localPath to $remotePath has failed with this message: ${result.logMessage}")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Throws(Exception::class)
 | 
					 | 
				
			||||||
    protected open fun uploadFile(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        val fileToUpload = File(localPath)
 | 
					 | 
				
			||||||
        val mediaType: MediaType? = mimeType.toMediaTypeOrNull()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fileRequestBody = FileRequestBody(fileToUpload, mediaType).also {
 | 
					 | 
				
			||||||
            synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply {
 | 
					 | 
				
			||||||
            retryOnConnectionFailure = false
 | 
					 | 
				
			||||||
            if (!requiredEtag.isNullOrBlank()) {
 | 
					 | 
				
			||||||
                addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileToUpload.length().toString())
 | 
					 | 
				
			||||||
            addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModifiedTimestamp)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        val status = client.executeHttpMethod(putMethod)
 | 
					 | 
				
			||||||
        return if (isSuccess(status)) {
 | 
					 | 
				
			||||||
            etag = WebdavUtils.getEtagFromResponse(putMethod)
 | 
					 | 
				
			||||||
            // Get rid of extra quotas
 | 
					 | 
				
			||||||
            etag = etag.replace("\"", "")
 | 
					 | 
				
			||||||
            if (etag.isEmpty()) {
 | 
					 | 
				
			||||||
                Timber.e("Could not read eTag from response uploading %s", localPath)
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                Timber.d("File uploaded successfully. New etag for file ${fileToUpload.name} is $etag")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            RemoteOperationResult<Unit>(ResultCode.OK).apply { data = Unit }
 | 
					 | 
				
			||||||
        } else { // synchronization failed
 | 
					 | 
				
			||||||
            RemoteOperationResult<Unit>(putMethod)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun addDataTransferProgressListener(listener: OnDatatransferProgressListener) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListener) { dataTransferListener.add(listener) }
 | 
					 | 
				
			||||||
        fileRequestBody?.addDatatransferProgressListener(listener)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun removeDataTransferProgressListener(listener: OnDatatransferProgressListener) {
 | 
					 | 
				
			||||||
        synchronized(dataTransferListener) { dataTransferListener.remove(listener) }
 | 
					 | 
				
			||||||
        fileRequestBody?.removeDatatransferProgressListener(listener)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun cancel() {
 | 
					 | 
				
			||||||
        synchronized(cancellationRequested) {
 | 
					 | 
				
			||||||
            cancellationRequested.set(true)
 | 
					 | 
				
			||||||
            putMethod?.abort()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun isSuccess(status: Int): Boolean {
 | 
					 | 
				
			||||||
        return status.isOneOf(HttpConstants.HTTP_OK, HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,181 @@
 | 
				
			|||||||
 | 
					/* 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.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.OwnCloudClient;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.HttpConstants;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.PutMethod;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.network.FileRequestBody;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.network.WebdavUtils;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.operations.OperationCancelledException;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperation;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 | 
				
			||||||
 | 
					import okhttp3.MediaType;
 | 
				
			||||||
 | 
					import timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicBoolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the upload of a remote file to the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author masensio
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class UploadRemoteFileOperation extends RemoteOperation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
 | 
				
			||||||
 | 
					    protected String mLocalPath;
 | 
				
			||||||
 | 
					    protected String mRemotePath;
 | 
				
			||||||
 | 
					    protected String mMimeType;
 | 
				
			||||||
 | 
					    protected String mFileLastModifTimestamp;
 | 
				
			||||||
 | 
					    protected PutMethod mPutMethod = null;
 | 
				
			||||||
 | 
					    protected String mRequiredEtag = null;
 | 
				
			||||||
 | 
					    protected Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected FileRequestBody mFileRequestBody = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public UploadRemoteFileOperation(String localPath, String remotePath, String mimeType,
 | 
				
			||||||
 | 
					                                     String fileLastModifTimestamp) {
 | 
				
			||||||
 | 
					        mLocalPath = localPath;
 | 
				
			||||||
 | 
					        mRemotePath = remotePath;
 | 
				
			||||||
 | 
					        mMimeType = mimeType;
 | 
				
			||||||
 | 
					        mFileLastModifTimestamp = fileLastModifTimestamp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public UploadRemoteFileOperation(String localPath, String remotePath, String mimeType,
 | 
				
			||||||
 | 
					                                     String requiredEtag, String fileLastModifTimestamp) {
 | 
				
			||||||
 | 
					        this(localPath, remotePath, mimeType, fileLastModifTimestamp);
 | 
				
			||||||
 | 
					        mRequiredEtag = requiredEtag;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult run(OwnCloudClient client) {
 | 
				
			||||||
 | 
					        RemoteOperationResult result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (mCancellationRequested.get()) {
 | 
				
			||||||
 | 
					                // the operation was cancelled before getting it's turn to be executed in the queue of uploads
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(new OperationCancelledException());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // perform the upload
 | 
				
			||||||
 | 
					                result = uploadFile(client);
 | 
				
			||||||
 | 
					                Timber.i("Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (mPutMethod != null && mPutMethod.isAborted()) {
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(new OperationCancelledException());
 | 
				
			||||||
 | 
					                Timber.e(result.getException(),
 | 
				
			||||||
 | 
					                        "Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(e);
 | 
				
			||||||
 | 
					                Timber.e(e, "Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected RemoteOperationResult<?> uploadFile(OwnCloudClient client) throws Exception {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        File fileToUpload = new File(mLocalPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MediaType mediaType = MediaType.parse(mMimeType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mFileRequestBody = new FileRequestBody(fileToUpload, mediaType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mPutMethod = new PutMethod(
 | 
				
			||||||
 | 
					                new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), mFileRequestBody);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mPutMethod.setRetryOnConnectionFailure(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (mRequiredEtag != null && mRequiredEtag.length() > 0) {
 | 
				
			||||||
 | 
					            mPutMethod.addRequestHeader(HttpConstants.IF_MATCH_HEADER, mRequiredEtag);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mPutMethod.addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, String.valueOf(fileToUpload.length()));
 | 
				
			||||||
 | 
					        mPutMethod.addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, mFileLastModifTimestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int status = client.executeHttpMethod(mPutMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isSuccess(status)) {
 | 
				
			||||||
 | 
					            return new RemoteOperationResult<>(OK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } else { // synchronization failed
 | 
				
			||||||
 | 
					            return new RemoteOperationResult<>(mPutMethod);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Set<OnDatatransferProgressListener> getDataTransferListeners() {
 | 
				
			||||||
 | 
					        return mDataTransferListeners;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mDataTransferListeners.add(listener);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (mFileRequestBody != null) {
 | 
				
			||||||
 | 
					            mFileRequestBody.addDatatransferProgressListener(listener);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mDataTransferListeners.remove(listener);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (mFileRequestBody != null) {
 | 
				
			||||||
 | 
					            mFileRequestBody.removeDatatransferProgressListener(listener);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void cancel() {
 | 
				
			||||||
 | 
					        synchronized (mCancellationRequested) {
 | 
				
			||||||
 | 
					            mCancellationRequested.set(true);
 | 
				
			||||||
 | 
					            if (mPutMethod != null) {
 | 
				
			||||||
 | 
					                mPutMethod.abort();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isSuccess(int status) {
 | 
				
			||||||
 | 
					        return ((status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_CREATED ||
 | 
				
			||||||
 | 
					                status == HttpConstants.HTTP_NO_CONTENT));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,122 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2022 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.files.chunks
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.PutMethod
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.OperationCancelledException
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.FileUtils.MODE_READ_ONLY
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.UploadFileFromFileSystemOperation
 | 
					 | 
				
			||||||
import okhttp3.MediaType
 | 
					 | 
				
			||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
 | 
					 | 
				
			||||||
import timber.log.Timber
 | 
					 | 
				
			||||||
import java.io.File
 | 
					 | 
				
			||||||
import java.io.RandomAccessFile
 | 
					 | 
				
			||||||
import java.net.URL
 | 
					 | 
				
			||||||
import java.nio.channels.FileChannel
 | 
					 | 
				
			||||||
import java.util.concurrent.TimeUnit
 | 
					 | 
				
			||||||
import kotlin.math.ceil
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Remote operation performing the chunked upload of a remote file to the ownCloud server.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author David A. Velasco
 | 
					 | 
				
			||||||
 * @author David González Verdugo
 | 
					 | 
				
			||||||
 * @author Abel García de Prada
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class ChunkedUploadFromFileSystemOperation(
 | 
					 | 
				
			||||||
    private val transferId: String,
 | 
					 | 
				
			||||||
    localPath: String,
 | 
					 | 
				
			||||||
    remotePath: String,
 | 
					 | 
				
			||||||
    mimeType: String,
 | 
					 | 
				
			||||||
    lastModifiedTimestamp: String,
 | 
					 | 
				
			||||||
    requiredEtag: String?,
 | 
					 | 
				
			||||||
) : UploadFileFromFileSystemOperation(
 | 
					 | 
				
			||||||
    localPath = localPath,
 | 
					 | 
				
			||||||
    remotePath = remotePath,
 | 
					 | 
				
			||||||
    mimeType = mimeType,
 | 
					 | 
				
			||||||
    lastModifiedTimestamp = lastModifiedTimestamp,
 | 
					 | 
				
			||||||
    requiredEtag = requiredEtag
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Throws(Exception::class)
 | 
					 | 
				
			||||||
    override fun uploadFile(client: OwnCloudClient): RemoteOperationResult<Unit> {
 | 
					 | 
				
			||||||
        lateinit var result: RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        val fileToUpload = File(localPath)
 | 
					 | 
				
			||||||
        val mediaType: MediaType? = mimeType.toMediaTypeOrNull()
 | 
					 | 
				
			||||||
        val raf = RandomAccessFile(fileToUpload, MODE_READ_ONLY)
 | 
					 | 
				
			||||||
        val channel: FileChannel = raf.channel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        val fileRequestBody = ChunkFromFileRequestBody(fileToUpload, mediaType, channel).also {
 | 
					 | 
				
			||||||
            synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        val uriPrefix = client.uploadsWebDavUri.toString() + File.separator + transferId
 | 
					 | 
				
			||||||
        val totalLength = fileToUpload.length()
 | 
					 | 
				
			||||||
        val chunkCount = ceil(totalLength.toDouble() / CHUNK_SIZE).toLong()
 | 
					 | 
				
			||||||
        var offset: Long = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (chunkIndex in 0..chunkCount) {
 | 
					 | 
				
			||||||
            fileRequestBody.setOffset(offset)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (cancellationRequested.get()) {
 | 
					 | 
				
			||||||
                result = RemoteOperationResult<Unit>(OperationCancelledException())
 | 
					 | 
				
			||||||
                break
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                putMethod = PutMethod(URL(uriPrefix + File.separator + chunkIndex), fileRequestBody).apply {
 | 
					 | 
				
			||||||
                    if (chunkIndex == chunkCount - 1) {
 | 
					 | 
				
			||||||
                        // Added a high timeout to the last chunk due to when the last chunk
 | 
					 | 
				
			||||||
                        // arrives to the server with the last PUT, all chunks get assembled
 | 
					 | 
				
			||||||
                        // within that PHP request, so last one takes longer.
 | 
					 | 
				
			||||||
                        setReadTimeout(LAST_CHUNK_TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                val status = client.executeHttpMethod(putMethod)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                Timber.d("Upload of $localPath to $remotePath, chunk index $chunkIndex, count $chunkCount, HTTP result status $status")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (isSuccess(status)) {
 | 
					 | 
				
			||||||
                    result = RemoteOperationResult<Unit>(ResultCode.OK)
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    result = RemoteOperationResult<Unit>(putMethod)
 | 
					 | 
				
			||||||
                    break
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            offset += CHUNK_SIZE
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        channel.close()
 | 
					 | 
				
			||||||
        raf.close()
 | 
					 | 
				
			||||||
        return result
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        const val CHUNK_SIZE = 10_240_000L // 10 MB
 | 
					 | 
				
			||||||
        private const val LAST_CHUNK_TIMEOUT = 900_000 // 15 mins.
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					/* 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.files.chunks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.OwnCloudClient;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.http.methods.webdav.PutMethod;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.operations.OperationCancelledException;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 | 
				
			||||||
 | 
					import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
 | 
				
			||||||
 | 
					import okhttp3.MediaType;
 | 
				
			||||||
 | 
					import timber.log.Timber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.RandomAccessFile;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.nio.channels.FileChannel;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the chunked upload of a remote file to the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David A. Velasco
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final long CHUNK_SIZE = 1024000;
 | 
				
			||||||
 | 
					    private static final int LAST_CHUNK_TIMEOUT = 900000; //15 mins.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String mTransferId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ChunkedUploadRemoteFileOperation(String transferId, String localPath, String remotePath, String mimeType,
 | 
				
			||||||
 | 
					                                            String requiredEtag, String fileLastModifTimestamp) {
 | 
				
			||||||
 | 
					        super(localPath, remotePath, mimeType, requiredEtag, fileLastModifTimestamp);
 | 
				
			||||||
 | 
					        mTransferId = transferId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected RemoteOperationResult uploadFile(OwnCloudClient client) throws Exception {
 | 
				
			||||||
 | 
					        int status;
 | 
				
			||||||
 | 
					        RemoteOperationResult result = null;
 | 
				
			||||||
 | 
					        FileChannel channel;
 | 
				
			||||||
 | 
					        RandomAccessFile raf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        File fileToUpload = new File(mLocalPath);
 | 
				
			||||||
 | 
					        MediaType mediaType = MediaType.parse(mMimeType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        raf = new RandomAccessFile(fileToUpload, "r");
 | 
				
			||||||
 | 
					        channel = raf.getChannel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mFileRequestBody = new ChunkFromFileRequestBody(fileToUpload, mediaType, channel, CHUNK_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        synchronized (mDataTransferListeners) {
 | 
				
			||||||
 | 
					            mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long offset = 0;
 | 
				
			||||||
 | 
					        String uriPrefix = client.getUploadsWebDavUri() + File.separator + mTransferId;
 | 
				
			||||||
 | 
					        long totalLength = fileToUpload.length();
 | 
				
			||||||
 | 
					        long chunkCount = (long) Math.ceil((double) totalLength / CHUNK_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ((ChunkFromFileRequestBody) mFileRequestBody).setOffset(offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (mCancellationRequested.get()) {
 | 
				
			||||||
 | 
					                result = new RemoteOperationResult<>(new OperationCancelledException());
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                mPutMethod = new PutMethod(
 | 
				
			||||||
 | 
					                        new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (chunkIndex == chunkCount - 1) {
 | 
				
			||||||
 | 
					                    // Added a high timeout to the last chunk due to when the last chunk
 | 
				
			||||||
 | 
					                    // arrives to the server with the last PUT, all chunks get assembled
 | 
				
			||||||
 | 
					                    // within that PHP request, so last one takes longer.
 | 
				
			||||||
 | 
					                    mPutMethod.setReadTimeout(LAST_CHUNK_TIMEOUT, TimeUnit.MILLISECONDS);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                status = client.executeHttpMethod(mPutMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Timber.d("Upload of " + mLocalPath + " to " + mRemotePath +
 | 
				
			||||||
 | 
					                        ", chunk index " + chunkIndex + ", count " + chunkCount +
 | 
				
			||||||
 | 
					                        ", HTTP result status " + status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (isSuccess(status)) {
 | 
				
			||||||
 | 
					                    result = new RemoteOperationResult<>(OK);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    result = new RemoteOperationResult<>(mPutMethod);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (channel != null) {
 | 
				
			||||||
 | 
					            channel.close();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (raf != null) {
 | 
				
			||||||
 | 
					            raf.close();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -22,8 +22,24 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.owncloud.android.lib.common.utils
 | 
					package com.owncloud.android.lib.resources.files.chunks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun Any.isOneOf(vararg values: Any): Boolean {
 | 
					import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation;
 | 
				
			||||||
    return this in values
 | 
					
 | 
				
			||||||
}
 | 
					/**
 | 
				
			||||||
 | 
					 * Remote operation performing the creation of a new folder to save chunks during an upload to the ownCloud server.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class CreateRemoteChunkFolderOperation extends CreateRemoteFolderOperation {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public CreateRemoteChunkFolderOperation(String remotePath, boolean createFullPath) {
 | 
				
			||||||
 | 
					        super(remotePath, createFullPath);
 | 
				
			||||||
 | 
					        createChunksFolder = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					/* ownCloud Android Library is available under MIT license
 | 
				
			||||||
 *   Copyright (C) 2021 ownCloud GmbH.
 | 
					 *   Copyright (C) 2020 ownCloud GmbH.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 *   of this software and associated documentation files (the "Software"), to deal
 | 
					 *   of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
@ -21,38 +21,30 @@
 | 
				
			|||||||
 *   THE SOFTWARE.
 | 
					 *   THE SOFTWARE.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
package com.owncloud.android.lib.resources.files.chunks
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.net.Uri
 | 
					package com.owncloud.android.lib.resources.files.chunks;
 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					
 | 
				
			||||||
import com.owncloud.android.lib.common.http.HttpConstants
 | 
					import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation;
 | 
				
			||||||
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Remote operation to move the file built from chunks after uploading it
 | 
					 * Remote operation to move the file built from chunks after uploading it
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author David González Verdugo
 | 
					 * @author David González Verdugo
 | 
				
			||||||
 * @author Abel García de Prada
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class MoveRemoteChunksFileOperation(
 | 
					public class MoveRemoteChunksFileOperation extends MoveRemoteFileOperation {
 | 
				
			||||||
    sourceRemotePath: String,
 | 
					 | 
				
			||||||
    targetRemotePath: String,
 | 
					 | 
				
			||||||
    private val fileLastModificationTimestamp: String,
 | 
					 | 
				
			||||||
    private val fileLength: Long
 | 
					 | 
				
			||||||
) : MoveRemoteFileOperation(
 | 
					 | 
				
			||||||
    sourceRemotePath = sourceRemotePath,
 | 
					 | 
				
			||||||
    targetRemotePath = targetRemotePath,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructor.
 | 
				
			||||||
    override fun addRequestHeaders(moveMethod: MoveMethod) {
 | 
					     *
 | 
				
			||||||
        super.addRequestHeaders(moveMethod)
 | 
					     * @param srcRemotePath    Remote path of the file/folder to move.
 | 
				
			||||||
 | 
					     * @param targetRemotePath Remove path desired for the file/folder after moving it.
 | 
				
			||||||
        moveMethod.apply {
 | 
					     * @param overwrite
 | 
				
			||||||
            addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, fileLastModificationTimestamp)
 | 
					     */
 | 
				
			||||||
            addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileLength.toString())
 | 
					    public MoveRemoteChunksFileOperation(String srcRemotePath, String targetRemotePath, boolean overwrite,
 | 
				
			||||||
        }
 | 
					                                         String fileLastModifTimestamp, long fileLength) {
 | 
				
			||||||
 | 
					        super(srcRemotePath, targetRemotePath, overwrite);
 | 
				
			||||||
 | 
					        moveChunkedFile = true;
 | 
				
			||||||
 | 
					        mFileLastModifTimestamp = fileLastModifTimestamp;
 | 
				
			||||||
 | 
					        mFileLength = fileLength;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -22,12 +22,20 @@
 | 
				
			|||||||
 *   THE SOFTWARE.
 | 
					 *   THE SOFTWARE.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
package com.owncloud.android.lib.resources.files.chunks
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.net.Uri
 | 
					package com.owncloud.android.lib.resources.files.chunks;
 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) {
 | 
					import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation;
 | 
				
			||||||
    override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri
 | 
					
 | 
				
			||||||
}
 | 
					public class RemoveRemoteChunksFolderOperation extends RemoveRemoteFileOperation {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructor
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param remotePath RemotePath of the remote file or folder to remove from the server
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public RemoveRemoteChunksFolderOperation(String remotePath) {
 | 
				
			||||||
 | 
					        super(remotePath);
 | 
				
			||||||
 | 
					        removeChunksFolder = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,41 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2021 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.files.services
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.Service
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface ChunkService : Service {
 | 
					 | 
				
			||||||
    fun removeFile(
 | 
					 | 
				
			||||||
        remotePath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun moveFile(
 | 
					 | 
				
			||||||
        sourceRemotePath: String,
 | 
					 | 
				
			||||||
        targetRemotePath: String,
 | 
					 | 
				
			||||||
        fileLastModificationTimestamp: String,
 | 
					 | 
				
			||||||
        fileLength: Long
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -25,53 +25,8 @@ package com.owncloud.android.lib.resources.files.services
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
				
			||||||
import com.owncloud.android.lib.resources.Service
 | 
					import com.owncloud.android.lib.resources.Service
 | 
				
			||||||
import com.owncloud.android.lib.resources.files.RemoteFile
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface FileService : Service {
 | 
					interface FileService : Service {
 | 
				
			||||||
 | 
					    fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean>
 | 
				
			||||||
    fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String>
 | 
					    fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String>
 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun checkPathExistence(
 | 
					 | 
				
			||||||
        path: String,
 | 
					 | 
				
			||||||
        isUserLogged: Boolean
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Boolean>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun copyFile(
 | 
					 | 
				
			||||||
        sourceRemotePath: String,
 | 
					 | 
				
			||||||
        targetRemotePath: String,
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<String>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun createFolder(
 | 
					 | 
				
			||||||
        remotePath: String,
 | 
					 | 
				
			||||||
        createFullPath: Boolean,
 | 
					 | 
				
			||||||
        isChunkFolder: Boolean = false
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun downloadFile(
 | 
					 | 
				
			||||||
        remotePath: String,
 | 
					 | 
				
			||||||
        localTempPath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun moveFile(
 | 
					 | 
				
			||||||
        sourceRemotePath: String,
 | 
					 | 
				
			||||||
        targetRemotePath: String,
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun readFile(
 | 
					 | 
				
			||||||
        remotePath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<RemoteFile>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun refreshFolder(
 | 
					 | 
				
			||||||
        remotePath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<ArrayList<RemoteFile>>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun removeFile(
 | 
					 | 
				
			||||||
        remotePath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fun renameFile(
 | 
					 | 
				
			||||||
        oldName: String,
 | 
					 | 
				
			||||||
        oldRemotePath: String,
 | 
					 | 
				
			||||||
        newName: String,
 | 
					 | 
				
			||||||
        isFolder: Boolean,
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit>
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,50 +0,0 @@
 | 
				
			|||||||
/* ownCloud Android Library is available under MIT license
 | 
					 | 
				
			||||||
 *   Copyright (C) 2021 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.files.services.implementation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.chunks.MoveRemoteChunksFileOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.chunks.RemoveRemoteChunksFolderOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.services.ChunkService
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class OCChunkService(override val client: OwnCloudClient) : ChunkService {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun removeFile(remotePath: String): RemoteOperationResult<Unit> =
 | 
					 | 
				
			||||||
        RemoveRemoteChunksFolderOperation(remotePath = remotePath).execute(client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun moveFile(
 | 
					 | 
				
			||||||
        sourceRemotePath: String,
 | 
					 | 
				
			||||||
        targetRemotePath: String,
 | 
					 | 
				
			||||||
        fileLastModificationTimestamp: String,
 | 
					 | 
				
			||||||
        fileLength: Long
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit> =
 | 
					 | 
				
			||||||
        MoveRemoteChunksFileOperation(
 | 
					 | 
				
			||||||
            sourceRemotePath = sourceRemotePath,
 | 
					 | 
				
			||||||
            targetRemotePath = targetRemotePath,
 | 
					 | 
				
			||||||
            fileLastModificationTimestamp = fileLastModificationTimestamp,
 | 
					 | 
				
			||||||
            fileLength = fileLength,
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -26,24 +26,12 @@ package com.owncloud.android.lib.resources.files.services.implementation
 | 
				
			|||||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
					import com.owncloud.android.lib.common.OwnCloudClient
 | 
				
			||||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
					import com.owncloud.android.lib.common.operations.RemoteOperationResult
 | 
				
			||||||
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
 | 
					import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
 | 
				
			||||||
import com.owncloud.android.lib.resources.files.CopyRemoteFileOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation
 | 
					import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation
 | 
				
			||||||
import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.RemoteFile
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.RenameRemoteFileOperation
 | 
					 | 
				
			||||||
import com.owncloud.android.lib.resources.files.services.FileService
 | 
					import com.owncloud.android.lib.resources.files.services.FileService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OCFileService(override val client: OwnCloudClient) : FileService {
 | 
					class OCFileService(override val client: OwnCloudClient) :
 | 
				
			||||||
 | 
					    FileService {
 | 
				
			||||||
    override fun checkPathExistence(
 | 
					    override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean> =
 | 
				
			||||||
        path: String,
 | 
					 | 
				
			||||||
        isUserLogged: Boolean
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Boolean> =
 | 
					 | 
				
			||||||
        CheckPathExistenceRemoteOperation(
 | 
					        CheckPathExistenceRemoteOperation(
 | 
				
			||||||
            remotePath = path,
 | 
					            remotePath = path,
 | 
				
			||||||
            isUserLoggedIn = isUserLogged
 | 
					            isUserLoggedIn = isUserLogged
 | 
				
			||||||
@ -52,75 +40,4 @@ class OCFileService(override val client: OwnCloudClient) : FileService {
 | 
				
			|||||||
    override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String> =
 | 
					    override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String> =
 | 
				
			||||||
        GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client)
 | 
					        GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun copyFile(
 | 
					 | 
				
			||||||
        sourceRemotePath: String,
 | 
					 | 
				
			||||||
        targetRemotePath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<String> =
 | 
					 | 
				
			||||||
        CopyRemoteFileOperation(
 | 
					 | 
				
			||||||
            srcRemotePath = sourceRemotePath,
 | 
					 | 
				
			||||||
            targetRemotePath = targetRemotePath
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun createFolder(
 | 
					 | 
				
			||||||
        remotePath: String,
 | 
					 | 
				
			||||||
        createFullPath: Boolean,
 | 
					 | 
				
			||||||
        isChunkFolder: Boolean
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit> =
 | 
					 | 
				
			||||||
        CreateRemoteFolderOperation(
 | 
					 | 
				
			||||||
            remotePath = remotePath,
 | 
					 | 
				
			||||||
            createFullPath = createFullPath,
 | 
					 | 
				
			||||||
            isChunksFolder = isChunkFolder
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun downloadFile(
 | 
					 | 
				
			||||||
        remotePath: String,
 | 
					 | 
				
			||||||
        localTempPath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit> =
 | 
					 | 
				
			||||||
        DownloadRemoteFileOperation(
 | 
					 | 
				
			||||||
            remotePath = remotePath,
 | 
					 | 
				
			||||||
            localFolderPath = localTempPath
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun moveFile(
 | 
					 | 
				
			||||||
        sourceRemotePath: String,
 | 
					 | 
				
			||||||
        targetRemotePath: String,
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit> =
 | 
					 | 
				
			||||||
        MoveRemoteFileOperation(
 | 
					 | 
				
			||||||
            sourceRemotePath = sourceRemotePath,
 | 
					 | 
				
			||||||
            targetRemotePath = targetRemotePath,
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun readFile(
 | 
					 | 
				
			||||||
        remotePath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<RemoteFile> =
 | 
					 | 
				
			||||||
        ReadRemoteFileOperation(
 | 
					 | 
				
			||||||
            remotePath = remotePath
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun refreshFolder(
 | 
					 | 
				
			||||||
        remotePath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<ArrayList<RemoteFile>> =
 | 
					 | 
				
			||||||
        ReadRemoteFolderOperation(
 | 
					 | 
				
			||||||
            remotePath = remotePath
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun removeFile(
 | 
					 | 
				
			||||||
        remotePath: String
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit> =
 | 
					 | 
				
			||||||
        RemoveRemoteFileOperation(
 | 
					 | 
				
			||||||
            remotePath = remotePath
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    override fun renameFile(
 | 
					 | 
				
			||||||
        oldName: String,
 | 
					 | 
				
			||||||
        oldRemotePath: String,
 | 
					 | 
				
			||||||
        newName: String,
 | 
					 | 
				
			||||||
        isFolder: Boolean
 | 
					 | 
				
			||||||
    ): RemoteOperationResult<Unit> =
 | 
					 | 
				
			||||||
        RenameRemoteFileOperation(
 | 
					 | 
				
			||||||
            oldName = oldName,
 | 
					 | 
				
			||||||
            oldRemotePath = oldRemotePath,
 | 
					 | 
				
			||||||
            newName = newName,
 | 
					 | 
				
			||||||
            isFolder = isFolder
 | 
					 | 
				
			||||||
        ).execute(client)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -99,11 +99,11 @@ class GetRemoteShareesOperation
 | 
				
			|||||||
            .appendQueryParameter(PARAM_PER_PAGE, perPage.toString())
 | 
					            .appendQueryParameter(PARAM_PER_PAGE, perPage.toString())
 | 
				
			||||||
            .build()
 | 
					            .build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun parseResponse(response: String?): ShareeOcsResponse? {
 | 
					    private fun parseResponse(response: String): ShareeOcsResponse? {
 | 
				
			||||||
        val moshi = Moshi.Builder().build()
 | 
					        val moshi = Moshi.Builder().build()
 | 
				
			||||||
        val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java)
 | 
					        val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java)
 | 
				
			||||||
        val adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>> = moshi.adapter(type)
 | 
					        val adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>> = moshi.adapter(type)
 | 
				
			||||||
        return response?.let { adapter.fromJson(it)?.ocs?.data }
 | 
					        return adapter.fromJson(response)!!.ocs.data
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun onResultUnsuccessful(
 | 
					    private fun onResultUnsuccessful(
 | 
				
			||||||
@ -123,7 +123,7 @@ class GetRemoteShareesOperation
 | 
				
			|||||||
    private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareeOcsResponse> {
 | 
					    private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareeOcsResponse> {
 | 
				
			||||||
        val result = RemoteOperationResult<ShareeOcsResponse>(OK)
 | 
					        val result = RemoteOperationResult<ShareeOcsResponse>(OK)
 | 
				
			||||||
        Timber.d("Successful response: $response")
 | 
					        Timber.d("Successful response: $response")
 | 
				
			||||||
        result.data = parseResponse(response)
 | 
					        result.data = parseResponse(response!!)
 | 
				
			||||||
        Timber.d("*** Get Users or groups completed ")
 | 
					        Timber.d("*** Get Users or groups completed ")
 | 
				
			||||||
        return result
 | 
					        return result
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -63,31 +63,31 @@ class ShareeResponseTest {
 | 
				
			|||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    fun `example response - ok - correct sturcture`() {
 | 
					    fun `example response - ok - correct sturcture`() {
 | 
				
			||||||
        val response = loadResponses(EXAMPLE_RESPONSE_JSON)!!
 | 
					        val response = loadResponses(EXAMPLE_RESPONSE_JSON)!!
 | 
				
			||||||
        assertEquals(2, response.ocs.data?.groups?.size)
 | 
					        assertEquals(2, response.ocs.data.groups.size)
 | 
				
			||||||
        assertEquals(0, response.ocs.data?.remotes?.size)
 | 
					        assertEquals(0, response.ocs.data.remotes.size)
 | 
				
			||||||
        assertEquals(2, response.ocs.data?.users?.size)
 | 
					        assertEquals(2, response.ocs.data.users.size)
 | 
				
			||||||
        assertEquals(0, response.ocs.data?.exact?.groups?.size)
 | 
					        assertEquals(0, response.ocs.data.exact?.groups?.size)
 | 
				
			||||||
        assertEquals(0, response.ocs.data?.exact?.remotes?.size)
 | 
					        assertEquals(0, response.ocs.data.exact?.remotes?.size)
 | 
				
			||||||
        assertEquals(1, response.ocs.data?.exact?.users?.size)
 | 
					        assertEquals(1, response.ocs.data.exact?.users?.size)
 | 
				
			||||||
        assertEquals("user1@user1.com", response.ocs.data?.users?.get(0)?.value?.additionalInfo)
 | 
					        assertEquals("user1@user1.com", response.ocs.data.users.get(0).value.additionalInfo)
 | 
				
			||||||
        assertNull(response.ocs.data?.users?.get(1)?.value?.additionalInfo)
 | 
					        assertNull(response.ocs.data.users[1].value.additionalInfo)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    fun `check empty response - ok - parsing ok`() {
 | 
					    fun `check empty response - ok - parsing ok`() {
 | 
				
			||||||
        val response = loadResponses(EMPTY_RESPONSE_JSON)!!
 | 
					        val response = loadResponses(EMPTY_RESPONSE_JSON)!!
 | 
				
			||||||
        assertTrue(response.ocs.data?.exact?.groups?.isEmpty()!!)
 | 
					        assertTrue(response.ocs.data.exact?.groups?.isEmpty()!!)
 | 
				
			||||||
        assertTrue(response.ocs.data?.exact?.remotes?.isEmpty()!!)
 | 
					        assertTrue(response.ocs.data.exact?.remotes?.isEmpty()!!)
 | 
				
			||||||
        assertTrue(response.ocs.data?.exact?.users?.isEmpty()!!)
 | 
					        assertTrue(response.ocs.data.exact?.users?.isEmpty()!!)
 | 
				
			||||||
        assertTrue(response.ocs.data?.groups?.isEmpty()!!)
 | 
					        assertTrue(response.ocs.data.groups.isEmpty())
 | 
				
			||||||
        assertTrue(response.ocs.data?.remotes?.isEmpty()!!)
 | 
					        assertTrue(response.ocs.data.remotes.isEmpty())
 | 
				
			||||||
        assertTrue(response.ocs.data?.users?.isEmpty()!!)
 | 
					        assertTrue(response.ocs.data.users.isEmpty())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
        private const val RESOURCES_PATH =
 | 
					        val RESOURCES_PATH =
 | 
				
			||||||
            "src/test/responses/com.owncloud.android.lib.resources.sharees.responses"
 | 
					            "src/test/responses/com.owncloud.android.lib.resources.sharees.responses"
 | 
				
			||||||
        const val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json"
 | 
					        val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json"
 | 
				
			||||||
        const val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json"
 | 
					        val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user