From 3f2abc91a8a0a02c4afab4780ac0c4c6eae02f9d Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Thu, 21 Nov 2024 15:04:09 +0100 Subject: [PATCH] Added capabilities to manage share download limits. - Requesting share download limit capability of files_downloadlimit app. - Augmented WebDAV metadata requests and responses with optional share download limits. - Extended NextcloudKit with methods to manage share download limits via OCS. Signed-off-by: Iva Horn --- .../NextcloudKit/Models/NKDownloadLimit.swift | 33 +++++++ Sources/NextcloudKit/Models/NKFile.swift | 6 ++ .../NextcloudKit/Models/NKProperties.swift | 9 ++ Sources/NextcloudKit/NKDataFileXML.swift | 18 +++- .../NextcloudKit+ShareDownloadLimit.swift | 90 +++++++++++++++++++ 5 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 Sources/NextcloudKit/Models/NKDownloadLimit.swift create mode 100644 Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift diff --git a/Sources/NextcloudKit/Models/NKDownloadLimit.swift b/Sources/NextcloudKit/Models/NKDownloadLimit.swift new file mode 100644 index 0000000..b3722a4 --- /dev/null +++ b/Sources/NextcloudKit/Models/NKDownloadLimit.swift @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2024 Iva Horn +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation + +/// +/// Data model for a download limit as returned in the WebDAV response for file properties. +/// +/// Each relates to a share of a file and is optionally provided by the [Files Download Limit](https://github.com/nextcloud/files_downloadlimit) app for Nextcloud server. +/// +public class NKDownloadLimit: NSObject { + /// + /// The number of downloads which already happened. + /// + public let count: Int + + /// + /// Total number of allowed downloas. + /// + public let limit: Int + + /// + /// The token identifying the related share. + /// + public let token: String + + init(count: Int, limit: Int, token: String) { + self.count = count + self.limit = limit + self.token = token + } +} diff --git a/Sources/NextcloudKit/Models/NKFile.swift b/Sources/NextcloudKit/Models/NKFile.swift index 1da7b9d..dde57ee 100644 --- a/Sources/NextcloudKit/Models/NKFile.swift +++ b/Sources/NextcloudKit/Models/NKFile.swift @@ -16,6 +16,12 @@ public class NKFile: NSObject { public var date = Date() public var directory: Bool = false public var downloadURL = "" + + /// + /// Download limits for shares of this file. + /// + public var downloadLimits = [NKDownloadLimit]() + public var e2eEncrypted: Bool = false public var etag = "" public var favorite: Bool = false diff --git a/Sources/NextcloudKit/Models/NKProperties.swift b/Sources/NextcloudKit/Models/NKProperties.swift index 929d447..f5482f5 100644 --- a/Sources/NextcloudKit/Models/NKProperties.swift +++ b/Sources/NextcloudKit/Models/NKProperties.swift @@ -3,9 +3,18 @@ // SPDX-FileCopyrightText: 2023 Claudio Cambra // SPDX-License-Identifier: GPL-3.0-or-later +/// +/// Definition of properties used for decoding in ``NKDataFileXML``. +/// public enum NKProperties: String, CaseIterable { /// DAV case displayname = "" + + /// + /// Download limits for shares of a file as optionally provided by the [Files Download Limit](https://github.com/nextcloud/files_downloadlimit) app for Nextcloud server. + /// + case downloadLimit = "" + case getlastmodified = "" case getetag = "" case getcontenttype = "" diff --git a/Sources/NextcloudKit/NKDataFileXML.swift b/Sources/NextcloudKit/NKDataFileXML.swift index 2225fce..459109e 100644 --- a/Sources/NextcloudKit/NKDataFileXML.swift +++ b/Sources/NextcloudKit/NKDataFileXML.swift @@ -560,7 +560,23 @@ class NKDataFileXML: NSObject { } file.placePhotos = propstat["d:prop", "nc:metadata-photos-place"].text - + + for downloadLimit in propstat["d:prop", "nc:share-download-limits", "nc:share-download-limit"] { + guard let token = downloadLimit["nc:token"].text else { + continue + } + + guard let limit = downloadLimit["nc:limit"].int else { + continue + } + + guard let count = downloadLimit["nc:count"].int else { + continue + } + + file.downloadLimits.append(NKDownloadLimit(count: count, limit: limit, token: token)) + } + let results = self.nkCommonInstance.getInternalType(fileName: file.fileName, mimeType: file.contentType, directory: file.directory, account: nkSession.account) file.contentType = results.mimeType diff --git a/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift b/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift new file mode 100644 index 0000000..448e86e --- /dev/null +++ b/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2024 Iva Horn +// SPDX-License-Identifier: GPL-3.0-or-later + +import Alamofire +import Foundation + +public extension NextcloudKit { + private func makeEndpoint(with token: String) -> String { + "ocs/v2.php/apps/files_downloadlimit/api/v1/\(token)/limit" + } + + func removeShareDownloadLimit(account: String, token: String, completion: @escaping (_ error: NKError) -> Void) { + let endpoint = makeEndpoint(with: token) + let options = NKRequestOptions() + + guard let nkSession = nkCommonInstance.getSession(account: account), + let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), + let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { + return options.queue.async { + completion(.urlError) + } + } + + nkSession + .sessionData + .request(url, method: .delete, parameters: nil, encoding: URLEncoding.default, headers: headers, interceptor: nil) + .validate(statusCode: 200..<300) + .response(queue: self.nkCommonInstance.backgroundQueue) { response in + if self.nkCommonInstance.levelLog > 0 { + debugPrint(response) + } + + switch response.result { + case .failure(let error): + let error = NKError(error: error, afResponse: response, responseData: response.data) + + options.queue.async { + completion(error) + } + case .success: + options.queue.async { + completion(.success) + } + } + } + } + + func setShareDownloadLimit(account: String, token: String, limit: Int, completion: @escaping (_ error: NKError) -> Void) { + let endpoint = makeEndpoint(with: token) + let options = NKRequestOptions() + options.contentType = "application/json" + + guard let nkSession = nkCommonInstance.getSession(account: account), + let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), + let headers = nkCommonInstance.getStandardHeaders(account: account, options: options), + var urlRequest = try? URLRequest(url: url, method: .put, headers: headers) else { + return options.queue.async { + completion(.urlError) + } + } + + urlRequest.httpBody = try? JSONEncoder().encode([ + "limit": limit + ]) + + nkSession + .sessionData + .request(urlRequest) + .validate(statusCode: 200..<300) + .response(queue: self.nkCommonInstance.backgroundQueue) { response in + if self.nkCommonInstance.levelLog > 0 { + debugPrint(response) + } + + switch response.result { + case .failure(let error): + let error = NKError(error: error, afResponse: response, responseData: response.data) + + options.queue.async { + completion(error) + } + case .success: + options.queue.async { + completion(.success) + } + } + } + } +}