Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 1a8e497c authored by Tobias Kaminsky's avatar Tobias Kaminsky Committed by GitHub
Browse files

Merge pull request #1631 from nextcloud/feature/file_download_limits

Support Files Download Limits
parents 4c07e6a6 062bcf33
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -109,6 +109,8 @@ services:
      - su www-data -c "cd /var/www/html/apps/assistant; source ~/.bashrc; composer install --no-dev"
      - su www-data -c "php /var/www/html/occ app:enable -f assistant"
      - su www-data -c "php /var/www/html/occ app:enable -f testing"
      - su www-data -c "git clone --depth 1 https://github.com/nextcloud/files_downloadlimit.git /var/www/html/apps/files_downloadlimit/"
      - su www-data -c "php /var/www/html/occ app:enable -f files_downloadlimit"
      - /usr/local/bin/run.sh

trigger:
@@ -224,6 +226,8 @@ services:
      - su www-data -c "cd /var/www/html/apps/assistant; source ~/.bashrc; composer install --no-dev"
      - su www-data -c "php /var/www/html/occ app:enable assistant"
      - su www-data -c "php /var/www/html/occ app:enable -f testing"
      - su www-data -c "git clone --depth 1 -b $SERVER_VERSION https://github.com/nextcloud/files_downloadlimit.git /var/www/html/apps/files_downloadlimit/"
      - su www-data -c "php /var/www/html/occ app:enable -f files_downloadlimit"
      - /usr/local/bin/run.sh

trigger:
@@ -235,6 +239,6 @@ trigger:
    - pull_request
---
kind: signature
hmac: 3e71a44f6f57a4d4d853c586c0c322bf0b718d96627906b92864e12353e5a014
hmac: fe00fcbb3bf41f6aa84193e380345c3b009ef933d295dda86ea3c959a8373381

...
+115 −0
Original line number Diff line number Diff line
/*
 * Nextcloud Android Library
 *
 * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+zetatom@users.noreply.github.com>
 * SPDX-License-Identifier: MIT
 */

package com.owncloud.android.lib.resources.files

import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation
import com.nextcloud.android.lib.resources.files.RemoveFilesDownloadLimitRemoteOperation
import com.nextcloud.android.lib.resources.files.SetFilesDownloadLimitRemoteOperation
import com.owncloud.android.AbstractIT
import com.owncloud.android.lib.resources.shares.CreateShareRemoteOperation
import com.owncloud.android.lib.resources.shares.OCShare
import com.owncloud.android.lib.resources.shares.ShareType
import com.owncloud.android.lib.resources.status.GetCapabilitiesRemoteOperation
import com.owncloud.android.lib.resources.status.NextcloudVersion
import com.owncloud.android.lib.resources.status.OCCapability
import junit.framework.TestCase.assertEquals
import org.junit.Before
import org.junit.Test

class FilesDownloadLimitIT : AbstractIT() {
    @Before
    fun before() {
        testOnlyOnServer(NextcloudVersion.nextcloud_30)
        assert(getCapability().filesDownloadLimit.isTrue)
    }

    @Test
    fun getDefaultLimit() {
        val defaultLimit = getCapability().filesDownloadLimitDefault
        assertEquals(-1, defaultLimit)
    }

    @Test
    @Suppress("Detekt.MagicNumber")
    fun downloadLimit() {
        createTestFile()

        val shareTokens = mutableListOf<String>()

        DOWNLOAD_LIMITS.forEach { limit ->
            val share = createTestShare()
            shareTokens.add(share.token!!)

            val resultSet = SetFilesDownloadLimitRemoteOperation(share.token!!, limit).execute(nextcloudClient)
            assert(resultSet.isSuccess)

            shortSleep()

            val resultGet = GetFilesDownloadLimitRemoteOperation(REMOTE_PATH, false).execute(client)
            assert(resultGet.isSuccess)
            assertEquals(shareTokens.size, resultGet.resultData.size)

            val downloadLimit =
                resultGet.resultData.first {
                    it.token == share.token
                }

            assertEquals(limit, downloadLimit.limit)
            assertEquals(0, downloadLimit.count)
        }

        shortSleep()

        for (i in shareTokens.lastIndex downTo 0) {
            val token = shareTokens[i]
            val resultRemove = RemoveFilesDownloadLimitRemoteOperation(token).execute(nextcloudClient)
            assert(resultRemove.isSuccess)

            shortSleep()

            val resultGet = GetFilesDownloadLimitRemoteOperation(REMOTE_PATH, false).execute(client)
            assert(resultGet.isSuccess)
            assertEquals(i, resultGet.resultData.size)
        }
    }

    private fun getCapability(): OCCapability =
        GetCapabilitiesRemoteOperation().execute(nextcloudClient).singleData as OCCapability

    private fun createTestFile(): Boolean {
        val localPath = createFile("test")
        val result =
            UploadFileRemoteOperation(localPath, REMOTE_PATH, "text/plain", RANDOM_MTIME)
                .execute(client)
                .isSuccess
        assert(result)
        return result
    }

    private fun createTestShare(): OCShare {
        val result =
            CreateShareRemoteOperation(
                REMOTE_PATH,
                ShareType.PUBLIC_LINK,
                "",
                false,
                "",
                1
            ).execute(client)

        assert(result.isSuccess)
        val share = result.resultData.first()
        assert(share.token != null)
        return share
    }

    companion object {
        private const val REMOTE_PATH = "/downloadLimits.txt"
        private val DOWNLOAD_LIMITS = listOf(5, 10)
    }
}
+16 −0
Original line number Diff line number Diff line
/*
 * Nextcloud Android Library
 *
 * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+zetatom@users.noreply.github.com>
 * SPDX-License-Identifier: MIT
 */

package com.nextcloud.android.lib.resources.files

import java.io.Serializable

data class FileDownloadLimit(
    val token: String,
    val limit: Int,
    val count: Int
) : Serializable
+67 −0
Original line number Diff line number Diff line
/*
 * Nextcloud Android Library
 *
 * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+zetatom@users.noreply.github.com>
 * SPDX-License-Identifier: MIT
 */

package com.nextcloud.android.lib.resources.files

import com.owncloud.android.lib.common.OwnCloudClient
import com.owncloud.android.lib.common.network.WebdavEntry
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.resources.OCSRemoteOperation
import org.apache.commons.httpclient.HttpStatus
import org.apache.jackrabbit.webdav.DavConstants
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet
import org.apache.jackrabbit.webdav.xml.Namespace

class GetFilesDownloadLimitRemoteOperation
    @JvmOverloads
    constructor(
        val remotePath: String,
        val subfiles: Boolean = false
    ) : OCSRemoteOperation<List<FileDownloadLimit>>() {
        @Deprecated("Deprecated in Java")
        override fun run(client: OwnCloudClient): RemoteOperationResult<List<FileDownloadLimit>> {
            val result: RemoteOperationResult<List<FileDownloadLimit>>
            val propSet = DavPropertyNameSet()
            val depth = if (subfiles) DavConstants.DEPTH_1 else DavConstants.DEPTH_0

            propSet.add(
                WebdavEntry.EXTENDED_PROPERTY_FILE_DOWNLOAD_LIMITS,
                Namespace.getNamespace(WebdavEntry.NAMESPACE_NC)
            )

            val propFindMethod = PropFindMethod(client.getFilesDavUri(remotePath), propSet, depth)

            val status = client.executeMethod(propFindMethod)

            if (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) {
                val response = propFindMethod.responseBodyAsMultiStatus

                val fileDownloadLimits =
                    response.responses.flatMap {
                        val webdavEntry = WebdavEntry(it, client.filesDavUri.encodedPath!!)
                        webdavEntry.fileDownloadLimit
                    }

                result = RemoteOperationResult(true, propFindMethod)
                result.resultData = fileDownloadLimits
            } else {
                Log_OC.e(TAG, "Failed to get download limit")
                result = RemoteOperationResult(false, propFindMethod)
                client.exhaustResponse(propFindMethod.responseBodyAsStream)
            }

            propFindMethod.releaseConnection()

            return result
        }

        companion object {
            private val TAG = GetFilesDownloadLimitRemoteOperation::class.java.simpleName
        }
    }
+45 −0
Original line number Diff line number Diff line
/*
 * Nextcloud Android Library
 *
 * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+zetatom@users.noreply.github.com>
 * SPDX-License-Identifier: MIT
 */

package com.nextcloud.android.lib.resources.files

import com.nextcloud.common.NextcloudClient
import com.nextcloud.operations.DeleteMethod
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.lib.resources.OCSRemoteOperation
import org.apache.commons.httpclient.HttpStatus

class RemoveFilesDownloadLimitRemoteOperation(
    val token: String
) : OCSRemoteOperation<Void>() {
    override fun run(client: NextcloudClient): RemoteOperationResult<Void> {
        val result: RemoteOperationResult<Void>

        val url = client.baseUri.toString() + String.format(FILES_DOWNLOAD_LIMIT_ENDPOINT, token) + JSON_FORMAT
        val deleteMethod = DeleteMethod(url, true)

        val status = deleteMethod.execute(client)

        if (status == HttpStatus.SC_OK) {
            result = RemoteOperationResult(true, deleteMethod)
        } else {
            result = RemoteOperationResult(false, deleteMethod)
            Log_OC.e(TAG, "Failed to remove download limit")
            Log_OC.e(TAG, "*** status code: " + status + "; response: " + deleteMethod.getResponseBodyAsString())
        }

        deleteMethod.releaseConnection()

        return result
    }

    companion object {
        private val TAG = RemoveFilesDownloadLimitRemoteOperation::class.java.simpleName
        private const val FILES_DOWNLOAD_LIMIT_ENDPOINT = "/ocs/v2.php/apps/files_downloadlimit/api/v1/%s/limit"
    }
}
Loading