Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ buildscript {
junit_version = '4.13.2'
}
dependencies {
classpath 'com.android.tools.build:gradle:9.4.0'
classpath 'com.android.tools.build:gradle:9.4.1'
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.5.11'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Nextcloud Android Library
*
* SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-FileCopyrightText: 2023 Tobias Kaminsky <tobias@kaminsky.me>
* SPDX-License-Identifier: MIT
*/
Expand All @@ -10,14 +11,24 @@ package com.owncloud.android.lib.resources.files.webdav
import com.owncloud.android.AbstractIT
import com.owncloud.android.lib.common.network.WebdavEntry
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.resources.files.ChunkedFileUploadRemoteOperation
import com.owncloud.android.lib.resources.files.FileUtils
import junit.framework.TestCase
import junit.framework.TestCase.assertNotNull
import org.apache.commons.httpclient.HttpStatus
import org.apache.commons.httpclient.methods.PutMethod
import org.apache.commons.httpclient.methods.StringRequestEntity
import org.apache.jackrabbit.webdav.DavConstants
import org.apache.jackrabbit.webdav.client.methods.DeleteMethod
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.io.File
import java.net.URLEncoder

class ChunkedFileUploadRemoteOperationIT : AbstractIT() {
@Test
Expand Down Expand Up @@ -120,6 +131,66 @@ class ChunkedFileUploadRemoteOperationIT : AbstractIT() {
assert(File(filePath).length() == getRemoteSize(remotePath))
}

@Test
fun uploadExceedingQuotaIsRejectedBeforeAnyChunkIsStored() {
val filePath = createFile("chunkedFile.txt", BIG_FILE_ITERATION)
val uploadFolderUri = "${client.uploadUri}/${client.userId}/${FileUtils.md5Sum(File(filePath))}"
val sut =
ChunkedFileUploadRemoteOperation(
filePath,
"/bigFile.md",
"text/markdown",
"",
RANDOM_MTIME,
System.currentTimeMillis() / MILLI_TO_SECOND,
false,
true
)

setQuota(QUOTA_SMALLER_THAN_FILE)
try {
val uploadResult = sut.execute(client)

assertEquals(ResultCode.QUOTA_EXCEEDED, uploadResult.code)
assertEquals(0, storedChunkCount(uploadFolderUri))
} finally {
setQuota(QUOTA_UNLIMITED)
DeleteMethod(uploadFolderUri).also {
client.executeMethod(it)
it.releaseConnection()
}
}
}

private fun setQuota(quota: String) {
val method = PutMethod("${client2.baseUri}/ocs/v2.php/cloud/users/${client.userId}")
method.addRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE)
method.requestEntity =
StringRequestEntity(
"key=quota&value=${URLEncoder.encode(quota, Charsets.UTF_8.name())}",
"application/x-www-form-urlencoded",
Charsets.UTF_8.name()
)

try {
assertEquals(HttpStatus.SC_OK, client2.executeMethod(method))
} finally {
method.releaseConnection()
}
}

private fun storedChunkCount(uploadFolderUri: String): Int {
val propFind = PropFindMethod(uploadFolderUri, WebdavUtils.getChunksPropSet(), DavConstants.DEPTH_1)

try {
client.executeMethod(propFind)
assertTrue(propFind.succeeded())
return propFind.responseBodyAsMultiStatus.responses.size - UPLOAD_FOLDER_ENTRY
} finally {
propFind.releaseConnection()
}
}

private fun genLargeUpload(onWifiConnection: Boolean): ChunkedFileUploadRemoteOperation {
// create file
val filePath = createFile("chunkedFile.txt", BIG_FILE_ITERATION)
Expand Down Expand Up @@ -149,5 +220,8 @@ class ChunkedFileUploadRemoteOperationIT : AbstractIT() {
companion object {
const val BIG_FILE_ITERATION = 1000000
const val MAX_TRIES = 30
const val QUOTA_SMALLER_THAN_FILE = "1 MB"
const val QUOTA_UNLIMITED = "none"
const val UPLOAD_FOLDER_ENTRY = 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ class ChunkedFileUploadRemoteOperation

val put = createPutMethod("$uploadFolderUri/$chunkName")
put.addRequestHeader(DESTINATION_HEADER, destinationUri)
put.addRequestHeader(OC_TOTAL_LENGTH_HEADER, file.length().toString())
token?.let { put.addRequestHeader(E2E_TOKEN, it) }

val status = client.executeMethod(put)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
*/

public class UploadFileRemoteOperation extends RemoteOperation<String> {
private static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length";
protected static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length";
private static final String IF_MATCH_HEADER = "If-Match";
protected static final String RESULT_ETAG_HEADER = "etag";
protected static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime";
Expand Down
2 changes: 1 addition & 1 deletion sample_client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:9.4.0'
classpath 'com.android.tools.build:gradle:9.4.1'
classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.5.11'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.8"
Expand Down
Loading