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: 'kotlin-android'
 | 
			
		||||
apply plugin: 'kotlin-kapt'
 | 
			
		||||
apply plugin: 'kotlin-parcelize'
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    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.methods.HttpBaseMethod;
 | 
			
		||||
import com.owncloud.android.lib.common.utils.RandomUtils;
 | 
			
		||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 | 
			
		||||
import okhttp3.Cookie;
 | 
			
		||||
import okhttp3.HttpUrl;
 | 
			
		||||
import timber.log.Timber;
 | 
			
		||||
@ -58,6 +59,7 @@ public class OwnCloudClient extends HttpClient {
 | 
			
		||||
    private OwnCloudCredentials mCredentials = null;
 | 
			
		||||
    private int mInstanceNumber;
 | 
			
		||||
    private Uri mBaseUri;
 | 
			
		||||
    private OwnCloudVersion mVersion = null;
 | 
			
		||||
    private OwnCloudAccount mAccount;
 | 
			
		||||
    private final ConnectionValidator mConnectionValidator;
 | 
			
		||||
    private Object mRequestMutex = new Object();
 | 
			
		||||
@ -239,6 +241,14 @@ public class OwnCloudClient extends HttpClient {
 | 
			
		||||
                HttpUrl.parse(mBaseUri.toString()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public OwnCloudVersion getOwnCloudVersion() {
 | 
			
		||||
        return mVersion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setOwnCloudVersion(OwnCloudVersion version) {
 | 
			
		||||
        mVersion = version;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public OwnCloudAccount getAccount() {
 | 
			
		||||
        return mAccount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -94,6 +94,26 @@ public class AccountUtils {
 | 
			
		||||
        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
 | 
			
		||||
     * @throws IOException
 | 
			
		||||
@ -189,6 +209,11 @@ public class AccountUtils {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
         * http://server/path or https://owncloud.server
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ import java.net.URL
 | 
			
		||||
class CopyMethod(
 | 
			
		||||
    val url: URL,
 | 
			
		||||
    private val destinationUrl: String,
 | 
			
		||||
    private val forceOverride: Boolean = false
 | 
			
		||||
    private val forceOverride: Boolean
 | 
			
		||||
) : DavMethod(url) {
 | 
			
		||||
    @Throws(Exception::class)
 | 
			
		||||
    public override fun onDavExecute(davResource: DavOCResource): Int {
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ import java.net.URL
 | 
			
		||||
class MoveMethod(
 | 
			
		||||
    url: URL,
 | 
			
		||||
    private val destinationUrl: String,
 | 
			
		||||
    private val forceOverride: Boolean = false
 | 
			
		||||
    private val forceOverride: Boolean
 | 
			
		||||
) : DavMethod(url) {
 | 
			
		||||
    @Throws(Exception::class)
 | 
			
		||||
    override fun onDavExecute(davResource: DavOCResource): Int {
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,7 @@ class PropfindMethod(
 | 
			
		||||
            depth = depth,
 | 
			
		||||
            reqProp = propertiesToRequest,
 | 
			
		||||
            listOfHeaders = super.getRequestHeadersAsHashMap(),
 | 
			
		||||
            callback = { response: Response, hrefRelation: HrefRelation ->
 | 
			
		||||
            callback = { response: Response, hrefRelation: HrefRelation? ->
 | 
			
		||||
                when (hrefRelation) {
 | 
			
		||||
                    HrefRelation.MEMBER -> members.add(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)
 | 
			
		||||
data class OCSResponse<T>(
 | 
			
		||||
    val meta: MetaData,
 | 
			
		||||
    val data: T?
 | 
			
		||||
    val data: T
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@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 MIME_DIR = "DIR";
 | 
			
		||||
    public static final String MIME_DIR_UNIX = "httpd/unix-directory";
 | 
			
		||||
    public static final String MODE_READ_ONLY = "r";
 | 
			
		||||
 | 
			
		||||
    static String getParentPath(String remotePath) {
 | 
			
		||||
        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
 | 
			
		||||
 *   Copyright (C) 2022 ownCloud GmbH.
 | 
			
		||||
 *   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
 | 
			
		||||
@ -24,15 +24,22 @@
 | 
			
		||||
 | 
			
		||||
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.http.HttpConstants
 | 
			
		||||
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.operations.RemoteOperation
 | 
			
		||||
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 java.io.IOException
 | 
			
		||||
import java.net.URL
 | 
			
		||||
 | 
			
		||||
class UploadFileFromContentUriOperation(
 | 
			
		||||
@ -63,6 +70,34 @@ class UploadFileFromContentUriOperation(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 {
 | 
			
		||||
    return this in values
 | 
			
		||||
import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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
 | 
			
		||||
 *   Copyright (C) 2021 ownCloud GmbH.
 | 
			
		||||
 *   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
 | 
			
		||||
@ -21,38 +21,30 @@
 | 
			
		||||
 *   THE SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
package com.owncloud.android.lib.resources.files.chunks
 | 
			
		||||
 | 
			
		||||
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.resources.files.MoveRemoteFileOperation
 | 
			
		||||
package com.owncloud.android.lib.resources.files.chunks;
 | 
			
		||||
 | 
			
		||||
import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Remote operation to move the file built from chunks after uploading it
 | 
			
		||||
 *
 | 
			
		||||
 * @author David González Verdugo
 | 
			
		||||
 * @author Abel García de Prada
 | 
			
		||||
 */
 | 
			
		||||
class MoveRemoteChunksFileOperation(
 | 
			
		||||
    sourceRemotePath: String,
 | 
			
		||||
    targetRemotePath: String,
 | 
			
		||||
    private val fileLastModificationTimestamp: String,
 | 
			
		||||
    private val fileLength: Long
 | 
			
		||||
) : MoveRemoteFileOperation(
 | 
			
		||||
    sourceRemotePath = sourceRemotePath,
 | 
			
		||||
    targetRemotePath = targetRemotePath,
 | 
			
		||||
) {
 | 
			
		||||
public class MoveRemoteChunksFileOperation extends MoveRemoteFileOperation {
 | 
			
		||||
 | 
			
		||||
    override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri
 | 
			
		||||
 | 
			
		||||
    override fun addRequestHeaders(moveMethod: MoveMethod) {
 | 
			
		||||
        super.addRequestHeaders(moveMethod)
 | 
			
		||||
 | 
			
		||||
        moveMethod.apply {
 | 
			
		||||
            addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, fileLastModificationTimestamp)
 | 
			
		||||
            addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileLength.toString())
 | 
			
		||||
        }
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor.
 | 
			
		||||
     *
 | 
			
		||||
     * @param srcRemotePath    Remote path of the file/folder to move.
 | 
			
		||||
     * @param targetRemotePath Remove path desired for the file/folder after moving it.
 | 
			
		||||
     * @param overwrite
 | 
			
		||||
     */
 | 
			
		||||
    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.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
package com.owncloud.android.lib.resources.files.chunks
 | 
			
		||||
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import com.owncloud.android.lib.common.OwnCloudClient
 | 
			
		||||
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation
 | 
			
		||||
package com.owncloud.android.lib.resources.files.chunks;
 | 
			
		||||
 | 
			
		||||
class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) {
 | 
			
		||||
    override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri
 | 
			
		||||
import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation;
 | 
			
		||||
 | 
			
		||||
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.resources.Service
 | 
			
		||||
import com.owncloud.android.lib.resources.files.RemoteFile
 | 
			
		||||
 | 
			
		||||
interface FileService : Service {
 | 
			
		||||
    fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean>
 | 
			
		||||
    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.operations.RemoteOperationResult
 | 
			
		||||
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.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
 | 
			
		||||
 | 
			
		||||
class OCFileService(override val client: OwnCloudClient) : FileService {
 | 
			
		||||
 | 
			
		||||
    override fun checkPathExistence(
 | 
			
		||||
        path: String,
 | 
			
		||||
        isUserLogged: Boolean
 | 
			
		||||
    ): RemoteOperationResult<Boolean> =
 | 
			
		||||
class OCFileService(override val client: OwnCloudClient) :
 | 
			
		||||
    FileService {
 | 
			
		||||
    override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult<Boolean> =
 | 
			
		||||
        CheckPathExistenceRemoteOperation(
 | 
			
		||||
            remotePath = path,
 | 
			
		||||
            isUserLoggedIn = isUserLogged
 | 
			
		||||
@ -52,75 +40,4 @@ class OCFileService(override val client: OwnCloudClient) : FileService {
 | 
			
		||||
    override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult<String> =
 | 
			
		||||
        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())
 | 
			
		||||
            .build()
 | 
			
		||||
 | 
			
		||||
    private fun parseResponse(response: String?): ShareeOcsResponse? {
 | 
			
		||||
    private fun parseResponse(response: String): ShareeOcsResponse? {
 | 
			
		||||
        val moshi = Moshi.Builder().build()
 | 
			
		||||
        val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java)
 | 
			
		||||
        val adapter: JsonAdapter<CommonOcsResponse<ShareeOcsResponse>> = moshi.adapter(type)
 | 
			
		||||
        return response?.let { adapter.fromJson(it)?.ocs?.data }
 | 
			
		||||
        return adapter.fromJson(response)!!.ocs.data
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun onResultUnsuccessful(
 | 
			
		||||
@ -123,7 +123,7 @@ class GetRemoteShareesOperation
 | 
			
		||||
    private fun onRequestSuccessful(response: String?): RemoteOperationResult<ShareeOcsResponse> {
 | 
			
		||||
        val result = RemoteOperationResult<ShareeOcsResponse>(OK)
 | 
			
		||||
        Timber.d("Successful response: $response")
 | 
			
		||||
        result.data = parseResponse(response)
 | 
			
		||||
        result.data = parseResponse(response!!)
 | 
			
		||||
        Timber.d("*** Get Users or groups completed ")
 | 
			
		||||
        return result
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -63,31 +63,31 @@ class ShareeResponseTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun `example response - ok - correct sturcture`() {
 | 
			
		||||
        val response = loadResponses(EXAMPLE_RESPONSE_JSON)!!
 | 
			
		||||
        assertEquals(2, response.ocs.data?.groups?.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data?.remotes?.size)
 | 
			
		||||
        assertEquals(2, response.ocs.data?.users?.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data?.exact?.groups?.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data?.exact?.remotes?.size)
 | 
			
		||||
        assertEquals(1, response.ocs.data?.exact?.users?.size)
 | 
			
		||||
        assertEquals("user1@user1.com", response.ocs.data?.users?.get(0)?.value?.additionalInfo)
 | 
			
		||||
        assertNull(response.ocs.data?.users?.get(1)?.value?.additionalInfo)
 | 
			
		||||
        assertEquals(2, response.ocs.data.groups.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data.remotes.size)
 | 
			
		||||
        assertEquals(2, response.ocs.data.users.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data.exact?.groups?.size)
 | 
			
		||||
        assertEquals(0, response.ocs.data.exact?.remotes?.size)
 | 
			
		||||
        assertEquals(1, response.ocs.data.exact?.users?.size)
 | 
			
		||||
        assertEquals("user1@user1.com", response.ocs.data.users.get(0).value.additionalInfo)
 | 
			
		||||
        assertNull(response.ocs.data.users[1].value.additionalInfo)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun `check empty response - ok - parsing ok`() {
 | 
			
		||||
        val response = loadResponses(EMPTY_RESPONSE_JSON)!!
 | 
			
		||||
        assertTrue(response.ocs.data?.exact?.groups?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data?.exact?.remotes?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data?.exact?.users?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data?.groups?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data?.remotes?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data?.users?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data.exact?.groups?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data.exact?.remotes?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data.exact?.users?.isEmpty()!!)
 | 
			
		||||
        assertTrue(response.ocs.data.groups.isEmpty())
 | 
			
		||||
        assertTrue(response.ocs.data.remotes.isEmpty())
 | 
			
		||||
        assertTrue(response.ocs.data.users.isEmpty())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private const val RESOURCES_PATH =
 | 
			
		||||
        val RESOURCES_PATH =
 | 
			
		||||
            "src/test/responses/com.owncloud.android.lib.resources.sharees.responses"
 | 
			
		||||
        const val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json"
 | 
			
		||||
        const val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json"
 | 
			
		||||
        val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json"
 | 
			
		||||
        val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user